1
#!/usr/bin/env python
2
"""
3
Pick up all of the changes of a tree and reparent them elsewhere.
4
5
Given some arbitrary sequence of commits, all of the changes from the
6
sequence will be applied in the new location.
7
8
This has an effect similar to rebase, but is purely content driven.
9
Each commit is recorded with the exact state the tree was in, but
10
repositioned on top of the current tree.  This is likely the wrong
11
tool for whatever job you're hoping to use it on.
12
13
It will never have a conflict, but at the consequence of sometimes
14
providing you with diffs that don't make a lot of sense.
15
16
Read more about this here:
17
<http://dustin.github.com/2009/01/06/git-reroot.html>
18
"""
19
20
import os
21
import sys
22
import commands
23
import subprocess
24
25
def run_cmd(cmd):
26
    exitstatus, outtext = commands.getstatusoutput(cmd)
27
    if exitstatus != 0:
28
        raise RuntimeException("Command failed.")
29
    return outtext
30
31
def cleanup_log(commit, c):
32
    return c[:c.index("--" + commit + "--")]
33
34
def recommit(commit_log, onto):
35
    commit, tree, an, ae, ad, cn, ce, cd, log = commit_log
36
    log = cleanup_log(commit, log)
37
    env = {'GIT_COMMITTER_NAME': cn, 'GIT_COMMITER_EMAIL': ce,
38
           'GIT_AUTHOR_NAME': an, 'GIT_AUTHOR_EMAIL': ae,
39
           'GIT_COMMITER_DATE': cd, 'GIT_AUTHOR_DATE': ad,
40
           'PATH': os.getenv("PATH")}
41
    args=["git", "commit-tree", tree, '-p', onto]
42
    p = subprocess.Popen(args, stdin=subprocess.PIPE,
43
                         stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env)
44
    stdout, stderr = p.communicate(log)
45
    sys.stderr.write(stderr)
46
    p.wait()
47
    if p.returncode != 0:
48
        sys.exit(p.returncode)
49
50
    return stdout.strip()
51
52
if __name__ == '__main__':
53
54
    log = run_cmd('git log --reverse --pretty=format:"%H%n%T%n%an%n%ae%n%ad%n%cn%n%ce%n%cd%n%s%n%n%b--%H--%x00" '
55
                  + sys.argv[1])
56
    h = run_cmd("git rev-parse HEAD")
57
    commits=[s.split("\n", 8) for s in log.split("\0\n")]
58
    done=0
59
    for commit in commits:
60
        h = recommit(commit, h)
61
        done += 1
62
        sys.stdout.write("  %d/%d\r" % (done, len(commits)))
63
        sys.stdout.flush()
64
65
    print "The newly created history is available as " + h