hgbook / contrib / hg-replay

#!/usr/bin/env python
# Adapter for using interdiff with mercurial's extdiff extension.
# Copyright 2006 Bryan O'Sullivan <>
# This software may be used and distributed according to the terms of
# the GNU General Public License, incorporated herein by reference.

import os
import shutil
import sys
import tempfile

if len(sys.argv) < 4:
    print >> sys.stderr, ('usage: %s srcrepo destrepo cset-to-omit [...]' %

srcrepo, destrepo = sys.argv[1], sys.argv[2]
omit = sys.argv[3:]
changemap = {}
revs = []

parent = None

sys.stdout.write('gathering history...')

for line in os.popen("hg --cwd %r log -r0:tip --template '{rev}:{node} {parents}\n'" % srcrepo):
    changes = line.split()
    cset = changes[0].split(':')[1]
    rev = len(revs)
    changemap[cset] = rev
    if len(changes) >= 2:
        p1 = int(changes[1].split(':', 1)[0])
    if len(changes) == 3:
        p2 = int(changes[2].split(':', 1)[0])
        p2 = None
    if len(changes) == 1:
        p1 = parent
    revs.append((cset, p1, p2))
    parent = rev

sys.stdout.write(' %d revs\n' % len(revs))

def findrev(r):
        i = int(r)
        if str(i) == r:
            rev = i
        if rev < 0:
            rev += len(revs)
        if rev < 0 or rev > len(revs):
            print >> sys.stderr, 'bad changeset: %r' % r
        cset = revs[rev][0]
    except ValueError:
        cset = r
        matches = [changemap[c] for c in changemap if c.startswith(cset)]
        if len(matches) != 1:
            print >> sys.stderr, 'bad changeset: %r' % r
        rev = matches[0]
    return rev

def run(cmd):
    print cmd
    ret = os.system(cmd)
    if ret:
        print >> sys.stderr, 'failure:', cmd

omit = map(findrev, omit)
newrevs = revs[:omit[0]]
tip = len(newrevs) - 1
run('hg clone -q -r%s %r %r' % (tip, srcrepo, destrepo))
os.environ['HGMERGE'] = 'true'

patchdir = tempfile.mkdtemp(prefix='replay.')
    run('hg --cwd %r export --git -o %r%s%%R %d:tip' %
        (srcrepo, patchdir, os.sep, omit[0]+1))
    for rev in xrange(omit[0], len(revs)):
        if rev in omit:
            print 'omit', rev
            newrevs.append((None, revs[rev][1], None))
        _, p1, p2 = revs[rev]
        np1 = newrevs[p1][1]
        if tip != np1:
            run('hg --cwd %r update -q -C %s' % (destrepo, np1))
        np2 = None
        if p2:
            np2 = newrevs[p2][1]
            run('hg --cwd %r merge -q %s' % (destrepo, np2))
            print >> sys.stderr, 'XXX - cannot handle merges properly yet'
        run('hg --cwd %r import -q -f %r%s%d' % (destrepo, patchdir, os.sep, rev))
        tip = len(newrevs) - 1
        newrevs.append((None, tip, np2))
    print 'cleaning up ...'
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
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.