]> git.ipfire.org Git - thirdparty/git.git/blame - contrib/hg-to-git/hg-to-git.py
hg-to-git: don't import the unused popen2 module
[thirdparty/git.git] / contrib / hg-to-git / hg-to-git.py
CommitLineData
98d47d4c
SP
1#! /usr/bin/python
2
1bc7c13a 3""" hg-to-git.py - A Mercurial to GIT converter
98d47d4c
SP
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
22import os, os.path, sys
b0c051d1 23import tempfile, pickle, getopt
98d47d4c
SP
24import re
25
26# Maps hg version -> git version
27hgvers = {}
28# List of children for each hg revision
29hgchildren = {}
1bc7c13a
MD
30# List of parents for each hg revision
31hgparents = {}
98d47d4c
SP
32# Current branch for each hg revision
33hgbranch = {}
7c0d741a
MG
34# Number of new changesets converted from hg
35hgnewcsets = 0
98d47d4c
SP
36
37#------------------------------------------------------------------------------
38
39def usage():
40
41 print """\
42%s: [OPTIONS] <hgprj>
43
44options:
45 -s, --gitstate=FILE: name of the state to be saved/read
46 for incrementals
7c0d741a
MG
47 -n, --nrepack=INT: number of changesets that will trigger
48 a repack (default=0, -1 to deactivate)
37a12dda 49 -v, --verbose: be verbose
98d47d4c
SP
50
51required:
52 hgprj: name of the HG project to import (directory)
53""" % sys.argv[0]
54
55#------------------------------------------------------------------------------
56
57def getgitenv(user, date):
58 env = ''
59 elems = re.compile('(.*?)\s+<(.*)>').match(user)
60 if elems:
61 env += 'export GIT_AUTHOR_NAME="%s" ;' % elems.group(1)
62 env += 'export GIT_COMMITER_NAME="%s" ;' % elems.group(1)
63 env += 'export GIT_AUTHOR_EMAIL="%s" ;' % elems.group(2)
64 env += 'export GIT_COMMITER_EMAIL="%s" ;' % elems.group(2)
65 else:
66 env += 'export GIT_AUTHOR_NAME="%s" ;' % user
67 env += 'export GIT_COMMITER_NAME="%s" ;' % user
68 env += 'export GIT_AUTHOR_EMAIL= ;'
69 env += 'export GIT_COMMITER_EMAIL= ;'
70
71 env += 'export GIT_AUTHOR_DATE="%s" ;' % date
72 env += 'export GIT_COMMITTER_DATE="%s" ;' % date
73 return env
74
75#------------------------------------------------------------------------------
76
77state = ''
7c0d741a 78opt_nrepack = 0
37a12dda 79verbose = False
98d47d4c
SP
80
81try:
37a12dda 82 opts, args = getopt.getopt(sys.argv[1:], 's:t:n:v', ['gitstate=', 'tempdir=', 'nrepack=', 'verbose'])
98d47d4c
SP
83 for o, a in opts:
84 if o in ('-s', '--gitstate'):
85 state = a
86 state = os.path.abspath(state)
7c0d741a
MG
87 if o in ('-n', '--nrepack'):
88 opt_nrepack = int(a)
37a12dda
JS
89 if o in ('-v', '--verbose'):
90 verbose = True
98d47d4c 91 if len(args) != 1:
6376cffa 92 raise Exception('params')
98d47d4c
SP
93except:
94 usage()
95 sys.exit(1)
96
97hgprj = args[0]
98os.chdir(hgprj)
99
100if state:
101 if os.path.exists(state):
37a12dda
JS
102 if verbose:
103 print 'State does exist, reading'
98d47d4c
SP
104 f = open(state, 'r')
105 hgvers = pickle.load(f)
106 else:
107 print 'State does not exist, first run'
108
2553ede5
MV
109sock = os.popen('hg tip --template "{rev}"')
110tip = sock.read()
111if sock.close():
112 sys.exit(1)
37a12dda
JS
113if verbose:
114 print 'tip is', tip
98d47d4c
SP
115
116# Calculate the branches
37a12dda
JS
117if verbose:
118 print 'analysing the branches...'
98d47d4c 119hgchildren["0"] = ()
1bc7c13a 120hgparents["0"] = (None, None)
98d47d4c
SP
121hgbranch["0"] = "master"
122for cset in range(1, int(tip) + 1):
123 hgchildren[str(cset)] = ()
13bf1a99 124 prnts = os.popen('hg log -r %d --template "{parents}"' % cset).read().strip().split(' ')
1bc7c13a
MD
125 prnts = map(lambda x: x[:x.find(':')], prnts)
126 if prnts[0] != '':
98d47d4c
SP
127 parent = prnts[0].strip()
128 else:
129 parent = str(cset - 1)
130 hgchildren[parent] += ( str(cset), )
131 if len(prnts) > 1:
132 mparent = prnts[1].strip()
133 hgchildren[mparent] += ( str(cset), )
134 else:
135 mparent = None
136
1bc7c13a
MD
137 hgparents[str(cset)] = (parent, mparent)
138
98d47d4c
SP
139 if mparent:
140 # For merge changesets, take either one, preferably the 'master' branch
141 if hgbranch[mparent] == 'master':
142 hgbranch[str(cset)] = 'master'
143 else:
144 hgbranch[str(cset)] = hgbranch[parent]
145 else:
146 # Normal changesets
147 # For first children, take the parent branch, for the others create a new branch
148 if hgchildren[parent][0] == str(cset):
149 hgbranch[str(cset)] = hgbranch[parent]
150 else:
151 hgbranch[str(cset)] = "branch-" + str(cset)
152
153if not hgvers.has_key("0"):
154 print 'creating repository'
af9a01e1 155 os.system('git init')
98d47d4c
SP
156
157# loop through every hg changeset
158for cset in range(int(tip) + 1):
159
160 # incremental, already seen
161 if hgvers.has_key(str(cset)):
162 continue
7c0d741a 163 hgnewcsets += 1
98d47d4c
SP
164
165 # get info
1bc7c13a
MD
166 log_data = os.popen('hg log -r %d --template "{tags}\n{date|date}\n{author}\n"' % cset).readlines()
167 tag = log_data[0].strip()
168 date = log_data[1].strip()
169 user = log_data[2].strip()
170 parent = hgparents[str(cset)][0]
171 mparent = hgparents[str(cset)][1]
172
173 #get comment
98d47d4c 174 (fdcomment, filecomment) = tempfile.mkstemp()
1bc7c13a 175 csetcomment = os.popen('hg log -r %d --template "{desc}"' % cset).read().strip()
98d47d4c
SP
176 os.write(fdcomment, csetcomment)
177 os.close(fdcomment)
178
98d47d4c
SP
179 print '-----------------------------------------'
180 print 'cset:', cset
181 print 'branch:', hgbranch[str(cset)]
182 print 'user:', user
183 print 'date:', date
184 print 'comment:', csetcomment
1bc7c13a
MD
185 if parent:
186 print 'parent:', parent
98d47d4c
SP
187 if mparent:
188 print 'mparent:', mparent
189 if tag:
190 print 'tag:', tag
191 print '-----------------------------------------'
192
193 # checkout the parent if necessary
194 if cset != 0:
195 if hgbranch[str(cset)] == "branch-" + str(cset):
196 print 'creating new branch', hgbranch[str(cset)]
96f23959 197 os.system('git checkout -b %s %s' % (hgbranch[str(cset)], hgvers[parent]))
98d47d4c
SP
198 else:
199 print 'checking out branch', hgbranch[str(cset)]
96f23959 200 os.system('git checkout %s' % hgbranch[str(cset)])
98d47d4c
SP
201
202 # merge
203 if mparent:
204 if hgbranch[parent] == hgbranch[str(cset)]:
205 otherbranch = hgbranch[mparent]
206 else:
207 otherbranch = hgbranch[parent]
208 print 'merging', otherbranch, 'into', hgbranch[str(cset)]
96f23959 209 os.system(getgitenv(user, date) + 'git merge --no-commit -s ours "" %s %s' % (hgbranch[str(cset)], otherbranch))
98d47d4c
SP
210
211 # remove everything except .git and .hg directories
212 os.system('find . \( -path "./.hg" -o -path "./.git" \) -prune -o ! -name "." -print | xargs rm -rf')
213
214 # repopulate with checkouted files
215 os.system('hg update -C %d' % cset)
216
217 # add new files
96f23959 218 os.system('git ls-files -x .hg --others | git update-index --add --stdin')
98d47d4c 219 # delete removed files
96f23959 220 os.system('git ls-files -x .hg --deleted | git update-index --remove --stdin')
98d47d4c
SP
221
222 # commit
90e0653b 223 os.system(getgitenv(user, date) + 'git commit --allow-empty -a -F %s' % filecomment)
98d47d4c
SP
224 os.unlink(filecomment)
225
226 # tag
227 if tag and tag != 'tip':
96f23959 228 os.system(getgitenv(user, date) + 'git tag %s' % tag)
98d47d4c
SP
229
230 # delete branch if not used anymore...
231 if mparent and len(hgchildren[str(cset)]):
232 print "Deleting unused branch:", otherbranch
96f23959 233 os.system('git branch -d %s' % otherbranch)
98d47d4c
SP
234
235 # retrieve and record the version
96f23959 236 vvv = os.popen('git show --quiet --pretty=format:%H').read()
98d47d4c
SP
237 print 'record', cset, '->', vvv
238 hgvers[str(cset)] = vvv
239
7c0d741a 240if hgnewcsets >= opt_nrepack and opt_nrepack != -1:
96f23959 241 os.system('git repack -a -d')
98d47d4c
SP
242
243# write the state for incrementals
244if state:
37a12dda
JS
245 if verbose:
246 print 'Writing state'
98d47d4c
SP
247 f = open(state, 'w')
248 pickle.dump(hgvers, f)
249
250# vim: et ts=8 sw=4 sts=4