Source: git-buildpackage
Version: 0.6.22
Severity: wishlist

Dear Maintainer,

I attach a pretty trivial merge of the command gbp pq-rpm from the
git-buildpackage-rpm tree. I basically included the script itself and
added two patches it depended on. Those two patches are intended to
make the patch queue branch name more configurable.

If those patches are not acceptable, I'll try rewriting the script to
avoid them.

-- 
Tzafrir Cohen         | tzaf...@jabber.org | VIM is
http://tzafrir.org.il |                    | a Mutt's
tzaf...@cohens.org.il |                    |  best
tzaf...@debian.org    |                    | friend
>From 4da187b8880d3a27c29bec7163feb4a499a95fa6 Mon Sep 17 00:00:00 2001
From: Markus Lehtonen <markus.lehto...@linux.intel.com>
Date: Fri, 6 Sep 2013 14:23:03 +0300
Subject: [PATCH 2/3] common/pq: support more flexible pq-branch naming

Now any string fields (e.g. '%(version)s') can be used in the pq-branch
format strings - in addition to the '%(branch)s' supported before.

Signed-off-by: Markus Lehtonen <markus.lehto...@linux.intel.com>
---
 gbp/scripts/common/pq.py | 67 +++++++++++++++++++++++++++++++-----------------
 1 file changed, 43 insertions(+), 24 deletions(-)

diff --git a/gbp/scripts/common/pq.py b/gbp/scripts/common/pq.py
index 9bc04e3..096469d 100644
--- a/gbp/scripts/common/pq.py
+++ b/gbp/scripts/common/pq.py
@@ -35,6 +35,22 @@ import gbp.log
 DEFAULT_PQ_BRANCH_NAME = "patch-queue/%(branch)s"
 
 
+def pq_branch_match(branch, pq_fmt_str):
+    """
+    Match branch name with pq branch name pattern
+
+    >>> pq_branch_match('patch-queue/foo', 'patch-queue/%(br)s').groupdict()
+    {'br': 'foo'}
+    >>> pq_branch_match('pq/foo/bar', 'pq/%(br)s/baz')
+    >>> pq_branch_match('pq/foo/bar', 'pq/%(br)s/bar').groupdict()
+    {'br': 'foo'}
+    >>> pq_branch_match('foo/bar/1.0/pq', 'foo/%(br)s/%(ver)s/pq').groupdict()
+    {'ver': '1.0', 'br': 'bar'}
+    """
+    pq_re = '^%s$' % re.sub('%\(([a-z_\-]+)\)s', r'(?P<\1>\S+)', pq_fmt_str)
+    return  re.match(pq_re, branch)
+
+
 def is_pq_branch(branch, options):
     """
     is branch a patch-queue branch?
@@ -58,18 +74,20 @@ def is_pq_branch(branch, options):
     False
     >>> is_pq_branch("my/foo/pq", opts)
     True
+    >>> opts.pq_branch = "my/%(branch)s/%(version)s"
+    >>> is_pq_branch("my/foo", opts)
+    False
+    >>> is_pq_branch("my/foo/1.0", opts)
+    True
     """
-    pq_format_str = DEFAULT_PQ_BRANCH_NAME
-    if hasattr(options, 'pq_branch'):
-        pq_format_str = options.pq_branch
-
-    pq_re = re.compile(r'^%s$' % (pq_format_str % dict(branch="(?P<base>\S+)")))
-    if pq_re.match(branch):
+    pq_format_str = (options.pq_branch if hasattr(options, 'pq_branch')
+                                       else DEFAULT_PQ_BRANCH_NAME)
+    if pq_branch_match(branch, pq_format_str):
         return True
     return False
 
 
-def pq_branch_name(branch, options):
+def pq_branch_name(branch, options, extra_keys=None):
     """
     get the patch queue branch corresponding to branch
 
@@ -84,13 +102,17 @@ def pq_branch_name(branch, options):
     >>> opts.pq_branch = "development"
     >>> pq_branch_name("foo", opts)
     'development'
+    >>> opts.pq_branch = "pq/%(branch)s/%(ver)s"
+    >>> pq_branch_name("foo", opts, {'ver': '1.0'})
+    'pq/foo/1.0'
     """
-    pq_format_str = DEFAULT_PQ_BRANCH_NAME
-    if hasattr(options, 'pq_branch'):
-        pq_format_str = options.pq_branch
-
+    pq_format_str = (options.pq_branch if hasattr(options, 'pq_branch')
+                                       else DEFAULT_PQ_BRANCH_NAME)
+    format_fields = {'branch': branch}
+    if extra_keys:
+        format_fields.update(extra_keys)
     if not is_pq_branch(branch, options):
-        return pq_format_str % dict(branch=branch)
+        return pq_format_str % format_fields
 
 
 def pq_branch_base(pq_branch, options):
@@ -115,15 +137,12 @@ def pq_branch_base(pq_branch, options):
     >>> pq_branch_base("development", opts)
     'packaging'
     """
-    pq_format_str = DEFAULT_PQ_BRANCH_NAME
-    if hasattr(options, 'pq_branch'):
-        pq_format_str = options.pq_branch
-
-    pq_re = re.compile(r'^%s$' % (pq_format_str % dict(branch="(?P<base>\S+)")))
-    m = pq_re.match(pq_branch)
+    pq_format_str = (options.pq_branch if hasattr(options, 'pq_branch')
+                                       else DEFAULT_PQ_BRANCH_NAME)
+    m = pq_branch_match(pq_branch, pq_format_str)
     if m:
-        if 'base' in m.groupdict():
-            return m.group('base')
+        if 'branch' in m.groupdict():
+            return m.group('branch')
         return options.packaging_branch
 
 
@@ -298,7 +317,7 @@ def get_maintainer_from_control(repo):
     return GitModifier()
 
 
-def switch_to_pq_branch(repo, branch, options):
+def switch_to_pq_branch(repo, branch, options, name_keys=None):
     """
     Switch to patch-queue branch if not already there, create it if it
     doesn't exist yet
@@ -306,7 +325,7 @@ def switch_to_pq_branch(repo, branch, options):
     if is_pq_branch(branch, options):
         return
 
-    pq_branch = pq_branch_name(branch, options)
+    pq_branch = pq_branch_name(branch, options, name_keys)
     if not repo.has_branch(pq_branch):
         try:
             repo.create_branch(pq_branch)
@@ -349,12 +368,12 @@ def apply_and_commit_patch(repo, patch, fallback_author, topic=None):
     repo.update_ref('HEAD', commit, msg="gbp-pq import %s" % patch.path)
 
 
