Class: GitReview

Inherits:
Object
  • Object
show all
Defined in:
lib/git-review.rb

Instance Method Summary collapse

Instance Method Details

#approveObject

Add an approving comment to the request.



107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
# File 'lib/git-review.rb', line 107

def approve
  return unless request_exists?
  comment = 'Reviewed and approved.'
  response = Octokit.add_comment source_repo, @current_request['number'], comment
  if response[:message] == 'Issues are disabled for this repo'
    # Workaround: Add a pull request comment to the last commit's first file's first line.
    comment += "\n\nNOTE:\n  Issues are disabled on this repository.\n  Comments created through API calls may only be inline.\n  So I chose to just post here. :P"
    last_commit = repo_call(:get, "pulls/#{@current_request['number']}/commits").last.sha
    first_file = repo_call(:get, "pulls/#{@current_request['number']}/files").first.filename
    response = repo_call(:post, "pulls/#{@current_request['number']}/comments",
      {:body => comment, :commit_id => last_commit, :path => first_file, :position => 1}
    )
  end
  if response[:body] == comment
    puts 'Successfully approved request.'
  else
    puts response[:message]
  end
end

#browseObject

Open a browser window and review a specified request.



62
63
64
# File 'lib/git-review.rb', line 62

def browse
  Launchy.open(@current_request['html_url']) if request_exists?
end

#checkoutObject

Checkout a specified request’s changes to your local repository.



67
68
69
70
71
72
73
74
75
76
# File 'lib/git-review.rb', line 67

def checkout
  return unless request_exists?
  create_local_branch = @args.shift == '--branch' ? '' : 'origin/'
  puts 'Checking out changes to your local repository.'
  puts 'To get back to your original state, just run:'
  puts
  puts '  git checkout master'
  puts
  git_call "checkout #{create_local_branch}#{@current_request['head']['ref']}"
end

#cleanObject

Deletes obsolete branches (left over from already closed requests).



196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
# File 'lib/git-review.rb', line 196

def clean
  # Pruning is needed to remove already deleted branches from your local track.
  git_call "remote prune origin"
  # Determine strategy to clean.
  case @args.size
    when 0
      puts 'Argument missing. Please provide either an ID or the option "--all".'
    when 1
      if @args.first == '--all'
        # git review clean --all
        clean_all
      else
        # git review clean ID
        clean_single
      end
    when 2
      # git review clean ID --force
      clean_single(@args.last == '--force')
    else
      puts 'Too many arguments.'
  end
end

#closeObject

Close a specified request.



128
129
130
131
132
# File 'lib/git-review.rb', line 128

def close
  return unless request_exists?
  Octokit.close_issue(source_repo, @current_request['number'])
  puts 'Successfully closed request.' unless request_exists?('open', @current_request['number'])
end

#consoleObject

Start a console session (used for debugging).



220
221
222
223
224
225
226
227
# File 'lib/git-review.rb', line 220

def console
  puts 'Entering debug console.'
  request_exists?
  require 'ruby-debug'
  Debugger.start
  debugger
  puts 'Leaving debug console.'
end

#createObject

Create a new request. TODO: Support creating requests to other repositories and branches (like the original repo, this has been forked from).



165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
# File 'lib/git-review.rb', line 165

def create
  # Prepare @local_branch.
  prepare
  # Don't create request with uncommitted changes in current branch.
  unless git_call('diff HEAD').empty?
    puts 'You have uncommitted changes. Please stash or commit before creating the request.'
    return
  end
  unless git_call("cherry #{target_branch}").empty?
    # Push latest commits to the remote branch (and by that, create it if necessary).
    git_call "push --set-upstream origin #{@local_branch}", debug_mode, true
    # Gather information.
    last_request_id = @current_requests.collect{|req| req['number'] }.sort.last.to_i
    title = "[Review] Request from '#{git_config['github.login']}' @ '#{source}'"
    # TODO: Insert commit messages (that are not yet in master) into body (since this will be displayed inside the mail that is sent out).
    body = 'Please review the following changes:'
    # Create the actual pull request.
    Octokit.create_pull_request(target_repo, target_branch, source_branch, title, body)
    # Switch back to target_branch and check for success.
    git_call "checkout #{target_branch}"
    update
    potential_new_request = @current_requests.find{ |req| req['title'] == title }
    if potential_new_request and potential_new_request['number'] > last_request_id
      puts "Successfully created new request ##{potential_new_request['number']}."
    end
  else
    puts 'Nothing to push to remote yet. Commit something first.'
  end
