Add support for mercurial subrepos
[fast-export.git] / hg2git.py
blob5d74a7512f8b74f39ec26d109086405c071bf2d1
1 #!/usr/bin/env python2
3 # Copyright (c) 2007, 2008 Rocco Rutte <pdmef@gmx.net> and others.
4 # License: MIT <http://www.opensource.org/licenses/mit-license.php>
6 from mercurial import hg,util,ui,templatefilters
7 from mercurial import error as hgerror
8 from mercurial.scmutil import revsymbol,binnode
10 import re
11 import os
12 import sys
13 import subprocess
15 # default git branch name
16 cfg_master='master'
17 # default origin name
18 origin_name=''
19 # silly regex to see if user field has email address
20 user_re=re.compile('([^<]+) (<[^>]*>)$')
21 # silly regex to clean out user names
22 user_clean_re=re.compile('^["]([^"]+)["]$')
24 def set_default_branch(name):
25 global cfg_master
26 cfg_master = name
28 def set_origin_name(name):
29 global origin_name
30 origin_name = name
32 def setup_repo(url):
33 try:
34 myui=ui.ui(interactive=False)
35 except TypeError:
36 myui=ui.ui()
37 myui.setconfig('ui', 'interactive', 'off')
38 return myui,hg.repository(myui,url)
40 def fixup_user(user,authors):
41 user=user.strip("\"")
42 if authors!=None:
43 # if we have an authors table, try to get mapping
44 # by defaulting to the current value of 'user'
45 user=authors.get(user,user)
46 name,mail,m='','',user_re.match(user)
47 if m==None:
48 # if we don't have 'Name <mail>' syntax, extract name
49 # and mail from hg helpers. this seems to work pretty well.
50 # if email doesn't contain @, replace it with devnull@localhost
51 name=templatefilters.person(user)
52 mail='<%s>' % templatefilters.email(user)
53 if '@' not in mail:
54 mail = '<devnull@localhost>'
55 else:
56 # if we have 'Name <mail>' syntax, everything is fine :)
57 name,mail=m.group(1),m.group(2)
59 # remove any silly quoting from username
60 m2=user_clean_re.match(name)
61 if m2!=None:
62 name=m2.group(1)
63 return '%s %s' % (name,mail)
65 def get_branch(name):
66 # 'HEAD' is the result of a bug in mutt's cvs->hg conversion,
67 # other CVS imports may need it, too
68 if name=='HEAD' or name=='default' or name=='':
69 name=cfg_master
70 if origin_name:
71 return origin_name + '/' + name
72 return name
74 def get_changeset(ui,repo,revision,authors={},encoding=''):
75 # Starting with Mercurial 4.6 lookup no longer accepts raw hashes
76 # for lookups. Work around it by changing our behaviour depending on
77 # how it fails
78 try:
79 node=repo.lookup(revision)
80 except hgerror.ProgrammingError:
81 node=binnode(revsymbol(repo,str(revision))) # We were given a numeric rev
82 except hgerror.RepoLookupError:
83 node=revision # We got a raw hash
84 (manifest,user,(time,timezone),files,desc,extra)=repo.changelog.read(node)
85 if encoding:
86 user=user.decode(encoding).encode('utf8')
87 desc=desc.decode(encoding).encode('utf8')
88 tz="%+03d%02d" % (-timezone / 3600, ((-timezone % 3600) / 60))
89 branch=get_branch(extra.get('branch','master'))
90 return (node,manifest,fixup_user(user,authors),(time,tz),files,desc,branch,extra)
92 def mangle_key(key):
93 return key
95 def load_cache(filename,get_key=mangle_key):
96 cache={}
97 if not os.path.exists(filename):
98 return cache
99 f=open(filename,'r')
101 for line in f.readlines():
102 l+=1
103 fields=line.split(' ')
104 if fields==None or not len(fields)==2 or fields[0][0]!=':':
105 sys.stderr.write('Invalid file format in [%s], line %d\n' % (filename,l))
106 continue
107 # put key:value in cache, key without ^:
108 cache[get_key(fields[0][1:])]=fields[1].split('\n')[0]
109 f.close()
110 return cache
112 def save_cache(filename,cache):
113 f=open(filename,'w+')
114 map(lambda x: f.write(':%s %s\n' % (str(x),str(cache.get(x)))),cache.keys())
115 f.close()
117 def get_git_sha1(name,type='heads'):
118 try:
119 # use git-rev-parse to support packed refs
120 ref="refs/%s/%s" % (type,name)
121 l=subprocess.check_output(["git", "rev-parse", "--verify", "--quiet", ref])
122 if l == None or len(l) == 0:
123 return None
124 return l[0:40]
125 except subprocess.CalledProcessError:
126 return None