-def drop_pq(repo, branch, options):
+def drop_pq(repo, branch, options, name_keys=None):
     if is_pq_branch(branch, options):
         gbp.log.err("On a patch-queue branch, can't drop it.")
         raise GbpError
     else:
-        pq_branch = pq_branch_name(branch, options)
+        pq_branch = pq_branch_name(branch, options, name_keys)
 
     if repo.has_branch(pq_branch):
         repo.delete_branch(pq_branch)
-- 
2.1.3

>From d99ca225c15f927f9acf61cac4ffd81263ca9067 Mon Sep 17 00:00:00 2001
From: Markus Lehtonen <markus.lehto...@linux.intel.com>
Date: Fri, 11 May 2012 10:56:17 +0300
Subject: [PATCH 1/3] gbp-pq: readiness to configure the pq branch name

All other gbp branches have configurable names. This commit adds the
readiness for user to configure/change the name of the patch-queue
branches, as well.

Patch-queue is defined in options as a format string, where '%(branch)s'
refers to the debian/packaging branch. If the pq-branch format string
does not contain '%(branch)s', there is only one patch-queue branch and
the debian/packaging branch is used as its base branch. That is, e.g. a
'gbp-pq switch' operation from the patch-queue branch always switches to
the debian/packaging branch.

Signed-off-by: Markus Lehtonen <markus.lehto...@linux.intel.com>
Signed-off-by: Olev Kartau <olev.kar...@intel.com>
---
 gbp/scripts/common/pq.py | 107 ++++++++++++++++++++++++++++++++++++-----------
 gbp/scripts/pq.py        |  52 +++++++++++------------
 tests/13_test_gbp_pq.py  |   5 ++-
 3 files changed, 111 insertions(+), 53 deletions(-)

diff --git a/gbp/scripts/common/pq.py b/gbp/scripts/common/pq.py
index d3c07d1..9bc04e3 100644
--- a/gbp/scripts/common/pq.py
+++ b/gbp/scripts/common/pq.py
@@ -32,43 +32,99 @@ from gbp.git.modifier import GitModifier, GitTz
 from gbp.errors import GbpError
 import gbp.log
 
-PQ_BRANCH_PREFIX = "patch-queue/"
+DEFAULT_PQ_BRANCH_NAME = "patch-queue/%(branch)s"
 
 
-def is_pq_branch(branch):
+def is_pq_branch(branch, options):
     """
     is branch a patch-queue branch?
 
-    >>> is_pq_branch("foo")
+    >>> from optparse import OptionParser
+    >>> (opts, args) = OptionParser().parse_args([])
+    >>> is_pq_branch("foo", opts)
     False
-    >>> is_pq_branch("patch-queue/foo")
+    >>> is_pq_branch("patch-queue/foo", opts)
+    True
+    >>> opts.pq_branch = "%(branch)s/development"
+    >>> is_pq_branch("foo/development/bar", opts)
+    False
+    >>> is_pq_branch("bar/foo/development", opts)
+    True
+    >>> opts.pq_branch = "development"
+    >>> is_pq_branch("development", opts)
+    True
+    >>> opts.pq_branch = "my/%(branch)s/pq"
+    >>> is_pq_branch("my/foo/pqb", opts)
+    False
+    >>> is_pq_branch("my/foo/pq", opts)
     True
     """
-    return [False, True][branch.startswith(PQ_BRANCH_PREFIX)]
+    pq_format_str = DEFAULT_PQ_BRANCH_NAME
+    if hasattr(options, 'pq_branch'):
+        pq_format_str = options.pq_branch
 
+    pq_re = re.compile(r'^%s$' % (pq_format_str % dict(branch="(?P<base>\S+)")))
+    if pq_re.match(branch):
+        return True
+    return False
 
-def pq_branch_name(branch):
+
+def pq_branch_name(branch, options):
     """
     get the patch queue branch corresponding to branch
 
-    >>> pq_branch_name("patch-queue/master")
-    >>> pq_branch_name("foo")
+    >>> from optparse import OptionParser
+    >>> (opts, args) = OptionParser().parse_args([])
+    >>> pq_branch_name("patch-queue/master", opts)
+    >>> pq_branch_name("foo", opts)
     'patch-queue/foo'
+    >>> opts.pq_branch = "%(branch)s/development"
+    >>> pq_branch_name("foo", opts)
+    'foo/development'
+    >>> opts.pq_branch = "development"
+    >>> pq_branch_name("foo", opts)
+    'development'
     """
-    if not is_pq_branch(branch):
-        return PQ_BRANCH_PREFIX + branch
+    pq_format_str = DEFAULT_PQ_BRANCH_NAME
+    if hasattr(options, 'pq_branch'):
+        pq_format_str = options.pq_branch
 
+    if not is_pq_branch(branch, options):
+        return pq_format_str % dict(branch=branch)
 
-def pq_branch_base(pq_branch):
-    """
-    get the branch corresponding to the given patch queue branch
 
-    >>> pq_branch_base("patch-queue/master")
+def pq_branch_base(pq_branch, options):
+    """
+    Get the branch corresponding to the given patch queue branch.
+    Returns the packaging/debian branch if pq format string doesn't contain
+    '%(branch)s' key.
+
+    >>> from optparse import OptionParser
+    >>> (opts, args) = OptionParser().parse_args([])
+    >>> opts.packaging_branch = "packaging"
+    >>> pq_branch_base("patch-queue/master", opts)
     'master'
