chris@1544
|
1 #!/usr/bin/env python
|
chris@1544
|
2
|
chris@1544
|
3 # Copyright (c) 2007, 2008 Rocco Rutte <pdmef@gmx.net> and others.
|
chris@1544
|
4 # License: MIT <http://www.opensource.org/licenses/mit-license.php>
|
chris@1544
|
5
|
chris@1544
|
6 from mercurial import node
|
chris@1544
|
7 from hg2git import setup_repo,fixup_user,get_branch,get_changeset
|
chris@1544
|
8 from hg2git import load_cache,save_cache,get_git_sha1,set_default_branch,set_origin_name
|
chris@1544
|
9 from optparse import OptionParser
|
chris@1544
|
10 import re
|
chris@1544
|
11 import sys
|
chris@1544
|
12 import os
|
chris@1544
|
13
|
chris@1544
|
14 if sys.platform == "win32":
|
chris@1544
|
15 # On Windows, sys.stdout is initially opened in text mode, which means that
|
chris@1544
|
16 # when a LF (\n) character is written to sys.stdout, it will be converted
|
chris@1544
|
17 # into CRLF (\r\n). That makes git blow up, so use this platform-specific
|
chris@1544
|
18 # code to change the mode of sys.stdout to binary.
|
chris@1544
|
19 import msvcrt
|
chris@1544
|
20 msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY)
|
chris@1544
|
21
|
chris@1544
|
22 # silly regex to catch Signed-off-by lines in log message
|
chris@1544
|
23 sob_re=re.compile('^Signed-[Oo]ff-[Bb]y: (.+)$')
|
chris@1544
|
24 # insert 'checkpoint' command after this many commits or none at all if 0
|
chris@1544
|
25 cfg_checkpoint_count=0
|
chris@1544
|
26 # write some progress message every this many file contents written
|
chris@1544
|
27 cfg_export_boundary=1000
|
chris@1544
|
28
|
chris@1544
|
29 def gitmode(flags):
|
chris@1544
|
30 return 'l' in flags and '120000' or 'x' in flags and '100755' or '100644'
|
chris@1544
|
31
|
chris@1544
|
32 def wr_no_nl(msg=''):
|
chris@1544
|
33 if msg:
|
chris@1544
|
34 sys.stdout.write(msg)
|
chris@1544
|
35
|
chris@1544
|
36 def wr(msg=''):
|
chris@1544
|
37 wr_no_nl(msg)
|
chris@1544
|
38 sys.stdout.write('\n')
|
chris@1544
|
39 #map(lambda x: sys.stderr.write('\t[%s]\n' % x),msg.split('\n'))
|
chris@1544
|
40
|
chris@1544
|
41 def checkpoint(count):
|
chris@1544
|
42 count=count+1
|
chris@1544
|
43 if cfg_checkpoint_count>0 and count%cfg_checkpoint_count==0:
|
chris@1544
|
44 sys.stderr.write("Checkpoint after %d commits\n" % count)
|
chris@1544
|
45 wr('checkpoint')
|
chris@1544
|
46 wr()
|
chris@1544
|
47 return count
|
chris@1544
|
48
|
chris@1544
|
49 def revnum_to_revref(rev, old_marks):
|
chris@1544
|
50 """Convert an hg revnum to a git-fast-import rev reference (an SHA1
|
chris@1544
|
51 or a mark)"""
|
chris@1544
|
52 return old_marks.get(rev) or ':%d' % (rev+1)
|
chris@1544
|
53
|
chris@1544
|
54 def file_mismatch(f1,f2):
|
chris@1544
|
55 """See if two revisions of a file are not equal."""
|
chris@1544
|
56 return node.hex(f1)!=node.hex(f2)
|
chris@1544
|
57
|
chris@1544
|
58 def split_dict(dleft,dright,l=[],c=[],r=[],match=file_mismatch):
|
chris@1544
|
59 """Loop over our repository and find all changed and missing files."""
|
chris@1544
|
60 for left in dleft.keys():
|
chris@1544
|
61 right=dright.get(left,None)
|
chris@1544
|
62 if right==None:
|
chris@1544
|
63 # we have the file but our parent hasn't: add to left set
|
chris@1544
|
64 l.append(left)
|
chris@1544
|
65 elif match(dleft[left],right) or gitmode(dleft.flags(left))!=gitmode(dright.flags(left)):
|
chris@1544
|
66 # we have it but checksums mismatch: add to center set
|
chris@1544
|
67 c.append(left)
|
chris@1544
|
68 for right in dright.keys():
|
chris@1544
|
69 left=dleft.get(right,None)
|
chris@1544
|
70 if left==None:
|
chris@1544
|
71 # if parent has file but we don't: add to right set
|
chris@1544
|
72 r.append(right)
|
chris@1544
|
73 # change is already handled when comparing child against parent
|
chris@1544
|
74 return l,c,r
|
chris@1544
|
75
|
chris@1544
|
76 def get_filechanges(repo,revision,parents,mleft):
|
chris@1544
|
77 """Given some repository and revision, find all changed/deleted files."""
|
chris@1544
|
78 l,c,r=[],[],[]
|
chris@1544
|
79 for p in parents:
|
chris@1544
|
80 if p<0: continue
|
chris@1544
|
81 mright=repo.changectx(p).manifest()
|
chris@1544
|
82 l,c,r=split_dict(mleft,mright,l,c,r)
|
chris@1544
|
83 l.sort()
|
chris@1544
|
84 c.sort()
|
chris@1544
|
85 r.sort()
|
chris@1544
|
86 return l,c,r
|
chris@1544
|
87
|
chris@1544
|
88 def get_author(logmessage,committer,authors):
|
chris@1544
|
89 """As git distincts between author and committer of a patch, try to
|
chris@1544
|
90 extract author by detecting Signed-off-by lines.
|
chris@1544
|
91
|
chris@1544
|
92 This walks from the end of the log message towards the top skipping
|
chris@1544
|
93 empty lines. Upon the first non-empty line, it walks all Signed-off-by
|
chris@1544
|
94 lines upwards to find the first one. For that (if found), it extracts
|
chris@1544
|
95 authorship information the usual way (authors table, cleaning, etc.)
|
chris@1544
|
96
|
chris@1544
|
97 If no Signed-off-by line is found, this defaults to the committer.
|
chris@1544
|
98
|
chris@1544
|
99 This may sound stupid (and it somehow is), but in log messages we
|
chris@1544
|
100 accidentially may have lines in the middle starting with
|
chris@1544
|
101 "Signed-off-by: foo" and thus matching our detection regex. Prevent
|
chris@1544
|
102 that."""
|
chris@1544
|
103
|
chris@1544
|
104 loglines=logmessage.split('\n')
|
chris@1544
|
105 i=len(loglines)
|
chris@1544
|
106 # from tail walk to top skipping empty lines
|
chris@1544
|
107 while i>=0:
|
chris@1544
|
108 i-=1
|
chris@1544
|
109 if len(loglines[i].strip())==0: continue
|
chris@1544
|
110 break
|
chris@1544
|
111 if i>=0:
|
chris@1544
|
112 # walk further upwards to find first sob line, store in 'first'
|
chris@1544
|
113 first=None
|
chris@1544
|
114 while i>=0:
|
chris@1544
|
115 m=sob_re.match(loglines[i])
|
chris@1544
|
116 if m==None: break
|
chris@1544
|
117 first=m
|
chris@1544
|
118 i-=1
|
chris@1544
|
119 # if the last non-empty line matches our Signed-Off-by regex: extract username
|
chris@1544
|
120 if first!=None:
|
chris@1544
|
121 r=fixup_user(first.group(1),authors)
|
chris@1544
|
122 return r
|
chris@1544
|
123 return committer
|
chris@1544
|
124
|
chris@1544
|
125 def export_file_contents(ctx,manifest,files,hgtags,encoding=''):
|
chris@1544
|
126 count=0
|
chris@1544
|
127 max=len(files)
|
chris@1544
|
128 for file in files:
|
chris@1544
|
129 # Skip .hgtags files. They only get us in trouble.
|
chris@1544
|
130 if not hgtags and file == ".hgtags":
|
chris@1544
|
131 sys.stderr.write('Skip %s\n' % (file))
|
chris@1544
|
132 continue
|
chris@1544
|
133 d=ctx.filectx(file).data()
|
chris@1544
|
134 if encoding:
|
chris@1544
|
135 filename=file.decode(encoding).encode('utf8')
|
chris@1544
|
136 else:
|
chris@1544
|
137 filename=file
|
chris@1544
|
138 wr('M %s inline %s' % (gitmode(manifest.flags(file)),
|
chris@1544
|
139 strip_leading_slash(filename)))
|
chris@1544
|
140 wr('data %d' % len(d)) # had some trouble with size()
|
chris@1544
|
141 wr(d)
|
chris@1544
|
142 count+=1
|
chris@1544
|
143 if count%cfg_export_boundary==0:
|
chris@1544
|
144 sys.stderr.write('Exported %d/%d files\n' % (count,max))
|
chris@1544
|
145 if max>cfg_export_boundary:
|
chris@1544
|
146 sys.stderr.write('Exported %d/%d files\n' % (count,max))
|
chris@1544
|
147
|
chris@1544
|
148 def sanitize_name(name,what="branch"):
|
chris@1544
|
149 """Sanitize input roughly according to git-check-ref-format(1)"""
|
chris@1544
|
150
|
chris@1544
|
151 def dot(name):
|
chris@1544
|
152 if name[0] == '.': return '_'+name[1:]
|
chris@1544
|
153 return name
|
chris@1544
|
154
|
chris@1544
|
155 n=name
|
chris@1544
|
156 p=re.compile('([[ ~^:?\\\\*]|\.\.)')
|
chris@1544
|
157 n=p.sub('_', n)
|
chris@1544
|
158 if n[-1] in ('/', '.'): n=n[:-1]+'_'
|
chris@1544
|
159 n='/'.join(map(dot,n.split('/')))
|
chris@1544
|
160 p=re.compile('_+')
|
chris@1544
|
161 n=p.sub('_', n)
|
chris@1544
|
162
|
chris@1544
|
163 if n!=name:
|
chris@1544
|
164 sys.stderr.write('Warning: sanitized %s [%s] to [%s]\n' % (what,name,n))
|
chris@1544
|
165 return n
|
chris@1544
|
166
|
chris@1544
|
167 def strip_leading_slash(filename):
|
chris@1544
|
168 if filename[0] == '/':
|
chris@1544
|
169 return filename[1:]
|
chris@1544
|
170 return filename
|
chris@1544
|
171
|
chris@1544
|
172 def export_commit(ui,repo,revision,old_marks,max,count,authors,
|
chris@1544
|
173 branchesmap,sob,brmap,hgtags,notes,encoding='',fn_encoding=''):
|
chris@1544
|
174 def get_branchname(name):
|
chris@1544
|
175 if brmap.has_key(name):
|
chris@1544
|
176 return brmap[name]
|
chris@1544
|
177 n=sanitize_name(branchesmap.get(name,name))
|
chris@1544
|
178 brmap[name]=n
|
chris@1544
|
179 return n
|
chris@1544
|
180
|
chris@1544
|
181 (revnode,_,user,(time,timezone),files,desc,branch,_)=get_changeset(ui,repo,revision,authors,encoding)
|
chris@1544
|
182
|
chris@1544
|
183 branch=get_branchname(branch)
|
chris@1544
|
184
|
chris@1544
|
185 parents = [p for p in repo.changelog.parentrevs(revision) if p >= 0]
|
chris@1544
|
186
|
chris@1544
|
187 if len(parents)==0 and revision != 0:
|
chris@1544
|
188 wr('reset refs/heads/%s' % branch)
|
chris@1544
|
189
|
chris@1544
|
190 wr('commit refs/heads/%s' % branch)
|
chris@1544
|
191 wr('mark :%d' % (revision+1))
|
chris@1544
|
192 if sob:
|
chris@1544
|
193 wr('author %s %d %s' % (get_author(desc,user,authors),time,timezone))
|
chris@1544
|
194 wr('committer %s %d %s' % (user,time,timezone))
|
chris@1544
|
195 wr('data %d' % (len(desc)+1)) # wtf?
|
chris@1544
|
196 wr(desc)
|
chris@1544
|
197 wr()
|
chris@1544
|
198
|
chris@1544
|
199 ctx=repo.changectx(str(revision))
|
chris@1544
|
200 man=ctx.manifest()
|
chris@1544
|
201 added,changed,removed,type=[],[],[],''
|
chris@1544
|
202
|
chris@1544
|
203 if len(parents) == 0:
|
chris@1544
|
204 # first revision: feed in full manifest
|
chris@1544
|
205 added=man.keys()
|
chris@1544
|
206 added.sort()
|
chris@1544
|
207 type='full'
|
chris@1544
|
208 else:
|
chris@1544
|
209 wr('from %s' % revnum_to_revref(parents[0], old_marks))
|
chris@1544
|
210 if len(parents) == 1:
|
chris@1544
|
211 # later non-merge revision: feed in changed manifest
|
chris@1544
|
212 # if we have exactly one parent, just take the changes from the
|
chris@1544
|
213 # manifest without expensively comparing checksums
|
chris@1544
|
214 f=repo.status(repo.lookup(parents[0]),revnode)[:3]
|
chris@1544
|
215 added,changed,removed=f[1],f[0],f[2]
|
chris@1544
|
216 type='simple delta'
|
chris@1544
|
217 else: # a merge with two parents
|
chris@1544
|
218 wr('merge %s' % revnum_to_revref(parents[1], old_marks))
|
chris@1544
|
219 # later merge revision: feed in changed manifest
|
chris@1544
|
220 # for many files comparing checksums is expensive so only do it for
|
chris@1544
|
221 # merges where we really need it due to hg's revlog logic
|
chris@1544
|
222 added,changed,removed=get_filechanges(repo,revision,parents,man)
|
chris@1544
|
223 type='thorough delta'
|
chris@1544
|
224
|
chris@1544
|
225 sys.stderr.write('%s: Exporting %s revision %d/%d with %d/%d/%d added/changed/removed files\n' %
|
chris@1544
|
226 (branch,type,revision+1,max,len(added),len(changed),len(removed)))
|
chris@1544
|
227
|
chris@1544
|
228 if fn_encoding:
|
chris@1544
|
229 removed=[r.decode(fn_encoding).encode('utf8') for r in removed]
|
chris@1544
|
230
|
chris@1544
|
231 removed=[strip_leading_slash(x) for x in removed]
|
chris@1544
|
232
|
chris@1544
|
233 map(lambda r: wr('D %s' % r),removed)
|
chris@1544
|
234 export_file_contents(ctx,man,added,hgtags,fn_encoding)
|
chris@1544
|
235 export_file_contents(ctx,man,changed,hgtags,fn_encoding)
|
chris@1544
|
236 wr()
|
chris@1544
|
237
|
chris@1544
|
238 count=checkpoint(count)
|
chris@1544
|
239 count=generate_note(user,time,timezone,revision,ctx,count,notes)
|
chris@1544
|
240 return count
|
chris@1544
|
241
|
chris@1544
|
242 def generate_note(user,time,timezone,revision,ctx,count,notes):
|
chris@1544
|
243 if not notes:
|
chris@1544
|
244 return count
|
chris@1544
|
245 wr('commit refs/notes/hg')
|
chris@1544
|
246 wr('committer %s %d %s' % (user,time,timezone))
|
chris@1544
|
247 wr('data 0')
|
chris@1544
|
248 wr('N inline :%d' % (revision+1))
|
chris@1544
|
249 hg_hash=ctx.hex()
|
chris@1544
|
250 wr('data %d' % (len(hg_hash)))
|
chris@1544
|
251 wr_no_nl(hg_hash)
|
chris@1544
|
252 wr()
|
chris@1544
|
253 return checkpoint(count)
|
chris@1544
|
254
|
chris@1544
|
255 def export_tags(ui,repo,old_marks,mapping_cache,count,authors,tagsmap):
|
chris@1544
|
256 l=repo.tagslist()
|
chris@1544
|
257 for tag,node in l:
|
chris@1544
|
258 # Remap the branch name
|
chris@1544
|
259 tag=sanitize_name(tagsmap.get(tag,tag),"tag")
|
chris@1544
|
260 # ignore latest revision
|
chris@1544
|
261 if tag=='tip': continue
|
chris@1544
|
262 # ignore tags to nodes that are missing (ie, 'in the future')
|
chris@1544
|
263 if node.encode('hex_codec') not in mapping_cache:
|
chris@1544
|
264 sys.stderr.write('Tag %s refers to unseen node %s\n' % (tag, node.encode('hex_codec')))
|
chris@1544
|
265 continue
|
chris@1544
|
266
|
chris@1544
|
267 rev=int(mapping_cache[node.encode('hex_codec')])
|
chris@1544
|
268
|
chris@1544
|
269 ref=revnum_to_revref(rev, old_marks)
|
chris@1544
|
270 if ref==None:
|
chris@1544
|
271 sys.stderr.write('Failed to find reference for creating tag'
|
chris@1544
|
272 ' %s at r%d\n' % (tag,rev))
|
chris@1544
|
273 continue
|
chris@1544
|
274 sys.stderr.write('Exporting tag [%s] at [hg r%d] [git %s]\n' % (tag,rev,ref))
|
chris@1544
|
275 wr('reset refs/tags/%s' % tag)
|
chris@1544
|
276 wr('from %s' % ref)
|
chris@1544
|
277 wr()
|
chris@1544
|
278 count=checkpoint(count)
|
chris@1544
|
279 return count
|
chris@1544
|
280
|
chris@1544
|
281 def load_mapping(name, filename):
|
chris@1544
|
282 cache={}
|
chris@1544
|
283 if not os.path.exists(filename):
|
chris@1544
|
284 return cache
|
chris@1544
|
285 f=open(filename,'r')
|
chris@1544
|
286 l=0
|
chris@1544
|
287 a=0
|
chris@1544
|
288 lre=re.compile('^([^=]+)[ ]*=[ ]*(.+)$')
|
chris@1544
|
289 for line in f.readlines():
|
chris@1544
|
290 l+=1
|
chris@1544
|
291 line=line.strip()
|
chris@1544
|
292 if line=='' or line[0]=='#':
|
chris@1544
|
293 continue
|
chris@1544
|
294 m=lre.match(line)
|
chris@1544
|
295 if m==None:
|
chris@1544
|
296 sys.stderr.write('Invalid file format in [%s], line %d\n' % (filename,l))
|
chris@1544
|
297 continue
|
chris@1544
|
298 # put key:value in cache, key without ^:
|
chris@1544
|
299 cache[m.group(1).strip()]=m.group(2).strip()
|
chris@1544
|
300 a+=1
|
chris@1544
|
301 f.close()
|
chris@1544
|
302 sys.stderr.write('Loaded %d %s\n' % (a, name))
|
chris@1544
|
303 return cache
|
chris@1544
|
304
|
chris@1544
|
305 def branchtip(repo, heads):
|
chris@1544
|
306 '''return the tipmost branch head in heads'''
|
chris@1544
|
307 tip = heads[-1]
|
chris@1544
|
308 for h in reversed(heads):
|
chris@1544
|
309 if 'close' not in repo.changelog.read(h)[5]:
|
chris@1544
|
310 tip = h
|
chris@1544
|
311 break
|
chris@1544
|
312 return tip
|
chris@1544
|
313
|
chris@1544
|
314 def verify_heads(ui,repo,cache,force):
|
chris@1544
|
315 branches={}
|
chris@1544
|
316 for bn, heads in repo.branchmap().iteritems():
|
chris@1544
|
317 branches[bn] = branchtip(repo, heads)
|
chris@1544
|
318 l=[(-repo.changelog.rev(n), n, t) for t, n in branches.items()]
|
chris@1544
|
319 l.sort()
|
chris@1544
|
320
|
chris@1544
|
321 # get list of hg's branches to verify, don't take all git has
|
chris@1544
|
322 for _,_,b in l:
|
chris@1544
|
323 b=get_branch(b)
|
chris@1544
|
324 sha1=get_git_sha1(b)
|
chris@1544
|
325 c=cache.get(b)
|
chris@1544
|
326 if sha1!=c:
|
chris@1544
|
327 sys.stderr.write('Error: Branch [%s] modified outside hg-fast-export:'
|
chris@1544
|
328 '\n%s (repo) != %s (cache)\n' % (b,sha1,c))
|
chris@1544
|
329 if not force: return False
|
chris@1544
|
330
|
chris@1544
|
331 # verify that branch has exactly one head
|
chris@1544
|
332 t={}
|
chris@1544
|
333 for h in repo.heads():
|
chris@1544
|
334 (_,_,_,_,_,_,branch,_)=get_changeset(ui,repo,h)
|
chris@1544
|
335 if t.get(branch,False):
|
chris@1544
|
336 sys.stderr.write('Error: repository has at least one unnamed head: hg r%s\n' %
|
chris@1544
|
337 repo.changelog.rev(h))
|
chris@1544
|
338 if not force: return False
|
chris@1544
|
339 t[branch]=True
|
chris@1544
|
340
|
chris@1544
|
341 return True
|
chris@1544
|
342
|
chris@1544
|
343 def hg2git(repourl,m,marksfile,mappingfile,headsfile,tipfile,
|
chris@1544
|
344 authors={},branchesmap={},tagsmap={},
|
chris@1544
|
345 sob=False,force=False,hgtags=False,notes=False,encoding='',fn_encoding=''):
|
chris@1544
|
346 _max=int(m)
|
chris@1544
|
347
|
chris@1544
|
348 old_marks=load_cache(marksfile,lambda s: int(s)-1)
|
chris@1544
|
349 mapping_cache=load_cache(mappingfile)
|
chris@1544
|
350 heads_cache=load_cache(headsfile)
|
chris@1544
|
351 state_cache=load_cache(tipfile)
|
chris@1544
|
352
|
chris@1544
|
353 ui,repo=setup_repo(repourl)
|
chris@1544
|
354
|
chris@1544
|
355 if not verify_heads(ui,repo,heads_cache,force):
|
chris@1544
|
356 return 1
|
chris@1544
|
357
|
chris@1544
|
358 try:
|
chris@1544
|
359 tip=repo.changelog.count()
|
chris@1544
|
360 except AttributeError:
|
chris@1544
|
361 tip=len(repo)
|
chris@1544
|
362
|
chris@1544
|
363 min=int(state_cache.get('tip',0))
|
chris@1544
|
364 max=_max
|
chris@1544
|
365 if _max<0 or max>tip:
|
chris@1544
|
366 max=tip
|
chris@1544
|
367
|
chris@1544
|
368 for rev in range(0,max):
|
chris@1544
|
369 (revnode,_,_,_,_,_,_,_)=get_changeset(ui,repo,rev,authors)
|
chris@1544
|
370 mapping_cache[revnode.encode('hex_codec')] = str(rev)
|
chris@1544
|
371
|
chris@1544
|
372
|
chris@1544
|
373 c=0
|
chris@1544
|
374 brmap={}
|
chris@1544
|
375 for rev in range(min,max):
|
chris@1544
|
376 c=export_commit(ui,repo,rev,old_marks,max,c,authors,branchesmap,
|
chris@1544
|
377 sob,brmap,hgtags,notes,encoding,fn_encoding)
|
chris@1544
|
378
|
chris@1544
|
379 state_cache['tip']=max
|
chris@1544
|
380 state_cache['repo']=repourl
|
chris@1544
|
381 save_cache(tipfile,state_cache)
|
chris@1544
|
382 save_cache(mappingfile,mapping_cache)
|
chris@1544
|
383
|
chris@1544
|
384 c=export_tags(ui,repo,old_marks,mapping_cache,c,authors,tagsmap)
|
chris@1544
|
385
|
chris@1544
|
386 sys.stderr.write('Issued %d commands\n' % c)
|
chris@1544
|
387
|
chris@1544
|
388 return 0
|
chris@1544
|
389
|
chris@1544
|
390 if __name__=='__main__':
|
chris@1544
|
391 def bail(parser,opt):
|
chris@1544
|
392 sys.stderr.write('Error: No %s option given\n' % opt)
|
chris@1544
|
393 parser.print_help()
|
chris@1544
|
394 sys.exit(2)
|
chris@1544
|
395
|
chris@1544
|
396 parser=OptionParser()
|
chris@1544
|
397
|
chris@1544
|
398 parser.add_option("-m","--max",type="int",dest="max",
|
chris@1544
|
399 help="Maximum hg revision to import")
|
chris@1544
|
400 parser.add_option("--mapping",dest="mappingfile",
|
chris@1544
|
401 help="File to read last run's hg-to-git SHA1 mapping")
|
chris@1544
|
402 parser.add_option("--marks",dest="marksfile",
|
chris@1544
|
403 help="File to read git-fast-import's marks from")
|
chris@1544
|
404 parser.add_option("--heads",dest="headsfile",
|
chris@1544
|
405 help="File to read last run's git heads from")
|
chris@1544
|
406 parser.add_option("--status",dest="statusfile",
|
chris@1544
|
407 help="File to read status from")
|
chris@1544
|
408 parser.add_option("-r","--repo",dest="repourl",
|
chris@1544
|
409 help="URL of repo to import")
|
chris@1544
|
410 parser.add_option("-s",action="store_true",dest="sob",
|
chris@1544
|
411 default=False,help="Enable parsing Signed-off-by lines")
|
chris@1544
|
412 parser.add_option("--hgtags",action="store_true",dest="hgtags",
|
chris@1544
|
413 default=False,help="Enable exporting .hgtags files")
|
chris@1544
|
414 parser.add_option("-A","--authors",dest="authorfile",
|
chris@1544
|
415 help="Read authormap from AUTHORFILE")
|
chris@1544
|
416 parser.add_option("-B","--branches",dest="branchesfile",
|
chris@1544
|
417 help="Read branch map from BRANCHESFILE")
|
chris@1544
|
418 parser.add_option("-T","--tags",dest="tagsfile",
|
chris@1544
|
419 help="Read tags map from TAGSFILE")
|
chris@1544
|
420 parser.add_option("-f","--force",action="store_true",dest="force",
|
chris@1544
|
421 default=False,help="Ignore validation errors by force")
|
chris@1544
|
422 parser.add_option("-M","--default-branch",dest="default_branch",
|
chris@1544
|
423 help="Set the default branch")
|
chris@1544
|
424 parser.add_option("-o","--origin",dest="origin_name",
|
chris@1544
|
425 help="use <name> as namespace to track upstream")
|
chris@1544
|
426 parser.add_option("--hg-hash",action="store_true",dest="notes",
|
chris@1544
|
427 default=False,help="Annotate commits with the hg hash as git notes in the hg namespace")
|
chris@1544
|
428 parser.add_option("-e",dest="encoding",
|
chris@1544
|
429 help="Assume commit and author strings retrieved from Mercurial are encoded in <encoding>")
|
chris@1544
|
430 parser.add_option("--fe",dest="fn_encoding",
|
chris@1544
|
431 help="Assume file names from Mercurial are encoded in <filename_encoding>")
|
chris@1544
|
432
|
chris@1544
|
433 (options,args)=parser.parse_args()
|
chris@1544
|
434
|
chris@1544
|
435 m=-1
|
chris@1544
|
436 if options.max!=None: m=options.max
|
chris@1544
|
437
|
chris@1544
|
438 if options.marksfile==None: bail(parser,'--marks')
|
chris@1544
|
439 if options.mappingfile==None: bail(parser,'--mapping')
|
chris@1544
|
440 if options.headsfile==None: bail(parser,'--heads')
|
chris@1544
|
441 if options.statusfile==None: bail(parser,'--status')
|
chris@1544
|
442 if options.repourl==None: bail(parser,'--repo')
|
chris@1544
|
443
|
chris@1544
|
444 a={}
|
chris@1544
|
445 if options.authorfile!=None:
|
chris@1544
|
446 a=load_mapping('authors', options.authorfile)
|
chris@1544
|
447
|
chris@1544
|
448 b={}
|
chris@1544
|
449 if options.branchesfile!=None:
|
chris@1544
|
450 b=load_mapping('branches', options.branchesfile)
|
chris@1544
|
451
|
chris@1544
|
452 t={}
|
chris@1544
|
453 if options.tagsfile!=None:
|
chris@1544
|
454 t=load_mapping('tags', options.tagsfile)
|
chris@1544
|
455
|
chris@1544
|
456 if options.default_branch!=None:
|
chris@1544
|
457 set_default_branch(options.default_branch)
|
chris@1544
|
458
|
chris@1544
|
459 if options.origin_name!=None:
|
chris@1544
|
460 set_origin_name(options.origin_name)
|
chris@1544
|
461
|
chris@1544
|
462 encoding=''
|
chris@1544
|
463 if options.encoding!=None:
|
chris@1544
|
464 encoding=options.encoding
|
chris@1544
|
465
|
chris@1544
|
466 fn_encoding=encoding
|
chris@1544
|
467 if options.fn_encoding!=None:
|
chris@1544
|
468 fn_encoding=options.fn_encoding
|
chris@1544
|
469
|
chris@1544
|
470 sys.exit(hg2git(options.repourl,m,options.marksfile,options.mappingfile,
|
chris@1544
|
471 options.headsfile, options.statusfile,
|
chris@1544
|
472 authors=a,branchesmap=b,tagsmap=t,
|
chris@1544
|
473 sob=options.sob,force=options.force,hgtags=options.hgtags,
|
chris@1544
|
474 notes=options.notes,encoding=encoding,fn_encoding=fn_encoding))
|