summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDan Winship <danw@gnome.org>2011-07-09 10:56:30 -0400
committerOwen W. Taylor <otaylor@fishsoup.net>2012-03-01 13:25:53 -0500
commitce11237046a1af9728f7d0544c6aae8eb1d5c7d3 (patch)
tree7e867a995230ad0aaf8b33301f550287249229ef
parente422eea9999a02262d3bad996879378ce1824541 (diff)
apply: add --continue/--skip/--abort
Rather than failing completely when git-am fails to apply a patch, write out our current state to git-am's temporary directory, and then tell the user to use "git bz apply --continue/--skip/--abort" after handling the merge failure. When the user uses one of those options, read back our state, pass the flag on to git-am, and (assuming git-am succeeds), continue with the next patch. https://bugzilla.gnome.org/show_bug.cgi?id=657558
-rwxr-xr-xgit-bz161
-rw-r--r--git-bz.txt12
2 files changed, 128 insertions, 45 deletions
diff --git a/git-bz b/git-bz
index 150598c..f9e12da 100755
--- a/git-bz
+++ b/git-bz
@@ -82,7 +82,7 @@ import base64
import cPickle as pickle
from ConfigParser import RawConfigParser, NoOptionError
import httplib
-from optparse import OptionParser
+import optparse
import os
try:
from sqlite3 import dbapi2 as sqlite
@@ -1538,56 +1538,105 @@ def do_add_url(bug_reference, commit_or_revision_range):
print
add_url(bug, commits)
-def do_apply(bug_reference):
- bug = Bug.load(BugHandle.parse_or_die(bug_reference),
+resolvemsg = '''When you have resolved this problem run "git bz apply --continue".
+If you would prefer to skip this patch, instead run "git bz apply --skip".
+To restore the original branch and stop patching run "git bz apply --abort".'''
+
+def do_apply(*args):
+ git_dir = git.rev_parse(git_dir=True)
+ resuming = global_options.resolved or global_options.skip or global_options.abort
+
+ if len(args) == 0:
+ if not resuming:
+ die(parser.get_usage())
+
+ if global_options.resolved:
+ arg = "--resolved"
+ elif global_options.skip:
+ arg = "--skip"
+ elif global_options.abort:
+ arg = "--abort"
+
+ try:
+ f = open(git_dir + "/rebase-apply/git-bz", "r")
+ lines = f.read().rstrip().split('\n')
+ bug_ref = lines[0]
+ orig_head = lines[1]
+ patch_ids = map(int, lines[2:])
+ f.close()
+ except:
+ die("Not inside a 'git bz apply' operation")
+
+ try:
+ process = git.am(arg, resolvemsg=resolvemsg, _interactive=True)
+ except CalledProcessError:
+ sys.exit(1)
+
+ if global_options.abort:
+ sys.exit(0)
+
+ else:
+ if resuming:
+ die(parser.get_usage())
+
+ bug_ref = args[0]
+ orig_head = git.rev_parse("HEAD")
+
+ bug = Bug.load(BugHandle.parse_or_die(bug_ref),
attachmentdata=True)
if len(bug.patches) == 0:
die("No patches on bug %d" % bug.id)
patches = []
patches_by_id = {}
-
- print "Bug %d - %s" % (bug.id, bug.short_desc)
- print
-
for patch in bug.patches:
- if patch.status == 'committed' or patch.status == 'rejected':
- print "%d (skipping, %s) - %s" % (patch.attach_id, patch.status, patch.description)
- else:
- patches.append(patch)
+ patches_by_id[patch.attach_id] = patch
- for patch in patches:
- print "%d - %s" % (patch.attach_id, patch.description)
- print
- opt = prompt_multi("Apply? [(y)es, (n)o, (i)nteractive]", ["y", "n", "i"])
+ if resuming:
+ for pid in patch_ids:
+ patches.append(patches_by_id[pid])
+ else:
+ print "Bug %d - %s" % (bug.id, bug.short_desc)
+ print
- if opt == "n":
- return
- elif opt == "i":
- template = StringIO()
- template.write("# Bug %d - %s\n\n" % (bug.id, bug.short_desc))
for patch in bug.patches:
- patches_by_id[patch.attach_id] = patch
if patch.status == 'committed' or patch.status == 'rejected':
- template.write("#%d - %s (%s)\n" % (patch.attach_id, patch.description, patch.status))
+ print "%d (skipping, %s) - %s" % (patch.attach_id, patch.status, patch.description)
else:
- template.write("%d - %s\n" % (patch.attach_id, patch.description))
- template.write("\n")
- template.write("""# Uncommented patches will be applied in the order they appear.
+ patches.append(patch)
+
+ for patch in patches:
+ print "%d - %s" % (patch.attach_id, patch.description)
+ print
+ opt = prompt_multi("Apply? [(y)es, (n)o, (i)nteractive]", ["y", "n", "i"])
+
+ if opt == "n":
+ return
+ elif opt == "i":
+ template = StringIO()
+ template.write("# Bug %d - %s\n\n" % (bug.id, bug.short_desc))
+ for patch in bug.patches:
+ patches_by_id[patch.attach_id] = patch
+ if patch.status == 'committed' or patch.status == 'rejected':
+ template.write("#%d - %s (%s)\n" % (patch.attach_id, patch.description, patch.status))
+ else:
+ template.write("%d - %s\n" % (patch.attach_id, patch.description))
+ template.write("\n")
+ template.write("""# Uncommented patches will be applied in the order they appear.
# Lines starting with '#' will be ignored. Delete everything to abort.
""")
- lines = edit_template(template.getvalue())
- patches = []
- for line in lines:
- match = re.match('^(\d+)', line)
- if match:
- pid = int(match.group(1))
- if not patches_by_id.has_key(pid):
- die("Unknown attachment id " + pid)
- patches.append(patches_by_id[pid])
-
- if len(patches) == 0:
+ lines = edit_template(template.getvalue())
+ patches = []
+ for line in lines:
+ match = re.match('^(\d+)', line)
+ if match:
+ pid = int(match.group(1))
+ if not patches_by_id.has_key(pid):
+ die("Unknown attachment id " + pid)
+ patches.append(patches_by_id[pid])
+
+ if len(patches) == 0 and not resuming:
die("No patches to apply, aborting")
for patch in patches:
@@ -1597,17 +1646,30 @@ def do_apply(bug_reference):
f.close()
try:
- process = git.am("-3", filename, _interactive=True)
+ process = git.am("-3", filename, resolvemsg=resolvemsg,
+ _interactive=True)
except CalledProcessError:
+ if os.access(git_dir + "/rebase-apply", os.F_OK):
+ # git-am saved its state for an abort or continue,
+ # so save our state too
+ f = open(git_dir + "/rebase-apply/git-bz", "w")
+ f.write("%s\n" % bug_ref)
+ f.write("%s\n" % orig_head)
+ for i in range(patches.index(patch) + 1, len(patches)):
+ f.write("%s\n" % patches[i].attach_id)
+ f.close()
print "Patch left in %s" % filename
- break
+ return
os.remove(filename)
- if global_options.add_url:
- # Slightly hacky, would be better to just commit right the first time
- commits = rev_list_commits("HEAD^!")
- add_url(bug, commits)
+ if global_options.add_url:
+ # Slightly hacky. We could add the URLs as we go by using
+ # git-mailinfo to parse each patch, calling
+ # add_url_to_subject_body(), and then reassembling. That would
+ # be much more complicated though.
+ commits = rev_list_commits(orig_head + "..")
+ add_url(bug, commits)
def strip_bug_url(bug, commit_body):
# Strip off the trailing bug URLs we add with -u; we do this before
@@ -2219,7 +2281,7 @@ else:
sys.argv[1:2] = []
-parser = OptionParser()
+parser = optparse.OptionParser()
parser.add_option("-b", "--bugzilla", metavar="<host or alias>",
help="bug tracker to use")
@@ -2242,8 +2304,19 @@ if command == 'add-url':
min_args = max_args = 2
elif command == 'apply':
parser.set_usage("git bz apply [options] <bug reference>");
+ # git am accepts either --continue or --resolved, so we do too. Call
+ # it "resolved" in the options object, since "continue" is reserved
+ parser.add_option("", "--continue", action="store_true", dest="resolved",
+ help="continue applying a patch set after a failure")
+ parser.add_option("", "--resolved", action="store_true",
+ help=optparse.SUPPRESS_HELP)
+ parser.add_option("", "--skip", action="store_true",
+ help="skip the current patch after a failure")
+ parser.add_option("", "--abort", action="store_true",
+ help="abort the current patch set and revert to original state")
add_add_url_options()
- min_args = max_args = 1
+ min_args = 0
+ max_args = 1
elif command == 'attach':
parser.set_usage("git bz attach [options] [<bug reference>] (<commit> | <revision range>)");
add_add_url_options()
diff --git a/git-bz.txt b/git-bz.txt
index 0dcf7f7..2fa298f 100644
--- a/git-bz.txt
+++ b/git-bz.txt
@@ -10,6 +10,7 @@ SYNOPSIS
[verse]
'git bz add-url' <bug reference> (<commit> | <revision range>)
'git bz apply' [-n | --no-add-url] <bug reference>
+'git bz apply' (--continue | --skip | --abort)
'git bz attach' [-n | --no-add-url] [-e |--edit] [<bug reference>] (<commit> | <revision range>)
'git bz components' [<product>]
'git bz edit' (<bug reference> | <commit> | <revision range>)
@@ -132,7 +133,9 @@ section <<add-url-method, ``Add URL Method''>> below for how to change this.
apply
~~~~~
+[verse]
'git bz apply' [-n | --no-add-url] <bug reference>
+'git bz apply' (--continue | --skip | --abort)
Lists all "pending" patches on the specified bug (ie, the patches that
are not obsolete, committed, or rejected), and then prompts whether to
@@ -140,7 +143,14 @@ apply them. In addition to simply accepting or rejecting the list of
patches, you can also type "i" to interactively choose which patches
to apply, and in what order, as with 'git rebase -i'. If any patches
are selected, it runs 'git am' on each one to apply it to the current
-branch. Aborts if 'git am' fails, to allow cleaning up conflicts.
+branch.
+
+If a 'git am' operation fails, 'git bz apply' will save its state and
+then exit, at which point you can attempt to apply the patch by hand
+and then resume with 'git bz apply --continue'; skip this patch but
+continue applying the remaining patches with 'git bz apply --skip'; or
+abort the operation and return to the original tree state with 'git bz
+apply --abort'.
Examples: