a7238ea by Dustin Sallings at 2009-01-07 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.
34880f9 by Dustin Sallings at 2009-01-07 15
16
Read more about this here:
17
<http://dustin.github.com/2009/01/06/git-reroot.html>
a7238ea by Dustin Sallings at 2009-01-07 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
ffa4698 by Dustin Sallings at 2009-01-07 31
def cleanup_log(commit, c):
a7238ea by Dustin Sallings at 2009-01-07 32
    return c[:c.index("--" + commit + "--")]
33
ffa4698 by Dustin Sallings at 2009-01-07 34
def recommit(commit_log, onto):
35
    commit, tree, an, ae, ad, cn, ce, cd, log = commit_log
36
    log = cleanup_log(commit, log)
a7238ea by Dustin Sallings at 2009-01-07 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)
ffa4698 by Dustin Sallings at 2009-01-07 44
    stdout, stderr = p.communicate(log)
a7238ea by Dustin Sallings at 2009-01-07 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
ffa4698 by Dustin Sallings at 2009-01-07 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])
a7238ea by Dustin Sallings at 2009-01-07 56
    h = run_cmd("git rev-parse HEAD")
ffa4698 by Dustin Sallings at 2009-01-07 57
    commits=[s.split("\n", 8) for s in log.split("\0\n")]
a7238ea by Dustin Sallings at 2009-01-07 58
    done=0
ffa4698 by Dustin Sallings at 2009-01-07 59
    for commit in commits:
60
        h = recommit(commit, h)
a7238ea by Dustin Sallings at 2009-01-07 61
        done += 1
ffa4698 by Dustin Sallings at 2009-01-07 62
        sys.stdout.write("  %d/%d\r" % (done, len(commits)))
a7238ea by Dustin Sallings at 2009-01-07 63
        sys.stdout.flush()
64
f04894c by Dustin Sallings at 2009-01-07 65
    print "The newly created history is available as " + h