end

#listObject

List all pending requests.



18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# File 'lib/git-review.rb', line 18

def list
  @current_requests.reverse! if @args.shift == '--reverse'
  output = @current_requests.collect do |pending_request|
    # Find only pending (= unmerged) requests and output summary. GitHub might
    # still think of them as pending, as it doesn't know about local changes.
    next if merged?(pending_request['head']['sha'])
    line = format_text(pending_request['number'], 8)
    date_string = format_time(pending_request['updated_at'])
    line << format_text(date_string, 11)
    line << format_text(pending_request['comments'], 10)
    line << format_text(pending_request['title'], 91)
    line
  end
  if output.compact.empty?
    puts "No pending requests for '#{source}'"
    return
  end
  puts "Pending requests for '#{source}'"
  puts 'ID      Updated    Comments  Title'
  puts output.compact
end

#mergeObject

Accept a specified request by merging it into master.



79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
# File 'lib/git-review.rb', line 79

def merge
  return unless request_exists?
  option = @args.shift
  unless @current_request['head']['repository']
    # Someone deleted the source repo.
    user = @current_request['head']['user']['login']
    url = @current_request['patch_url']
    puts "Sorry, #{user} deleted the source repository, git-review doesn't support this."
    puts 'You can manually patch your repo by running:'
    puts
    puts "  curl #{url} | git am"
    puts
    puts 'Tell the contributor not to do this.'
    return false
  end
  message = "Accept request ##{@current_request['number']} and merge changes into \"#{target}\""
  exec_cmd = "merge #{option} -m '#{message}' #{@current_request['head']['sha']}"
  puts
  puts 'Request title:'
  puts "  #{@current_request['title']}"
  puts
  puts 'Merge command:'
  puts "  git #{exec_cmd}"
  puts
  puts git_call(exec_cmd)
end

#prepareObject

Prepare local repository to create a new request. Sets @local_branch.



136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
# File 'lib/git-review.rb', line 136

def prepare
  # People should work on local branches, but especially for single commit changes,
  # more often than not, they don't. Therefore we create a branch for them,
  # to be able to use code review the way it is intended.
  if source_branch == target_branch
    # Unless a branch name is already provided, ask for one.
    if (branch_name = @args.shift).nil?
      puts 'Please provide a name for the branch:'
      branch_name = gets.chomp.gsub(/\W+/, '_').downcase
    end
    # Create the new branch (as a copy of the current one).
    @local_branch = "review_#{Time.now.strftime("%y%m%d")}_#{branch_name}"
    git_call "checkout -b #{@local_branch}"
    if source_branch == @local_branch
      # Stash any uncommitted changes.
      git_call('stash') if (save_uncommitted_changes = !git_call('diff HEAD').empty?)
      # Go back to master and get rid of pending commits (as these are now on the new branch).
      git_call "checkout #{target_branch}"
      git_call "reset --hard origin/#{target_branch}"
      git_call "checkout #{@local_branch}"
      git_call('stash pop') if save_uncommitted_changes
    end
  else
    @local_branch = source_branch
  end
end

#showObject

Show details for a single request.



41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
# File 'lib/git-review.rb', line 41

def show
  return unless request_exists?
  option = @args.shift == '--full' ? '' : '--stat '
  sha = @current_request['head']['sha']
  puts "ID        : #{@current_request['number']}"
  puts "Label     : #{@current_request['head']['label']}"
  puts "Updated   : #{format_time(@current_request['updated_at'])}"
  puts "Comments  : #{@current_request['comments']}"
  puts
  puts @current_request['title']
  puts
  puts @current_request['body']
  puts
  puts git_call("diff --color=always #{option}HEAD...#{sha}")
  puts
  puts "Progress  :"
  puts
  discussion
end