Merge branch 'PR/199'
[fast-export.git] / hg2git.py
blob1e740f1e32b8dfdea49e0ea15f11b5aa22f1ce0b
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 # Avoids a warning when the repository has obsolete markers
39 myui.setconfig('experimental', 'evolution.createmarkers', True)
40 return myui,hg.repository(myui,url).unfiltered()
42 def fixup_user(user,authors):
43 user=user.strip("\"")
44 if authors!=None:
45 # if we have an authors table, try to get mapping
46 # by defaulting to the current value of 'user'
47 user=authors.get(user,user)
48 name,mail,m='','',user_re.match(user)
49 if m==None:
50 # if we don't have 'Name <mail>' syntax, extract name
51 # and mail from hg helpers. this seems to work pretty well.
52 # if email doesn't contain @, replace it with devnull@localhost
53 name=templatefilters.person(user)
54 mail='<%s>' % templatefilters.email(user)
55 if '@' not in mail:
56 mail = '<devnull@localhost>'
57 else:
58 # if we have 'Name <mail>' syntax, everything is fine :)
59 name,mail=m.group(1),m.group(2)
61 # remove any silly quoting from username
62 m2=user_clean_re.match(name)
63 if m2!=None:
64 name=m2.group(1)
65 return '%s %s' % (name,mail)
67 def get_branch(name):
68 # 'HEAD' is the result of a bug in mutt's cvs->hg conversion,
69 # other CVS imports may need it, too
70 if name=='HEAD' or name=='default' or name=='':
71 name=cfg_master
72 if origin_name:
73 return origin_name + '/' + name
74 return name
76 def get_changeset(ui,repo,revision,authors={},encoding=''):
77 # Starting with Mercurial 4.6 lookup no longer accepts raw hashes
78 # for lookups. Work around it by changing our behaviour depending on
79 # how it fails
80 try:
81 node=repo.lookup(revision)
82 except hgerror.ProgrammingError:
83 node=binnode(revsymbol(repo,str(revision))) # We were given a numeric rev
84 except hgerror.RepoLookupError:
85 node=revision # We got a raw hash
86 (manifest,user,(time,timezone),files,desc,extra)=repo.changelog.read(node)
87 if encoding:
88 user=user.decode(encoding).encode('utf8')
89 desc=desc.decode(encoding).encode('utf8')
90 tz="%+03d%02d" % (-timezone / 3600, ((-timezone % 3600) / 60))
91 branch=get_branch(extra.get('branch','master'))
92 return (node,manifest,fixup_user(user,authors),(time,tz),files,desc,branch,extra)
94 def mangle_key(key):
95 return key
97 def load_cache(filename,get_key=mangle_key):
98 cache={}
99 if not os.path.exists(filename):
100 return cache
101 f=open(filename,'r')
103 for line in f.readlines():
104 l+=1
105 fields=line.split(' ')
106 if fields==None or not len(fields)==2 or fields[0][0]!=':':
107 sys.stderr.write('Invalid file format in [%s], line %d\n' % (filename,l))
108 continue
109 # put key:value in cache, key without ^:
110 cache[get_key(fields[0][1:])]=fields[1].split('\n')[0]
111 f.close()
112 return cache
114 def save_cache(filename,cache):
115 f=open(filename,'w+')
116 map(lambda x: f.write(':%s %s\n' % (str(x),str(cache.get(x)))),cache.keys())
117 f.close()
119 def get_git_sha1(name,type='heads'):
120 try:
121 # use git-rev-parse to support packed refs
122 ref="refs/%s/%s" % (type,name)
123 l=subprocess.check_output(["git", "rev-parse", "--verify", "--quiet", ref])
124 if l == None or len(l) == 0:
125 return None
126 return l[0:40]
127 except subprocess.CalledProcessError:
128 return None