-    >>> pq_branch_base("foo")
+    >>> pq_branch_base("foo", opts)
+    >>> opts.pq_branch = "my/%(branch)s/development"
+    >>> pq_branch_base("foo/development", opts)
+    >>> pq_branch_base("my/foo/development/bar", opts)
+    >>> pq_branch_base("my/foo/development", opts)
+    'foo'
+    >>> opts.pq_branch = "development"
+    >>> pq_branch_base("foo/development", opts)
+    >>> pq_branch_base("development", opts)
+    'packaging'
     """
-    if is_pq_branch(pq_branch):
-        return pq_branch[len(PQ_BRANCH_PREFIX):]
+    pq_format_str = DEFAULT_PQ_BRANCH_NAME
+    if hasattr(options, 'pq_branch'):
+        pq_format_str = options.pq_branch
+
+    pq_re = re.compile(r'^%s$' % (pq_format_str % dict(branch="(?P<base>\S+)")))
+    m = pq_re.match(pq_branch)
+    if m:
+        if 'base' in m.groupdict():
+            return m.group('base')
+        return options.packaging_branch
 
 
 def parse_gbp_commands(info, cmd_tag, noarg_cmds, arg_cmds):
@@ -242,15 +298,15 @@ def get_maintainer_from_control(repo):
     return GitModifier()
 
 
-def switch_to_pq_branch(repo, branch):
+def switch_to_pq_branch(repo, branch, options):
     """
     Switch to patch-queue branch if not already there, create it if it
     doesn't exist yet
     """
-    if is_pq_branch(branch):
+    if is_pq_branch(branch, options):
         return
 
-    pq_branch = pq_branch_name(branch)
+    pq_branch = pq_branch_name(branch, options)
     if not repo.has_branch(pq_branch):
         try:
             repo.create_branch(pq_branch)
@@ -262,8 +318,9 @@ def switch_to_pq_branch(repo, branch):
     repo.set_branch(pq_branch)
 
 
-def apply_single_patch(repo, branch, patch, fallback_author, topic=None):
-    switch_to_pq_branch(repo, branch)
+def apply_single_patch(repo, branch, patch, fallback_author, options):
+    switch_to_pq_branch(repo, branch, options)
+    topic = None if not hasattr(options, 'topic') else options.topic
     apply_and_commit_patch(repo, patch, fallback_author, topic)
 
 
@@ -292,12 +349,12 @@ def apply_and_commit_patch(repo, patch, fallback_author, topic=None):
     repo.update_ref('HEAD', commit, msg="gbp-pq import %s" % patch.path)
 
 
-def drop_pq(repo, branch):
-    if is_pq_branch(branch):
+def drop_pq(repo, branch, options):
+    if is_pq_branch(branch, options):
         gbp.log.err("On a patch-queue branch, can't drop it.")
         raise GbpError
     else:
-        pq_branch = pq_branch_name(branch)
+        pq_branch = pq_branch_name(branch, options)
 
     if repo.has_branch(pq_branch):
         repo.delete_branch(pq_branch)
diff --git a/gbp/scripts/pq.py b/gbp/scripts/pq.py
index 194145e..34d8e0a 100755
--- a/gbp/scripts/pq.py
+++ b/gbp/scripts/pq.py
@@ -135,13 +135,13 @@ def commit_patches(repo, branch, patches, options):
 
 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)
+    if is_pq_branch(branch, options):
+        base = pq_branch_base(branch, options)
         gbp.log.info("On '%s', switching to '%s'" % (branch, base))
         branch = base
         repo.set_branch(branch)
 
-    pq_branch = pq_branch_name(branch)
+    pq_branch = pq_branch_name(branch, options)
     try:
         shutil.rmtree(PATCH_DIR)
     except OSError as (e, msg):
@@ -170,7 +170,7 @@ def export_patches(repo, branch, options):
         gbp.log.info("No patches on '%s' - nothing to do." % pq_branch)
 
     if options.drop:
-        drop_pq(repo, branch)
+        drop_pq(repo, branch, options)
 
 
 def safe_patches(series):
@@ -196,7 +196,7 @@ def safe_patches(series):
     return (tmpdir, series)
 
 
-def import_quilt_patches(repo, branch, series, tries, force):
+def import_quilt_patches(repo, branch, series, tries, options):
     """
     apply a series of quilt patches in the series file 'series' to branch
     the patch-queue branch for 'branch'
@@ -206,24 +206,24 @@ def import_quilt_patches(repo, branch, series, tries, force):
     @param series; series file to read patches from
     @param tries: try that many times to apply the patches going back one
                   commit in the branches history after each failure.
