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