##// END OF EJS Templates
py3: use b'%d' to convert int to bytes instead of str()...
Pulkit Goyal -
r37599:5b836a4c default
parent child Browse files
Show More
@@ -1,478 +1,478 b''
1 # git.py - git support for the convert extension
1 # git.py - git support for the convert extension
2 #
2 #
3 # Copyright 2005-2009 Matt Mackall <mpm@selenic.com> and others
3 # Copyright 2005-2009 Matt Mackall <mpm@selenic.com> and others
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7 from __future__ import absolute_import
7 from __future__ import absolute_import
8
8
9 import os
9 import os
10
10
11 from mercurial.i18n import _
11 from mercurial.i18n import _
12 from mercurial import (
12 from mercurial import (
13 config,
13 config,
14 error,
14 error,
15 node as nodemod,
15 node as nodemod,
16 )
16 )
17
17
18 from . import (
18 from . import (
19 common,
19 common,
20 )
20 )
21
21
22 class submodule(object):
22 class submodule(object):
23 def __init__(self, path, node, url):
23 def __init__(self, path, node, url):
24 self.path = path
24 self.path = path
25 self.node = node
25 self.node = node
26 self.url = url
26 self.url = url
27
27
28 def hgsub(self):
28 def hgsub(self):
29 return "%s = [git]%s" % (self.path, self.url)
29 return "%s = [git]%s" % (self.path, self.url)
30
30
31 def hgsubstate(self):
31 def hgsubstate(self):
32 return "%s %s" % (self.node, self.path)
32 return "%s %s" % (self.node, self.path)
33
33
34 # Keys in extra fields that should not be copied if the user requests.
34 # Keys in extra fields that should not be copied if the user requests.
35 bannedextrakeys = {
35 bannedextrakeys = {
36 # Git commit object built-ins.
36 # Git commit object built-ins.
37 'tree',
37 'tree',
38 'parent',
38 'parent',
39 'author',
39 'author',
40 'committer',
40 'committer',
41 # Mercurial built-ins.
41 # Mercurial built-ins.
42 'branch',
42 'branch',
43 'close',
43 'close',
44 }
44 }
45
45
46 class convert_git(common.converter_source, common.commandline):
46 class convert_git(common.converter_source, common.commandline):
47 # Windows does not support GIT_DIR= construct while other systems
47 # Windows does not support GIT_DIR= construct while other systems
48 # cannot remove environment variable. Just assume none have
48 # cannot remove environment variable. Just assume none have
49 # both issues.
49 # both issues.
50
50
51 def _gitcmd(self, cmd, *args, **kwargs):
51 def _gitcmd(self, cmd, *args, **kwargs):
52 return cmd('--git-dir=%s' % self.path, *args, **kwargs)
52 return cmd('--git-dir=%s' % self.path, *args, **kwargs)
53
53
54 def gitrun0(self, *args, **kwargs):
54 def gitrun0(self, *args, **kwargs):
55 return self._gitcmd(self.run0, *args, **kwargs)
55 return self._gitcmd(self.run0, *args, **kwargs)
56
56
57 def gitrun(self, *args, **kwargs):
57 def gitrun(self, *args, **kwargs):
58 return self._gitcmd(self.run, *args, **kwargs)
58 return self._gitcmd(self.run, *args, **kwargs)
59
59
60 def gitrunlines0(self, *args, **kwargs):
60 def gitrunlines0(self, *args, **kwargs):
61 return self._gitcmd(self.runlines0, *args, **kwargs)
61 return self._gitcmd(self.runlines0, *args, **kwargs)
62
62
63 def gitrunlines(self, *args, **kwargs):
63 def gitrunlines(self, *args, **kwargs):
64 return self._gitcmd(self.runlines, *args, **kwargs)
64 return self._gitcmd(self.runlines, *args, **kwargs)
65
65
66 def gitpipe(self, *args, **kwargs):
66 def gitpipe(self, *args, **kwargs):
67 return self._gitcmd(self._run3, *args, **kwargs)
67 return self._gitcmd(self._run3, *args, **kwargs)
68
68
69 def __init__(self, ui, repotype, path, revs=None):
69 def __init__(self, ui, repotype, path, revs=None):
70 super(convert_git, self).__init__(ui, repotype, path, revs=revs)
70 super(convert_git, self).__init__(ui, repotype, path, revs=revs)
71 common.commandline.__init__(self, ui, 'git')
71 common.commandline.__init__(self, ui, 'git')
72
72
73 # Pass an absolute path to git to prevent from ever being interpreted
73 # Pass an absolute path to git to prevent from ever being interpreted
74 # as a URL
74 # as a URL
75 path = os.path.abspath(path)
75 path = os.path.abspath(path)
76
76
77 if os.path.isdir(path + "/.git"):
77 if os.path.isdir(path + "/.git"):
78 path += "/.git"
78 path += "/.git"
79 if not os.path.exists(path + "/objects"):
79 if not os.path.exists(path + "/objects"):
80 raise common.NoRepo(_("%s does not look like a Git repository") %
80 raise common.NoRepo(_("%s does not look like a Git repository") %
81 path)
81 path)
82
82
83 # The default value (50) is based on the default for 'git diff'.
83 # The default value (50) is based on the default for 'git diff'.
84 similarity = ui.configint('convert', 'git.similarity')
84 similarity = ui.configint('convert', 'git.similarity')
85 if similarity < 0 or similarity > 100:
85 if similarity < 0 or similarity > 100:
86 raise error.Abort(_('similarity must be between 0 and 100'))
86 raise error.Abort(_('similarity must be between 0 and 100'))
87 if similarity > 0:
87 if similarity > 0:
88 self.simopt = ['-C%d%%' % similarity]
88 self.simopt = ['-C%d%%' % similarity]
89 findcopiesharder = ui.configbool('convert', 'git.findcopiesharder')
89 findcopiesharder = ui.configbool('convert', 'git.findcopiesharder')
90 if findcopiesharder:
90 if findcopiesharder:
91 self.simopt.append('--find-copies-harder')
91 self.simopt.append('--find-copies-harder')
92
92
93 renamelimit = ui.configint('convert', 'git.renamelimit')
93 renamelimit = ui.configint('convert', 'git.renamelimit')
94 self.simopt.append('-l%d' % renamelimit)
94 self.simopt.append('-l%d' % renamelimit)
95 else:
95 else:
96 self.simopt = []
96 self.simopt = []
97
97
98 common.checktool('git', 'git')
98 common.checktool('git', 'git')
99
99
100 self.path = path
100 self.path = path
101 self.submodules = []
101 self.submodules = []
102
102
103 self.catfilepipe = self.gitpipe('cat-file', '--batch')
103 self.catfilepipe = self.gitpipe('cat-file', '--batch')
104
104
105 self.copyextrakeys = self.ui.configlist('convert', 'git.extrakeys')
105 self.copyextrakeys = self.ui.configlist('convert', 'git.extrakeys')
106 banned = set(self.copyextrakeys) & bannedextrakeys
106 banned = set(self.copyextrakeys) & bannedextrakeys
107 if banned:
107 if banned:
108 raise error.Abort(_('copying of extra key is forbidden: %s') %
108 raise error.Abort(_('copying of extra key is forbidden: %s') %
109 _(', ').join(sorted(banned)))
109 _(', ').join(sorted(banned)))
110
110
111 committeractions = self.ui.configlist('convert', 'git.committeractions')
111 committeractions = self.ui.configlist('convert', 'git.committeractions')
112
112
113 messagedifferent = None
113 messagedifferent = None
114 messagealways = None
114 messagealways = None
115 for a in committeractions:
115 for a in committeractions:
116 if a.startswith(('messagedifferent', 'messagealways')):
116 if a.startswith(('messagedifferent', 'messagealways')):
117 k = a
117 k = a
118 v = None
118 v = None
119 if '=' in a:
119 if '=' in a:
120 k, v = a.split('=', 1)
120 k, v = a.split('=', 1)
121
121
122 if k == 'messagedifferent':
122 if k == 'messagedifferent':
123 messagedifferent = v or 'committer:'
123 messagedifferent = v or 'committer:'
124 elif k == 'messagealways':
124 elif k == 'messagealways':
125 messagealways = v or 'committer:'
125 messagealways = v or 'committer:'
126
126
127 if messagedifferent and messagealways:
127 if messagedifferent and messagealways:
128 raise error.Abort(_('committeractions cannot define both '
128 raise error.Abort(_('committeractions cannot define both '
129 'messagedifferent and messagealways'))
129 'messagedifferent and messagealways'))
130
130
131 dropcommitter = 'dropcommitter' in committeractions
131 dropcommitter = 'dropcommitter' in committeractions
132 replaceauthor = 'replaceauthor' in committeractions
132 replaceauthor = 'replaceauthor' in committeractions
133
133
134 if dropcommitter and replaceauthor:
134 if dropcommitter and replaceauthor:
135 raise error.Abort(_('committeractions cannot define both '
135 raise error.Abort(_('committeractions cannot define both '
136 'dropcommitter and replaceauthor'))
136 'dropcommitter and replaceauthor'))
137
137
138 if dropcommitter and messagealways:
138 if dropcommitter and messagealways:
139 raise error.Abort(_('committeractions cannot define both '
139 raise error.Abort(_('committeractions cannot define both '
140 'dropcommitter and messagealways'))
140 'dropcommitter and messagealways'))
141
141
142 if not messagedifferent and not messagealways:
142 if not messagedifferent and not messagealways:
143 messagedifferent = 'committer:'
143 messagedifferent = 'committer:'
144
144
145 self.committeractions = {
145 self.committeractions = {
146 'dropcommitter': dropcommitter,
146 'dropcommitter': dropcommitter,
147 'replaceauthor': replaceauthor,
147 'replaceauthor': replaceauthor,
148 'messagedifferent': messagedifferent,
148 'messagedifferent': messagedifferent,
149 'messagealways': messagealways,
149 'messagealways': messagealways,
150 }
150 }
151
151
152 def after(self):
152 def after(self):
153 for f in self.catfilepipe:
153 for f in self.catfilepipe:
154 f.close()
154 f.close()
155
155
156 def getheads(self):
156 def getheads(self):
157 if not self.revs:
157 if not self.revs:
158 output, status = self.gitrun('rev-parse', '--branches', '--remotes')
158 output, status = self.gitrun('rev-parse', '--branches', '--remotes')
159 heads = output.splitlines()
159 heads = output.splitlines()
160 if status:
160 if status:
161 raise error.Abort(_('cannot retrieve git heads'))
161 raise error.Abort(_('cannot retrieve git heads'))
162 else:
162 else:
163 heads = []
163 heads = []
164 for rev in self.revs:
164 for rev in self.revs:
165 rawhead, ret = self.gitrun('rev-parse', '--verify', rev)
165 rawhead, ret = self.gitrun('rev-parse', '--verify', rev)
166 heads.append(rawhead[:-1])
166 heads.append(rawhead[:-1])
167 if ret:
167 if ret:
168 raise error.Abort(_('cannot retrieve git head "%s"') % rev)
168 raise error.Abort(_('cannot retrieve git head "%s"') % rev)
169 return heads
169 return heads
170
170
171 def catfile(self, rev, ftype):
171 def catfile(self, rev, ftype):
172 if rev == nodemod.nullhex:
172 if rev == nodemod.nullhex:
173 raise IOError
173 raise IOError
174 self.catfilepipe[0].write(rev+'\n')
174 self.catfilepipe[0].write(rev+'\n')
175 self.catfilepipe[0].flush()
175 self.catfilepipe[0].flush()
176 info = self.catfilepipe[1].readline().split()
176 info = self.catfilepipe[1].readline().split()
177 if info[1] != ftype:
177 if info[1] != ftype:
178 raise error.Abort(_('cannot read %r object at %s') % (ftype, rev))
178 raise error.Abort(_('cannot read %r object at %s') % (ftype, rev))
179 size = int(info[2])
179 size = int(info[2])
180 data = self.catfilepipe[1].read(size)
180 data = self.catfilepipe[1].read(size)
181 if len(data) < size:
181 if len(data) < size:
182 raise error.Abort(_('cannot read %r object at %s: unexpected size')
182 raise error.Abort(_('cannot read %r object at %s: unexpected size')
183 % (ftype, rev))
183 % (ftype, rev))
184 # read the trailing newline
184 # read the trailing newline
185 self.catfilepipe[1].read(1)
185 self.catfilepipe[1].read(1)
186 return data
186 return data
187
187
188 def getfile(self, name, rev):
188 def getfile(self, name, rev):
189 if rev == nodemod.nullhex:
189 if rev == nodemod.nullhex:
190 return None, None
190 return None, None
191 if name == '.hgsub':
191 if name == '.hgsub':
192 data = '\n'.join([m.hgsub() for m in self.submoditer()])
192 data = '\n'.join([m.hgsub() for m in self.submoditer()])
193 mode = ''
193 mode = ''
194 elif name == '.hgsubstate':
194 elif name == '.hgsubstate':
195 data = '\n'.join([m.hgsubstate() for m in self.submoditer()])
195 data = '\n'.join([m.hgsubstate() for m in self.submoditer()])
196 mode = ''
196 mode = ''
197 else:
197 else:
198 data = self.catfile(rev, "blob")
198 data = self.catfile(rev, "blob")
199 mode = self.modecache[(name, rev)]
199 mode = self.modecache[(name, rev)]
200 return data, mode
200 return data, mode
201
201
202 def submoditer(self):
202 def submoditer(self):
203 null = nodemod.nullhex
203 null = nodemod.nullhex
204 for m in sorted(self.submodules, key=lambda p: p.path):
204 for m in sorted(self.submodules, key=lambda p: p.path):
205 if m.node != null:
205 if m.node != null:
206 yield m
206 yield m
207
207
208 def parsegitmodules(self, content):
208 def parsegitmodules(self, content):
209 """Parse the formatted .gitmodules file, example file format:
209 """Parse the formatted .gitmodules file, example file format:
210 [submodule "sub"]\n
210 [submodule "sub"]\n
211 \tpath = sub\n
211 \tpath = sub\n
212 \turl = git://giturl\n
212 \turl = git://giturl\n
213 """
213 """
214 self.submodules = []
214 self.submodules = []
215 c = config.config()
215 c = config.config()
216 # Each item in .gitmodules starts with whitespace that cant be parsed
216 # Each item in .gitmodules starts with whitespace that cant be parsed
217 c.parse('.gitmodules', '\n'.join(line.strip() for line in
217 c.parse('.gitmodules', '\n'.join(line.strip() for line in
218 content.split('\n')))
218 content.split('\n')))
219 for sec in c.sections():
219 for sec in c.sections():
220 s = c[sec]
220 s = c[sec]
221 if 'url' in s and 'path' in s:
221 if 'url' in s and 'path' in s:
222 self.submodules.append(submodule(s['path'], '', s['url']))
222 self.submodules.append(submodule(s['path'], '', s['url']))
223
223
224 def retrievegitmodules(self, version):
224 def retrievegitmodules(self, version):
225 modules, ret = self.gitrun('show', '%s:%s' % (version, '.gitmodules'))
225 modules, ret = self.gitrun('show', '%s:%s' % (version, '.gitmodules'))
226 if ret:
226 if ret:
227 # This can happen if a file is in the repo that has permissions
227 # This can happen if a file is in the repo that has permissions
228 # 160000, but there is no .gitmodules file.
228 # 160000, but there is no .gitmodules file.
229 self.ui.warn(_("warning: cannot read submodules config file in "
229 self.ui.warn(_("warning: cannot read submodules config file in "
230 "%s\n") % version)
230 "%s\n") % version)
231 return
231 return
232
232
233 try:
233 try:
234 self.parsegitmodules(modules)
234 self.parsegitmodules(modules)
235 except error.ParseError:
235 except error.ParseError:
236 self.ui.warn(_("warning: unable to parse .gitmodules in %s\n")
236 self.ui.warn(_("warning: unable to parse .gitmodules in %s\n")
237 % version)
237 % version)
238 return
238 return
239
239
240 for m in self.submodules:
240 for m in self.submodules:
241 node, ret = self.gitrun('rev-parse', '%s:%s' % (version, m.path))
241 node, ret = self.gitrun('rev-parse', '%s:%s' % (version, m.path))
242 if ret:
242 if ret:
243 continue
243 continue
244 m.node = node.strip()
244 m.node = node.strip()
245
245
246 def getchanges(self, version, full):
246 def getchanges(self, version, full):
247 if full:
247 if full:
248 raise error.Abort(_("convert from git does not support --full"))
248 raise error.Abort(_("convert from git does not support --full"))
249 self.modecache = {}
249 self.modecache = {}
250 cmd = ['diff-tree','-z', '--root', '-m', '-r'] + self.simopt + [version]
250 cmd = ['diff-tree','-z', '--root', '-m', '-r'] + self.simopt + [version]
251 output, status = self.gitrun(*cmd)
251 output, status = self.gitrun(*cmd)
252 if status:
252 if status:
253 raise error.Abort(_('cannot read changes in %s') % version)
253 raise error.Abort(_('cannot read changes in %s') % version)
254 changes = []
254 changes = []
255 copies = {}
255 copies = {}
256 seen = set()
256 seen = set()
257 entry = None
257 entry = None
258 subexists = [False]
258 subexists = [False]
259 subdeleted = [False]
259 subdeleted = [False]
260 difftree = output.split('\x00')
260 difftree = output.split('\x00')
261 lcount = len(difftree)
261 lcount = len(difftree)
262 i = 0
262 i = 0
263
263
264 skipsubmodules = self.ui.configbool('convert', 'git.skipsubmodules')
264 skipsubmodules = self.ui.configbool('convert', 'git.skipsubmodules')
265 def add(entry, f, isdest):
265 def add(entry, f, isdest):
266 seen.add(f)
266 seen.add(f)
267 h = entry[3]
267 h = entry[3]
268 p = (entry[1] == "100755")
268 p = (entry[1] == "100755")
269 s = (entry[1] == "120000")
269 s = (entry[1] == "120000")
270 renamesource = (not isdest and entry[4][0] == 'R')
270 renamesource = (not isdest and entry[4][0] == 'R')
271
271
272 if f == '.gitmodules':
272 if f == '.gitmodules':
273 if skipsubmodules:
273 if skipsubmodules:
274 return
274 return
275
275
276 subexists[0] = True
276 subexists[0] = True
277 if entry[4] == 'D' or renamesource:
277 if entry[4] == 'D' or renamesource:
278 subdeleted[0] = True
278 subdeleted[0] = True
279 changes.append(('.hgsub', nodemod.nullhex))
279 changes.append(('.hgsub', nodemod.nullhex))
280 else:
280 else:
281 changes.append(('.hgsub', ''))
281 changes.append(('.hgsub', ''))
282 elif entry[1] == '160000' or entry[0] == ':160000':
282 elif entry[1] == '160000' or entry[0] == ':160000':
283 if not skipsubmodules:
283 if not skipsubmodules:
284 subexists[0] = True
284 subexists[0] = True
285 else:
285 else:
286 if renamesource:
286 if renamesource:
287 h = nodemod.nullhex
287 h = nodemod.nullhex
288 self.modecache[(f, h)] = (p and "x") or (s and "l") or ""
288 self.modecache[(f, h)] = (p and "x") or (s and "l") or ""
289 changes.append((f, h))
289 changes.append((f, h))
290
290
291 while i < lcount:
291 while i < lcount:
292 l = difftree[i]
292 l = difftree[i]
293 i += 1
293 i += 1
294 if not entry:
294 if not entry:
295 if not l.startswith(':'):
295 if not l.startswith(':'):
296 continue
296 continue
297 entry = l.split()
297 entry = l.split()
298 continue
298 continue
299 f = l
299 f = l
300 if entry[4][0] == 'C':
300 if entry[4][0] == 'C':
301 copysrc = f
301 copysrc = f
302 copydest = difftree[i]
302 copydest = difftree[i]
303 i += 1
303 i += 1
304 f = copydest
304 f = copydest
305 copies[copydest] = copysrc
305 copies[copydest] = copysrc
306 if f not in seen:
306 if f not in seen:
307 add(entry, f, False)
307 add(entry, f, False)
308 # A file can be copied multiple times, or modified and copied
308 # A file can be copied multiple times, or modified and copied
309 # simultaneously. So f can be repeated even if fdest isn't.
309 # simultaneously. So f can be repeated even if fdest isn't.
310 if entry[4][0] == 'R':
310 if entry[4][0] == 'R':
311 # rename: next line is the destination
311 # rename: next line is the destination
312 fdest = difftree[i]
312 fdest = difftree[i]
313 i += 1
313 i += 1
314 if fdest not in seen:
314 if fdest not in seen:
315 add(entry, fdest, True)
315 add(entry, fdest, True)
316 # .gitmodules isn't imported at all, so it being copied to
316 # .gitmodules isn't imported at all, so it being copied to
317 # and fro doesn't really make sense
317 # and fro doesn't really make sense
318 if f != '.gitmodules' and fdest != '.gitmodules':
318 if f != '.gitmodules' and fdest != '.gitmodules':
319 copies[fdest] = f
319 copies[fdest] = f
320 entry = None
320 entry = None
321
321
322 if subexists[0]:
322 if subexists[0]:
323 if subdeleted[0]:
323 if subdeleted[0]:
324 changes.append(('.hgsubstate', nodemod.nullhex))
324 changes.append(('.hgsubstate', nodemod.nullhex))
325 else:
325 else:
326 self.retrievegitmodules(version)
326 self.retrievegitmodules(version)
327 changes.append(('.hgsubstate', ''))
327 changes.append(('.hgsubstate', ''))
328 return (changes, copies, set())
328 return (changes, copies, set())
329
329
330 def getcommit(self, version):
330 def getcommit(self, version):
331 c = self.catfile(version, "commit") # read the commit hash
331 c = self.catfile(version, "commit") # read the commit hash
332 end = c.find("\n\n")
332 end = c.find("\n\n")
333 message = c[end + 2:]
333 message = c[end + 2:]
334 message = self.recode(message)
334 message = self.recode(message)
335 l = c[:end].splitlines()
335 l = c[:end].splitlines()
336 parents = []
336 parents = []
337 author = committer = None
337 author = committer = None
338 extra = {}
338 extra = {}
339 for e in l[1:]:
339 for e in l[1:]:
340 n, v = e.split(" ", 1)
340 n, v = e.split(" ", 1)
341 if n == "author":
341 if n == "author":
342 p = v.split()
342 p = v.split()
343 tm, tz = p[-2:]
343 tm, tz = p[-2:]
344 author = " ".join(p[:-2])
344 author = " ".join(p[:-2])
345 if author[0] == "<":
345 if author[0] == "<":
346 author = author[1:-1]
346 author = author[1:-1]
347 author = self.recode(author)
347 author = self.recode(author)
348 if n == "committer":
348 if n == "committer":
349 p = v.split()
349 p = v.split()
350 tm, tz = p[-2:]
350 tm, tz = p[-2:]
351 committer = " ".join(p[:-2])
351 committer = " ".join(p[:-2])
352 if committer[0] == "<":
352 if committer[0] == "<":
353 committer = committer[1:-1]
353 committer = committer[1:-1]
354 committer = self.recode(committer)
354 committer = self.recode(committer)
355 if n == "parent":
355 if n == "parent":
356 parents.append(v)
356 parents.append(v)
357 if n in self.copyextrakeys:
357 if n in self.copyextrakeys:
358 extra[n] = v
358 extra[n] = v
359
359
360 if self.committeractions['dropcommitter']:
360 if self.committeractions['dropcommitter']:
361 committer = None
361 committer = None
362 elif self.committeractions['replaceauthor']:
362 elif self.committeractions['replaceauthor']:
363 author = committer
363 author = committer
364
364
365 if committer:
365 if committer:
366 messagealways = self.committeractions['messagealways']
366 messagealways = self.committeractions['messagealways']
367 messagedifferent = self.committeractions['messagedifferent']
367 messagedifferent = self.committeractions['messagedifferent']
368 if messagealways:
368 if messagealways:
369 message += '\n%s %s\n' % (messagealways, committer)
369 message += '\n%s %s\n' % (messagealways, committer)
370 elif messagedifferent and author != committer:
370 elif messagedifferent and author != committer:
371 message += '\n%s %s\n' % (messagedifferent, committer)
371 message += '\n%s %s\n' % (messagedifferent, committer)
372
372
373 tzs, tzh, tzm = tz[-5:-4] + "1", tz[-4:-2], tz[-2:]
373 tzs, tzh, tzm = tz[-5:-4] + "1", tz[-4:-2], tz[-2:]
374 tz = -int(tzs) * (int(tzh) * 3600 + int(tzm))
374 tz = -int(tzs) * (int(tzh) * 3600 + int(tzm))
375 date = tm + " " + str(tz)
375 date = tm + " " + (b"%d" % tz)
376 saverev = self.ui.configbool('convert', 'git.saverev')
376 saverev = self.ui.configbool('convert', 'git.saverev')
377
377
378 c = common.commit(parents=parents, date=date, author=author,
378 c = common.commit(parents=parents, date=date, author=author,
379 desc=message,
379 desc=message,
380 rev=version,
380 rev=version,
381 extra=extra,
381 extra=extra,
382 saverev=saverev)
382 saverev=saverev)
383 return c
383 return c
384
384
385 def numcommits(self):
385 def numcommits(self):
386 output, ret = self.gitrunlines('rev-list', '--all')
386 output, ret = self.gitrunlines('rev-list', '--all')
387 if ret:
387 if ret:
388 raise error.Abort(_('cannot retrieve number of commits in %s') \
388 raise error.Abort(_('cannot retrieve number of commits in %s') \
389 % self.path)
389 % self.path)
390 return len(output)
390 return len(output)
391
391
392 def gettags(self):
392 def gettags(self):
393 tags = {}
393 tags = {}
394 alltags = {}
394 alltags = {}
395 output, status = self.gitrunlines('ls-remote', '--tags', self.path)
395 output, status = self.gitrunlines('ls-remote', '--tags', self.path)
396
396
397 if status:
397 if status:
398 raise error.Abort(_('cannot read tags from %s') % self.path)
398 raise error.Abort(_('cannot read tags from %s') % self.path)
399 prefix = 'refs/tags/'
399 prefix = 'refs/tags/'
400
400
401 # Build complete list of tags, both annotated and bare ones
401 # Build complete list of tags, both annotated and bare ones
402 for line in output:
402 for line in output:
403 line = line.strip()
403 line = line.strip()
404 if line.startswith("error:") or line.startswith("fatal:"):
404 if line.startswith("error:") or line.startswith("fatal:"):
405 raise error.Abort(_('cannot read tags from %s') % self.path)
405 raise error.Abort(_('cannot read tags from %s') % self.path)
406 node, tag = line.split(None, 1)
406 node, tag = line.split(None, 1)
407 if not tag.startswith(prefix):
407 if not tag.startswith(prefix):
408 continue
408 continue
409 alltags[tag[len(prefix):]] = node
409 alltags[tag[len(prefix):]] = node
410
410
411 # Filter out tag objects for annotated tag refs
411 # Filter out tag objects for annotated tag refs
412 for tag in alltags:
412 for tag in alltags:
413 if tag.endswith('^{}'):
413 if tag.endswith('^{}'):
414 tags[tag[:-3]] = alltags[tag]
414 tags[tag[:-3]] = alltags[tag]
415 else:
415 else:
416 if tag + '^{}' in alltags:
416 if tag + '^{}' in alltags:
417 continue
417 continue
418 else:
418 else:
419 tags[tag] = alltags[tag]
419 tags[tag] = alltags[tag]
420
420
421 return tags
421 return tags
422
422
423 def getchangedfiles(self, version, i):
423 def getchangedfiles(self, version, i):
424 changes = []
424 changes = []
425 if i is None:
425 if i is None:
426 output, status = self.gitrunlines('diff-tree', '--root', '-m',
426 output, status = self.gitrunlines('diff-tree', '--root', '-m',
427 '-r', version)
427 '-r', version)
428 if status:
428 if status:
429 raise error.Abort(_('cannot read changes in %s') % version)
429 raise error.Abort(_('cannot read changes in %s') % version)
430 for l in output:
430 for l in output:
431 if "\t" not in l:
431 if "\t" not in l:
432 continue
432 continue
433 m, f = l[:-1].split("\t")
433 m, f = l[:-1].split("\t")
434 changes.append(f)
434 changes.append(f)
435 else:
435 else:
436 output, status = self.gitrunlines('diff-tree', '--name-only',
436 output, status = self.gitrunlines('diff-tree', '--name-only',
437 '--root', '-r', version,
437 '--root', '-r', version,
438 '%s^%d' % (version, i + 1), '--')
438 '%s^%d' % (version, i + 1), '--')
439 if status:
439 if status:
440 raise error.Abort(_('cannot read changes in %s') % version)
440 raise error.Abort(_('cannot read changes in %s') % version)
441 changes = [f.rstrip('\n') for f in output]
441 changes = [f.rstrip('\n') for f in output]
442
442
443 return changes
443 return changes
444
444
445 def getbookmarks(self):
445 def getbookmarks(self):
446 bookmarks = {}
446 bookmarks = {}
447
447
448 # Handle local and remote branches
448 # Handle local and remote branches
449 remoteprefix = self.ui.config('convert', 'git.remoteprefix')
449 remoteprefix = self.ui.config('convert', 'git.remoteprefix')
450 reftypes = [
450 reftypes = [
451 # (git prefix, hg prefix)
451 # (git prefix, hg prefix)
452 ('refs/remotes/origin/', remoteprefix + '/'),
452 ('refs/remotes/origin/', remoteprefix + '/'),
453 ('refs/heads/', '')
453 ('refs/heads/', '')
454 ]
454 ]
455
455
456 exclude = {
456 exclude = {
457 'refs/remotes/origin/HEAD',
457 'refs/remotes/origin/HEAD',
458 }
458 }
459
459
460 try:
460 try:
461 output, status = self.gitrunlines('show-ref')
461 output, status = self.gitrunlines('show-ref')
462 for line in output:
462 for line in output:
463 line = line.strip()
463 line = line.strip()
464 rev, name = line.split(None, 1)
464 rev, name = line.split(None, 1)
465 # Process each type of branch
465 # Process each type of branch
466 for gitprefix, hgprefix in reftypes:
466 for gitprefix, hgprefix in reftypes:
467 if not name.startswith(gitprefix) or name in exclude:
467 if not name.startswith(gitprefix) or name in exclude:
468 continue
468 continue
469 name = '%s%s' % (hgprefix, name[len(gitprefix):])
469 name = '%s%s' % (hgprefix, name[len(gitprefix):])
470 bookmarks[name] = rev
470 bookmarks[name] = rev
471 except Exception:
471 except Exception:
472 pass
472 pass
473
473
474 return bookmarks
474 return bookmarks
475
475
476 def checkrevformat(self, revstr, mapname='splicemap'):
476 def checkrevformat(self, revstr, mapname='splicemap'):
477 """ git revision string is a 40 byte hex """
477 """ git revision string is a 40 byte hex """
478 self.checkhexformat(revstr, mapname)
478 self.checkhexformat(revstr, mapname)
General Comments 0
You need to be logged in to leave comments. Login now