]> git.ipfire.org Git - thirdparty/git.git/blob - contrib/hg-to-git/hg-to-git.py
GIT-VERSION-FILE: check ./version first.
[thirdparty/git.git] / contrib / hg-to-git / hg-to-git.py
1 #! /usr/bin/python
2
3 """ hg-to-svn.py - A Mercurial to GIT converter
4
5 Copyright (C)2007 Stelian Pop <stelian@popies.net>
6
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2, or (at your option)
10 any later version.
11
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 """
21
22 import os, os.path, sys
23 import tempfile, popen2, pickle, getopt
24 import re
25
26 # Maps hg version -> git version
27 hgvers = {}
28 # List of children for each hg revision
29 hgchildren = {}
30 # Current branch for each hg revision
31 hgbranch = {}
32
33 #------------------------------------------------------------------------------
34
35 def usage():
36
37 print """\
38 %s: [OPTIONS] <hgprj>
39
40 options:
41 -s, --gitstate=FILE: name of the state to be saved/read
42 for incrementals
43
44 required:
45 hgprj: name of the HG project to import (directory)
46 """ % sys.argv[0]
47
48 #------------------------------------------------------------------------------
49
50 def getgitenv(user, date):
51 env = ''
52 elems = re.compile('(.*?)\s+<(.*)>').match(user)
53 if elems:
54 env += 'export GIT_AUTHOR_NAME="%s" ;' % elems.group(1)
55 env += 'export GIT_COMMITER_NAME="%s" ;' % elems.group(1)
56 env += 'export GIT_AUTHOR_EMAIL="%s" ;' % elems.group(2)
57 env += 'export GIT_COMMITER_EMAIL="%s" ;' % elems.group(2)
58 else:
59 env += 'export GIT_AUTHOR_NAME="%s" ;' % user
60 env += 'export GIT_COMMITER_NAME="%s" ;' % user
61 env += 'export GIT_AUTHOR_EMAIL= ;'
62 env += 'export GIT_COMMITER_EMAIL= ;'
63
64 env += 'export GIT_AUTHOR_DATE="%s" ;' % date
65 env += 'export GIT_COMMITTER_DATE="%s" ;' % date
66 return env
67
68 #------------------------------------------------------------------------------
69
70 state = ''
71
72 try:
73 opts, args = getopt.getopt(sys.argv[1:], 's:t:', ['gitstate=', 'tempdir='])
74 for o, a in opts:
75 if o in ('-s', '--gitstate'):
76 state = a
77 state = os.path.abspath(state)
78
79 if len(args) != 1:
80 raise('params')
81 except:
82 usage()
83 sys.exit(1)
84
85 hgprj = args[0]
86 os.chdir(hgprj)
87
88 if state:
89 if os.path.exists(state):
90 print 'State does exist, reading'
91 f = open(state, 'r')
92 hgvers = pickle.load(f)
93 else:
94 print 'State does not exist, first run'
95
96 tip = os.popen('hg tip | head -1 | cut -f 2 -d :').read().strip()
97 print 'tip is', tip
98
99 # Calculate the branches
100 print 'analysing the branches...'
101 hgchildren["0"] = ()
102 hgbranch["0"] = "master"
103 for cset in range(1, int(tip) + 1):
104 hgchildren[str(cset)] = ()
105 prnts = os.popen('hg log -r %d | grep ^parent: | cut -f 2 -d :' % cset).readlines()
106 if len(prnts) > 0:
107 parent = prnts[0].strip()
108 else:
109 parent = str(cset - 1)
110 hgchildren[parent] += ( str(cset), )
111 if len(prnts) > 1:
112 mparent = prnts[1].strip()
113 hgchildren[mparent] += ( str(cset), )
114 else:
115 mparent = None
116
117 if mparent:
118 # For merge changesets, take either one, preferably the 'master' branch
119 if hgbranch[mparent] == 'master':
120 hgbranch[str(cset)] = 'master'
121 else:
122 hgbranch[str(cset)] = hgbranch[parent]
123 else:
124 # Normal changesets
125 # For first children, take the parent branch, for the others create a new branch
126 if hgchildren[parent][0] == str(cset):
127 hgbranch[str(cset)] = hgbranch[parent]
128 else:
129 hgbranch[str(cset)] = "branch-" + str(cset)
130
131 if not hgvers.has_key("0"):
132 print 'creating repository'
133 os.system('git-init-db')
134
135 # loop through every hg changeset
136 for cset in range(int(tip) + 1):
137
138 # incremental, already seen
139 if hgvers.has_key(str(cset)):
140 continue
141
142 # get info
143 prnts = os.popen('hg log -r %d | grep ^parent: | cut -f 2 -d :' % cset).readlines()
144 if len(prnts) > 0:
145 parent = prnts[0].strip()
146 else:
147 parent = str(cset - 1)
148 if len(prnts) > 1:
149 mparent = prnts[1].strip()
150 else:
151 mparent = None
152
153 (fdcomment, filecomment) = tempfile.mkstemp()
154 csetcomment = os.popen('hg log -r %d -v | grep -v ^changeset: | grep -v ^parent: | grep -v ^user: | grep -v ^date | grep -v ^files: | grep -v ^description: | grep -v ^tag:' % cset).read().strip()
155 os.write(fdcomment, csetcomment)
156 os.close(fdcomment)
157
158 date = os.popen('hg log -r %d | grep ^date: | cut -f 2- -d :' % cset).read().strip()
159
160 tag = os.popen('hg log -r %d | grep ^tag: | cut -f 2- -d :' % cset).read().strip()
161
162 user = os.popen('hg log -r %d | grep ^user: | cut -f 2- -d :' % cset).read().strip()
163
164 print '-----------------------------------------'
165 print 'cset:', cset
166 print 'branch:', hgbranch[str(cset)]
167 print 'user:', user
168 print 'date:', date
169 print 'comment:', csetcomment
170 print 'parent:', parent
171 if mparent:
172 print 'mparent:', mparent
173 if tag:
174 print 'tag:', tag
175 print '-----------------------------------------'
176
177 # checkout the parent if necessary
178 if cset != 0:
179 if hgbranch[str(cset)] == "branch-" + str(cset):
180 print 'creating new branch', hgbranch[str(cset)]
181 os.system('git-checkout -b %s %s' % (hgbranch[str(cset)], hgvers[parent]))
182 else:
183 print 'checking out branch', hgbranch[str(cset)]
184 os.system('git-checkout %s' % hgbranch[str(cset)])
185
186 # merge
187 if mparent:
188 if hgbranch[parent] == hgbranch[str(cset)]:
189 otherbranch = hgbranch[mparent]
190 else:
191 otherbranch = hgbranch[parent]
192 print 'merging', otherbranch, 'into', hgbranch[str(cset)]
193 os.system(getgitenv(user, date) + 'git-merge --no-commit -s ours "" %s %s' % (hgbranch[str(cset)], otherbranch))
194
195 # remove everything except .git and .hg directories
196 os.system('find . \( -path "./.hg" -o -path "./.git" \) -prune -o ! -name "." -print | xargs rm -rf')
197
198 # repopulate with checkouted files
199 os.system('hg update -C %d' % cset)
200
201 # add new files
202 os.system('git-ls-files -x .hg --others | git-update-index --add --stdin')
203 # delete removed files
204 os.system('git-ls-files -x .hg --deleted | git-update-index --remove --stdin')
205
206 # commit
207 os.system(getgitenv(user, date) + 'git-commit -a -F %s' % filecomment)
208 os.unlink(filecomment)
209
210 # tag
211 if tag and tag != 'tip':
212 os.system(getgitenv(user, date) + 'git-tag %s' % tag)
213
214 # delete branch if not used anymore...
215 if mparent and len(hgchildren[str(cset)]):
216 print "Deleting unused branch:", otherbranch
217 os.system('git-branch -d %s' % otherbranch)
218
219 # retrieve and record the version
220 vvv = os.popen('git-show | head -1').read()
221 vvv = vvv[vvv.index(' ') + 1 : ].strip()
222 print 'record', cset, '->', vvv
223 hgvers[str(cset)] = vvv
224
225 os.system('git-repack -a -d')
226
227 # write the state for incrementals
228 if state:
229 print 'Writing state'
230 f = open(state, 'w')
231 pickle.dump(hgvers, f)
232
233 # vim: et ts=8 sw=4 sts=4