Use a full repo path so we can expand to include trunk/scripts later.
[invirt/scripts/git-migration.git] / git-migrate
1 #!/usr/bin/python
2
3 import os
4 import sys
5 import subprocess
6 import shutil
7
8 def tagBase(pkg):
9     p = subprocess.Popen(['git', 'tag',
10                           '-l',
11                           'base'],
12                          cwd='%s.git' % pkg,
13                          stdout=subprocess.PIPE)
14     p.wait()
15     if p.stdout.read().strip() != '':
16         return
17     
18     p = subprocess.Popen(['git', 'rev-list',
19                           '--reverse',
20                           'master'],
21                          cwd='%s.git' % pkg,
22                          stdout=subprocess.PIPE)
23     p.wait()
24     base = p.stdout.read().split()[0]
25     
26     subprocess.check_call(['git', 'tag',
27                            'base',
28                            base],
29                           cwd='%s.git' % pkg)
30
31 def clonePackage(base, pkg):
32     path = '%s/%s' % (base, pkg)
33     pkg = os.path.basename(pkg)
34     
35     if not os.path.isdir('%s.git' % pkg):
36         if os.path.isdir(pkg):
37             shutil.rmtree(pkg)
38         # Use --no-follow-parent because we're going to handle that with
39         # grafts.
40         subprocess.check_call(['git', 'svn', 'clone',
41                                '--no-follow-parent',
42                                '-Aauthors',
43                                '-q',
44                                '--no-metadata',
45                                '%s' % path],
46                               stdout=subprocess.PIPE)
47         
48         # Then make the repository bare, because git-svn can't do this
49         shutil.move('%s/.git' % pkg, '%s.git' % pkg)
50         shutil.rmtree(pkg)
51         subprocess.check_call(['git', 'config', 'core.bare', 'true'],
52                               cwd='%s.git' % pkg)
53         
54     # Some of these repos have a rev where everything was deleted
55     # as a result of the move. We don't want that rev to exist.
56     p = subprocess.Popen(['git', 'ls-tree', 'HEAD'],
57                          cwd='%s.git' % pkg,
58                          stdout=subprocess.PIPE)
59     p.wait()
60     if len(p.stdout.read()) == 0:
61         subprocess.check_call(['git', 'reset', '--soft', 'HEAD^'],
62                               cwd='%s.git' % pkg)
63     
64     tagBase(pkg)
65
66 def cloneAllPackages(base):
67     for pkg in open('package-list'):
68         clonePackage(base, pkg.strip())
69
70 def mergeHistory(old_pkg, new_pkg, n):
71     n = int(n)
72     
73     subprocess.check_call(['git', 'push',
74                            '../%s.git' % new_pkg,
75                            'master:refs/heads/%s' % old_pkg],
76                           cwd='%s.git' % old_pkg)
77     
78     # Find the merge commit
79     if n == 0:
80         p = subprocess.Popen(['git', 'rev-parse',
81                               'base'],
82                              cwd='%s.git' % new_pkg,
83                              stdout=subprocess.PIPE)
84     else:
85         p = subprocess.Popen(['git', 'rev-list',
86                               '--reverse',
87                               '--boundary',
88                               '--skip=%s' % (n - 1),
89                               'base..master'],
90                              cwd='%s.git' % new_pkg,
91                              stdout=subprocess.PIPE)
92     p.wait()
93     new_rev = p.stdout.read().split()[0].strip('-')
94     
95     # Find any other parents of the merge commit
96     p = subprocess.Popen(['git', 'log',
97                           '-1',
98                           '--pretty=format:%P',
99                           new_rev],
100                          cwd='%s.git' % new_pkg,
101                          stdout=subprocess.PIPE)
102     p.wait()
103     parents = p.stdout.read().split()
104     
105     # Find the additional parent we're adding
106     p = subprocess.Popen(['git', 'rev-parse',
107                           old_pkg],
108                          cwd='%s.git' % new_pkg,
109                          stdout=subprocess.PIPE)
110     p.wait()
111     parents.append(p.stdout.read().strip())
112     
113     # Write out the grafts file
114     f = open('%s.git/info/grafts' % new_pkg, 'a')
115     print >>f, '%s %s' % (new_rev, ' '.join(parents))
116     f.close()
117     
118     # Run filter-branch
119     subprocess.call(['git', 'filter-branch',
120                      '--tag-name-filter', 'cat',
121                      '--',
122                      '--all'],
123                     cwd='%s.git' % new_pkg)
124     
125     subprocess.call(['git', 'branch',
126                      '-D',
127                      old_pkg],
128                     cwd='%s.git' % new_pkg)
129     shutil.rmtree('%s.git/refs/original' % new_pkg, True)
130
131 def mergeHistories():
132     merges = []
133     for line in open('merges'):
134         line = line.strip()
135         if line == '' or line[0] == '#':
136             continue
137         
138         merges.append(line.split())
139     
140     for merge in merges:
141         mergeHistory(*merge)
142     
143     for merge in merges:
144         shutil.rmtree('%s.git' % merge[0])
145
146 if __name__ == '__main__':
147     try:
148         base = sys.argv[1]
149     except:
150         base = 'svn://invirt.mit.edu/trunk'
151     
152     cloneAllPackages(base)
153     mergeHistories()