rpm: support squashing commits in patch generation
authorMarkus Lehtonen <markus.lehtonen@linux.intel.com>
Tue, 12 Jun 2012 07:42:41 +0000 (10:42 +0300)
committerMarkus Lehtonen <markus.lehtonen@linux.intel.com>
Fri, 14 Nov 2014 12:45:07 +0000 (14:45 +0200)
Implements an option for git-buildpackage-rpm and gbp-pq to squash
commits (from upstream) up to certain tree-ish into one monolithic diff.
Useful e.g. if you wan't to auto-generate a stable update patch.

The new format of the cmdline option filename is commit-ish followed by
(optionally) a colon and the desired diff filename base. Suffix '.diff'
is added by GBP.

Magic word 'HEAD' translates to the end-commit when given as the
squash-point.  This allows one to configure gbp to always squash all
commits into one monolithic diff.

Signed-off-by: Markus Lehtonen <markus.lehtonen@linux.intel.com>
gbp-rpm.conf
gbp/config.py
gbp/scripts/buildpackage_rpm.py
gbp/scripts/pq_rpm.py

index 2e31a68..27f8025 100644 (file)
@@ -26,6 +26,8 @@
 #spec-file = gbp.spec
 # Compress auto-generated patches
 #patch-export-compress=100k
+# Squash commits until certain tree-ish into one diff
+#patch-export-squash-until = stable-updates:stable
 # Export patches with numbering in filenames
 #patch-numbers = False
 
 ###
 [gbp-pq-rpm]
 # Name of the patch-queue / development branch
-pq-branch = %(branch)s-devel
+#pq-branch = %(branch)s-devel
 
 ###
 ### Options only affecting gbp-clone
index cbc33a5..294eb92 100644 (file)
@@ -611,6 +611,7 @@ class GbpOptionParserRpm(GbpOptionParser):
             'rpmbuild-buildrootdir'     : 'BUILDROOT',
             'patch-export'              : 'False',
             'patch-export-compress'     : '0',
+            'patch-export-squash-until' : '',
             'merge'                     : 'False',
             'pristine-tarball-name'     : 'auto',
             'orig-prefix'               : 'auto',
@@ -650,6 +651,10 @@ class GbpOptionParserRpm(GbpOptionParser):
                 "Compress (auto-generated) patches larger than given number of "
                 "bytes, 0 never compresses, default is "
                 "'%(patch-export-compress)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'",
             'pristine-tarball-name':
                 "Filename to record to pristine-tar, set to 'auto' to not "
                 "mangle the file name, default is '%(pristine-tarball-name)s'",
index 5e71caa..a84c378 100755 (executable)
@@ -368,6 +368,7 @@ def parse_args(argv, prefix):
                       help="only export packaging files, don't build")
     export_group.add_boolean_config_file_option("patch-export", dest="patch_export")
     export_group.add_config_file_option("patch-export-compress", dest="patch_export_compress")
+    export_group.add_config_file_option("patch-export-squash-until", dest="patch_export_squash_until")
     export_group.add_boolean_config_file_option(option_name="patch-numbers", dest="patch_numbers")
     options, args = parser.parse_args(args)
 
index 204091e..377ae9f 100755 (executable)
@@ -29,7 +29,7 @@ import gzip
 import subprocess
 from gbp.config import (GbpOptionParserRpm, GbpOptionGroup)
 from gbp.rpm.git import (GitRepositoryError, RpmGitRepository)
-from gbp.git import GitModifier
+from gbp.git.modifier import GitModifier, GitTz
 from gbp.command_wrappers import (Command, GitCommand, RunAtCommand,
                                   CommandExecFailed)
 from gbp.errors import GbpError
@@ -61,7 +61,7 @@ def compress_patches(patches, compress_size=0):
     return ret_patches
 
 
-def generate_patches(repo, start, end, outdir, options):
+def generate_patches(repo, start, squash, end, outdir, options):
     """
     Generate patch files from git
     """
@@ -72,6 +72,7 @@ def generate_patches(repo, start, end, outdir, options):
         if not repo.has_treeish(treeish):
             raise GbpError('%s not a valid tree-ish' % treeish)
 
+    start_sha1 = repo.rev_parse("%s^0" % start)
     try:
         end_commit = end
         end_commit_sha1 = repo.rev_parse("%s^0" % end_commit)
@@ -85,6 +86,26 @@ def generate_patches(repo, start, end, outdir, options):
     if repo.get_merge_base(start_sha1, end_commit_sha1) != start_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)
+            if patch_fn:
+                patches.append(patch_fn)
+                start = squash_sha1
+
     # Generate patches
     for commit in reversed(repo.get_commits(start, end_commit)):
         info = repo.get_commit_info(commit)
@@ -133,10 +154,16 @@ 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, end,
+    patches, _commands = generate_patches(repo, start, squash, end,
                                           spec.specdir, options)
     spec.update_patches(patches)
     spec.write_spec_file()
@@ -394,6 +421,7 @@ def main(argv):
                            "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")
 
     (options, args) = parser.parse_args(argv)
     gbp.log.setup(options.color, options.verbose)