-    @param force: import the patch series even if the branch already exists
+    @param options: gbp-pq command options
     """
     tmpdir = None
 
-    if is_pq_branch(branch):
-        if force:
-            branch = pq_branch_base(branch)
-            pq_branch = pq_branch_name(branch)
+    if is_pq_branch(branch, options):
+        if options.force:
+            branch = pq_branch_base(branch, options)
+            pq_branch = pq_branch_name(branch, options)
             repo.checkout(branch)
         else:
             gbp.log.err("Already on a patch-queue branch '%s' - doing nothing." % branch)
             raise GbpError
     else:
-        pq_branch = pq_branch_name(branch)
+        pq_branch = pq_branch_name(branch, options)
 
     if repo.has_branch(pq_branch):
-        if force:
-            drop_pq(repo, branch)
+        if options.force:
+            drop_pq(repo, branch, options)
         else:
             raise GbpError("Patch queue branch '%s'. already exists. Try 'rebase' instead."
                            % pq_branch)
@@ -270,23 +270,23 @@ def import_quilt_patches(repo, branch, series, tries, force):
         shutil.rmtree(tmpdir)
 
 
-def rebase_pq(repo, branch):
-    if is_pq_branch(branch):
-        base = pq_branch_base(branch)
+def rebase_pq(repo, branch, options):
+    if is_pq_branch(branch, options):
+        base = pq_branch_base(branch, options)
     else:
-        switch_to_pq_branch(repo, branch)
+        switch_to_pq_branch(repo, branch, options)
         base = branch
     GitCommand("rebase")([base])
 
 
-def switch_pq(repo, current):
+def switch_pq(repo, current, options):
     """Switch to patch-queue branch if on base branch and vice versa"""
-    if is_pq_branch(current):
-        base = pq_branch_base(current)
+    if is_pq_branch(current, options):
+        base = pq_branch_base(current, options)
         gbp.log.info("Switching to %s" % base)
         repo.checkout(base)
     else:
-        switch_to_pq_branch(repo, current)
+        switch_to_pq_branch(repo, current, options)
 
 
 def build_parser(name):
@@ -370,20 +370,20 @@ def main(argv):
         elif action == "import":
             series = SERIES_FILE
             tries = options.time_machine if (options.time_machine > 0) else 1
-            import_quilt_patches(repo, current, series, tries, options.force)
+            import_quilt_patches(repo, current, series, tries, options)
             current = repo.get_branch()
             gbp.log.info("Patches listed in '%s' imported on '%s'" %
                           (series, current))
         elif action == "drop":
-            drop_pq(repo, current)
+            drop_pq(repo, current, options)
         elif action == "rebase":
-            rebase_pq(repo, current)
+            rebase_pq(repo, current, options)
         elif action == "apply":
             patch = Patch(patchfile)
             maintainer = get_maintainer_from_control(repo)
-            apply_single_patch(repo, current, patch, maintainer, options.topic)
+            apply_single_patch(repo, current, patch, maintainer, options)
         elif action == "switch":
-            switch_pq(repo, current)
+            switch_pq(repo, current, options)
     except CommandExecFailed:
         retval = 1
     except (GbpError, GitRepositoryError) as err:
diff --git a/tests/13_test_gbp_pq.py b/tests/13_test_gbp_pq.py
index 910ce20..1ae4b62 100644
--- a/tests/13_test_gbp_pq.py
+++ b/tests/13_test_gbp_pq.py
@@ -91,7 +91,8 @@ class TestApplySinglePatch(testutils.DebianGitTestRepo):
 
         patch = gbp.patch_series.Patch(_patch_path('foo.patch'))
 
-        pq.apply_single_patch(self.repo, 'master', patch, None)
+        dummy_opts = object()
+        pq.apply_single_patch(self.repo, 'master', patch, None, dummy_opts)
         self.assertIn('foo', self.repo.list_files())
 
 class TestWritePatch(testutils.DebianGitTestRepo):
@@ -146,7 +147,7 @@ class TestExport(testutils.DebianGitTestRepo):
         repo = self.repo
         start = repo.get_branch()
         pq = os.path.join('patch-queue', start)
-        switch_pq(repo, start)
+        switch_pq(repo, start, TestExport.Options)
         self.assertEqual(repo.get_branch(), pq)
         export_patches(repo, pq, TestExport.Options)
         self.assertEqual(repo.get_branch(), start)
-- 
2.1.3

>From 54d947cbbfeff51afc8d6e8377134d45393723e4 Mon Sep 17 00:00:00 2001
From: Tzafrir Cohen <tzaf...@debian.org>
Date: Thu, 27 Nov 2014 14:41:28 +0200
Subject: [PATCH 3/3] add script pq_rpm

---
 debian/git-buildpackage-rpm.install |   1 +
 gbp/config.py                       |  33 ++
 gbp/scripts/pq_rpm.py               | 731 ++++++++++++++++++++++++++++++++++++
 3 files changed, 765 insertions(+)
 create mode 100644 gbp/scripts/pq_rpm.py

diff --git a/debian/git-buildpackage-rpm.install b/debian/git-buildpackage-rpm.install
index 67c0309..2568b4c 100644
--- a/debian/git-buildpackage-rpm.install
+++ b/debian/git-buildpackage-rpm.install
@@ -1,2 +1,3 @@
 usr/lib/python2.?/dist-packages/gbp/rpm/
 usr/lib/python2.7/dist-packages/gbp/scripts/import_srpm.py
+usr/lib/python2.7/dist-packages/gbp/scripts/pq_rpm.py
diff --git a/gbp/config.py b/gbp/config.py
index 174eba4..167ee7e 100644
--- a/gbp/config.py
+++ b/gbp/config.py
@@ -541,6 +541,14 @@ class GbpOptionParserRpm(GbpOptionParser):
             'packaging-branch'          : 'master',
             'packaging-dir'             : '',
             'packaging-tag'             : 'packaging/%(version)s',
+            'pq-branch'                 : 'development/%(branch)s',
+            'import-files'              : ['.gbp.conf',
+                                           'debian/gbp.conf'],
+            'spec-file'                 : 'auto',
+            'patch-export'              : 'False',
+            'patch-export-compress'     : '0',
+            'patch-export-ignore-path'  : '',
+            'patch-export-squash-until' : '',
                     })
 
     help = dict(GbpOptionParser.help)
@@ -560,6 +568,31 @@ class GbpOptionParserRpm(GbpOptionParser):
             'packaging-tag':
                 "Format string for packaging tags, RPM counterpart of the "
                 "'debian-tag' option, default is '%(packaging-tag)s'",
+            'pq-branch':
+                "format string for the patch-queue branch name, default is "
+                "'%(pq-branch)s'",
+            'import-files':
+                "Comma-separated list of additional file(s) to import from "
+                "packaging branch. These will appear as one monolithic patch "
+                "in the pq/development branch. Default is %(import-files)s",
+            'spec-file':
+                "Spec file to use, 'auto' makes gbp to guess, other values "
+                "make the packaging-dir option to be ignored, default is "
+                "'%(spec-file)s'",
+            'patch-export':
+                "Create patches between upstream and export-treeish, default "
+                "is '%(patch-export)s'",
+            'patch-export-compress':
+                "Compress (auto-generated) patches larger than given number of "
+                "bytes, 0 never compresses, default is "
+                "'%(patch-export-compress)s'",
+            'patch-export-ignore-path':
+                "Exclude changes to path(s) matching regex, default is "
+                "'%(patch-export-ignore-path)s'",
+            'patch-export-squash-until':
+                "Squash commits (from upstream) until given tree-ish into one "
+                "big diff, format is '<commit_ish>[:<filename_base>]'. "
+                "Default is '%(patch-export-squash-until)s'",
                  })
 
 # vim:et:ts=4:sw=4:et:sts=4:ai:set list listchars=tab\:»·,trail\:·:
diff --git a/gbp/scripts/pq_rpm.py b/gbp/scripts/pq_rpm.py
new file mode 100644
index 0000000..ee076aa
--- /dev/null
+++ b/gbp/scripts/pq_rpm.py
@@ -0,0 +1,731 @@
+# vim: set fileencoding=utf-8 :
+#
+# (C) 2011 Guido Günther <a...@sigxcpu.org>
+# (C) 2012 Intel Corporation <markus.lehto...@linux.intel.com>
+#    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 ConfigParser
+import errno
+import os
+import shutil
+import sys
+import re
+import gzip
+import bz2
+import subprocess
+
+import gbp.tmpfile as tempfile
+from gbp.config import GbpOptionParserRpm
+from gbp.rpm.git import GitRepositoryError, RpmGitRepository
+from gbp.git.modifier import GitModifier
+from gbp.command_wrappers import GitCommand, CommandExecFailed
+from gbp.errors import GbpError
+import gbp.log
+from gbp.patch_series import PatchSeries, Patch
+from gbp.pkg import parse_archive_filename
+from gbp.rpm import (SpecFile, NoSpecError, guess_spec, guess_spec_repo,
+                     spec_from_repo, string_to_int)
+from gbp.scripts.common.pq import (is_pq_branch, pq_branch_name, pq_branch_base,
+                                   parse_gbp_commands, format_patch,
+                                   format_diff, apply_and_commit_patch, drop_pq)
+from gbp.scripts.common.buildpackage import dump_tree
+
+USAGE_STRING = \
+"""%prog [options] action - maintain patches on a patch queue branch
+tions:
+export         Export the patch queue / devel branch associated to the
+               current branch into a patch series in and update the spec file
+import         Create a patch queue / devel branch from spec file
+               and patches in current dir.
+rebase         Switch to patch queue / devel branch associated to the current
+               branch and rebase against upstream.
+drop           Drop (delete) the patch queue /devel branch associated to
+               the current branch.
+apply          Apply a patch
+switch         Switch to patch-queue branch and vice versa.
+convert        [experimental] Convert package from single-branch development
+               model (packaging and source code changes in the same branch)
+               into the orphan-packaging plus patch-queue / development branch
+               development model."""
+
+def compress_patches(patches, compress_size=0):
+    """
+    Rename and/or compress patches
+    """
+    ret_patches = []
+    for patch in patches:
+        # Compress if patch file is larger than "threshold" value
+        suffix = ''
+        if compress_size and os.path.getsize(patch) > compress_size:
+            gbp.log.debug("Compressing %s" % os.path.basename(patch))
+            subprocess.Popen(['gzip', '-n', patch]).communicate()
+            suffix = '.gz'
+
+        ret_patches.append(os.path.basename(patch) + suffix)
+    return ret_patches
+
+def is_ancestor(repo, parent, child):
+    """Check if commit is ancestor of another"""
+    parent_sha1 = repo.rev_parse("%s^0" % parent)
+    child_sha1 = repo.rev_parse("%s^0" % child)
+    try:
+        merge_base = repo.get_merge_base(parent_sha1, child_sha1)
+    except GitRepositoryError:
+        merge_base = None
+    return merge_base == parent_sha1
+
+def generate_patches(repo, start, squash, end, outdir, options):
+    """
+    Generate patch files from git
+    """
+    gbp.log.info("Generating patches from git (%s..%s)" % (start, end))
+    patches = []
+    commands = {}
+    for treeish in [start, end]:
+        if not repo.has_treeish(treeish):
+            raise GbpError('Invalid treeish object %s' % treeish)
+
+    start_sha1 = repo.rev_parse("%s^0" % start)
+    try:
+        end_commit = end
+        end_commit_sha1 = repo.rev_parse("%s^0" % end_commit)
+    except GitRepositoryError:
+        # In case of plain tree-ish objects, assume current branch head is the
+        # last commit
+        end_commit = "HEAD"
+        end_commit_sha1 = repo.rev_parse("%s^0" % end_commit)
+
+    start_sha1 = repo.rev_parse("%s^0" % start)
+
+    if not is_ancestor(repo, start_sha1, end_commit_sha1):
+        raise GbpError("Start commit '%s' not an ancestor of end commit "
+                       "'%s'" % (start, end_commit))
+    # Squash commits, if requested
+    if squash[0]:
+        if squash[0] == 'HEAD':
+            squash[0] = end_commit
+        squash_sha1 = repo.rev_parse("%s^0" % squash[0])
+        if start_sha1 != squash_sha1:
+            if not squash_sha1 in repo.get_commits(start, end_commit):
+                raise GbpError("Given squash point '%s' not in the history "
+                               "of end commit '%s'" % (squash[0], end_commit))
+            # Shorten SHA1s
+            squash_sha1 = repo.rev_parse(squash_sha1, short=7)
+            start_sha1 = repo.rev_parse(start_sha1, short=7)
+            gbp.log.info("Squashing commits %s..%s into one monolithic diff" %
+                         (start_sha1, squash_sha1))
+            patch_fn = format_diff(outdir, squash[1], repo,
+                                   start_sha1, squash_sha1,
+                                   options.patch_export_ignore_path)
+            if patch_fn:
+                patches.append(patch_fn)
+                start = squash_sha1
+    # Check for merge commits, yet another squash if merges found
+    merges = repo.get_commits(start, end_commit, options=['--merges'])
+    if merges:
+        # Shorten SHA1s
+        start_sha1 = repo.rev_parse(start, short=7)
+        merge_sha1 = repo.rev_parse(merges[0], short=7)
+        patch_fn = format_diff(outdir, None, repo, start_sha1, merge_sha1,
+                               options.patch_export_ignore_path)
+        if patch_fn:
+            gbp.log.info("Merge commits found! Diff between %s..%s written "
+                         "into one monolithic diff" % (start_sha1, merge_sha1))
+            patches.append(patch_fn)
+            start = merge_sha1
+            print start
+
+    # Generate patches
+    for commit in reversed(repo.get_commits(start, end_commit)):
+        info = repo.get_commit_info(commit)
+        cmds = parse_gbp_commands(info, 'gbp-rpm', ('ignore'),
+                                  ('if', 'ifarch'))[0]
+        if not 'ignore' in cmds:
+            patch_fn = format_patch(outdir, repo, info, patches,
+                                    options.patch_numbers,
+                                    options.patch_export_ignore_path)
+            if patch_fn:
+                commands[os.path.basename(patch_fn)] = cmds
+        else:
+            gbp.log.info('Ignoring commit %s' % info['id'])
+
+    # Generate diff to the tree-ish object
+    if end_commit != end:
+        gbp.log.info("Generating diff file %s..%s" % (end_commit, end))
+        patch_fn = format_diff(outdir, None, repo, end_commit, end,
+                               options.patch_export_ignore_path)
+        if patch_fn:
+            patches.append(patch_fn)
+
+    # Compress
+    patches = compress_patches(patches, options.patch_export_compress)
+
+    return patches, commands
+
+
+def rm_patch_files(spec):
+    """
+    Delete the patch files listed in the spec files. Doesn't delete patches
+    marked as not maintained by gbp.
+    """
+    # Remove all old patches from the spec dir
+    for patch in spec.patchseries(unapplied=True):
+        gbp.log.debug("Removing '%s'" % patch.path)
+        try:
+            os.unlink(patch.path)
+        except OSError as err:
+            if err.errno != errno.ENOENT:
+                raise GbpError("Failed to remove patch: %s" % err)
+            else:
+                gbp.log.debug("Patch %s does not exist." % patch.path)
+
+
+def update_patch_series(repo, spec, start, end, options):
+    """
+    Export patches to packaging directory and update spec file accordingly.
+    """
+    squash = options.patch_export_squash_until.split(':', 1)
+    if len(squash) == 1:
+        squash.append(None)
+    else:
+        squash[1] += '.diff'
+
+    # Unlink old patch files and generate new patches
+    rm_patch_files(spec)
+
+    patches, commands = generate_patches(repo, start, squash, end,
+                                         spec.specdir, options)
+    spec.update_patches(patches, commands)
+    spec.write_spec_file()
+    return patches
+
+
+def parse_spec(options, repo, treeish=None):
+    """
+    Find and parse spec file.
+
+    If treeish is given, try to find the spec file from that. Otherwise, search
+    for the spec file in the working copy.
+    """
+    try:
+        if options.spec_file != 'auto':
+            options.packaging_dir = os.path.dirname(options.spec_file)
+            if not treeish:
+                spec = SpecFile(options.spec_file)
+            else:
+                spec = spec_from_repo(repo, treeish, options.spec_file)
+        else:
+            preferred_name = os.path.basename(repo.path) + '.spec'
+            if not treeish:
+                spec = guess_spec(options.packaging_dir, True, preferred_name)
+            else:
+                spec = guess_spec_repo(repo, treeish, options.packaging_dir,
+                                       True, preferred_name)
+    except NoSpecError as err:
+        raise GbpError("Can't parse spec: %s" % err)
+    relpath = spec.specpath if treeish else os.path.relpath(spec.specpath,
+                                                            repo.path)
+    gbp.log.debug("Using '%s' from '%s'" % (relpath, treeish or 'working copy'))
+    return spec
+
+
+def find_upstream_commit(repo, spec, upstream_tag):
+    """Find commit corresponding upstream version"""
+    tag_str_fields = {'upstreamversion': spec.upstreamversion,
+                      'vendor': 'Upstream'}
+    upstream_commit = repo.find_version(upstream_tag, tag_str_fields)
+    if not upstream_commit:
+        raise GbpError("Couldn't find upstream version %s" %
+                       spec.upstreamversion)
+    return upstream_commit
+
+
+def export_patches(repo, options):
+    """Export patches from the pq branch into a packaging branch"""
+    current = repo.get_branch()
+    if is_pq_branch(current, options):
+        base = pq_branch_base(current, options)
+        gbp.log.info("On branch '%s', switching to '%s'" % (current, base))
+        repo.set_branch(base)
+        spec = parse_spec(options, repo)
+        pq_branch = current
+    else:
+        spec = parse_spec(options, repo)
+        pq_branch = pq_branch_name(current, options, spec.version)
+    upstream_commit = find_upstream_commit(repo, spec, options.upstream_tag)
+
+    export_treeish = options.export_rev if options.export_rev else pq_branch
+
+    update_patch_series(repo, spec, upstream_commit, export_treeish, options)
+
+    GitCommand('status')(['--', spec.specdir])
+
+
+def safe_patches(queue, tmpdir_base):
+    """
+    Safe the current patches in a temporary directory
+    below 'tmpdir_base'. Also, uncompress compressed patches here.
+
+    @param queue: an existing patch queue
+    @param tmpdir_base: base under which to create tmpdir
+    @return: tmpdir and a safed queue (with patches in tmpdir)
+    @rtype: tuple
+    """
+
+    tmpdir = tempfile.mkdtemp(dir=tmpdir_base, prefix='patchimport_')
+    safequeue = PatchSeries()
+
+    if len(queue) > 0:
+        gbp.log.debug("Safeing patches '%s' in '%s'" %
+                        (os.path.dirname(queue[0].path), tmpdir))
+    for patch in queue:
+        base, _archive_fmt, comp = parse_archive_filename(patch.path)
+        uncompressors = {'gzip': gzip.open, 'bzip2': bz2.BZ2File}
+        if comp in uncompressors:
+            gbp.log.debug("Uncompressing '%s'" % os.path.basename(patch.path))
+            src = uncompressors[comp](patch.path, 'r')
+            dst_name = os.path.join(tmpdir, os.path.basename(base))
+        elif comp:
+            raise GbpError("Unsupported patch compression '%s', giving up"
+                           % comp)
+        else:
+            src = open(patch.path, 'r')
+            dst_name = os.path.join(tmpdir, os.path.basename(patch.path))
+
+        dst = open(dst_name, 'w')
+        dst.writelines(src)
+        src.close()
+        dst.close()
+
+        safequeue.append(patch)
+        safequeue[-1].path = dst_name
+
+    return safequeue
+
+
+def get_packager(spec):
+    """Get packager information from spec"""
+    if spec.packager:
+        match = re.match(r'(?P<name>.*[^ ])\s*<(?P<email>\S*)>',
+                         spec.packager.strip())
+        if match:
+            return GitModifier(match.group('name'), match.group('email'))
+    return GitModifier()
+
+def import_extra_files(repo, commitish, files, patch_ignore=True):
+    """Import branch-specific gbp.conf files to current branch"""
+    found = {}
+    for fname in files:
+        if fname:
+            try:
+                found[fname] = repo.show('%s:%s' % (commitish, fname))
+            except GitRepositoryError:
+                pass
+    if found:
+        gbp.log.info("Importing additional file(s) from branch '%s' into '%s'" %
+                     (commitish, repo.get_branch()))
+        for fname, content in found.iteritems():
+            dirname = os.path.dirname(fname)
+            if dirname and not os.path.exists(dirname):
+                os.makedirs(dirname)
+            with open(fname, 'w') as fobj:
+                fobj.write(content)
+
+        files = found.keys()
+        gbp.log.debug('Adding/commiting %s' % files)
+        repo.add_files(files, force=True)
+        commit_msg = ("Auto-import file(s) from branch '%s':\n    %s\n" %
+                      (commitish, '    '.join(files)))
+        if patch_ignore:
+            commit_msg += "\nGbp: Ignore\nGbp-Rpm: Ignore"
+        repo.commit_files(files, msg=commit_msg)
+    return found.keys()
+
+def import_spec_patches(repo, options):
+    """
+    apply a series of patches in a spec/packaging dir to branch
+    the patch-queue branch for 'branch'
+
+    @param repo: git repository to work on
+    @param options: command options
+    """
+    current = repo.get_branch()
+    # Get spec and related information
+    if is_pq_branch(current, options):
+        base = pq_branch_base(current, options)
+        if options.force:
+            spec = parse_spec(options, repo, base)
+            spec_treeish = base
+        else:
+            raise GbpError("Already on a patch-queue branch '%s' - doing "
+                           "nothing." % current)
+    else:
+        spec = parse_spec(options, repo)
+        spec_treeish = None
+        base = current
+    upstream_commit = find_upstream_commit(repo, spec, options.upstream_tag)
+    packager = get_packager(spec)
+    pq_branch = pq_branch_name(base, options, spec.version)
+
+    # Create pq-branch
+    if repo.has_branch(pq_branch) and not options.force:
+        raise GbpError("Patch-queue branch '%s' already exists. "
+                       "Try 'switch' instead." % pq_branch)
+    try:
+        if repo.get_branch() == pq_branch:
+            repo.force_head(upstream_commit, hard=True)
+        else:
+            repo.create_branch(pq_branch, upstream_commit, force=True)
+    except GitRepositoryError as err:
+        raise GbpError("Cannot create patch-queue branch '%s': %s" %
+                        (pq_branch, err))
+
+    # Put patches in a safe place
+    if spec_treeish:
+        packaging_tmp = tempfile.mkdtemp(prefix='dump_', dir=options.tmp_dir)
+        packaging_tree = '%s:%s' % (spec_treeish, options.packaging_dir)
+        dump_tree(repo, packaging_tmp, packaging_tree, with_submodules=False,
+                  recursive=False)
+        spec.specdir = packaging_tmp
+    in_queue = spec.patchseries()
+    queue = safe_patches(in_queue, options.tmp_dir)
+    # Do import
+    try:
+        gbp.log.info("Switching to branch '%s'" % pq_branch)
+        repo.set_branch(pq_branch)
+        import_extra_files(repo, base, options.import_files)
+
+        if not queue:
+            return
+        gbp.log.info("Trying to apply patches from branch '%s' onto '%s'" %
+                        (base, upstream_commit))
+        for patch in queue:
+            gbp.log.debug("Applying %s" % patch.path)
+            apply_and_commit_patch(repo, patch, packager)
+    except (GbpError, GitRepositoryError) as err:
+        repo.set_branch(base)
+        repo.delete_branch(pq_branch)
+        raise GbpError('Import failed: %s' % err)
+
+    gbp.log.info("Patches listed in '%s' imported on '%s'" % (spec.specfile,
+                                                              pq_branch))
+
+
+def rebase_pq(repo, options):
+    """Rebase pq branch on the correct upstream version (from spec file)."""
+    current = repo.get_branch()
+    if is_pq_branch(current, options):
+        base = pq_branch_base(current, options)
+        spec = parse_spec(options, repo, base)
+    else:
+        base = current
+        spec = parse_spec(options, repo)
+    upstream_commit = find_upstream_commit(repo, spec, options.upstream_tag)
+
+    switch_to_pq_branch(repo, base, options)
+    GitCommand("rebase")([upstream_commit])
+
+
+def switch_pq(repo, options):
+    """Switch to patch-queue branch if on base branch and vice versa"""
+    current = repo.get_branch()
+    if is_pq_branch(current, options):
+        base = pq_branch_base(current, options)
+        gbp.log.info("Switching to branch '%s'" % base)
+        repo.checkout(base)
+    else:
+        switch_to_pq_branch(repo, current, options)
+
+
+def drop_pq_rpm(repo, options):
+    """Remove pq branch"""
+    current = repo.get_branch()
+    if is_pq_branch(current, options):
+        base = pq_branch_base(current, options)
+        spec = parse_spec(options, repo, base)
+    else:
+        spec = parse_spec(options, repo)
+    drop_pq(repo, current, options, spec.version)
+
+
+def switch_to_pq_branch(repo, branch, options):
+    """
+    Switch to patch-queue branch if not already there, create it if it
+    doesn't exist yet
+    """
+    if is_pq_branch(branch, options):
+        return
+
+    spec = parse_spec(options, repo, branch)
+    pq_branch = pq_branch_name(branch, options, spec.version)
+    if not repo.has_branch(pq_branch):
+        raise GbpError("Branch '%s' does not exist" % pq_branch)
+
+    gbp.log.info("Switching to branch '%s'" % pq_branch)
+    repo.set_branch(pq_branch)
+
+def apply_single_patch(repo, patchfile, options):
+    """Apply a single patch onto the pq branch"""
+    current = repo.get_branch()
+    if not is_pq_branch(current, options):
+        switch_to_pq_branch(repo, current, options)
+    patch = Patch(patchfile)
+    apply_and_commit_patch(repo, patch, fallback_author=None)
+
+def convert_package(repo, options):
+    """Convert package to orphan-packaging model"""
+    old_packaging = repo.get_branch()
+    # Check if we're on pq branch, already
+    err_msg_base = "Seems you're already using orphan-packaging model - "
+    if is_pq_branch(old_packaging, options):
+        raise GbpError(err_msg_base + "you're on patch-queue branch")
+    # Check if a pq branch already exists
+    spec = parse_spec(options, repo, treeish=old_packaging)
+    pq_branch = pq_branch_name(old_packaging, options, spec.version)
+    if repo.has_branch(pq_branch):
+        pq_branch = pq_branch_name(old_packaging, options, spec.version)
+        raise GbpError(err_msg_base + "pq branch %s already exists" % pq_branch)
+    # Check that the current branch is based on upstream
+    upstream_commit = find_upstream_commit(repo, spec, options.upstream_tag)
+    if not is_ancestor(repo, upstream_commit, old_packaging):
+        raise GbpError(err_msg_base + "%s is not based on upstream version %s" %
+                       (old_packaging, spec.upstreamversion))
+    # Check new branch
+    new_branch = old_packaging + "-orphan"
+    if repo.has_branch(new_branch):
+        if not options.force:
+            raise GbpError("Branch '%s' already exists!" % new_branch)
+        else:
+            gbp.log.info("Dropping branch '%s'" % new_branch)
+            repo.delete_branch(new_branch)
+
+    # Determine "history"
+    if options.retain_history:
+        # Find first commit that has the spec file and list commits from there
+        try:
+            repo.show('%s:%s' % (upstream_commit, spec.specpath))
+            history = repo.get_commits(upstream_commit, old_packaging)
+        except GitRepositoryError:
+            history_start = repo.get_commits(upstream_commit, old_packaging,
+                                             spec.specpath)[-1]
+            history = repo.get_commits('%s^' % history_start, old_packaging)
+    else:
+        history = [repo.rev_parse(old_packaging)]
+    history.reverse()
+
+    # Do import
+    gbp.log.info("Importing packaging files from branch '%s' to '%s'" %
+                 (old_packaging, new_branch))
+    convert_with_history(repo, upstream_commit, history, new_branch,
+                         spec.specfile, options)
+    # Copy extra files
+    import_extra_files(repo, old_packaging, options.import_files,
+                       patch_ignore=False)
+
+    gbp.log.info("Package successfully converted to orphan-packaging.")
+    gbp.log.info("You're now on the new '%s' packaging branch (the old "
+            "packaging branch '%s' was left intact)." %
+            (new_branch, old_packaging))
+    gbp.log.info("Please check all files and test building the package!")
+
+
+def convert_with_history(repo, upstream, commits, new_branch, spec_fn, options):
+    """Auto-import packaging files and (auto-generated) patches"""
+
+    # Dump and commit packaging files
+    packaging_tree = '%s:%s' % (commits[0], options.packaging_dir)
+    packaging_tmp = tempfile.mkdtemp(prefix='pack_', dir=options.tmp_dir)
+    dump_packaging_dir = os.path.join(packaging_tmp, options.new_packaging_dir)
+    dump_tree(repo, dump_packaging_dir, packaging_tree, with_submodules=False,
+              recursive=False)
+
+    msg = "Auto-import packaging files\n\n" \
+          "Imported initial packaging files from commit '%s'" % (commits[0])
+    new_tree = repo.create_tree(packaging_tmp)
+    tip_commit = repo.commit_tree(new_tree, msg, [])
+
+    # Generate initial patches
+    spec = SpecFile(os.path.join(dump_packaging_dir, spec_fn))
+    update_patch_series(repo, spec, upstream, commits[0], options)
+    # Commit updated packaging files only if something was changed
+    new_tree = repo.create_tree(packaging_tmp)
+    if new_tree != repo.rev_parse(tip_commit + ':'):
+        msg = "Auto-generate patches\n\n" \
+              "Generated patches from\n'%s..%s'\n\n" \
+              "updating spec file and possibly removing old patches." \
+              % (upstream, commits[0])
+        tip_commit = repo.commit_tree(new_tree, msg, [tip_commit])
+
+    # Import rest of the commits
+    for commit in commits[1:]:
+        shutil.rmtree(dump_packaging_dir)
+        packaging_tree = '%s:%s' % (commit, options.packaging_dir)
+        dump_tree(repo, dump_packaging_dir, packaging_tree,
+                  with_submodules=False, recursive=False)
+        try:
+            spec = SpecFile(os.path.join(dump_packaging_dir, spec_fn))
+            update_patch_series(repo, spec, upstream, commit, options)
+        except (NoSpecError, GbpError):
+            gbp.log.warn("Failed to generate patches from '%s'" % commit)
+
+        new_tree = repo.create_tree(packaging_tmp)
+        if new_tree == repo.rev_parse(tip_commit + ':'):
+            gbp.log.info("Skipping commit '%s' which generated no change" %
+                         commit)
+        else:
+            info = repo.get_commit_info(commit)
+            msg = "%s\n\n%sAuto-imported by gbp from '%s'" % (info['subject'],
+                        info['body'], commit)
+            tip_commit = repo.commit_tree(new_tree, msg, [tip_commit])
+
+    repo.create_branch(new_branch, tip_commit)
+    repo.set_branch(new_branch)
+
+
+def opt_split_cb(option, opt_str, value, parser):
+    """Split option string into a list"""
+    setattr(parser.values, option.dest, value.split(','))
+
+
+def main(argv):
+    """Main function for the gbp pq-rpm command"""
+    retval = 0
+
+    try:
+        parser = GbpOptionParserRpm(command=os.path.basename(argv[0]),
+                                    prefix='', usage=USAGE_STRING)
+    except ConfigParser.ParsingError as err:
+        gbp.log.err('Invalid config file: %s' % err)
+        return 1
+
+    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("--force", dest="force", action="store_true",
+            default=False,
+            help="In case of import even import if the branch already exists")
+    parser.add_config_file_option(option_name="vendor", action="store",
+            dest="vendor")
+    parser.add_config_file_option(option_name="color", dest="color",
+            type='tristate')
+    parser.add_config_file_option(option_name="color-scheme",
+            dest="color_scheme")
+    parser.add_config_file_option(option_name="tmp-dir", dest="tmp_dir")
+    parser.add_config_file_option(option_name="upstream-tag",
+            dest="upstream_tag")
+    parser.add_config_file_option(option_name="spec-file", dest="spec_file")
+    parser.add_config_file_option(option_name="packaging-dir",
+            dest="packaging_dir")
+    parser.add_option("--new-packaging-dir",
+            help="Packaging directory in the new packaging branch. Only "
+                 "relevant for the 'convert' action. If not defined, defaults "
+                 "to '--packaging-dir'")
+    parser.add_config_file_option(option_name="packaging-branch",
+            dest="packaging_branch",
+            help="Branch the packaging is being maintained on. Only relevant "
+                 "if a invariable/single pq-branch is defined, in which case "
+                 "this is used as the 'base' branch. Default is "
+                 "'%(packaging-branch)s'")
+    parser.add_config_file_option(option_name="pq-branch", dest="pq_branch")
+    parser.add_config_file_option(option_name="import-files",
+            dest="import_files", type="string", action="callback",
+            callback=opt_split_cb)
+    parser.add_option("--retain-history", action="store_true",
+            help="When doing convert, preserve as much of the git history as "
+                 "possible, i.e. create one commit per commit. Only "
+                 "relevant for the 'convert' action.")
+    parser.add_option("--export-rev", action="store", dest="export_rev",
+            default="",
+            help="Export patches from treeish object TREEISH instead of head "
+                 "of patch-queue branch", metavar="TREEISH")
+    parser.add_config_file_option("patch-export-compress",
+            dest="patch_export_compress")
+    parser.add_config_file_option("patch-export-squash-until",
+            dest="patch_export_squash_until")
+    parser.add_config_file_option("patch-export-ignore-path",
+            dest="patch_export_ignore_path")
+
+    (options, args) = parser.parse_args(argv)
+    gbp.log.setup(options.color, options.verbose, options.color_scheme)
+    options.patch_export_compress = string_to_int(options.patch_export_compress)
+    if options.new_packaging_dir is None:
+        options.new_packaging_dir = options.packaging_dir
+
+    if len(args) < 2:
+        gbp.log.err("No action given.")
+        return 1
+    else:
+        action = args[1]
+
+    if args[1] in ["export", "import", "rebase", "drop", "switch", "convert"]:
+        pass
+    elif args[1] in ["apply"]:
+        if len(args) != 3:
+            gbp.log.err("No patch name given.")
+            return 1
+        else:
+            patchfile = args[2]
+    else:
+        gbp.log.err("Unknown action '%s'." % args[1])
+        return 1
+
+    try:
+        repo = RpmGitRepository(os.path.curdir)
+    except GitRepositoryError:
+        gbp.log.err("%s is not a git repository" % (os.path.abspath('.')))
+        return 1
+
+    if os.path.abspath('.') != repo.path:
+        gbp.log.warn("Switching to topdir before running commands")
+        os.chdir(repo.path)
+
+    try:
+        # Create base temporary directory for this run
+        options.tmp_dir = tempfile.mkdtemp(dir=options.tmp_dir,
+                                           prefix='gbp-pq-rpm_')
+        if action == "export":
+            export_patches(repo, options)
+        elif action == "import":
+            import_spec_patches(repo, options)
+        elif action == "drop":
+            drop_pq_rpm(repo, options)
+        elif action == "rebase":
+            rebase_pq(repo, options)
+        elif action == "apply":
+            apply_single_patch(repo, patchfile, options)
+        elif action == "switch":
+            switch_pq(repo, options)
+        elif action == "convert":
+            convert_package(repo, options)
+    except CommandExecFailed:
+        retval = 1
+    except GitRepositoryError as err:
+        gbp.log.err("Git command failed: %s" % err)
+        retval = 1
+    except GbpError, err:
+        if len(err.__str__()):
+            gbp.log.err(err)
+        retval = 1
+    finally:
+        shutil.rmtree(options.tmp_dir, ignore_errors=True)
+
+    return retval
+
+if __name__ == '__main__':
+    sys.exit(main(sys.argv))
+
-- 
2.1.3

Reply via email to