diff options
author | Guido Günther <agx@sigxcpu.org> | 2011-03-11 18:08:58 +0100 |
---|---|---|
committer | Guido Günther <agx@sigxcpu.org> | 2011-03-12 23:12:22 +0100 |
commit | 40f4709668425b03b9f59c28176547c313957715 (patch) | |
tree | 166980187712abd948005dce6b348899956a0a8b /gbp-pq | |
parent | 9bb73b46d2f215dc70482e55311a64debeca4d6e (diff) |
gbp-pq: Allow to specify subdirs for patches
via the "Gbp-Pq-Tag: <subdir>" directive in the patch header. This also
gets rid sed callouts.
Add "apply" action to apply single patches
Diffstat (limited to 'gbp-pq')
-rwxr-xr-x | gbp-pq | 222 |
1 files changed, 180 insertions, 42 deletions
@@ -1,7 +1,7 @@ #!/usr/bin/python -u # vim: set fileencoding=utf-8 : # -# (C) 2011 Guido Guenther <agx@sigxcpu.org> +# (C) 2011 Guido Günther <agx@sigxcpu.org> # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or @@ -33,7 +33,7 @@ import gbp.log PQ_BRANCH_PREFIX = "patch-queue/" PATCH_DIR = "debian/patches/" -SERIES_FILE = PATCH_DIR + "series" +SERIES_FILE = os.path.join(PATCH_DIR,"series") def is_pq_branch(branch): return [False, True][branch.startswith(PQ_BRANCH_PREFIX)] @@ -43,32 +43,68 @@ def pq_branch_name(branch): if not is_pq_branch(branch): return PQ_BRANCH_PREFIX + branch + def pq_branch_base(pq_branch): """get the branch corresponding to the given patch queue branch""" if is_pq_branch(pq_branch): return pq_branch[len(PQ_BRANCH_PREFIX):] +def write_patch(patch, options): + """Write the patch exported by 'git-format-patch' to it's final location + (as specified in the commit)""" + oldname = patch[len(PATCH_DIR):] + newname = oldname + tmpname = patch + ".gbp" + old = file(patch, 'r') + tmp = file(tmpname, 'w') + in_patch = False + topic = None + + # Skip first line (from <sha1>) + old.readline() + for line in old: + if in_patch: + if line.startswith('-- '): + # Found final signature, we're done: + tmp.write(line) + break + else: + if line.lower().startswith("gbp-pq-topic: "): + topic = line.split(" ",1)[1].strip() + gbp.log.debug("Topic %s found for %s" % (topic, patch)) + continue + elif (line.startswith("diff --git a/") or + line.startswith("---")): + in_patch = True + tmp.write(line) + + tmp.close() + old.close() -def write_patch(patch, options) - # delete the first line (from sha1) and last two lines (git version - # info) of the patch file - Command("sed -i -e '1d' -e 'N;$!P;$!D;$d' %s" % patch, shell=True)() - Command("sed -i -e 's/^-- \\n[0-9\.]+$//' %s" % patch, shell=True)() - - name = patch[len(PATCH_DIR):] if not options.patch_numbers: - m = patch_re.match(name) + patch_re = re.compile("[0-9]+-(?P<name>.+)") + m = patch_re.match(oldname) if m: - filename = m.group('name') - shutil.move(patch, os.path.join(PATCH_DIR, filename)) - else: - filename = name - return filename + newname = m.group('name') + + if topic: + topicdir = os.path.join(PATCH_DIR, topic) + else: + topicdir = PATCH_DIR + if not os.path.isdir(topicdir): + os.makedirs(topicdir, 0755) -def export_patches(repo, branch, options): - patch_re = re.compile("[0-9]+-(?P<name>.+)") + os.unlink(patch) + dstname = os.path.join(topicdir, newname) + gbp.log.debug("Moving %s to %s" % (tmpname, dstname)) + shutil.move(tmpname, dstname) + + return dstname + +def export_patches(repo, branch, options): + """Export patches from the pq branch into a patch series""" if is_pq_branch(branch): base = pq_branch_base(branch) gbp.log.info("On '%s', switching to '%s'" % (branch, base)) @@ -90,21 +126,29 @@ def export_patches(repo, branch, options): gbp.log.info("Regenerating patch queue in '%s'." % PATCH_DIR) for patch in patches: filename = write_patch(patch, options) - f.write(filename + '\n') + f.write(filename[len(PATCH_DIR):] + '\n') + f.close() GitCommand('status')(['--', PATCH_DIR]) else: gbp.log.info("No patches on '%s' - nothing to do." % pq_branch) -def get_maintainer(): +def get_maintainer_from_control(): + """Get the maintainer from the control file""" cmd = 'sed -n -e \"s/Maintainer: \\+\\(.*\\)/\\1/p\" debian/control' maintainer = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE).stdout.readlines()[0].strip() - gbp.log.debug("Maintainer: %s" % maintainer) - return maintainer + + m = re.match('(?P<name>.*[^ ]) *<(?P<email>.*)>', maintainer) + if m: + return m.group('name'), m.group('email') + else: + return None, None -def import_patches(repo, branch): +def import_quilt_patches(repo, branch, series): + """apply a series of quilt patches in the series file 'series' to branch + the patch-queue branch for 'branch'""" if is_pq_branch(branch): gbp.log.err("Already on a patch-queue branch '%s' - doing nothing." % branch) raise GbpError @@ -118,14 +162,98 @@ def import_patches(repo, branch): % pq_branch) repo.set_branch(pq_branch) - if not os.path.exists(SERIES_FILE): + if not os.path.exists(series): gbp.log.info("Found no series file at '%s'. No patches to add to patch-queue branch." - % SERIES_FILE) + % series) + return + else: + patch_dir = os.path.dirname(series) + + try: + s = file(series) + except Exception, err: + raise GbpError("Cannot open series file: %s" % err) + + for patch in s: + patch = patch[:-1] + topic = os.path.dirname(patch) + if topic == '' or topic == '/': + topic = None + apply_and_commit_patch(repo, os.path.join(patch_dir, patch), topic) + s.close() + + +def get_mailinfo(patch): + """Read patch information into a structured form""" + + info = {} + body = os.path.join('.git', 'gbp_patchinfo') + pipe = subprocess.Popen("git mailinfo % s /dev/null < %s" % (body, patch), + shell=True, stdout=subprocess.PIPE).stdout + for line in pipe: + if ':' in line: + rfc_header, value = line.split(" ",1) + header = rfc_header[:-1].lower() + info[header] = value.strip() + + f = file(body) + commit_msg = "".join([ line for line in f ]) + f.close() + os.unlink(body) + + return info, commit_msg + + +def switch_to_pq_branch(repo, branch): + """Switch to patch-queue branch if not already there, create it if it + doesn't exist yet""" + if is_pq_branch (branch): return - maintainer = get_maintainer() - RunAtCommand('git', ['quiltimport', '--author', maintainer], - extra_env={'QUILT_PATCHES': PATCH_DIR})() + pq_branch = pq_branch_name(branch) + if not repo.has_branch(pq_branch): + try: + GitBranch()(pq_branch) + except CommandExecFailed: + raise GbpError, ("Cannot create patch-queue branch '%s'. Try 'rebase' instead." + % pq_branch) + + gbp.log.info("Switching to '%s'" % pq_branch) + repo.set_branch(pq_branch) + + +def apply_single_patch(repo, branch, patch, topic=None): + switch_to_pq_branch(repo, branch) + apply_and_commit_patch(repo, patch, topic) + + +def apply_and_commit_patch(repo, patch, topic=None): + """apply a single patch 'patch', add topic 'topic' and commit it""" + header, body = get_mailinfo(patch) + + if not header.has_key('subject'): + header['subject'] = os.path.basename(patch) + + if header.has_key('author') and header.has_key('email'): + header['name'] = header['author'] + else: + name, email = get_maintainer_from_control() + if name: + gbp.log.warn("Patch '%s' has no authorship information, using '%s <%s>'" + % (patch, name, email)) + header['name'] = name + header['email'] = email + else: + gbp.log.warn("Patch %s has no authorship information") + + head = repo.rev_parse('HEAD') + repo.apply_patch(patch) + tree = repo.write_tree() + msg = "%s\n\n%s" % (header['subject'], body) + if topic: + msg += "\nGbp-Pq-Topic: %s" % topic + commit = repo.commit_tree(tree, msg, [head], author=header) + repo.update_ref('HEAD', commit, msg="gbp-pq import %s" % patch) def drop_pq(repo, branch): @@ -137,19 +265,13 @@ def drop_pq(repo, branch): if repo.has_branch(pq_branch): repo.delete_branch(pq_branch) - gbp.log.info("Dropped branch %s." % pq_branch) + gbp.log.info("Dropped branch '%s'." % pq_branch) else: gbp.log.info("No patch queue branch found - doing nothing.") def rebase_pq(repo, branch): - if not is_pq_branch (branch): - pq_branch = pq_branch_name(branch) - gbp.log.info("Switching to '%s'" % pq_branch) - repo.set_branch(pq_branch) - else: - gbp.log.info("Already on '%s'" % branch) - + switch_to_pq_branch(repo, branch) GitCommand("rebase")([branch]) @@ -165,25 +287,35 @@ def main(argv): " import create a patch queue branch from quilt patches in debian/patches.\n" " rebase switch to patch queue branch associated to the current\n" " branch and rebase against current branch.\n" - " drop drop (delete) the patch queue associated to the current branch.") + " drop drop (delete) the patch queue associated to the current branch.\n" + " apply apply a patch\n") parser.add_boolean_config_file_option(option_name="patch-numbers", dest="patch_numbers") parser.add_option("-v", "--verbose", action="store_true", dest="verbose", default=False, help="verbose command execution") + parser.add_option("--topic", dest="topic", help="in case of 'apply' topic (subdir) to put patch into") parser.add_config_file_option(option_name="color", dest="color", type='tristate') - (options, args) = parser.parse_args(argv) gbp.log.setup(options.color, options.verbose) - if len(args) != 2: + if len(args) < 2: gbp.log.err("No action given.") return 1 - elif args[1] not in ["export", "import", "rebase", "drop"]: - gbp.log.err("Unknown action '%s'." % args[1]) - return 1 else: action = args[1] + if args[1] in ["export", "import", "rebase", "drop"]: + pass + elif args[1] in ["apply"]: + if len(args) != 3: + gbp.log.err("No patch name given.") + return 1 + else: + patch = args[2] + else: + gbp.log.err("Unknown action '%s'." % args[1]) + return 1 + try: repo = GitRepository(os.path.curdir) except GitRepositoryError: @@ -195,11 +327,17 @@ def main(argv): if action == "export": export_patches(repo, current, options) elif action == "import": - import_patches(repo, current) + series = SERIES_FILE + import_quilt_patches(repo, current, series) + current = repo.get_branch() + gbp.log.info("Patches listed in '%s' imported on '%s'" % + (series, current)) elif action == "drop": drop_pq(repo, current) elif action == "rebase": rebase_pq(repo, current) + elif action == "apply": + apply_single_patch(repo, current, patch, options.topic) except CommandExecFailed: retval = 1 except GbpError, err: |