26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
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
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
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
162
163
164
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
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
|
# File 'lib/git_pr/merge.rb', line 26
def self.merge_pull_cleanly git, pull, command_options
unless command_options.yolo
case pull.state
when 'failure'
failed_statuses = pull.statuses.select { |s| s.state == 'failure' }
max_context = failed_statuses.map { |s| s.context.length }.max
puts <<EOS
#{"ERROR".red}: One or more status checks have failed on this pull request!
You should fix these before merging.
EOS
failed_statuses.each do |status|
puts "#{GitPr::PullRequest.summary_icon(status.state)} #{status.context.ljust(max_context)} #{status.target_url}"
end
puts <<EOS
If you're still sure you want to merge, run 'git pr merge --yolo #{pull.number}'
EOS
exit 1
when 'success'
nil
else
unless pull.statuses.empty?
pending_statuses = pull.statuses.select { |s| s.state == 'pending' }
max_context = pending_statuses.map { |s| s.context.length }.max
puts <<EOS
#{"WARNING".yellow}: One or more status checks is in progress on this pull request.
You should let them finish.
EOS
pending_statuses.each do |status|
puts "#{GitPr::PullRequest.summary_icon(status.state)} #{status.context.ljust(max_context)} #{status.target_url}"
end
puts <<EOS
If you're still sure you want to merge, run 'git pr merge --yolo #{pull.number}'
EOS
exit 1
end
end
end
pull_number = pull.number
source_branch = pull.head.ref
source_repo_ssh_url = pull.head.repo.git_url
source_repo_clone_url = pull.head.repo.clone_url
target_branch = pull.base.ref
target_repo_ssh_url = pull.base.repo.git_url
target_repo_clone_url = pull.base.repo.clone_url
puts "Merging #{pull.summary}".cyan
puts "#{target_repo_ssh_url}/#{target_branch} <= #{source_repo_ssh_url}/#{source_branch}\n".cyan
source_remote, target_remote = self.ensure_remotes_for_pull_request git, pull
puts "Fetching latest changes from '#{source_remote}'"
source_remote.fetch
unless target_remote.name == source_remote.name
puts "Fetching latest changes from '#{target_remote}'"
target_remote.fetch
end
current_branch = `git rev-parse --abbrev-ref HEAD`.strip!
at_exit do
if git.branches[current_branch]
GitPr.run_command "git checkout -q #{current_branch}"
end
end
puts "Update branch '#{target_branch}' from remote"
GitPr.run_command "git checkout -q #{target_branch}"
GitPr.run_command "git pull --no-rebase --ff-only", :failure => lambda {
"Unable to update local target branch '#{target_branch}'. Please repair manually before continuing.".red
}
remote_target_branch = "#{target_remote}/#{target_branch}"
unless GitPr.are_branches_identical? git, "remotes/#{remote_target_branch}", target_branch
puts "Local branch '#{target_branch}' differs from remote branch '#{remote_target_branch}'. Please reconcile before continuing.".red
exit -1
end
remote_source_branch = "#{source_remote}/#{source_branch}"
if git.is_local_branch? source_branch and
not GitPr.are_branches_identical? git, "remotes/#{remote_source_branch}", source_branch
puts "Local branch '#{source_branch}' differs from remote branch '#{remote_source_branch}'. Please reconcile before continuing.".red
exit -1
end
rebase_branch = "#{source_branch}-rebase"
puts "Create temporary branch '#{rebase_branch}'"
if git.is_local_branch? rebase_branch
puts "Local rebase branch '#{rebase_branch}' already exists. Please remove before continuing.".red
GitPr.run_command "git checkout -q #{current_branch}"
exit -1
end
GitPr.run_command "git checkout -q -b #{rebase_branch} #{remote_source_branch}"
at_exit do
if git.is_local_branch? rebase_branch
puts "Removing temporary branch #{rebase_branch}" if $verbose
GitPr.run_command "git checkout -q #{target_branch}"
GitPr.run_command "git branch -D #{rebase_branch}"
end
end
puts "Rebasing '#{rebase_branch}' on top of '#{target_branch}'"
GitPr.run_command "git rebase #{target_branch} 2>&1", :failure => lambda {
GitPr.run_command "git rebase --abort"
puts "Unable to automatically rebase #{remote_source_branch} on top of #{target_branch}. Rebase manually and push before trying again.".red
puts "Run: " + "git checkout #{source_branch}".yellow
puts " " + "git rebase #{target_branch}".yellow + " and fix up any conflicts."
puts " " + "git push --force-with-lease".yellow
}
puts "Pushing changes from '#{rebase_branch}' to '#{source_remote.name}/#{source_branch}'"
GitPr.run_command "git push --force-with-lease #{source_remote.name} HEAD:#{source_branch} 2>&1"
puts "Merging changes from '#{rebase_branch}' to '#{target_branch}'"
GitPr.run_command "git checkout -q #{target_branch}"
commit_message = <<EOS
Merge #{pull.summary}
#{pull.body}
EOS
GitPr.run_command "git merge --no-ff #{rebase_branch} -m #{Shellwords.escape commit_message}"
puts "\nVerify that the merge looks clean:\n".cyan
origin_parent = `git rev-list --abbrev-commit --parents -n 1 #{target_remote}/#{target_branch}`.split().last
GitPr.run_command "git log --graph --decorate --pretty=oneline --abbrev-commit --color #{target_branch} #{origin_parent}..#{target_branch}", :force_print_output => true
if GitPr.prompt "\nDo you want to proceed with the merge (y/n)? ".cyan
puts "Pushing changes to '#{target_remote}'"
GitPr.run_command "git push #{target_remote} #{target_branch} 2>&1"
source_branch_sha = git.branches["#{source_remote}/#{source_branch}"].gcommit.sha[0..6]
GitPr.run_command "git push #{source_remote} :#{source_branch} 2>&1"
if git.is_local_branch? source_branch
source_branch_sha = git.branches[source_branch].gcommit.sha[0..6]
GitPr.run_command "git branch -D #{source_branch}"
end
puts "Feature branch '#{source_branch}' deleted. To restore it, run: " + "git branch #{source_branch} #{source_branch_sha}".green
puts "\nMerge complete!".cyan
else
puts "\nUndoing local merge"
GitPr.run_command "git reset --hard #{target_remote}/#{target_branch}"
end
end
|