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