#!/usr/bin/python -u # vim: set fileencoding=utf-8 : # # (C) 2011 Guido Guenther # 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 # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # """manage patches in a patch queue""" import errno import re import os import shutil import subprocess import sys from gbp.config import (GbpOptionParser, GbpOptionGroup) from gbp.git import (GitRepositoryError, GitRepository) from gbp.command_wrappers import (Command, GitCommand, RunAtCommand, GitBranch, CommandExecFailed) from gbp.errors import GbpError import gbp.log PQ_BRANCH_PREFIX = "patch-queue/" PATCH_DIR = "debian/patches/" SERIES_FILE = PATCH_DIR + "series" def is_pq_branch(branch): return [False, True][branch.startswith(PQ_BRANCH_PREFIX)] def pq_branch_name(branch): """get the patch queue branch corresponding to 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 export_patches(repo, branch, options): patch_re = re.compile("[0-9]+-(?P.+)") if is_pq_branch(branch): base = pq_branch_base(branch) gbp.log.info("On '%s', switching to '%s'" % (branch, base)) branch = base repo.set_branch(branch) pq_branch = pq_branch_name(branch) try: shutil.rmtree(PATCH_DIR) except OSError, (e, msg): if e != errno.ENOENT: raise GbpError, "Failed to remove patch dir: %s" % msg else: gbp.log.debug("%s does not exist." % PATCH_DIR) patches = repo.format_patches(branch, pq_branch, PATCH_DIR) if patches: f = file(SERIES_FILE, 'w') gbp.log.info("Regenerating patch queue in '%s'." % PATCH_DIR) for patch in patches: # 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) if m: name = m.group('name') shutil.move(patch, os.path.join(PATCH_DIR, name)) f.write(name + '\n') f.close() GitCommand('status')(['--', PATCH_DIR]) else: gbp.log.info("No patches on '%s' - nothing to do." % pq_branch) def get_maintainer(): 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 def import_patches(repo, branch): if is_pq_branch(branch): gbp.log.err("Already on a patch-queue branch '%s' - doing nothing." % branch) raise GbpError else: pq_branch = pq_branch_name(branch) try: GitBranch()(pq_branch) except CommandExecFailed: raise GbpError, ("Cannot create patch-queue branch '%s'. Try 'rebase' instead." % pq_branch) repo.set_branch(pq_branch) if not os.path.exists(SERIES_FILE): gbp.log.info("Found no series file at '%s'. No patches to add to patch-queue branch." % SERIES_FILE) return maintainer = get_maintainer() RunAtCommand('git', ['quiltimport', '--author', maintainer], extra_env={'QUILT_PATCHES': PATCH_DIR})() def drop_pq(repo, branch): if is_pq_branch(branch): gbp.log.err("On a patch-queue branch, can't drop it.") raise GbpError else: pq_branch = pq_branch_name(branch) if repo.has_branch(pq_branch): repo.delete_branch(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) GitCommand("rebase")([branch]) def main(argv): retval = 0 parser = GbpOptionParser(command=os.path.basename(argv[0]), prefix='', usage="%prog [options] action - maintain patches on a patch queue branch\n" "Actions:\n" " export export the patch queue associated to the current branch\n" " into a quilt patch series in debian/patches/ and update the\n" " series file.\n" " 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.") 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_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: 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] try: repo = GitRepository(os.path.curdir) except GitRepositoryError: gbp.log.err("%s is not a git repository" % (os.path.abspath('.'))) return 1 try: current = repo.get_branch() if action == "export": export_patches(repo, current, options) elif action == "import": import_patches(repo, current) elif action == "drop": drop_pq(repo, current) elif action == "rebase": rebase_pq(repo, current) except CommandExecFailed: retval = 1 except GbpError, err: if len(err.__str__()): gbp.log.err(err) retval = 1 return retval if __name__ == '__main__': sys.exit(main(sys.argv))