hg-uniquify-closed-branch / hucb.py

#!/usr/bin/env python

import subprocess


def command(cmds, *args, **kwds):
    cmds = list(a for a in cmds if a)  # exclude empty string
    proc = subprocess.Popen(
        cmds, *args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, **kwds)
    ret = proc.wait()
    stdout = proc.stdout.read()
    if ret != 0:
        print stdout
        import sys
        sys.exit(1)
    return stdout


def hg_heads(sep, *args):
    return command(
        ['hg', 'heads', '--template', sep.join(['{branch}', '{node}\n'])]
        + list(args))


def hg_branch(revision, name):
    command(['hg', 'update', revision])
    command(['hg', 'branch', name])
    date = command(['hg', 'log', '--rev', revision,
                    '--template', '{date|hgdate}'])
    (ts, tz) = date.split(' ', 1)
    newts = int(ts) + 1
    newdate = '{0} {1}'.format(newts, tz)
    command(
        ['hg', 'commit', '--date', newdate, '--message',
         "[HUCB]\n\nAdd new branch '{0}' to uniquify heads.".format(name)])


def append_branch(branch_heads, heads, sep):
    """Parse strings in `heads` and add them to `branch_heads`."""
    for head in heads:
        (branch, node) = head.split(sep, 1)
        branch_heads.setdefault(branch, [])
        branch_heads[branch].append(node)


def sort_branches(branch_heads):
    """Make sure 'default' branch comes first (for cosmetic reason)."""
    brancheset = set(branch_heads)
    if 'default' in brancheset:
        brancheset.remove('default')
        return ['default'] + list(brancheset)
    else:
        return list(brancheset)


def hucb(dry_run, sep, branch_temp):
    heads_active = hg_heads(sep).splitlines()
    heads_all = hg_heads(sep, '--closed').splitlines()
    heads_closed = set(heads_all) - set(heads_active)

    branch_heads = {}
    append_branch(branch_heads, heads_active, sep)
    append_branch(branch_heads, heads_closed, sep)

    i = 0
    for br in sort_branches(branch_heads):
        heads = branch_heads[br]
        for node in heads[1:]:
            newbranch = branch_temp.format(i=i)
            print 'node:{node} | branch: {branch} -> {newbranch}'.format(
                node=node, branch=br, newbranch=newbranch)
            if not dry_run:
                hg_branch(node, newbranch)
            i += 1


def main():
    from argparse import ArgumentParser
    parser = ArgumentParser(description=__doc__)
    parser.add_argument('--dry-run', '-n', default=False, action='store_true')
    parser.add_argument('--sep', default='#',
                        help='A character to be used to parse '
                        'branch and node name.  Use a character '
                        'which is not part of your branch names.')
    parser.add_argument('--branch-temp', default='br{i:02d}',
                        help='branch name template (default: %(default)s)')
    args = parser.parse_args()
    hucb(**vars(args))


if __name__ == '__main__':
    main()
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.