summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHans Ulrich Niedermann <hun@n-dimensional.de>2010-08-08 17:24:03 +0200
committerHans Ulrich Niedermann <hun@n-dimensional.de>2010-08-08 17:24:03 +0200
commit57d5f2d932cce6895a7fe7307d241c35c0caa0c7 (patch)
treea286ae4bf38b38a65b1ac6fb87ad79d3a31d67da
parent63570e8513a930a3d61cf8f3b07fe6cfdd8a1d2c (diff)
downloadfedora-packager-57d5f2d932cce6895a7fe7307d241c35c0caa0c7.tar.gz
fedora-packager-57d5f2d932cce6895a7fe7307d241c35c0caa0c7.tar.xz
fedora-packager-57d5f2d932cce6895a7fe7307d241c35c0caa0c7.zip
Add "fedpkg initial-merge" command
Performs a 'git merge' of all git branches with the same content (i.e. with the same package spec files, patch files, etc.), regardless of their history. This is useful after Fedora's dist-cvs to dist-git migration, as often different branches have different histories but the same content on the filesystem. After these initial merges of identical trees, future merges between the branches will be a lot easier: Easier to follow in the dependency graph, and easier to perform without conflicts.
-rwxr-xr-xsrc/fedpkg.py3
-rw-r--r--src/pyfedpkg/__init__.py2
-rwxr-xr-xsrc/pyfedpkg/initial_merge.py222
3 files changed, 227 insertions, 0 deletions
diff --git a/src/fedpkg.py b/src/fedpkg.py
index 08d3274..ec04620 100755
--- a/src/fedpkg.py
+++ b/src/fedpkg.py
@@ -939,6 +939,9 @@ packages will be built sequentially.
' name-version-release')
parser_verrel.set_defaults(command = verrel)
+ # Initial branch merges
+ pyfedpkg.initial_merge.add_parser_to(subparsers)
+
# Parse the args
args = parser.parse_args()
diff --git a/src/pyfedpkg/__init__.py b/src/pyfedpkg/__init__.py
index fe4f3a0..1df460a 100644
--- a/src/pyfedpkg/__init__.py
+++ b/src/pyfedpkg/__init__.py
@@ -28,6 +28,8 @@ import stat
import StringIO
import OpenSSL
+from . import initial_merge
+
# Define some global variables, put them here to make it easy to change
LOOKASIDE = 'http://pkgs.fedoraproject.org/repo/pkgs'
diff --git a/src/pyfedpkg/initial_merge.py b/src/pyfedpkg/initial_merge.py
new file mode 100755
index 0000000..18ec3a8
--- /dev/null
+++ b/src/pyfedpkg/initial_merge.py
@@ -0,0 +1,222 @@
+#!/usr/bin/python
+# initial_merge.py - perform initial merge after dist-git migration
+#
+# Copyright (C) 2010 Hans Ulrich Niedermann <hun@n-dimensional.de>
+#
+# 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. See http://www.gnu.org/copyleft/gpl.html for
+# the full text of the license.
+
+
+# Note that this docstring is used as description for the command.
+"""\
+Performs a 'git merge' of all git branches with the same content
+(i.e. with the same package spec files, patch files, etc.), regardless
+of their history.
+
+This is useful after Fedora's dist-cvs to dist-git migration, as often
+different branches have different histories but the same content on the
+filesystem.
+
+After these initial merges of identical trees, future merges between
+the branches will be a lot easier: Easier to follow in the dependency
+graph, and easier to perform without conflicts.
+"""
+
+import argparse
+import sys
+import os
+import subprocess
+
+import git
+import pyfedpkg
+
+
+__all__ = [
+ 'add_parser_to',
+ 'handle_path',
+ 'handle_curdir'
+ ]
+
+
+def str_numsplit(s):
+ """Helper function for properly ordering branch names
+
+ Handles branch names like 'fc6', 'f8', 'f10', 'master'.
+ """
+ assert(not s[0].isdigit())
+ if not s[-1].isdigit():
+ return s
+ i = len(s)
+ while i>0:
+ i = i - 1
+ if not s[i].isdigit():
+ break
+ prefix, numstr = s[0:i+1], s[i+1:]
+ return (prefix, int(numstr))
+
+
+def cmp_relbranch(a, b):
+ """Comparison function for sort() calls"""
+ asplit = str_numsplit(a)
+ bsplit = str_numsplit(b)
+ if type(asplit) == str and type(bsplit) == str:
+ return cmp(asplit,bsplit)
+ elif type(asplit) == str and type(bsplit) == tuple:
+ return 1
+ elif type(asplit) == tuple and type(bsplit) == str:
+ return -1
+ elif type(asplit) == tuple and type(bsplit) == tuple:
+ if asplit[0] in ('f','fc') and bsplit[0] in ('f','fc'):
+ return cmp(asplit[1], bsplit[1])
+ else:
+ return cmp(asplit,bsplit)
+ else:
+ return cmp(asplit,bsplit)
+
+
+def cmp_Branch(a, b):
+ """Comparison function for sort() calls"""
+ if a.sha == b.sha:
+ return cmp_relbranch(a.localbranch,b.localbranch)
+ else:
+ return cmp(a.sha,b.sha)
+
+
+class Branch(object):
+
+ """Convenience class for handling branches to initial-merge"""
+
+ def __init__(self, sha, origbranch):
+ self.sha, self.origbranch = sha, origbranch
+ a = self.origbranch.split('/')
+ assert(a[0] == 'origin')
+ assert(a[-1] == 'master')
+ self.localbranch = a[1]
+
+ def __repr__(self):
+ return "%(sha)s %(origbranch)s" % self.__dict__
+
+
+def do_initial_merge(into, to_merge):
+ print
+ print "######## Merging", [ x.localbranch for x in to_merge ], \
+ "into", into.localbranch, "########"
+ pyfedpkg.switch_branch(into.localbranch)
+ repo = git.Repo()
+ print "Merging", [ x.origbranch for x in to_merge], "into", into.localbranch
+ repo.git.merge(*(['-m', '"Initial peudo merge for dist-git setup"',
+ '-s', 'ours'] +
+ [ x.origbranch for x in to_merge]))
+ for t in to_merge:
+ pyfedpkg.switch_branch(t.localbranch)
+ print "Merging", into.localbranch, "into", t.localbranch
+ repo.git.merge(into.localbranch)
+ pyfedpkg.switch_branch(into.localbranch)
+
+
+class Filter(object):
+
+ """Branch filter
+
+ Feed branches to this filter via eat(), and flush() it after you
+ are done. The filter will detect branches with the same tree sha,
+ and call do_initial_merge() for them.
+ """
+
+ def __reset(self):
+ self.branch_list = []
+
+ def __init__(self):
+ self.__reset()
+
+ def __git_merge(self):
+ head = self.branch_list[-1]
+ others = self.branch_list[:-1]
+ do_initial_merge(head, others)
+
+ def flush(self):
+ if len(self.branch_list) < 2:
+ return
+ # print "Flush", self.branch_list
+ self.__git_merge()
+ self.__reset()
+
+ def eat(self, item):
+ if self.branch_list:
+ last = self.branch_list[-1]
+ if last.sha == item.sha:
+ # print " ", "=", item
+ self.branch_list.extend([item])
+ else:
+ # print " ", "?", item
+ self.flush()
+ self.branch_list = [item]
+ else:
+ self.branch_list = [item]
+
+
+def handle_curdir():
+ repo = git.Repo()
+ _locals, remotes = pyfedpkg._list_branches(repo=repo)
+ aa = [ Branch(repo.git.rev_parse('%s^{tree}' % b), b) for b in remotes ]
+ aa.sort(cmp_Branch)
+ print "Branches sorted by tree sha:"
+ for x in aa: print " ", x
+
+ n = 0
+ f = Filter()
+ while n < len(aa):
+ f.eat(aa[n])
+ n = n + 1
+ f.flush()
+
+ print
+ print "FINISHED."
+
+
+def handle_path(path=None):
+ if not path:
+ path = os.getcwd()
+ oldcwd = os.getcwd()
+ os.chdir(path)
+ print "########", "Entering", path, "########"
+ handle_curdir()
+ print "Leaving", os.getcwd()
+ os.chdir(oldcwd)
+
+
+class DirListAction(argparse.Action):
+
+ def __init__(self, *args, **kwargs):
+ super(DirListAction, self).__init__(*args, **kwargs)
+ self.default_value = ['.']
+
+ def __call__(self, parser, namespace, values, option_string=None):
+ if values:
+ setattr(namespace, self.dest, values)
+ else:
+ setattr(namespace, self.dest, self.default)
+
+
+def fedpkg_command(args):
+ for repo in args.repos:
+ handle_path(repo)
+
+
+_module_doc = __doc__
+
+
+def add_parser_to(subparsers):
+ sp = subparsers.add_parser('initial-merge',
+ help = 'git merge to join branches with identical trees',
+ formatter_class=argparse.RawDescriptionHelpFormatter,
+ description = _module_doc)
+ sp.add_argument('repos', metavar='repo-path',
+ nargs='*', default=['.'],
+ action=DirListAction,
+ help = 'Path to a repo to initial-merge')
+ sp.set_defaults(command = fedpkg_command)
+ return sp