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