From fde186b65504c3cada82f84ac9763acf0fed128a Mon Sep 17 00:00:00 2001 From: "Owen W. Taylor" Date: Sun, 16 Nov 2008 11:59:29 -0500 Subject: Add add-url subcommand and -u/--add-url command line options git bz add-url: new subcommand to rewrite old commit messages to include a bug URL. git bz attach/file: -u/--add-url option rewrites commit messages as with 'add-url' before attaching patches to bugzilla. git bz apply: -u/--add-url option rewrites commit messages after applying patches to the current tree. --- TODO | 21 +++------ git-bz | 161 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 158 insertions(+), 24 deletions(-) diff --git a/TODO b/TODO index 2fc20fa..010c89c 100644 --- a/TODO +++ b/TODO @@ -6,20 +6,6 @@ any intention of working on it myself. - Owen - --u/--add-url Option - - When specified, local commits are edited to append the Bug URL. - - attach: Before attaching them to the bug - - Requires: clean index and a series of bugs that leads to the - current HEAD commit. - Can't be the default because commits might have already been pushed - Should be smart if the URL is already in the bug - Might be useful to have a standalone 'add-url' subcommand that can - be used to fix up if you forget to specify it. - Use XML-RPC when available. Maybe use python-bugzilla: http://fedorahosted.org/python-bugzilla/ @@ -45,3 +31,10 @@ More general patch application as well. For general patches, you would use information from bugzilla to prime the author and commit message, but allow further editing of the commit message. + +Make -u/--add-url kinder on the reflog + + -u works by resetting, then on each patch, running git cherry-pick + followed by git commit --amend. It would be nice to only have one + (informative) reflog entry for the entire process, or at least avoid + the double commits. diff --git a/git-bz b/git-bz index 5057b93..7dafee5 100755 --- a/git-bz +++ b/git-bz @@ -30,6 +30,18 @@ # Usage # ===== # +# git bz add-url [-] [options] [] +# +# For each specified commit, rewrite the commit message to add the URL +# of the given bug. You should only do this if you haven't already pushed +# the commit publically. Running this directly is most useful as a fixup if +# you forget to pass -u/--add-url to 'git bz attach' or 'git bz file'. +# +# Example: +# +# # Add a bug URL to the last commit +# git bz attach 1234 HEAD^ +# # git bz apply [options] # # For each patch attachment (except for obsolete patches) of the specified @@ -37,13 +49,17 @@ # the patch to apply it to the current branch. Aborts if 'git am' fails to # allow cleaning up conflicts. # -# Example: +# Examples: # +# # Apply patches from the given bug # git bz apply bugzillla.gnome.org:1234 # +# # Same, but add the bug URL to the commit messages +# git bz apply -u bugzillla.gnome.org:1234 +# # git bz attach [-] [options] [] # -# For each commit or commits, formats as a patch and attaches to the +# For each specified commit, formats as a patch and attaches to the # specified bug, with the subject of the commit as the description and # the body of the commit as the comment. The patch formatting and and # specification of which commits are as for 'git format-patch' @@ -58,8 +74,9 @@ # # Attach everything starting at an old commit # git bz attach bugzilla.gnome.org:1234 b50ea9bd^ # -# # Attach a single old commit -# git bz attach bugzilla.gnome.org:1234 b50ea9bd -1 +# # Attach a single old commit and rewrite the commit message +# # to include the bug URL. (See 'git bz add-url') +# git bz attach -u bugzilla.gnome.org:1234 b50ea9bd -1 # # git bz file [-] [options] / [ | ] # @@ -73,6 +90,10 @@ # # File the last commit as a new bug on the default tracker # git bz file my-product/some-component HEAD^ # +# # Same but rewrite the commit message to include the URL of the +# # newly filed bug. (See 'git bz add-url') +# git bz file -u my-product/some-component HEAD^ +# # # File a bug with a series of patches starting from an old commit # # on a different bug tracker # git bz -b bugs.freedesktop.org file my-product/some-component b50ea9bd^ -1 @@ -173,6 +194,7 @@ import subprocess import sys import tempfile import time +import traceback import urllib from xml.etree.cElementTree import ElementTree @@ -529,7 +551,7 @@ class Bug(object): print "Successfully created" print "Bug %d - %s" % (self.id, short_desc) - print "http://%s/show_bug.cgi?id=%d" % (self.host, self.id) + print self.get_url() def create_patch(self, description, comment, filename, data): fields = {} @@ -562,6 +584,9 @@ class Bug(object): return response.read() + def get_url(self): + return "%s://%s/show_bug.cgi?id=%d" % ("https" if self.https else "http", self.host, self.id) + @staticmethod def load(bug_reference): (host, https, id) = resolve_bug_reference(bug_reference) @@ -585,6 +610,92 @@ class Bug(object): # The Commands # ============= +def check_add_url(commits): + try: + global_repo.git.diff(exit_code=True) + global_repo.git.diff(exit_code=True, cached=True) + except git.errors.GitCommandError: + die("You must commit (or stash) all changes before using -u/--add-url") + + # We should check that all the commits are ancestors of the current + # current revision, and maybe also check make sure that there are no + # merge commits. + +def add_url(bug, commits): + oldest_commit = commits[-1] + + newer_commits = git.Commit.find_all(global_repo, commits[0].id + "..HEAD") + + head_id = newer_commits[0].id if newer_commits else oldest_commit.id + + try: + print "Resetting to the parent revision" + global_repo.git.reset(oldest_commit.id + "^", hard=True) + + for commit in reversed(commits): + body = get_body(commit) + + if str(bug.id) in body: + print "Recommitting", commit.id[0:7], commit.message, "(already has bug #)" + global_repo.git.cherry_pick(commit.id) + # Find the new commit ID, though it doesn't matter much here + commit.id = global_repo.git.rev_list("HEAD^!") + continue + + print "Adding URL ", commit.id[0:7], commit.message + global_repo.git.cherry_pick(commit.id) + + process = subprocess.Popen(['git', 'commit', '--file=-', '--amend'], + stdin=subprocess.PIPE, stdout=subprocess.PIPE) + process.stdin.write(commit.message) + process.stdin.write("\n\n") + process.stdin.write(body) + process.stdin.write("\n\n") + process.stdin.write(bug.get_url()) + process.stdin.close() + # Discard output + process.stdout.read() + process.stdout.close() + process.wait() + if process.returncode != 0: + raise RuntimeException("git commit --amend failed") + + # In this case, we need the new commit ID, so that when we later format the + # patch, we format the patch with the added bug URL + commit.id = global_repo.git.rev_list("HEAD^!") + + for commit in reversed(newer_commits): + print "Recommitting", commit.id[0:7], commit.message + global_repo.git.cherry_pick(commit.id) + commit.id = global_repo.git.rev_list("HEAD^!") + except: + traceback.print_exc(None, sys.stderr) + print >>sys.stderr + print >>sys.stderr, "Something went wrong rewriting commmits to add URLs" + print >>sys.stderr, "To restore to the original state: git reset --hard %s" % head_id[0:12] + sys.exit(1) + +def do_add_url(bug_reference, since_or_revision_range): + commits = get_commits(since_or_revision_range) + check_add_url(commits) + + bug = Bug.load(bug_reference) + + print "Bug %d - %s" % (bug.id, bug.short_desc) + print bug.get_url() + print + + for commit in commits: + print commit.id[0:7], commit.message + + print + if not prompt("Add bug URL to above commits?"): + print "Aborting" + sys.exit(0) + + print + add_url(bug, commits) + def do_apply(bug_reference): bug = Bug.load(bug_reference) @@ -612,6 +723,11 @@ def do_apply(bug_reference): os.remove(filename) + if global_options.add_url: + # Slightly hacky, would be better to just commit right the first time + commits = git.Commit.find_all(global_repo, "HEAD^!") + add_url(bug, commits) + def attach_commits(bug, commits, include_comments=True): # We want to attach the patches in chronological order commits = list(commits) @@ -627,12 +743,15 @@ def attach_commits(bug, commits, include_comments=True): bug.create_patch(commit.message, body, filename, patch) def do_attach(bug_reference, since_or_revision_range): + commits = get_commits(since_or_revision_range) + if global_options.add_url: + check_add_url(commits) + bug = Bug.load(bug_reference) print "Bug %d - %s" % (bug.id, bug.short_desc) print - commits = get_commits(since_or_revision_range) for commit in commits: print commit.id[0:7], commit.message @@ -641,6 +760,9 @@ def do_attach(bug_reference, since_or_revision_range): print "Aborting" sys.exit(0) + if global_options.add_url: + add_url(bug, commits) + attach_commits(bug, commits) def do_file(product_component, since_or_revision_range): @@ -652,6 +774,9 @@ def do_file(product_component, since_or_revision_range): commits = get_commits(since_or_revision_range) + if global_options.add_url: + check_add_url(commits) + template = StringIO() if len(commits) == 1: template.write(commits[0].message) @@ -700,6 +825,9 @@ def do_file(product_component, since_or_revision_range): bug = Bug.create(tracker, product, component, summary, description) + if global_options.add_url: + add_url(bug, commits) + attach_commits(bug, commits, include_comments=(len(commits) > 1)) ################################################################################ @@ -721,21 +849,32 @@ def add_num_option(): if m: sys.argv[i] = "--num=" + m.group(1) -if command == 'apply': +def add_add_url_option(): + parser.add_option("-u", "--add-url", action="store_true", + help="rewrite commits to add the bug URL") + +if command == 'add-url': + parser.set_usage("git bz add-url [-] [options] []"); + add_num_option() + n_args = 2 +elif command == 'apply': parser.set_usage("git bz apply [options] "); + add_add_url_option() n_args = 1 elif command == 'attach': parser.set_usage("git bz attach [-] [options] []"); + add_add_url_option() add_num_option() n_args = 2 elif command == 'file': parser.set_usage("git bz file [-] [options] / [ | ]"); parser.add_option("-b", "--bugzilla", metavar="HOST_OR_ALIAS", help="bug tracker to file bug on") + add_add_url_option() add_num_option() n_args = 2 else: - print >>sys.stderr, "Usage: git bz [apply|attach|file] [options]" + print >>sys.stderr, "Usage: git bz [add-url|apply|attach|file] [options]" sys.exit(1) global_options, args = parser.parse_args() @@ -746,9 +885,11 @@ if len(args) != n_args: global_repo = git.Repo() -if command == 'apply': +if command == 'add-url': + do_add_url(*args) +elif command == 'apply': do_apply(*args) -if command == 'attach': +elif command == 'attach': do_attach(*args) elif command == 'file': do_file(*args) -- cgit v1.2.3