##// END OF EJS Templates
git convert: some versions of git use fatal: instead of error:...
Augie Fackler -
r18572:5fe58f93 default
parent child Browse files
Show More
@@ -1,298 +1,298 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
10 from mercurial import util, config
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
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):
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 else:
49 else:
50 def gitopen(self, s, err=None):
50 def gitopen(self, s, err=None):
51 if err == subprocess.PIPE:
51 if err == subprocess.PIPE:
52 (sin, so, se) = util.popen3('GIT_DIR=%s %s' % (self.path, s))
52 (sin, so, se) = util.popen3('GIT_DIR=%s %s' % (self.path, s))
53 return so
53 return so
54 elif err == subprocess.STDOUT:
54 elif err == subprocess.STDOUT:
55 return self.popen_with_stderr(s)
55 return self.popen_with_stderr(s)
56 else:
56 else:
57 return util.popen('GIT_DIR=%s %s' % (self.path, s), 'rb')
57 return util.popen('GIT_DIR=%s %s' % (self.path, s), 'rb')
58
58
59 def popen_with_stderr(self, s):
59 def popen_with_stderr(self, s):
60 p = subprocess.Popen(s, shell=True, bufsize=-1,
60 p = subprocess.Popen(s, shell=True, bufsize=-1,
61 close_fds=util.closefds,
61 close_fds=util.closefds,
62 stdin=subprocess.PIPE,
62 stdin=subprocess.PIPE,
63 stdout=subprocess.PIPE,
63 stdout=subprocess.PIPE,
64 stderr=subprocess.STDOUT,
64 stderr=subprocess.STDOUT,
65 universal_newlines=False,
65 universal_newlines=False,
66 env=None)
66 env=None)
67 return p.stdout
67 return p.stdout
68
68
69 def gitread(self, s):
69 def gitread(self, s):
70 fh = self.gitopen(s)
70 fh = self.gitopen(s)
71 data = fh.read()
71 data = fh.read()
72 return data, fh.close()
72 return data, fh.close()
73
73
74 def __init__(self, ui, path, rev=None):
74 def __init__(self, ui, path, rev=None):
75 super(convert_git, self).__init__(ui, path, rev=rev)
75 super(convert_git, self).__init__(ui, path, rev=rev)
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 NoRepo(_("%s does not look like a Git repository") % path)
80 raise NoRepo(_("%s does not look like a Git repository") % path)
81
81
82 checktool('git', 'git')
82 checktool('git', 'git')
83
83
84 self.path = path
84 self.path = path
85 self.submodules = []
85 self.submodules = []
86
86
87 def getheads(self):
87 def getheads(self):
88 if not self.rev:
88 if not self.rev:
89 heads, ret = self.gitread('git rev-parse --branches --remotes')
89 heads, ret = self.gitread('git rev-parse --branches --remotes')
90 heads = heads.splitlines()
90 heads = heads.splitlines()
91 else:
91 else:
92 heads, ret = self.gitread("git rev-parse --verify %s" % self.rev)
92 heads, ret = self.gitread("git rev-parse --verify %s" % self.rev)
93 heads = [heads[:-1]]
93 heads = [heads[:-1]]
94 if ret:
94 if ret:
95 raise util.Abort(_('cannot retrieve git heads'))
95 raise util.Abort(_('cannot retrieve git heads'))
96 return heads
96 return heads
97
97
98 def catfile(self, rev, type):
98 def catfile(self, rev, type):
99 if rev == hex(nullid):
99 if rev == hex(nullid):
100 raise IOError
100 raise IOError
101 data, ret = self.gitread("git cat-file %s %s" % (type, rev))
101 data, ret = self.gitread("git cat-file %s %s" % (type, rev))
102 if ret:
102 if ret:
103 raise util.Abort(_('cannot read %r object at %s') % (type, rev))
103 raise util.Abort(_('cannot read %r object at %s') % (type, rev))
104 return data
104 return data
105
105
106 def getfile(self, name, rev):
106 def getfile(self, name, rev):
107 if name == '.hgsub':
107 if name == '.hgsub':
108 data = '\n'.join([m.hgsub() for m in self.submoditer()])
108 data = '\n'.join([m.hgsub() for m in self.submoditer()])
109 mode = ''
109 mode = ''
110 elif name == '.hgsubstate':
110 elif name == '.hgsubstate':
111 data = '\n'.join([m.hgsubstate() for m in self.submoditer()])
111 data = '\n'.join([m.hgsubstate() for m in self.submoditer()])
112 mode = ''
112 mode = ''
113 else:
113 else:
114 data = self.catfile(rev, "blob")
114 data = self.catfile(rev, "blob")
115 mode = self.modecache[(name, rev)]
115 mode = self.modecache[(name, rev)]
116 return data, mode
116 return data, mode
117
117
118 def submoditer(self):
118 def submoditer(self):
119 null = hex(nullid)
119 null = hex(nullid)
120 for m in sorted(self.submodules, key=lambda p: p.path):
120 for m in sorted(self.submodules, key=lambda p: p.path):
121 if m.node != null:
121 if m.node != null:
122 yield m
122 yield m
123
123
124 def parsegitmodules(self, content):
124 def parsegitmodules(self, content):
125 """Parse the formatted .gitmodules file, example file format:
125 """Parse the formatted .gitmodules file, example file format:
126 [submodule "sub"]\n
126 [submodule "sub"]\n
127 \tpath = sub\n
127 \tpath = sub\n
128 \turl = git://giturl\n
128 \turl = git://giturl\n
129 """
129 """
130 self.submodules = []
130 self.submodules = []
131 c = config.config()
131 c = config.config()
132 # Each item in .gitmodules starts with \t that cant be parsed
132 # Each item in .gitmodules starts with \t that cant be parsed
133 c.parse('.gitmodules', content.replace('\t',''))
133 c.parse('.gitmodules', content.replace('\t',''))
134 for sec in c.sections():
134 for sec in c.sections():
135 s = c[sec]
135 s = c[sec]
136 if 'url' in s and 'path' in s:
136 if 'url' in s and 'path' in s:
137 self.submodules.append(submodule(s['path'], '', s['url']))
137 self.submodules.append(submodule(s['path'], '', s['url']))
138
138
139 def retrievegitmodules(self, version):
139 def retrievegitmodules(self, version):
140 modules, ret = self.gitread("git show %s:%s" % (version, '.gitmodules'))
140 modules, ret = self.gitread("git show %s:%s" % (version, '.gitmodules'))
141 if ret:
141 if ret:
142 raise util.Abort(_('cannot read submodules config file in %s') %
142 raise util.Abort(_('cannot read submodules config file in %s') %
143 version)
143 version)
144 self.parsegitmodules(modules)
144 self.parsegitmodules(modules)
145 for m in self.submodules:
145 for m in self.submodules:
146 node, ret = self.gitread("git rev-parse %s:%s" % (version, m.path))
146 node, ret = self.gitread("git rev-parse %s:%s" % (version, m.path))
147 if ret:
147 if ret:
148 continue
148 continue
149 m.node = node.strip()
149 m.node = node.strip()
150
150
151 def getchanges(self, version):
151 def getchanges(self, version):
152 self.modecache = {}
152 self.modecache = {}
153 fh = self.gitopen("git diff-tree -z --root -m -r %s" % version)
153 fh = self.gitopen("git diff-tree -z --root -m -r %s" % version)
154 changes = []
154 changes = []
155 seen = set()
155 seen = set()
156 entry = None
156 entry = None
157 subexists = False
157 subexists = False
158 for l in fh.read().split('\x00'):
158 for l in fh.read().split('\x00'):
159 if not entry:
159 if not entry:
160 if not l.startswith(':'):
160 if not l.startswith(':'):
161 continue
161 continue
162 entry = l
162 entry = l
163 continue
163 continue
164 f = l
164 f = l
165 if f not in seen:
165 if f not in seen:
166 seen.add(f)
166 seen.add(f)
167 entry = entry.split()
167 entry = entry.split()
168 h = entry[3]
168 h = entry[3]
169 p = (entry[1] == "100755")
169 p = (entry[1] == "100755")
170 s = (entry[1] == "120000")
170 s = (entry[1] == "120000")
171
171
172 if f == '.gitmodules':
172 if f == '.gitmodules':
173 subexists = True
173 subexists = True
174 changes.append(('.hgsub', ''))
174 changes.append(('.hgsub', ''))
175 elif entry[1] == '160000' or entry[0] == ':160000':
175 elif entry[1] == '160000' or entry[0] == ':160000':
176 subexists = True
176 subexists = True
177 else:
177 else:
178 self.modecache[(f, h)] = (p and "x") or (s and "l") or ""
178 self.modecache[(f, h)] = (p and "x") or (s and "l") or ""
179 changes.append((f, h))
179 changes.append((f, h))
180 entry = None
180 entry = None
181 if fh.close():
181 if fh.close():
182 raise util.Abort(_('cannot read changes in %s') % version)
182 raise util.Abort(_('cannot read changes in %s') % version)
183
183
184 if subexists:
184 if subexists:
185 self.retrievegitmodules(version)
185 self.retrievegitmodules(version)
186 changes.append(('.hgsubstate', ''))
186 changes.append(('.hgsubstate', ''))
187 return (changes, {})
187 return (changes, {})
188
188
189 def getcommit(self, version):
189 def getcommit(self, version):
190 c = self.catfile(version, "commit") # read the commit hash
190 c = self.catfile(version, "commit") # read the commit hash
191 end = c.find("\n\n")
191 end = c.find("\n\n")
192 message = c[end + 2:]
192 message = c[end + 2:]
193 message = self.recode(message)
193 message = self.recode(message)
194 l = c[:end].splitlines()
194 l = c[:end].splitlines()
195 parents = []
195 parents = []
196 author = committer = None
196 author = committer = None
197 for e in l[1:]:
197 for e in l[1:]:
198 n, v = e.split(" ", 1)
198 n, v = e.split(" ", 1)
199 if n == "author":
199 if n == "author":
200 p = v.split()
200 p = v.split()
201 tm, tz = p[-2:]
201 tm, tz = p[-2:]
202 author = " ".join(p[:-2])
202 author = " ".join(p[:-2])
203 if author[0] == "<": author = author[1:-1]
203 if author[0] == "<": author = author[1:-1]
204 author = self.recode(author)
204 author = self.recode(author)
205 if n == "committer":
205 if n == "committer":
206 p = v.split()
206 p = v.split()
207 tm, tz = p[-2:]
207 tm, tz = p[-2:]
208 committer = " ".join(p[:-2])
208 committer = " ".join(p[:-2])
209 if committer[0] == "<": committer = committer[1:-1]
209 if committer[0] == "<": committer = committer[1:-1]
210 committer = self.recode(committer)
210 committer = self.recode(committer)
211 if n == "parent":
211 if n == "parent":
212 parents.append(v)
212 parents.append(v)
213
213
214 if committer and committer != author:
214 if committer and committer != author:
215 message += "\ncommitter: %s\n" % committer
215 message += "\ncommitter: %s\n" % committer
216 tzs, tzh, tzm = tz[-5:-4] + "1", tz[-4:-2], tz[-2:]
216 tzs, tzh, tzm = tz[-5:-4] + "1", tz[-4:-2], tz[-2:]
217 tz = -int(tzs) * (int(tzh) * 3600 + int(tzm))
217 tz = -int(tzs) * (int(tzh) * 3600 + int(tzm))
218 date = tm + " " + str(tz)
218 date = tm + " " + str(tz)
219
219
220 c = commit(parents=parents, date=date, author=author, desc=message,
220 c = commit(parents=parents, date=date, author=author, desc=message,
221 rev=version)
221 rev=version)
222 return c
222 return c
223
223
224 def gettags(self):
224 def gettags(self):
225 tags = {}
225 tags = {}
226 alltags = {}
226 alltags = {}
227 fh = self.gitopen('git ls-remote --tags "%s"' % self.path,
227 fh = self.gitopen('git ls-remote --tags "%s"' % self.path,
228 err=subprocess.STDOUT)
228 err=subprocess.STDOUT)
229 prefix = 'refs/tags/'
229 prefix = 'refs/tags/'
230
230
231 # Build complete list of tags, both annotated and bare ones
231 # Build complete list of tags, both annotated and bare ones
232 for line in fh:
232 for line in fh:
233 line = line.strip()
233 line = line.strip()
234 if line.startswith("error:"):
234 if line.startswith("error:") or line.startswith("fatal:"):
235 raise util.Abort(_('cannot read tags from %s') % self.path)
235 raise util.Abort(_('cannot read tags from %s') % self.path)
236 node, tag = line.split(None, 1)
236 node, tag = line.split(None, 1)
237 if not tag.startswith(prefix):
237 if not tag.startswith(prefix):
238 continue
238 continue
239 alltags[tag[len(prefix):]] = node
239 alltags[tag[len(prefix):]] = node
240 if fh.close():
240 if fh.close():
241 raise util.Abort(_('cannot read tags from %s') % self.path)
241 raise util.Abort(_('cannot read tags from %s') % self.path)
242
242
243 # Filter out tag objects for annotated tag refs
243 # Filter out tag objects for annotated tag refs
244 for tag in alltags:
244 for tag in alltags:
245 if tag.endswith('^{}'):
245 if tag.endswith('^{}'):
246 tags[tag[:-3]] = alltags[tag]
246 tags[tag[:-3]] = alltags[tag]
247 else:
247 else:
248 if tag + '^{}' in alltags:
248 if tag + '^{}' in alltags:
249 continue
249 continue
250 else:
250 else:
251 tags[tag] = alltags[tag]
251 tags[tag] = alltags[tag]
252
252
253 return tags
253 return tags
254
254
255 def getchangedfiles(self, version, i):
255 def getchangedfiles(self, version, i):
256 changes = []
256 changes = []
257 if i is None:
257 if i is None:
258 fh = self.gitopen("git diff-tree --root -m -r %s" % version)
258 fh = self.gitopen("git diff-tree --root -m -r %s" % version)
259 for l in fh:
259 for l in fh:
260 if "\t" not in l:
260 if "\t" not in l:
261 continue
261 continue
262 m, f = l[:-1].split("\t")
262 m, f = l[:-1].split("\t")
263 changes.append(f)
263 changes.append(f)
264 else:
264 else:
265 fh = self.gitopen('git diff-tree --name-only --root -r %s '
265 fh = self.gitopen('git diff-tree --name-only --root -r %s '
266 '"%s^%s" --' % (version, version, i + 1))
266 '"%s^%s" --' % (version, version, i + 1))
267 changes = [f.rstrip('\n') for f in fh]
267 changes = [f.rstrip('\n') for f in fh]
268 if fh.close():
268 if fh.close():
269 raise util.Abort(_('cannot read changes in %s') % version)
269 raise util.Abort(_('cannot read changes in %s') % version)
270
270
271 return changes
271 return changes
272
272
273 def getbookmarks(self):
273 def getbookmarks(self):
274 bookmarks = {}
274 bookmarks = {}
275
275
276 # Interesting references in git are prefixed
276 # Interesting references in git are prefixed
277 prefix = 'refs/heads/'
277 prefix = 'refs/heads/'
278 prefixlen = len(prefix)
278 prefixlen = len(prefix)
279
279
280 # factor two commands
280 # factor two commands
281 gitcmd = { 'remote/': 'git ls-remote --heads origin',
281 gitcmd = { 'remote/': 'git ls-remote --heads origin',
282 '': 'git show-ref'}
282 '': 'git show-ref'}
283
283
284 # Origin heads
284 # Origin heads
285 for reftype in gitcmd:
285 for reftype in gitcmd:
286 try:
286 try:
287 fh = self.gitopen(gitcmd[reftype], err=subprocess.PIPE)
287 fh = self.gitopen(gitcmd[reftype], err=subprocess.PIPE)
288 for line in fh:
288 for line in fh:
289 line = line.strip()
289 line = line.strip()
290 rev, name = line.split(None, 1)
290 rev, name = line.split(None, 1)
291 if not name.startswith(prefix):
291 if not name.startswith(prefix):
292 continue
292 continue
293 name = '%s%s' % (reftype, name[prefixlen:])
293 name = '%s%s' % (reftype, name[prefixlen:])
294 bookmarks[name] = rev
294 bookmarks[name] = rev
295 except Exception:
295 except Exception:
296 pass
296 pass
297
297
298 return bookmarks
298 return bookmarks
General Comments 0
You need to be logged in to leave comments. Login now