| 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 |