diff options
-rwxr-xr-x | src/fedpkg.py | 3 | ||||
-rw-r--r-- | src/pyfedpkg/__init__.py | 2 | ||||
-rwxr-xr-x | src/pyfedpkg/initial_merge.py | 222 |
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 |