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