##// END OF EJS Templates
convert: refactor hg getchanges and caching
Mads Kiilerich -
r22299:98aafdf4 default
parent child Browse files
Show More
@@ -1,472 +1,470
1 # hg.py - hg backend for convert extension
1 # hg.py - hg backend for 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 # Notes for hg->hg conversion:
8 # Notes for hg->hg conversion:
9 #
9 #
10 # * Old versions of Mercurial didn't trim the whitespace from the ends
10 # * Old versions of Mercurial didn't trim the whitespace from the ends
11 # of commit messages, but new versions do. Changesets created by
11 # of commit messages, but new versions do. Changesets created by
12 # those older versions, then converted, may thus have different
12 # those older versions, then converted, may thus have different
13 # hashes for changesets that are otherwise identical.
13 # hashes for changesets that are otherwise identical.
14 #
14 #
15 # * Using "--config convert.hg.saverev=true" will make the source
15 # * Using "--config convert.hg.saverev=true" will make the source
16 # identifier to be stored in the converted revision. This will cause
16 # identifier to be stored in the converted revision. This will cause
17 # the converted revision to have a different identity than the
17 # the converted revision to have a different identity than the
18 # source.
18 # source.
19
19
20
20
21 import os, time, cStringIO
21 import os, time, cStringIO
22 from mercurial.i18n import _
22 from mercurial.i18n import _
23 from mercurial.node import bin, hex, nullid
23 from mercurial.node import bin, hex, nullid
24 from mercurial import hg, util, context, bookmarks, error, scmutil
24 from mercurial import hg, util, context, bookmarks, error, scmutil
25
25
26 from common import NoRepo, commit, converter_source, converter_sink
26 from common import NoRepo, commit, converter_source, converter_sink
27
27
28 import re
28 import re
29 sha1re = re.compile(r'\b[0-9a-f]{6,40}\b')
29 sha1re = re.compile(r'\b[0-9a-f]{6,40}\b')
30
30
31 class mercurial_sink(converter_sink):
31 class mercurial_sink(converter_sink):
32 def __init__(self, ui, path):
32 def __init__(self, ui, path):
33 converter_sink.__init__(self, ui, path)
33 converter_sink.__init__(self, ui, path)
34 self.branchnames = ui.configbool('convert', 'hg.usebranchnames', True)
34 self.branchnames = ui.configbool('convert', 'hg.usebranchnames', True)
35 self.clonebranches = ui.configbool('convert', 'hg.clonebranches', False)
35 self.clonebranches = ui.configbool('convert', 'hg.clonebranches', False)
36 self.tagsbranch = ui.config('convert', 'hg.tagsbranch', 'default')
36 self.tagsbranch = ui.config('convert', 'hg.tagsbranch', 'default')
37 self.lastbranch = None
37 self.lastbranch = None
38 if os.path.isdir(path) and len(os.listdir(path)) > 0:
38 if os.path.isdir(path) and len(os.listdir(path)) > 0:
39 try:
39 try:
40 self.repo = hg.repository(self.ui, path)
40 self.repo = hg.repository(self.ui, path)
41 if not self.repo.local():
41 if not self.repo.local():
42 raise NoRepo(_('%s is not a local Mercurial repository')
42 raise NoRepo(_('%s is not a local Mercurial repository')
43 % path)
43 % path)
44 except error.RepoError, err:
44 except error.RepoError, err:
45 ui.traceback()
45 ui.traceback()
46 raise NoRepo(err.args[0])
46 raise NoRepo(err.args[0])
47 else:
47 else:
48 try:
48 try:
49 ui.status(_('initializing destination %s repository\n') % path)
49 ui.status(_('initializing destination %s repository\n') % path)
50 self.repo = hg.repository(self.ui, path, create=True)
50 self.repo = hg.repository(self.ui, path, create=True)
51 if not self.repo.local():
51 if not self.repo.local():
52 raise NoRepo(_('%s is not a local Mercurial repository')
52 raise NoRepo(_('%s is not a local Mercurial repository')
53 % path)
53 % path)
54 self.created.append(path)
54 self.created.append(path)
55 except error.RepoError:
55 except error.RepoError:
56 ui.traceback()
56 ui.traceback()
57 raise NoRepo(_("could not create hg repository %s as sink")
57 raise NoRepo(_("could not create hg repository %s as sink")
58 % path)
58 % path)
59 self.lock = None
59 self.lock = None
60 self.wlock = None
60 self.wlock = None
61 self.filemapmode = False
61 self.filemapmode = False
62
62
63 def before(self):
63 def before(self):
64 self.ui.debug('run hg sink pre-conversion action\n')
64 self.ui.debug('run hg sink pre-conversion action\n')
65 self.wlock = self.repo.wlock()
65 self.wlock = self.repo.wlock()
66 self.lock = self.repo.lock()
66 self.lock = self.repo.lock()
67
67
68 def after(self):
68 def after(self):
69 self.ui.debug('run hg sink post-conversion action\n')
69 self.ui.debug('run hg sink post-conversion action\n')
70 if self.lock:
70 if self.lock:
71 self.lock.release()
71 self.lock.release()
72 if self.wlock:
72 if self.wlock:
73 self.wlock.release()
73 self.wlock.release()
74
74
75 def revmapfile(self):
75 def revmapfile(self):
76 return self.repo.join("shamap")
76 return self.repo.join("shamap")
77
77
78 def authorfile(self):
78 def authorfile(self):
79 return self.repo.join("authormap")
79 return self.repo.join("authormap")
80
80
81 def setbranch(self, branch, pbranches):
81 def setbranch(self, branch, pbranches):
82 if not self.clonebranches:
82 if not self.clonebranches:
83 return
83 return
84
84
85 setbranch = (branch != self.lastbranch)
85 setbranch = (branch != self.lastbranch)
86 self.lastbranch = branch
86 self.lastbranch = branch
87 if not branch:
87 if not branch:
88 branch = 'default'
88 branch = 'default'
89 pbranches = [(b[0], b[1] and b[1] or 'default') for b in pbranches]
89 pbranches = [(b[0], b[1] and b[1] or 'default') for b in pbranches]
90 pbranch = pbranches and pbranches[0][1] or 'default'
90 pbranch = pbranches and pbranches[0][1] or 'default'
91
91
92 branchpath = os.path.join(self.path, branch)
92 branchpath = os.path.join(self.path, branch)
93 if setbranch:
93 if setbranch:
94 self.after()
94 self.after()
95 try:
95 try:
96 self.repo = hg.repository(self.ui, branchpath)
96 self.repo = hg.repository(self.ui, branchpath)
97 except Exception:
97 except Exception:
98 self.repo = hg.repository(self.ui, branchpath, create=True)
98 self.repo = hg.repository(self.ui, branchpath, create=True)
99 self.before()
99 self.before()
100
100
101 # pbranches may bring revisions from other branches (merge parents)
101 # pbranches may bring revisions from other branches (merge parents)
102 # Make sure we have them, or pull them.
102 # Make sure we have them, or pull them.
103 missings = {}
103 missings = {}
104 for b in pbranches:
104 for b in pbranches:
105 try:
105 try:
106 self.repo.lookup(b[0])
106 self.repo.lookup(b[0])
107 except Exception:
107 except Exception:
108 missings.setdefault(b[1], []).append(b[0])
108 missings.setdefault(b[1], []).append(b[0])
109
109
110 if missings:
110 if missings:
111 self.after()
111 self.after()
112 for pbranch, heads in sorted(missings.iteritems()):
112 for pbranch, heads in sorted(missings.iteritems()):
113 pbranchpath = os.path.join(self.path, pbranch)
113 pbranchpath = os.path.join(self.path, pbranch)
114 prepo = hg.peer(self.ui, {}, pbranchpath)
114 prepo = hg.peer(self.ui, {}, pbranchpath)
115 self.ui.note(_('pulling from %s into %s\n') % (pbranch, branch))
115 self.ui.note(_('pulling from %s into %s\n') % (pbranch, branch))
116 self.repo.pull(prepo, [prepo.lookup(h) for h in heads])
116 self.repo.pull(prepo, [prepo.lookup(h) for h in heads])
117 self.before()
117 self.before()
118
118
119 def _rewritetags(self, source, revmap, data):
119 def _rewritetags(self, source, revmap, data):
120 fp = cStringIO.StringIO()
120 fp = cStringIO.StringIO()
121 for line in data.splitlines():
121 for line in data.splitlines():
122 s = line.split(' ', 1)
122 s = line.split(' ', 1)
123 if len(s) != 2:
123 if len(s) != 2:
124 continue
124 continue
125 revid = revmap.get(source.lookuprev(s[0]))
125 revid = revmap.get(source.lookuprev(s[0]))
126 if not revid:
126 if not revid:
127 continue
127 continue
128 fp.write('%s %s\n' % (revid, s[1]))
128 fp.write('%s %s\n' % (revid, s[1]))
129 return fp.getvalue()
129 return fp.getvalue()
130
130
131 def putcommit(self, files, copies, parents, commit, source, revmap):
131 def putcommit(self, files, copies, parents, commit, source, revmap):
132
132
133 files = dict(files)
133 files = dict(files)
134 def getfilectx(repo, memctx, f):
134 def getfilectx(repo, memctx, f):
135 v = files[f]
135 v = files[f]
136 data, mode = source.getfile(f, v)
136 data, mode = source.getfile(f, v)
137 if data is None:
137 if data is None:
138 return None
138 return None
139 if f == '.hgtags':
139 if f == '.hgtags':
140 data = self._rewritetags(source, revmap, data)
140 data = self._rewritetags(source, revmap, data)
141 return context.memfilectx(self.repo, f, data, 'l' in mode,
141 return context.memfilectx(self.repo, f, data, 'l' in mode,
142 'x' in mode, copies.get(f))
142 'x' in mode, copies.get(f))
143
143
144 pl = []
144 pl = []
145 for p in parents:
145 for p in parents:
146 if p not in pl:
146 if p not in pl:
147 pl.append(p)
147 pl.append(p)
148 parents = pl
148 parents = pl
149 nparents = len(parents)
149 nparents = len(parents)
150 if self.filemapmode and nparents == 1:
150 if self.filemapmode and nparents == 1:
151 m1node = self.repo.changelog.read(bin(parents[0]))[0]
151 m1node = self.repo.changelog.read(bin(parents[0]))[0]
152 parent = parents[0]
152 parent = parents[0]
153
153
154 if len(parents) < 2:
154 if len(parents) < 2:
155 parents.append(nullid)
155 parents.append(nullid)
156 if len(parents) < 2:
156 if len(parents) < 2:
157 parents.append(nullid)
157 parents.append(nullid)
158 p2 = parents.pop(0)
158 p2 = parents.pop(0)
159
159
160 text = commit.desc
160 text = commit.desc
161
161
162 sha1s = re.findall(sha1re, text)
162 sha1s = re.findall(sha1re, text)
163 for sha1 in sha1s:
163 for sha1 in sha1s:
164 oldrev = source.lookuprev(sha1)
164 oldrev = source.lookuprev(sha1)
165 newrev = revmap.get(oldrev)
165 newrev = revmap.get(oldrev)
166 if newrev is not None:
166 if newrev is not None:
167 text = text.replace(sha1, newrev[:len(sha1)])
167 text = text.replace(sha1, newrev[:len(sha1)])
168
168
169 extra = commit.extra.copy()
169 extra = commit.extra.copy()
170
170
171 for label in ('source', 'transplant_source', 'rebase_source'):
171 for label in ('source', 'transplant_source', 'rebase_source'):
172 node = extra.get(label)
172 node = extra.get(label)
173
173
174 if node is None:
174 if node is None:
175 continue
175 continue
176
176
177 # Only transplant stores its reference in binary
177 # Only transplant stores its reference in binary
178 if label == 'transplant_source':
178 if label == 'transplant_source':
179 node = hex(node)
179 node = hex(node)
180
180
181 newrev = revmap.get(node)
181 newrev = revmap.get(node)
182 if newrev is not None:
182 if newrev is not None:
183 if label == 'transplant_source':
183 if label == 'transplant_source':
184 newrev = bin(newrev)
184 newrev = bin(newrev)
185
185
186 extra[label] = newrev
186 extra[label] = newrev
187
187
188 if self.branchnames and commit.branch:
188 if self.branchnames and commit.branch:
189 extra['branch'] = commit.branch
189 extra['branch'] = commit.branch
190 if commit.rev:
190 if commit.rev:
191 extra['convert_revision'] = commit.rev
191 extra['convert_revision'] = commit.rev
192
192
193 while parents:
193 while parents:
194 p1 = p2
194 p1 = p2
195 p2 = parents.pop(0)
195 p2 = parents.pop(0)
196 ctx = context.memctx(self.repo, (p1, p2), text, files.keys(),
196 ctx = context.memctx(self.repo, (p1, p2), text, files.keys(),
197 getfilectx, commit.author, commit.date, extra)
197 getfilectx, commit.author, commit.date, extra)
198 self.repo.commitctx(ctx)
198 self.repo.commitctx(ctx)
199 text = "(octopus merge fixup)\n"
199 text = "(octopus merge fixup)\n"
200 p2 = hex(self.repo.changelog.tip())
200 p2 = hex(self.repo.changelog.tip())
201
201
202 if self.filemapmode and nparents == 1:
202 if self.filemapmode and nparents == 1:
203 man = self.repo.manifest
203 man = self.repo.manifest
204 mnode = self.repo.changelog.read(bin(p2))[0]
204 mnode = self.repo.changelog.read(bin(p2))[0]
205 closed = 'close' in commit.extra
205 closed = 'close' in commit.extra
206 if not closed and not man.cmp(m1node, man.revision(mnode)):
206 if not closed and not man.cmp(m1node, man.revision(mnode)):
207 self.ui.status(_("filtering out empty revision\n"))
207 self.ui.status(_("filtering out empty revision\n"))
208 self.repo.rollback(force=True)
208 self.repo.rollback(force=True)
209 return parent
209 return parent
210 return p2
210 return p2
211
211
212 def puttags(self, tags):
212 def puttags(self, tags):
213 try:
213 try:
214 parentctx = self.repo[self.tagsbranch]
214 parentctx = self.repo[self.tagsbranch]
215 tagparent = parentctx.node()
215 tagparent = parentctx.node()
216 except error.RepoError:
216 except error.RepoError:
217 parentctx = None
217 parentctx = None
218 tagparent = nullid
218 tagparent = nullid
219
219
220 oldlines = set()
220 oldlines = set()
221 for branch, heads in self.repo.branchmap().iteritems():
221 for branch, heads in self.repo.branchmap().iteritems():
222 for h in heads:
222 for h in heads:
223 if '.hgtags' in self.repo[h]:
223 if '.hgtags' in self.repo[h]:
224 oldlines.update(
224 oldlines.update(
225 set(self.repo[h]['.hgtags'].data().splitlines(True)))
225 set(self.repo[h]['.hgtags'].data().splitlines(True)))
226 oldlines = sorted(list(oldlines))
226 oldlines = sorted(list(oldlines))
227
227
228 newlines = sorted([("%s %s\n" % (tags[tag], tag)) for tag in tags])
228 newlines = sorted([("%s %s\n" % (tags[tag], tag)) for tag in tags])
229 if newlines == oldlines:
229 if newlines == oldlines:
230 return None, None
230 return None, None
231
231
232 # if the old and new tags match, then there is nothing to update
232 # if the old and new tags match, then there is nothing to update
233 oldtags = set()
233 oldtags = set()
234 newtags = set()
234 newtags = set()
235 for line in oldlines:
235 for line in oldlines:
236 s = line.strip().split(' ', 1)
236 s = line.strip().split(' ', 1)
237 if len(s) != 2:
237 if len(s) != 2:
238 continue
238 continue
239 oldtags.add(s[1])
239 oldtags.add(s[1])
240 for line in newlines:
240 for line in newlines:
241 s = line.strip().split(' ', 1)
241 s = line.strip().split(' ', 1)
242 if len(s) != 2:
242 if len(s) != 2:
243 continue
243 continue
244 if s[1] not in oldtags:
244 if s[1] not in oldtags:
245 newtags.add(s[1].strip())
245 newtags.add(s[1].strip())
246
246
247 if not newtags:
247 if not newtags:
248 return None, None
248 return None, None
249
249
250 data = "".join(newlines)
250 data = "".join(newlines)
251 def getfilectx(repo, memctx, f):
251 def getfilectx(repo, memctx, f):
252 return context.memfilectx(repo, f, data, False, False, None)
252 return context.memfilectx(repo, f, data, False, False, None)
253
253
254 self.ui.status(_("updating tags\n"))
254 self.ui.status(_("updating tags\n"))
255 date = "%s 0" % int(time.mktime(time.gmtime()))
255 date = "%s 0" % int(time.mktime(time.gmtime()))
256 extra = {'branch': self.tagsbranch}
256 extra = {'branch': self.tagsbranch}
257 ctx = context.memctx(self.repo, (tagparent, None), "update tags",
257 ctx = context.memctx(self.repo, (tagparent, None), "update tags",
258 [".hgtags"], getfilectx, "convert-repo", date,
258 [".hgtags"], getfilectx, "convert-repo", date,
259 extra)
259 extra)
260 self.repo.commitctx(ctx)
260 self.repo.commitctx(ctx)
261 return hex(self.repo.changelog.tip()), hex(tagparent)
261 return hex(self.repo.changelog.tip()), hex(tagparent)
262
262
263 def setfilemapmode(self, active):
263 def setfilemapmode(self, active):
264 self.filemapmode = active
264 self.filemapmode = active
265
265
266 def putbookmarks(self, updatedbookmark):
266 def putbookmarks(self, updatedbookmark):
267 if not len(updatedbookmark):
267 if not len(updatedbookmark):
268 return
268 return
269
269
270 self.ui.status(_("updating bookmarks\n"))
270 self.ui.status(_("updating bookmarks\n"))
271 destmarks = self.repo._bookmarks
271 destmarks = self.repo._bookmarks
272 for bookmark in updatedbookmark:
272 for bookmark in updatedbookmark:
273 destmarks[bookmark] = bin(updatedbookmark[bookmark])
273 destmarks[bookmark] = bin(updatedbookmark[bookmark])
274 destmarks.write()
274 destmarks.write()
275
275
276 def hascommitfrommap(self, rev):
276 def hascommitfrommap(self, rev):
277 # the exact semantics of clonebranches is unclear so we can't say no
277 # the exact semantics of clonebranches is unclear so we can't say no
278 return rev in self.repo or self.clonebranches
278 return rev in self.repo or self.clonebranches
279
279
280 def hascommitforsplicemap(self, rev):
280 def hascommitforsplicemap(self, rev):
281 if rev not in self.repo and self.clonebranches:
281 if rev not in self.repo and self.clonebranches:
282 raise util.Abort(_('revision %s not found in destination '
282 raise util.Abort(_('revision %s not found in destination '
283 'repository (lookups with clonebranches=true '
283 'repository (lookups with clonebranches=true '
284 'are not implemented)') % rev)
284 'are not implemented)') % rev)
285 return rev in self.repo
285 return rev in self.repo
286
286
287 class mercurial_source(converter_source):
287 class mercurial_source(converter_source):
288 def __init__(self, ui, path, rev=None):
288 def __init__(self, ui, path, rev=None):
289 converter_source.__init__(self, ui, path, rev)
289 converter_source.__init__(self, ui, path, rev)
290 self.ignoreerrors = ui.configbool('convert', 'hg.ignoreerrors', False)
290 self.ignoreerrors = ui.configbool('convert', 'hg.ignoreerrors', False)
291 self.ignored = set()
291 self.ignored = set()
292 self.saverev = ui.configbool('convert', 'hg.saverev', False)
292 self.saverev = ui.configbool('convert', 'hg.saverev', False)
293 try:
293 try:
294 self.repo = hg.repository(self.ui, path)
294 self.repo = hg.repository(self.ui, path)
295 # try to provoke an exception if this isn't really a hg
295 # try to provoke an exception if this isn't really a hg
296 # repo, but some other bogus compatible-looking url
296 # repo, but some other bogus compatible-looking url
297 if not self.repo.local():
297 if not self.repo.local():
298 raise error.RepoError
298 raise error.RepoError
299 except error.RepoError:
299 except error.RepoError:
300 ui.traceback()
300 ui.traceback()
301 raise NoRepo(_("%s is not a local Mercurial repository") % path)
301 raise NoRepo(_("%s is not a local Mercurial repository") % path)
302 self.lastrev = None
302 self.lastrev = None
303 self.lastctx = None
303 self.lastctx = None
304 self._changescache = None
304 self._changescache = None, None
305 self.convertfp = None
305 self.convertfp = None
306 # Restrict converted revisions to startrev descendants
306 # Restrict converted revisions to startrev descendants
307 startnode = ui.config('convert', 'hg.startrev')
307 startnode = ui.config('convert', 'hg.startrev')
308 hgrevs = ui.config('convert', 'hg.revs')
308 hgrevs = ui.config('convert', 'hg.revs')
309 if hgrevs is None:
309 if hgrevs is None:
310 if startnode is not None:
310 if startnode is not None:
311 try:
311 try:
312 startnode = self.repo.lookup(startnode)
312 startnode = self.repo.lookup(startnode)
313 except error.RepoError:
313 except error.RepoError:
314 raise util.Abort(_('%s is not a valid start revision')
314 raise util.Abort(_('%s is not a valid start revision')
315 % startnode)
315 % startnode)
316 startrev = self.repo.changelog.rev(startnode)
316 startrev = self.repo.changelog.rev(startnode)
317 children = {startnode: 1}
317 children = {startnode: 1}
318 for r in self.repo.changelog.descendants([startrev]):
318 for r in self.repo.changelog.descendants([startrev]):
319 children[self.repo.changelog.node(r)] = 1
319 children[self.repo.changelog.node(r)] = 1
320 self.keep = children.__contains__
320 self.keep = children.__contains__
321 else:
321 else:
322 self.keep = util.always
322 self.keep = util.always
323 if rev:
323 if rev:
324 self._heads = [self.repo[rev].node()]
324 self._heads = [self.repo[rev].node()]
325 else:
325 else:
326 self._heads = self.repo.heads()
326 self._heads = self.repo.heads()
327 else:
327 else:
328 if rev or startnode is not None:
328 if rev or startnode is not None:
329 raise util.Abort(_('hg.revs cannot be combined with '
329 raise util.Abort(_('hg.revs cannot be combined with '
330 'hg.startrev or --rev'))
330 'hg.startrev or --rev'))
331 nodes = set()
331 nodes = set()
332 parents = set()
332 parents = set()
333 for r in scmutil.revrange(self.repo, [hgrevs]):
333 for r in scmutil.revrange(self.repo, [hgrevs]):
334 ctx = self.repo[r]
334 ctx = self.repo[r]
335 nodes.add(ctx.node())
335 nodes.add(ctx.node())
336 parents.update(p.node() for p in ctx.parents())
336 parents.update(p.node() for p in ctx.parents())
337 self.keep = nodes.__contains__
337 self.keep = nodes.__contains__
338 self._heads = nodes - parents
338 self._heads = nodes - parents
339
339
340 def changectx(self, rev):
340 def changectx(self, rev):
341 if self.lastrev != rev:
341 if self.lastrev != rev:
342 self.lastctx = self.repo[rev]
342 self.lastctx = self.repo[rev]
343 self.lastrev = rev
343 self.lastrev = rev
344 return self.lastctx
344 return self.lastctx
345
345
346 def parents(self, ctx):
346 def parents(self, ctx):
347 return [p for p in ctx.parents() if p and self.keep(p.node())]
347 return [p for p in ctx.parents() if p and self.keep(p.node())]
348
348
349 def getheads(self):
349 def getheads(self):
350 return [hex(h) for h in self._heads if self.keep(h)]
350 return [hex(h) for h in self._heads if self.keep(h)]
351
351
352 def getfile(self, name, rev):
352 def getfile(self, name, rev):
353 try:
353 try:
354 fctx = self.changectx(rev)[name]
354 fctx = self.changectx(rev)[name]
355 return fctx.data(), fctx.flags()
355 return fctx.data(), fctx.flags()
356 except error.LookupError:
356 except error.LookupError:
357 return None, None
357 return None, None
358
358
359 def getchanges(self, rev):
359 def getchanges(self, rev):
360 ctx = self.changectx(rev)
360 ctx = self.changectx(rev)
361 parents = self.parents(ctx)
361 parents = self.parents(ctx)
362 if not parents:
362 if not parents:
363 files = sorted(ctx.manifest())
363 files = copyfiles = ctx.manifest()
364 # getcopies() is not needed for roots, but it is a simple way to
365 # detect missing revlogs and abort on errors or populate
366 # self.ignored
367 self.getcopies(ctx, parents, files)
368 return [(f, rev) for f in files if f not in self.ignored], {}
369 if self._changescache and self._changescache[0] == rev:
370 m, a, r = self._changescache[1]
371 else:
364 else:
372 m, a, r = self.repo.status(parents[0].node(), ctx.node())[:3]
365 if self._changescache[0] == rev:
373 # getcopies() detects missing revlogs early, run it before
366 m, a, r = self._changescache[1]
374 # filtering the changes.
367 else:
375 copies = self.getcopies(ctx, parents, m + a)
368 m, a, r = self.repo.status(parents[0].node(), ctx.node())[:3]
376 changes = [(name, rev) for name in m + a + r
369 files = m + a + r
377 if name not in self.ignored]
370 copyfiles = m + a
378 return sorted(changes), copies
371 # getcopies() is also run for roots and before filtering so missing
372 # revlogs are detected early
373 copies = self.getcopies(ctx, parents, copyfiles)
374 changes = [(f, rev) for f in files if f not in self.ignored]
375 changes.sort()
376 return changes, copies
379
377
380 def getcopies(self, ctx, parents, files):
378 def getcopies(self, ctx, parents, files):
381 copies = {}
379 copies = {}
382 for name in files:
380 for name in files:
383 if name in self.ignored:
381 if name in self.ignored:
384 continue
382 continue
385 try:
383 try:
386 copysource, _copynode = ctx.filectx(name).renamed()
384 copysource, _copynode = ctx.filectx(name).renamed()
387 if copysource in self.ignored:
385 if copysource in self.ignored:
388 continue
386 continue
389 # Ignore copy sources not in parent revisions
387 # Ignore copy sources not in parent revisions
390 found = False
388 found = False
391 for p in parents:
389 for p in parents:
392 if copysource in p:
390 if copysource in p:
393 found = True
391 found = True
394 break
392 break
395 if not found:
393 if not found:
396 continue
394 continue
397 copies[name] = copysource
395 copies[name] = copysource
398 except TypeError:
396 except TypeError:
399 pass
397 pass
400 except error.LookupError, e:
398 except error.LookupError, e:
401 if not self.ignoreerrors:
399 if not self.ignoreerrors:
402 raise
400 raise
403 self.ignored.add(name)
401 self.ignored.add(name)
404 self.ui.warn(_('ignoring: %s\n') % e)
402 self.ui.warn(_('ignoring: %s\n') % e)
405 return copies
403 return copies
406
404
407 def getcommit(self, rev):
405 def getcommit(self, rev):
408 ctx = self.changectx(rev)
406 ctx = self.changectx(rev)
409 parents = [p.hex() for p in self.parents(ctx)]
407 parents = [p.hex() for p in self.parents(ctx)]
410 if self.saverev:
408 if self.saverev:
411 crev = rev
409 crev = rev
412 else:
410 else:
413 crev = None
411 crev = None
414 return commit(author=ctx.user(),
412 return commit(author=ctx.user(),
415 date=util.datestr(ctx.date(), '%Y-%m-%d %H:%M:%S %1%2'),
413 date=util.datestr(ctx.date(), '%Y-%m-%d %H:%M:%S %1%2'),
416 desc=ctx.description(), rev=crev, parents=parents,
414 desc=ctx.description(), rev=crev, parents=parents,
417 branch=ctx.branch(), extra=ctx.extra(),
415 branch=ctx.branch(), extra=ctx.extra(),
418 sortkey=ctx.rev())
416 sortkey=ctx.rev())
419
417
420 def gettags(self):
418 def gettags(self):
421 # This will get written to .hgtags, filter non global tags out.
419 # This will get written to .hgtags, filter non global tags out.
422 tags = [t for t in self.repo.tagslist()
420 tags = [t for t in self.repo.tagslist()
423 if self.repo.tagtype(t[0]) == 'global']
421 if self.repo.tagtype(t[0]) == 'global']
424 return dict([(name, hex(node)) for name, node in tags
422 return dict([(name, hex(node)) for name, node in tags
425 if self.keep(node)])
423 if self.keep(node)])
426
424
427 def getchangedfiles(self, rev, i):
425 def getchangedfiles(self, rev, i):
428 ctx = self.changectx(rev)
426 ctx = self.changectx(rev)
429 parents = self.parents(ctx)
427 parents = self.parents(ctx)
430 if not parents and i is None:
428 if not parents and i is None:
431 i = 0
429 i = 0
432 changes = [], ctx.manifest().keys(), []
430 changes = [], ctx.manifest().keys(), []
433 else:
431 else:
434 i = i or 0
432 i = i or 0
435 changes = self.repo.status(parents[i].node(), ctx.node())[:3]
433 changes = self.repo.status(parents[i].node(), ctx.node())[:3]
436 changes = [[f for f in l if f not in self.ignored] for l in changes]
434 changes = [[f for f in l if f not in self.ignored] for l in changes]
437
435
438 if i == 0:
436 if i == 0:
439 self._changescache = (rev, changes)
437 self._changescache = (rev, changes)
440
438
441 return changes[0] + changes[1] + changes[2]
439 return changes[0] + changes[1] + changes[2]
442
440
443 def converted(self, rev, destrev):
441 def converted(self, rev, destrev):
444 if self.convertfp is None:
442 if self.convertfp is None:
445 self.convertfp = open(self.repo.join('shamap'), 'a')
443 self.convertfp = open(self.repo.join('shamap'), 'a')
446 self.convertfp.write('%s %s\n' % (destrev, rev))
444 self.convertfp.write('%s %s\n' % (destrev, rev))
447 self.convertfp.flush()
445 self.convertfp.flush()
448
446
449 def before(self):
447 def before(self):
450 self.ui.debug('run hg source pre-conversion action\n')
448 self.ui.debug('run hg source pre-conversion action\n')
451
449
452 def after(self):
450 def after(self):
453 self.ui.debug('run hg source post-conversion action\n')
451 self.ui.debug('run hg source post-conversion action\n')
454
452
455 def hasnativeorder(self):
453 def hasnativeorder(self):
456 return True
454 return True
457
455
458 def hasnativeclose(self):
456 def hasnativeclose(self):
459 return True
457 return True
460
458
461 def lookuprev(self, rev):
459 def lookuprev(self, rev):
462 try:
460 try:
463 return hex(self.repo.lookup(rev))
461 return hex(self.repo.lookup(rev))
464 except error.RepoError:
462 except error.RepoError:
465 return None
463 return None
466
464
467 def getbookmarks(self):
465 def getbookmarks(self):
468 return bookmarks.listbookmarks(self.repo)
466 return bookmarks.listbookmarks(self.repo)
469
467
470 def checkrevformat(self, revstr, mapname='splicemap'):
468 def checkrevformat(self, revstr, mapname='splicemap'):
471 """ Mercurial, revision string is a 40 byte hex """
469 """ Mercurial, revision string is a 40 byte hex """
472 self.checkhexformat(revstr, mapname)
470 self.checkhexformat(revstr, mapname)
General Comments 0
You need to be logged in to leave comments. Login now