Skip to content

Commit e115a21

Browse files
author
MarcoFalke
committed
Merge #16223: devtools: Fetch and display ACKs at sign-off time in github-merge
0e01e45 devtools: Fetch and display ACKs at sign-off time in github-merge (Wladimir J. van der Laan) Pull request description: - Fetch the ACKs only at sign-off time. This makes sure that any last-minute ACKs are included (fixes #16200) - Show a list of ACKs that will be included and their author before signing off, and warn if there are none ![1](https://user-images.githubusercontent.com/126646/59605250-ad070980-910e-11e9-9f9a-d789c7f06ebb.png) ![2](https://user-images.githubusercontent.com/126646/59605255-b1332700-910e-11e9-80a5-d1e244f48264.png) There's a slight change to the merge commit format—before it was ``` ACKs for commit 88884c: (list of ACKs, could be empty) ``` now it is ``` ACKs for top commit: jnewbery: ACK 5ebc6b0 ... (list of ACKs cannot be empty) ``` or ``` Top commit has no ACKs. ``` I don't think there's a reason to have the abbreviated commit ID there, after all the full commit id is already in the beginning of the merge commit message, and at least the abbreviated one is in every single ACK message. ACKs for commit 0e01e4: fanquake: ACK 0e01e45 Tree-SHA512: 8576de016137d71cfc101747e9bb6779c13e0953cf2babee7afc9972bf2bd46f6912be4982b54fa5abf4d91e98e8fdae6b4ca3eef7d6892b7a5f04a7017b6882
2 parents 44e849c + 0e01e45 commit e115a21

File tree

1 file changed

+49
-19
lines changed

1 file changed

+49
-19
lines changed

contrib/devtools/github-merge.py

Lines changed: 49 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,14 @@
3232
# OS specific configuration for terminal attributes
3333
ATTR_RESET = ''
3434
ATTR_PR = ''
35+
ATTR_NAME = ''
36+
ATTR_WARN = ''
3537
COMMIT_FORMAT = '%H %s (%an)%d'
3638
if os.name == 'posix': # if posix, assume we can use basic terminal escapes
3739
ATTR_RESET = '\033[0m'
3840
ATTR_PR = '\033[1;36m'
41+
ATTR_NAME = '\033[0;36m'
42+
ATTR_WARN = '\033[1;31m'
3943
COMMIT_FORMAT = '%C(bold blue)%H%Creset %s %C(cyan)(%an)%Creset%C(green)%d%Creset'
4044

4145
def git_config_get(option, default=None):
@@ -164,18 +168,36 @@ def tree_sha512sum(commit='HEAD'):
164168
return overall.hexdigest()
165169

166170
def get_acks_from_comments(head_commit, comments):
167-
assert len(head_commit) == 6
168-
ack_str ='\n\nACKs for commit {}:\n'.format(head_commit)
171+
# Look for abbreviated commit id, because not everyone wants to type/paste
172+
# the whole thing and the chance of collisions within a PR is small enough
173+
head_abbrev = head_commit[0:6]
174+
acks = []
169175
for c in comments:
170-
review = [l for l in c['body'].split('\r\n') if 'ACK' in l and head_commit in l]
176+
review = [l for l in c['body'].split('\r\n') if 'ACK' in l and head_abbrev in l]
171177
if review:
172-
ack_str += ' {}:\n'.format(c['user']['login'])
173-
ack_str += ' {}\n'.format(review[0])
178+
acks.append((c['user']['login'], review[0]))
179+
return acks
180+
181+
def make_acks_message(head_commit, acks):
182+
if acks:
183+
ack_str ='\n\nACKs for top commit:\n'.format(head_commit)
184+
for name, msg in acks:
185+
ack_str += ' {}:\n'.format(name)
186+
ack_str += ' {}\n'.format(msg)
187+
else:
188+
ack_str ='\n\nTop commit has no ACKs.\n'
174189
return ack_str
175190

176-
def print_merge_details(pull, title, branch, base_branch, head_branch):
191+
def print_merge_details(pull, title, branch, base_branch, head_branch, acks):
177192
print('%s#%s%s %s %sinto %s%s' % (ATTR_RESET+ATTR_PR,pull,ATTR_RESET,title,ATTR_RESET+ATTR_PR,branch,ATTR_RESET))
178193
subprocess.check_call([GIT,'log','--graph','--topo-order','--pretty=format:'+COMMIT_FORMAT,base_branch+'..'+head_branch])
194+
if acks is not None:
195+
if acks:
196+
print('{}ACKs:{}'.format(ATTR_PR, ATTR_RESET))
197+
for (name, message) in acks:
198+
print('* {} {}({}){}'.format(message, ATTR_NAME, name, ATTR_RESET))
199+
else:
200+
print('{}Top commit has no ACKs!{}'.format(ATTR_WARN, ATTR_RESET))
179201

180202
def parse_arguments():
181203
epilog = '''
@@ -225,9 +247,6 @@ def main():
225247
info = retrieve_pr_info(repo,pull,ghtoken)
226248
if info is None:
227249
sys.exit(1)
228-
comments = retrieve_pr_comments(repo,pull,ghtoken) + retrieve_pr_reviews(repo,pull,ghtoken)
229-
if comments is None:
230-
sys.exit(1)
231250
title = info['title'].strip()
232251
body = info['body'].strip()
233252
# precedence order for destination branch argument:
@@ -257,6 +276,8 @@ def main():
257276
sys.exit(3)
258277
try:
259278
subprocess.check_call([GIT,'log','-q','-1','refs/heads/'+head_branch], stdout=devnull, stderr=stdout)
279+
head_commit = subprocess.check_output([GIT,'log','-1','--pretty=format:%H',head_branch]).decode('utf-8')
280+
assert len(head_commit) == 40
260281
except subprocess.CalledProcessError:
261282
print("ERROR: Cannot find head of pull request #%s on %s." % (pull,host_repo), file=stderr)
262283
sys.exit(3)
@@ -281,7 +302,6 @@ def main():
281302
message = firstline + '\n\n'
282303
message += subprocess.check_output([GIT,'log','--no-merges','--topo-order','--pretty=format:%H %s (%an)',base_branch+'..'+head_branch]).decode('utf-8')
283304
message += '\n\nPull request description:\n\n ' + body.replace('\n', '\n ') + '\n'
284-
message += get_acks_from_comments(head_commit=subprocess.check_output([GIT,'log','-1','--pretty=format:%H',head_branch]).decode('utf-8')[:6], comments=comments)
285305
try:
286306
subprocess.check_call([GIT,'merge','-q','--commit','--no-edit','--no-ff','--no-gpg-sign','-m',message.encode('utf-8'),head_branch])
287307
except subprocess.CalledProcessError:
@@ -299,20 +319,14 @@ def main():
299319
if len(symlink_files) > 0:
300320
sys.exit(4)
301321

302-
# Put tree SHA512 into the message
322+
# Compute SHA512 of git tree (to be able to detect changes before sign-off)
303323
try:
304324
first_sha512 = tree_sha512sum()
305-
message += '\n\nTree-SHA512: ' + first_sha512
306325
except subprocess.CalledProcessError:
307326
print("ERROR: Unable to compute tree hash")
308327
sys.exit(4)
309-
try:
310-
subprocess.check_call([GIT,'commit','--amend','--no-gpg-sign','-m',message.encode('utf-8')])
311-
except subprocess.CalledProcessError:
312-
print("ERROR: Cannot update message.", file=stderr)
313-
sys.exit(4)
314328

315-
print_merge_details(pull, title, branch, base_branch, head_branch)
329+
print_merge_details(pull, title, branch, base_branch, head_branch, None)
316330
print()
317331

318332
# Run test command if configured.
@@ -345,8 +359,24 @@ def main():
345359
print("ERROR: Tree hash changed unexpectedly",file=stderr)
346360
sys.exit(8)
347361

362+
# Retrieve PR comments and ACKs and add to commit message, store ACKs to print them with commit
363+
# description
364+
comments = retrieve_pr_comments(repo,pull,ghtoken) + retrieve_pr_reviews(repo,pull,ghtoken)
365+
if comments is None:
366+
print("ERROR: Could not fetch PR comments and reviews",file=stderr)
367+
sys.exit(1)
368+
acks = get_acks_from_comments(head_commit=head_commit, comments=comments)
369+
message += make_acks_message(head_commit=head_commit, acks=acks)
370+
# end message with SHA512 tree hash, then update message
371+
message += '\n\nTree-SHA512: ' + first_sha512
372+
try:
373+
subprocess.check_call([GIT,'commit','--amend','--no-gpg-sign','-m',message.encode('utf-8')])
374+
except subprocess.CalledProcessError:
375+
print("ERROR: Cannot update message.", file=stderr)
376+
sys.exit(4)
377+
348378
# Sign the merge commit.
349-
print_merge_details(pull, title, branch, base_branch, head_branch)
379+
print_merge_details(pull, title, branch, base_branch, head_branch, acks)
350380
while True:
351381
reply = ask_prompt("Type 's' to sign off on the above merge, or 'x' to reject and exit.").lower()
352382
if reply == 's':

0 commit comments

Comments
 (0)