##// END OF EJS Templates
convert: support non annotated tags in git backend...
Edouard Gomez -
r16259:589aab2c default
parent child Browse files
Show More
@@ -1,205 +1,215
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 from mercurial import util
9 from mercurial import util
10 from mercurial.node import hex, nullid
10 from mercurial.node import hex, nullid
11 from mercurial.i18n import _
11 from mercurial.i18n import _
12
12
13 from common import NoRepo, commit, converter_source, checktool
13 from common import NoRepo, commit, converter_source, checktool
14
14
15 class convert_git(converter_source):
15 class convert_git(converter_source):
16 # Windows does not support GIT_DIR= construct while other systems
16 # Windows does not support GIT_DIR= construct while other systems
17 # cannot remove environment variable. Just assume none have
17 # cannot remove environment variable. Just assume none have
18 # both issues.
18 # both issues.
19 if util.safehasattr(os, 'unsetenv'):
19 if util.safehasattr(os, 'unsetenv'):
20 def gitopen(self, s, noerr=False):
20 def gitopen(self, s, noerr=False):
21 prevgitdir = os.environ.get('GIT_DIR')
21 prevgitdir = os.environ.get('GIT_DIR')
22 os.environ['GIT_DIR'] = self.path
22 os.environ['GIT_DIR'] = self.path
23 try:
23 try:
24 if noerr:
24 if noerr:
25 (stdin, stdout, stderr) = util.popen3(s)
25 (stdin, stdout, stderr) = util.popen3(s)
26 return stdout
26 return stdout
27 else:
27 else:
28 return util.popen(s, 'rb')
28 return util.popen(s, 'rb')
29 finally:
29 finally:
30 if prevgitdir is None:
30 if prevgitdir is None:
31 del os.environ['GIT_DIR']
31 del os.environ['GIT_DIR']
32 else:
32 else:
33 os.environ['GIT_DIR'] = prevgitdir
33 os.environ['GIT_DIR'] = prevgitdir
34 else:
34 else:
35 def gitopen(self, s, noerr=False):
35 def gitopen(self, s, noerr=False):
36 if noerr:
36 if noerr:
37 (sin, so, se) = util.popen3('GIT_DIR=%s %s' % (self.path, s))
37 (sin, so, se) = util.popen3('GIT_DIR=%s %s' % (self.path, s))
38 return so
38 return so
39 else:
39 else:
40 return util.popen('GIT_DIR=%s %s' % (self.path, s), 'rb')
40 return util.popen('GIT_DIR=%s %s' % (self.path, s), 'rb')
41
41
42 def gitread(self, s):
42 def gitread(self, s):
43 fh = self.gitopen(s)
43 fh = self.gitopen(s)
44 data = fh.read()
44 data = fh.read()
45 return data, fh.close()
45 return data, fh.close()
46
46
47 def __init__(self, ui, path, rev=None):
47 def __init__(self, ui, path, rev=None):
48 super(convert_git, self).__init__(ui, path, rev=rev)
48 super(convert_git, self).__init__(ui, path, rev=rev)
49
49
50 if os.path.isdir(path + "/.git"):
50 if os.path.isdir(path + "/.git"):
51 path += "/.git"
51 path += "/.git"
52 if not os.path.exists(path + "/objects"):
52 if not os.path.exists(path + "/objects"):
53 raise NoRepo(_("%s does not look like a Git repository") % path)
53 raise NoRepo(_("%s does not look like a Git repository") % path)
54
54
55 checktool('git', 'git')
55 checktool('git', 'git')
56
56
57 self.path = path
57 self.path = path
58
58
59 def getheads(self):
59 def getheads(self):
60 if not self.rev:
60 if not self.rev:
61 heads, ret = self.gitread('git rev-parse --branches --remotes')
61 heads, ret = self.gitread('git rev-parse --branches --remotes')
62 heads = heads.splitlines()
62 heads = heads.splitlines()
63 else:
63 else:
64 heads, ret = self.gitread("git rev-parse --verify %s" % self.rev)
64 heads, ret = self.gitread("git rev-parse --verify %s" % self.rev)
65 heads = [heads[:-1]]
65 heads = [heads[:-1]]
66 if ret:
66 if ret:
67 raise util.Abort(_('cannot retrieve git heads'))
67 raise util.Abort(_('cannot retrieve git heads'))
68 return heads
68 return heads
69
69
70 def catfile(self, rev, type):
70 def catfile(self, rev, type):
71 if rev == hex(nullid):
71 if rev == hex(nullid):
72 raise IOError()
72 raise IOError()
73 data, ret = self.gitread("git cat-file %s %s" % (type, rev))
73 data, ret = self.gitread("git cat-file %s %s" % (type, rev))
74 if ret:
74 if ret:
75 raise util.Abort(_('cannot read %r object at %s') % (type, rev))
75 raise util.Abort(_('cannot read %r object at %s') % (type, rev))
76 return data
76 return data
77
77
78 def getfile(self, name, rev):
78 def getfile(self, name, rev):
79 data = self.catfile(rev, "blob")
79 data = self.catfile(rev, "blob")
80 mode = self.modecache[(name, rev)]
80 mode = self.modecache[(name, rev)]
81 return data, mode
81 return data, mode
82
82
83 def getchanges(self, version):
83 def getchanges(self, version):
84 self.modecache = {}
84 self.modecache = {}
85 fh = self.gitopen("git diff-tree -z --root -m -r %s" % version)
85 fh = self.gitopen("git diff-tree -z --root -m -r %s" % version)
86 changes = []
86 changes = []
87 seen = set()
87 seen = set()
88 entry = None
88 entry = None
89 for l in fh.read().split('\x00'):
89 for l in fh.read().split('\x00'):
90 if not entry:
90 if not entry:
91 if not l.startswith(':'):
91 if not l.startswith(':'):
92 continue
92 continue
93 entry = l
93 entry = l
94 continue
94 continue
95 f = l
95 f = l
96 if f not in seen:
96 if f not in seen:
97 seen.add(f)
97 seen.add(f)
98 entry = entry.split()
98 entry = entry.split()
99 h = entry[3]
99 h = entry[3]
100 p = (entry[1] == "100755")
100 p = (entry[1] == "100755")
101 s = (entry[1] == "120000")
101 s = (entry[1] == "120000")
102 self.modecache[(f, h)] = (p and "x") or (s and "l") or ""
102 self.modecache[(f, h)] = (p and "x") or (s and "l") or ""
103 changes.append((f, h))
103 changes.append((f, h))
104 entry = None
104 entry = None
105 if fh.close():
105 if fh.close():
106 raise util.Abort(_('cannot read changes in %s') % version)
106 raise util.Abort(_('cannot read changes in %s') % version)
107 return (changes, {})
107 return (changes, {})
108
108
109 def getcommit(self, version):
109 def getcommit(self, version):
110 c = self.catfile(version, "commit") # read the commit hash
110 c = self.catfile(version, "commit") # read the commit hash
111 end = c.find("\n\n")
111 end = c.find("\n\n")
112 message = c[end + 2:]
112 message = c[end + 2:]
113 message = self.recode(message)
113 message = self.recode(message)
114 l = c[:end].splitlines()
114 l = c[:end].splitlines()
115 parents = []
115 parents = []
116 author = committer = None
116 author = committer = None
117 for e in l[1:]:
117 for e in l[1:]:
118 n, v = e.split(" ", 1)
118 n, v = e.split(" ", 1)
119 if n == "author":
119 if n == "author":
120 p = v.split()
120 p = v.split()
121 tm, tz = p[-2:]
121 tm, tz = p[-2:]
122 author = " ".join(p[:-2])
122 author = " ".join(p[:-2])
123 if author[0] == "<": author = author[1:-1]
123 if author[0] == "<": author = author[1:-1]
124 author = self.recode(author)
124 author = self.recode(author)
125 if n == "committer":
125 if n == "committer":
126 p = v.split()
126 p = v.split()
127 tm, tz = p[-2:]
127 tm, tz = p[-2:]
128 committer = " ".join(p[:-2])
128 committer = " ".join(p[:-2])
129 if committer[0] == "<": committer = committer[1:-1]
129 if committer[0] == "<": committer = committer[1:-1]
130 committer = self.recode(committer)
130 committer = self.recode(committer)
131 if n == "parent":
131 if n == "parent":
132 parents.append(v)
132 parents.append(v)
133
133
134 if committer and committer != author:
134 if committer and committer != author:
135 message += "\ncommitter: %s\n" % committer
135 message += "\ncommitter: %s\n" % committer
136 tzs, tzh, tzm = tz[-5:-4] + "1", tz[-4:-2], tz[-2:]
136 tzs, tzh, tzm = tz[-5:-4] + "1", tz[-4:-2], tz[-2:]
137 tz = -int(tzs) * (int(tzh) * 3600 + int(tzm))
137 tz = -int(tzs) * (int(tzh) * 3600 + int(tzm))
138 date = tm + " " + str(tz)
138 date = tm + " " + str(tz)
139
139
140 c = commit(parents=parents, date=date, author=author, desc=message,
140 c = commit(parents=parents, date=date, author=author, desc=message,
141 rev=version)
141 rev=version)
142 return c
142 return c
143
143
144 def gettags(self):
144 def gettags(self):
145 tags = {}
145 tags = {}
146 alltags = {}
146 fh = self.gitopen('git ls-remote --tags "%s"' % self.path)
147 fh = self.gitopen('git ls-remote --tags "%s"' % self.path)
147 prefix = 'refs/tags/'
148 prefix = 'refs/tags/'
149
150 # Build complete list of tags, both annotated and bare ones
148 for line in fh:
151 for line in fh:
149 line = line.strip()
152 line = line.strip()
150 if not line.endswith("^{}"):
151 continue
152 node, tag = line.split(None, 1)
153 node, tag = line.split(None, 1)
153 if not tag.startswith(prefix):
154 if not tag.startswith(prefix):
154 continue
155 continue
155 tag = tag[len(prefix):-3]
156 alltags[tag[len(prefix):]] = node
156 tags[tag] = node
157 if fh.close():
157 if fh.close():
158 raise util.Abort(_('cannot read tags from %s') % self.path)
158 raise util.Abort(_('cannot read tags from %s') % self.path)
159
159
160 # Filter out tag objects for annotated tag refs
161 for tag in alltags:
162 if tag.endswith('^{}'):
163 tags[tag[:-3]] = alltags[tag]
164 else:
165 if tag + '^{}' in alltags:
166 continue
167 else:
168 tags[tag] = alltags[tag]
169
160 return tags
170 return tags
161
171
162 def getchangedfiles(self, version, i):
172 def getchangedfiles(self, version, i):
163 changes = []
173 changes = []
164 if i is None:
174 if i is None:
165 fh = self.gitopen("git diff-tree --root -m -r %s" % version)
175 fh = self.gitopen("git diff-tree --root -m -r %s" % version)
166 for l in fh:
176 for l in fh:
167 if "\t" not in l:
177 if "\t" not in l:
168 continue
178 continue
169 m, f = l[:-1].split("\t")
179 m, f = l[:-1].split("\t")
170 changes.append(f)
180 changes.append(f)
171 else:
181 else:
172 fh = self.gitopen('git diff-tree --name-only --root -r %s "%s^%s" --'
182 fh = self.gitopen('git diff-tree --name-only --root -r %s "%s^%s" --'
173 % (version, version, i + 1))
183 % (version, version, i + 1))
174 changes = [f.rstrip('\n') for f in fh]
184 changes = [f.rstrip('\n') for f in fh]
175 if fh.close():
185 if fh.close():
176 raise util.Abort(_('cannot read changes in %s') % version)
186 raise util.Abort(_('cannot read changes in %s') % version)
177
187
178 return changes
188 return changes
179
189
180 def getbookmarks(self):
190 def getbookmarks(self):
181 bookmarks = {}
191 bookmarks = {}
182
192
183 # Interesting references in git are prefixed
193 # Interesting references in git are prefixed
184 prefix = 'refs/heads/'
194 prefix = 'refs/heads/'
185 prefixlen = len(prefix)
195 prefixlen = len(prefix)
186
196
187 # factor two commands
197 # factor two commands
188 gitcmd = { 'remote/': 'git ls-remote --heads origin',
198 gitcmd = { 'remote/': 'git ls-remote --heads origin',
189 '': 'git show-ref'}
199 '': 'git show-ref'}
190
200
191 # Origin heads
201 # Origin heads
192 for reftype in gitcmd:
202 for reftype in gitcmd:
193 try:
203 try:
194 fh = self.gitopen(gitcmd[reftype], noerr=True)
204 fh = self.gitopen(gitcmd[reftype], noerr=True)
195 for line in fh:
205 for line in fh:
196 line = line.strip()
206 line = line.strip()
197 rev, name = line.split(None, 1)
207 rev, name = line.split(None, 1)
198 if not name.startswith(prefix):
208 if not name.startswith(prefix):
199 continue
209 continue
200 name = '%s%s' % (reftype, name[prefixlen:])
210 name = '%s%s' % (reftype, name[prefixlen:])
201 bookmarks[name] = rev
211 bookmarks[name] = rev
202 except:
212 except:
203 pass
213 pass
204
214
205 return bookmarks
215 return bookmarks
General Comments 0
You need to be logged in to leave comments. Login now