##// END OF EJS Templates
convert: fix bad conversion of copies when hg.startrev is specified...
Mads Kiilerich -
r19457:948df0f1 default
parent child Browse files
Show More
@@ -1,403 +1,403 b''
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
24 from mercurial import hg, util, context, bookmarks, error
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 class mercurial_sink(converter_sink):
28 class mercurial_sink(converter_sink):
29 def __init__(self, ui, path):
29 def __init__(self, ui, path):
30 converter_sink.__init__(self, ui, path)
30 converter_sink.__init__(self, ui, path)
31 self.branchnames = ui.configbool('convert', 'hg.usebranchnames', True)
31 self.branchnames = ui.configbool('convert', 'hg.usebranchnames', True)
32 self.clonebranches = ui.configbool('convert', 'hg.clonebranches', False)
32 self.clonebranches = ui.configbool('convert', 'hg.clonebranches', False)
33 self.tagsbranch = ui.config('convert', 'hg.tagsbranch', 'default')
33 self.tagsbranch = ui.config('convert', 'hg.tagsbranch', 'default')
34 self.lastbranch = None
34 self.lastbranch = None
35 if os.path.isdir(path) and len(os.listdir(path)) > 0:
35 if os.path.isdir(path) and len(os.listdir(path)) > 0:
36 try:
36 try:
37 self.repo = hg.repository(self.ui, path)
37 self.repo = hg.repository(self.ui, path)
38 if not self.repo.local():
38 if not self.repo.local():
39 raise NoRepo(_('%s is not a local Mercurial repository')
39 raise NoRepo(_('%s is not a local Mercurial repository')
40 % path)
40 % path)
41 except error.RepoError, err:
41 except error.RepoError, err:
42 ui.traceback()
42 ui.traceback()
43 raise NoRepo(err.args[0])
43 raise NoRepo(err.args[0])
44 else:
44 else:
45 try:
45 try:
46 ui.status(_('initializing destination %s repository\n') % path)
46 ui.status(_('initializing destination %s repository\n') % path)
47 self.repo = hg.repository(self.ui, path, create=True)
47 self.repo = hg.repository(self.ui, path, create=True)
48 if not self.repo.local():
48 if not self.repo.local():
49 raise NoRepo(_('%s is not a local Mercurial repository')
49 raise NoRepo(_('%s is not a local Mercurial repository')
50 % path)
50 % path)
51 self.created.append(path)
51 self.created.append(path)
52 except error.RepoError:
52 except error.RepoError:
53 ui.traceback()
53 ui.traceback()
54 raise NoRepo(_("could not create hg repository %s as sink")
54 raise NoRepo(_("could not create hg repository %s as sink")
55 % path)
55 % path)
56 self.lock = None
56 self.lock = None
57 self.wlock = None
57 self.wlock = None
58 self.filemapmode = False
58 self.filemapmode = False
59
59
60 def before(self):
60 def before(self):
61 self.ui.debug('run hg sink pre-conversion action\n')
61 self.ui.debug('run hg sink pre-conversion action\n')
62 self.wlock = self.repo.wlock()
62 self.wlock = self.repo.wlock()
63 self.lock = self.repo.lock()
63 self.lock = self.repo.lock()
64
64
65 def after(self):
65 def after(self):
66 self.ui.debug('run hg sink post-conversion action\n')
66 self.ui.debug('run hg sink post-conversion action\n')
67 if self.lock:
67 if self.lock:
68 self.lock.release()
68 self.lock.release()
69 if self.wlock:
69 if self.wlock:
70 self.wlock.release()
70 self.wlock.release()
71
71
72 def revmapfile(self):
72 def revmapfile(self):
73 return self.repo.join("shamap")
73 return self.repo.join("shamap")
74
74
75 def authorfile(self):
75 def authorfile(self):
76 return self.repo.join("authormap")
76 return self.repo.join("authormap")
77
77
78 def getheads(self):
78 def getheads(self):
79 h = self.repo.changelog.heads()
79 h = self.repo.changelog.heads()
80 return [hex(x) for x in h]
80 return [hex(x) for x in h]
81
81
82 def setbranch(self, branch, pbranches):
82 def setbranch(self, branch, pbranches):
83 if not self.clonebranches:
83 if not self.clonebranches:
84 return
84 return
85
85
86 setbranch = (branch != self.lastbranch)
86 setbranch = (branch != self.lastbranch)
87 self.lastbranch = branch
87 self.lastbranch = branch
88 if not branch:
88 if not branch:
89 branch = 'default'
89 branch = 'default'
90 pbranches = [(b[0], b[1] and b[1] or 'default') for b in pbranches]
90 pbranches = [(b[0], b[1] and b[1] or 'default') for b in pbranches]
91 pbranch = pbranches and pbranches[0][1] or 'default'
91 pbranch = pbranches and pbranches[0][1] or 'default'
92
92
93 branchpath = os.path.join(self.path, branch)
93 branchpath = os.path.join(self.path, branch)
94 if setbranch:
94 if setbranch:
95 self.after()
95 self.after()
96 try:
96 try:
97 self.repo = hg.repository(self.ui, branchpath)
97 self.repo = hg.repository(self.ui, branchpath)
98 except Exception:
98 except Exception:
99 self.repo = hg.repository(self.ui, branchpath, create=True)
99 self.repo = hg.repository(self.ui, branchpath, create=True)
100 self.before()
100 self.before()
101
101
102 # pbranches may bring revisions from other branches (merge parents)
102 # pbranches may bring revisions from other branches (merge parents)
103 # Make sure we have them, or pull them.
103 # Make sure we have them, or pull them.
104 missings = {}
104 missings = {}
105 for b in pbranches:
105 for b in pbranches:
106 try:
106 try:
107 self.repo.lookup(b[0])
107 self.repo.lookup(b[0])
108 except Exception:
108 except Exception:
109 missings.setdefault(b[1], []).append(b[0])
109 missings.setdefault(b[1], []).append(b[0])
110
110
111 if missings:
111 if missings:
112 self.after()
112 self.after()
113 for pbranch, heads in sorted(missings.iteritems()):
113 for pbranch, heads in sorted(missings.iteritems()):
114 pbranchpath = os.path.join(self.path, pbranch)
114 pbranchpath = os.path.join(self.path, pbranch)
115 prepo = hg.peer(self.ui, {}, pbranchpath)
115 prepo = hg.peer(self.ui, {}, pbranchpath)
116 self.ui.note(_('pulling from %s into %s\n') % (pbranch, branch))
116 self.ui.note(_('pulling from %s into %s\n') % (pbranch, branch))
117 self.repo.pull(prepo, [prepo.lookup(h) for h in heads])
117 self.repo.pull(prepo, [prepo.lookup(h) for h in heads])
118 self.before()
118 self.before()
119
119
120 def _rewritetags(self, source, revmap, data):
120 def _rewritetags(self, source, revmap, data):
121 fp = cStringIO.StringIO()
121 fp = cStringIO.StringIO()
122 for line in data.splitlines():
122 for line in data.splitlines():
123 s = line.split(' ', 1)
123 s = line.split(' ', 1)
124 if len(s) != 2:
124 if len(s) != 2:
125 continue
125 continue
126 revid = revmap.get(source.lookuprev(s[0]))
126 revid = revmap.get(source.lookuprev(s[0]))
127 if not revid:
127 if not revid:
128 continue
128 continue
129 fp.write('%s %s\n' % (revid, s[1]))
129 fp.write('%s %s\n' % (revid, s[1]))
130 return fp.getvalue()
130 return fp.getvalue()
131
131
132 def putcommit(self, files, copies, parents, commit, source, revmap):
132 def putcommit(self, files, copies, parents, commit, source, revmap):
133
133
134 files = dict(files)
134 files = dict(files)
135 def getfilectx(repo, memctx, f):
135 def getfilectx(repo, memctx, f):
136 v = files[f]
136 v = files[f]
137 data, mode = source.getfile(f, v)
137 data, mode = source.getfile(f, v)
138 if f == '.hgtags':
138 if f == '.hgtags':
139 data = self._rewritetags(source, revmap, data)
139 data = self._rewritetags(source, revmap, data)
140 return context.memfilectx(f, data, 'l' in mode, 'x' in mode,
140 return context.memfilectx(f, data, 'l' in mode, 'x' in mode,
141 copies.get(f))
141 copies.get(f))
142
142
143 pl = []
143 pl = []
144 for p in parents:
144 for p in parents:
145 if p not in pl:
145 if p not in pl:
146 pl.append(p)
146 pl.append(p)
147 parents = pl
147 parents = pl
148 nparents = len(parents)
148 nparents = len(parents)
149 if self.filemapmode and nparents == 1:
149 if self.filemapmode and nparents == 1:
150 m1node = self.repo.changelog.read(bin(parents[0]))[0]
150 m1node = self.repo.changelog.read(bin(parents[0]))[0]
151 parent = parents[0]
151 parent = parents[0]
152
152
153 if len(parents) < 2:
153 if len(parents) < 2:
154 parents.append(nullid)
154 parents.append(nullid)
155 if len(parents) < 2:
155 if len(parents) < 2:
156 parents.append(nullid)
156 parents.append(nullid)
157 p2 = parents.pop(0)
157 p2 = parents.pop(0)
158
158
159 text = commit.desc
159 text = commit.desc
160 extra = commit.extra.copy()
160 extra = commit.extra.copy()
161 if self.branchnames and commit.branch:
161 if self.branchnames and commit.branch:
162 extra['branch'] = commit.branch
162 extra['branch'] = commit.branch
163 if commit.rev:
163 if commit.rev:
164 extra['convert_revision'] = commit.rev
164 extra['convert_revision'] = commit.rev
165
165
166 while parents:
166 while parents:
167 p1 = p2
167 p1 = p2
168 p2 = parents.pop(0)
168 p2 = parents.pop(0)
169 ctx = context.memctx(self.repo, (p1, p2), text, files.keys(),
169 ctx = context.memctx(self.repo, (p1, p2), text, files.keys(),
170 getfilectx, commit.author, commit.date, extra)
170 getfilectx, commit.author, commit.date, extra)
171 self.repo.commitctx(ctx)
171 self.repo.commitctx(ctx)
172 text = "(octopus merge fixup)\n"
172 text = "(octopus merge fixup)\n"
173 p2 = hex(self.repo.changelog.tip())
173 p2 = hex(self.repo.changelog.tip())
174
174
175 if self.filemapmode and nparents == 1:
175 if self.filemapmode and nparents == 1:
176 man = self.repo.manifest
176 man = self.repo.manifest
177 mnode = self.repo.changelog.read(bin(p2))[0]
177 mnode = self.repo.changelog.read(bin(p2))[0]
178 closed = 'close' in commit.extra
178 closed = 'close' in commit.extra
179 if not closed and not man.cmp(m1node, man.revision(mnode)):
179 if not closed and not man.cmp(m1node, man.revision(mnode)):
180 self.ui.status(_("filtering out empty revision\n"))
180 self.ui.status(_("filtering out empty revision\n"))
181 self.repo.rollback(force=True)
181 self.repo.rollback(force=True)
182 return parent
182 return parent
183 return p2
183 return p2
184
184
185 def puttags(self, tags):
185 def puttags(self, tags):
186 try:
186 try:
187 parentctx = self.repo[self.tagsbranch]
187 parentctx = self.repo[self.tagsbranch]
188 tagparent = parentctx.node()
188 tagparent = parentctx.node()
189 except error.RepoError:
189 except error.RepoError:
190 parentctx = None
190 parentctx = None
191 tagparent = nullid
191 tagparent = nullid
192
192
193 try:
193 try:
194 oldlines = sorted(parentctx['.hgtags'].data().splitlines(True))
194 oldlines = sorted(parentctx['.hgtags'].data().splitlines(True))
195 except Exception:
195 except Exception:
196 oldlines = []
196 oldlines = []
197
197
198 newlines = sorted([("%s %s\n" % (tags[tag], tag)) for tag in tags])
198 newlines = sorted([("%s %s\n" % (tags[tag], tag)) for tag in tags])
199 if newlines == oldlines:
199 if newlines == oldlines:
200 return None, None
200 return None, None
201 data = "".join(newlines)
201 data = "".join(newlines)
202 def getfilectx(repo, memctx, f):
202 def getfilectx(repo, memctx, f):
203 return context.memfilectx(f, data, False, False, None)
203 return context.memfilectx(f, data, False, False, None)
204
204
205 self.ui.status(_("updating tags\n"))
205 self.ui.status(_("updating tags\n"))
206 date = "%s 0" % int(time.mktime(time.gmtime()))
206 date = "%s 0" % int(time.mktime(time.gmtime()))
207 extra = {'branch': self.tagsbranch}
207 extra = {'branch': self.tagsbranch}
208 ctx = context.memctx(self.repo, (tagparent, None), "update tags",
208 ctx = context.memctx(self.repo, (tagparent, None), "update tags",
209 [".hgtags"], getfilectx, "convert-repo", date,
209 [".hgtags"], getfilectx, "convert-repo", date,
210 extra)
210 extra)
211 self.repo.commitctx(ctx)
211 self.repo.commitctx(ctx)
212 return hex(self.repo.changelog.tip()), hex(tagparent)
212 return hex(self.repo.changelog.tip()), hex(tagparent)
213
213
214 def setfilemapmode(self, active):
214 def setfilemapmode(self, active):
215 self.filemapmode = active
215 self.filemapmode = active
216
216
217 def putbookmarks(self, updatedbookmark):
217 def putbookmarks(self, updatedbookmark):
218 if not len(updatedbookmark):
218 if not len(updatedbookmark):
219 return
219 return
220
220
221 self.ui.status(_("updating bookmarks\n"))
221 self.ui.status(_("updating bookmarks\n"))
222 destmarks = self.repo._bookmarks
222 destmarks = self.repo._bookmarks
223 for bookmark in updatedbookmark:
223 for bookmark in updatedbookmark:
224 destmarks[bookmark] = bin(updatedbookmark[bookmark])
224 destmarks[bookmark] = bin(updatedbookmark[bookmark])
225 destmarks.write()
225 destmarks.write()
226
226
227 def hascommit(self, rev):
227 def hascommit(self, rev):
228 if rev not in self.repo and self.clonebranches:
228 if rev not in self.repo and self.clonebranches:
229 raise util.Abort(_('revision %s not found in destination '
229 raise util.Abort(_('revision %s not found in destination '
230 'repository (lookups with clonebranches=true '
230 'repository (lookups with clonebranches=true '
231 'are not implemented)') % rev)
231 'are not implemented)') % rev)
232 return rev in self.repo
232 return rev in self.repo
233
233
234 class mercurial_source(converter_source):
234 class mercurial_source(converter_source):
235 def __init__(self, ui, path, rev=None):
235 def __init__(self, ui, path, rev=None):
236 converter_source.__init__(self, ui, path, rev)
236 converter_source.__init__(self, ui, path, rev)
237 self.ignoreerrors = ui.configbool('convert', 'hg.ignoreerrors', False)
237 self.ignoreerrors = ui.configbool('convert', 'hg.ignoreerrors', False)
238 self.ignored = set()
238 self.ignored = set()
239 self.saverev = ui.configbool('convert', 'hg.saverev', False)
239 self.saverev = ui.configbool('convert', 'hg.saverev', False)
240 try:
240 try:
241 self.repo = hg.repository(self.ui, path)
241 self.repo = hg.repository(self.ui, path)
242 # try to provoke an exception if this isn't really a hg
242 # try to provoke an exception if this isn't really a hg
243 # repo, but some other bogus compatible-looking url
243 # repo, but some other bogus compatible-looking url
244 if not self.repo.local():
244 if not self.repo.local():
245 raise error.RepoError
245 raise error.RepoError
246 except error.RepoError:
246 except error.RepoError:
247 ui.traceback()
247 ui.traceback()
248 raise NoRepo(_("%s is not a local Mercurial repository") % path)
248 raise NoRepo(_("%s is not a local Mercurial repository") % path)
249 self.lastrev = None
249 self.lastrev = None
250 self.lastctx = None
250 self.lastctx = None
251 self._changescache = None
251 self._changescache = None
252 self.convertfp = None
252 self.convertfp = None
253 # Restrict converted revisions to startrev descendants
253 # Restrict converted revisions to startrev descendants
254 startnode = ui.config('convert', 'hg.startrev')
254 startnode = ui.config('convert', 'hg.startrev')
255 if startnode is not None:
255 if startnode is not None:
256 try:
256 try:
257 startnode = self.repo.lookup(startnode)
257 startnode = self.repo.lookup(startnode)
258 except error.RepoError:
258 except error.RepoError:
259 raise util.Abort(_('%s is not a valid start revision')
259 raise util.Abort(_('%s is not a valid start revision')
260 % startnode)
260 % startnode)
261 startrev = self.repo.changelog.rev(startnode)
261 startrev = self.repo.changelog.rev(startnode)
262 children = {startnode: 1}
262 children = {startnode: 1}
263 for rev in self.repo.changelog.descendants([startrev]):
263 for rev in self.repo.changelog.descendants([startrev]):
264 children[self.repo.changelog.node(rev)] = 1
264 children[self.repo.changelog.node(rev)] = 1
265 self.keep = children.__contains__
265 self.keep = children.__contains__
266 else:
266 else:
267 self.keep = util.always
267 self.keep = util.always
268
268
269 def changectx(self, rev):
269 def changectx(self, rev):
270 if self.lastrev != rev:
270 if self.lastrev != rev:
271 self.lastctx = self.repo[rev]
271 self.lastctx = self.repo[rev]
272 self.lastrev = rev
272 self.lastrev = rev
273 return self.lastctx
273 return self.lastctx
274
274
275 def parents(self, ctx):
275 def parents(self, ctx):
276 return [p for p in ctx.parents() if p and self.keep(p.node())]
276 return [p for p in ctx.parents() if p and self.keep(p.node())]
277
277
278 def getheads(self):
278 def getheads(self):
279 if self.rev:
279 if self.rev:
280 heads = [self.repo[self.rev].node()]
280 heads = [self.repo[self.rev].node()]
281 else:
281 else:
282 heads = self.repo.heads()
282 heads = self.repo.heads()
283 return [hex(h) for h in heads if self.keep(h)]
283 return [hex(h) for h in heads if self.keep(h)]
284
284
285 def getfile(self, name, rev):
285 def getfile(self, name, rev):
286 try:
286 try:
287 fctx = self.changectx(rev)[name]
287 fctx = self.changectx(rev)[name]
288 return fctx.data(), fctx.flags()
288 return fctx.data(), fctx.flags()
289 except error.LookupError, err:
289 except error.LookupError, err:
290 raise IOError(err)
290 raise IOError(err)
291
291
292 def getchanges(self, rev):
292 def getchanges(self, rev):
293 ctx = self.changectx(rev)
293 ctx = self.changectx(rev)
294 parents = self.parents(ctx)
294 parents = self.parents(ctx)
295 if not parents:
295 if not parents:
296 files = sorted(ctx.manifest())
296 files = sorted(ctx.manifest())
297 # getcopies() is not needed for roots, but it is a simple way to
297 # getcopies() is not needed for roots, but it is a simple way to
298 # detect missing revlogs and abort on errors or populate
298 # detect missing revlogs and abort on errors or populate
299 # self.ignored
299 # self.ignored
300 self.getcopies(ctx, parents, files)
300 self.getcopies(ctx, parents, files)
301 return [(f, rev) for f in files if f not in self.ignored], {}
301 return [(f, rev) for f in files if f not in self.ignored], {}
302 if self._changescache and self._changescache[0] == rev:
302 if self._changescache and self._changescache[0] == rev:
303 m, a, r = self._changescache[1]
303 m, a, r = self._changescache[1]
304 else:
304 else:
305 m, a, r = self.repo.status(parents[0].node(), ctx.node())[:3]
305 m, a, r = self.repo.status(parents[0].node(), ctx.node())[:3]
306 # getcopies() detects missing revlogs early, run it before
306 # getcopies() detects missing revlogs early, run it before
307 # filtering the changes.
307 # filtering the changes.
308 copies = self.getcopies(ctx, parents, m + a)
308 copies = self.getcopies(ctx, parents, m + a)
309 changes = [(name, rev) for name in m + a + r
309 changes = [(name, rev) for name in m + a + r
310 if name not in self.ignored]
310 if name not in self.ignored]
311 return sorted(changes), copies
311 return sorted(changes), copies
312
312
313 def getcopies(self, ctx, parents, files):
313 def getcopies(self, ctx, parents, files):
314 copies = {}
314 copies = {}
315 for name in files:
315 for name in files:
316 if name in self.ignored:
316 if name in self.ignored:
317 continue
317 continue
318 try:
318 try:
319 copysource, copynode = ctx.filectx(name).renamed()
319 copysource, _copynode = ctx.filectx(name).renamed()
320 if copysource in self.ignored or not self.keep(copynode):
320 if copysource in self.ignored:
321 continue
321 continue
322 # Ignore copy sources not in parent revisions
322 # Ignore copy sources not in parent revisions
323 found = False
323 found = False
324 for p in parents:
324 for p in parents:
325 if copysource in p:
325 if copysource in p:
326 found = True
326 found = True
327 break
327 break
328 if not found:
328 if not found:
329 continue
329 continue
330 copies[name] = copysource
330 copies[name] = copysource
331 except TypeError:
331 except TypeError:
332 pass
332 pass
333 except error.LookupError, e:
333 except error.LookupError, e:
334 if not self.ignoreerrors:
334 if not self.ignoreerrors:
335 raise
335 raise
336 self.ignored.add(name)
336 self.ignored.add(name)
337 self.ui.warn(_('ignoring: %s\n') % e)
337 self.ui.warn(_('ignoring: %s\n') % e)
338 return copies
338 return copies
339
339
340 def getcommit(self, rev):
340 def getcommit(self, rev):
341 ctx = self.changectx(rev)
341 ctx = self.changectx(rev)
342 parents = [p.hex() for p in self.parents(ctx)]
342 parents = [p.hex() for p in self.parents(ctx)]
343 if self.saverev:
343 if self.saverev:
344 crev = rev
344 crev = rev
345 else:
345 else:
346 crev = None
346 crev = None
347 return commit(author=ctx.user(),
347 return commit(author=ctx.user(),
348 date=util.datestr(ctx.date(), '%Y-%m-%d %H:%M:%S %1%2'),
348 date=util.datestr(ctx.date(), '%Y-%m-%d %H:%M:%S %1%2'),
349 desc=ctx.description(), rev=crev, parents=parents,
349 desc=ctx.description(), rev=crev, parents=parents,
350 branch=ctx.branch(), extra=ctx.extra(),
350 branch=ctx.branch(), extra=ctx.extra(),
351 sortkey=ctx.rev())
351 sortkey=ctx.rev())
352
352
353 def gettags(self):
353 def gettags(self):
354 tags = [t for t in self.repo.tagslist() if t[0] != 'tip']
354 tags = [t for t in self.repo.tagslist() if t[0] != 'tip']
355 return dict([(name, hex(node)) for name, node in tags
355 return dict([(name, hex(node)) for name, node in tags
356 if self.keep(node)])
356 if self.keep(node)])
357
357
358 def getchangedfiles(self, rev, i):
358 def getchangedfiles(self, rev, i):
359 ctx = self.changectx(rev)
359 ctx = self.changectx(rev)
360 parents = self.parents(ctx)
360 parents = self.parents(ctx)
361 if not parents and i is None:
361 if not parents and i is None:
362 i = 0
362 i = 0
363 changes = [], ctx.manifest().keys(), []
363 changes = [], ctx.manifest().keys(), []
364 else:
364 else:
365 i = i or 0
365 i = i or 0
366 changes = self.repo.status(parents[i].node(), ctx.node())[:3]
366 changes = self.repo.status(parents[i].node(), ctx.node())[:3]
367 changes = [[f for f in l if f not in self.ignored] for l in changes]
367 changes = [[f for f in l if f not in self.ignored] for l in changes]
368
368
369 if i == 0:
369 if i == 0:
370 self._changescache = (rev, changes)
370 self._changescache = (rev, changes)
371
371
372 return changes[0] + changes[1] + changes[2]
372 return changes[0] + changes[1] + changes[2]
373
373
374 def converted(self, rev, destrev):
374 def converted(self, rev, destrev):
375 if self.convertfp is None:
375 if self.convertfp is None:
376 self.convertfp = open(self.repo.join('shamap'), 'a')
376 self.convertfp = open(self.repo.join('shamap'), 'a')
377 self.convertfp.write('%s %s\n' % (destrev, rev))
377 self.convertfp.write('%s %s\n' % (destrev, rev))
378 self.convertfp.flush()
378 self.convertfp.flush()
379
379
380 def before(self):
380 def before(self):
381 self.ui.debug('run hg source pre-conversion action\n')
381 self.ui.debug('run hg source pre-conversion action\n')
382
382
383 def after(self):
383 def after(self):
384 self.ui.debug('run hg source post-conversion action\n')
384 self.ui.debug('run hg source post-conversion action\n')
385
385
386 def hasnativeorder(self):
386 def hasnativeorder(self):
387 return True
387 return True
388
388
389 def hasnativeclose(self):
389 def hasnativeclose(self):
390 return True
390 return True
391
391
392 def lookuprev(self, rev):
392 def lookuprev(self, rev):
393 try:
393 try:
394 return hex(self.repo.lookup(rev))
394 return hex(self.repo.lookup(rev))
395 except error.RepoError:
395 except error.RepoError:
396 return None
396 return None
397
397
398 def getbookmarks(self):
398 def getbookmarks(self):
399 return bookmarks.listbookmarks(self.repo)
399 return bookmarks.listbookmarks(self.repo)
400
400
401 def checkrevformat(self, revstr):
401 def checkrevformat(self, revstr):
402 """ Mercurial, revision string is a 40 byte hex """
402 """ Mercurial, revision string is a 40 byte hex """
403 self.checkhexformat(revstr)
403 self.checkhexformat(revstr)
@@ -1,175 +1,185 b''
1
1
2 $ cat >> $HGRCPATH <<EOF
2 $ cat >> $HGRCPATH <<EOF
3 > [extensions]
3 > [extensions]
4 > graphlog =
4 > graphlog =
5 > convert =
5 > convert =
6 > [convert]
6 > [convert]
7 > hg.saverev = yes
7 > hg.saverev = yes
8 > EOF
8 > EOF
9
9
10 $ glog()
10 $ glog()
11 > {
11 > {
12 > hg -R "$1" glog --template '{rev} "{desc}" files: {files}\n'
12 > hg -R "$1" glog --template '{rev} "{desc}" files: {files}\n'
13 > }
13 > }
14
14
15 $ hg init source
15 $ hg init source
16 $ cd source
16 $ cd source
17
17
18 $ echo a > a
18 $ echo a > a
19 $ echo b > b
19 $ echo b > b
20 $ echo f > f
20 $ echo f > f
21 $ hg ci -d '0 0' -qAm '0: add a b f'
21 $ hg ci -d '0 0' -qAm '0: add a b f'
22 $ echo c > c
22 $ echo c > c
23 $ hg move f d
23 $ hg move f d
24 $ hg ci -d '1 0' -qAm '1: add c, move f to d'
24 $ hg ci -d '1 0' -qAm '1: add c, move f to d'
25 $ hg copy a e
25 $ hg copy a e
26 $ echo b >> b
26 $ echo b >> b
27 $ hg ci -d '2 0' -qAm '2: copy e from a, change b'
27 $ hg ci -d '2 0' -qAm '2: copy e from a, change b'
28 $ hg up -C 0
28 $ hg up -C 0
29 2 files updated, 0 files merged, 3 files removed, 0 files unresolved
29 2 files updated, 0 files merged, 3 files removed, 0 files unresolved
30 $ echo a >> a
30 $ echo a >> a
31 $ hg ci -d '3 0' -qAm '3: change a'
31 $ hg ci -d '3 0' -qAm '3: change a'
32 $ hg merge
32 $ hg merge
33 merging a and e to e
33 merging a and e to e
34 3 files updated, 1 files merged, 1 files removed, 0 files unresolved
34 3 files updated, 1 files merged, 1 files removed, 0 files unresolved
35 (branch merge, don't forget to commit)
35 (branch merge, don't forget to commit)
36 $ hg ci -d '4 0' -qAm '4: merge 2 and 3'
36 $ hg ci -d '4 0' -qAm '4: merge 2 and 3'
37 $ echo a >> a
37 $ echo a >> a
38 $ hg ci -d '5 0' -qAm '5: change a'
38 $ hg ci -d '5 0' -qAm '5: change a'
39 $ cd ..
39 $ cd ..
40
40
41 Convert from null revision
41 Convert from null revision
42
42
43 $ hg convert --config convert.hg.startrev=null source full
43 $ hg convert --config convert.hg.startrev=null source full
44 initializing destination full repository
44 initializing destination full repository
45 scanning source...
45 scanning source...
46 sorting...
46 sorting...
47 converting...
47 converting...
48 5 0: add a b f
48 5 0: add a b f
49 4 1: add c, move f to d
49 4 1: add c, move f to d
50 3 2: copy e from a, change b
50 3 2: copy e from a, change b
51 2 3: change a
51 2 3: change a
52 1 4: merge 2 and 3
52 1 4: merge 2 and 3
53 0 5: change a
53 0 5: change a
54
54
55 $ glog full
55 $ glog full
56 o 5 "5: change a" files: a
56 o 5 "5: change a" files: a
57 |
57 |
58 o 4 "4: merge 2 and 3" files: e f
58 o 4 "4: merge 2 and 3" files: e f
59 |\
59 |\
60 | o 3 "3: change a" files: a
60 | o 3 "3: change a" files: a
61 | |
61 | |
62 o | 2 "2: copy e from a, change b" files: b e
62 o | 2 "2: copy e from a, change b" files: b e
63 | |
63 | |
64 o | 1 "1: add c, move f to d" files: c d f
64 o | 1 "1: add c, move f to d" files: c d f
65 |/
65 |/
66 o 0 "0: add a b f" files: a b f
66 o 0 "0: add a b f" files: a b f
67
67
68 $ rm -Rf full
68 $ rm -Rf full
69
69
70 Convert from zero revision
70 Convert from zero revision
71
71
72 $ hg convert --config convert.hg.startrev=0 source full
72 $ hg convert --config convert.hg.startrev=0 source full
73 initializing destination full repository
73 initializing destination full repository
74 scanning source...
74 scanning source...
75 sorting...
75 sorting...
76 converting...
76 converting...
77 5 0: add a b f
77 5 0: add a b f
78 4 1: add c, move f to d
78 4 1: add c, move f to d
79 3 2: copy e from a, change b
79 3 2: copy e from a, change b
80 2 3: change a
80 2 3: change a
81 1 4: merge 2 and 3
81 1 4: merge 2 and 3
82 0 5: change a
82 0 5: change a
83
83
84 $ glog full
84 $ glog full
85 o 5 "5: change a" files: a
85 o 5 "5: change a" files: a
86 |
86 |
87 o 4 "4: merge 2 and 3" files: e f
87 o 4 "4: merge 2 and 3" files: e f
88 |\
88 |\
89 | o 3 "3: change a" files: a
89 | o 3 "3: change a" files: a
90 | |
90 | |
91 o | 2 "2: copy e from a, change b" files: b e
91 o | 2 "2: copy e from a, change b" files: b e
92 | |
92 | |
93 o | 1 "1: add c, move f to d" files: c d f
93 o | 1 "1: add c, move f to d" files: c d f
94 |/
94 |/
95 o 0 "0: add a b f" files: a b f
95 o 0 "0: add a b f" files: a b f
96
96
97 Convert from merge parent
97 Convert from merge parent
98
98
99 $ hg convert --config convert.hg.startrev=1 source conv1
99 $ hg convert --config convert.hg.startrev=1 source conv1
100 initializing destination conv1 repository
100 initializing destination conv1 repository
101 scanning source...
101 scanning source...
102 sorting...
102 sorting...
103 converting...
103 converting...
104 3 1: add c, move f to d
104 3 1: add c, move f to d
105 2 2: copy e from a, change b
105 2 2: copy e from a, change b
106 1 4: merge 2 and 3
106 1 4: merge 2 and 3
107 0 5: change a
107 0 5: change a
108
108
109 $ glog conv1
109 $ glog conv1
110 o 3 "5: change a" files: a
110 o 3 "5: change a" files: a
111 |
111 |
112 o 2 "4: merge 2 and 3" files: a e
112 o 2 "4: merge 2 and 3" files: a e
113 |
113 |
114 o 1 "2: copy e from a, change b" files: b e
114 o 1 "2: copy e from a, change b" files: b e
115 |
115 |
116 o 0 "1: add c, move f to d" files: a b c d
116 o 0 "1: add c, move f to d" files: a b c d
117
117
118 $ cd conv1
118 $ cd conv1
119 $ hg up -q
119 $ hg up -q
120
120
121 Check copy preservation
121 Check copy preservation
122
122
123 $ hg st -C --change 2 e
124 M e
125 $ hg st -C --change 1 e
126 A e
127 a
128 $ hg st -C --change 0 a
129 A a
130
131 (It seems like a bug in log that the following doesn't show rev 1.)
132
123 $ hg log --follow --copies e
133 $ hg log --follow --copies e
124 changeset: 2:60633ee11cfa
134 changeset: 2:82bbac3d2cf4
125 user: test
135 user: test
126 date: Thu Jan 01 00:00:04 1970 +0000
136 date: Thu Jan 01 00:00:04 1970 +0000
127 summary: 4: merge 2 and 3
137 summary: 4: merge 2 and 3
128
138
129 changeset: 1:d56e8baefff8
139 changeset: 0:23c3be426dce
130 user: test
140 user: test
131 date: Thu Jan 01 00:00:02 1970 +0000
141 date: Thu Jan 01 00:00:01 1970 +0000
132 summary: 2: copy e from a, change b
142 summary: 1: add c, move f to d
133
143
134 Check copy removal on missing parent
144 Check copy removal on missing parent
135
145
136 $ hg log --follow --copies d
146 $ hg log --follow --copies d
137 changeset: 0:23c3be426dce
147 changeset: 0:23c3be426dce
138 user: test
148 user: test
139 date: Thu Jan 01 00:00:01 1970 +0000
149 date: Thu Jan 01 00:00:01 1970 +0000
140 summary: 1: add c, move f to d
150 summary: 1: add c, move f to d
141
151
142 $ hg cat -r tip a b
152 $ hg cat -r tip a b
143 a
153 a
144 a
154 a
145 a
155 a
146 b
156 b
147 b
157 b
148 $ hg -q verify
158 $ hg -q verify
149 $ cd ..
159 $ cd ..
150
160
151 Convert from merge
161 Convert from merge
152
162
153 $ hg convert --config convert.hg.startrev=4 source conv4
163 $ hg convert --config convert.hg.startrev=4 source conv4
154 initializing destination conv4 repository
164 initializing destination conv4 repository
155 scanning source...
165 scanning source...
156 sorting...
166 sorting...
157 converting...
167 converting...
158 1 4: merge 2 and 3
168 1 4: merge 2 and 3
159 0 5: change a
169 0 5: change a
160 $ glog conv4
170 $ glog conv4
161 o 1 "5: change a" files: a
171 o 1 "5: change a" files: a
162 |
172 |
163 o 0 "4: merge 2 and 3" files: a b c d e
173 o 0 "4: merge 2 and 3" files: a b c d e
164
174
165 $ cd conv4
175 $ cd conv4
166 $ hg up -C
176 $ hg up -C
167 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
177 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
168 $ hg cat -r tip a b
178 $ hg cat -r tip a b
169 a
179 a
170 a
180 a
171 a
181 a
172 b
182 b
173 b
183 b
174 $ hg -q verify
184 $ hg -q verify
175 $ cd ..
185 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now