##// END OF EJS Templates
convert: update the transplant, rebase and graft references in 'extra'...
Matt Harbison -
r21765:44255f7c default
parent child Browse files
Show More
@@ -1,452 +1,470 b''
1 1 # hg.py - hg backend for convert extension
2 2 #
3 3 # Copyright 2005-2009 Matt Mackall <mpm@selenic.com> and others
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 # Notes for hg->hg conversion:
9 9 #
10 10 # * Old versions of Mercurial didn't trim the whitespace from the ends
11 11 # of commit messages, but new versions do. Changesets created by
12 12 # those older versions, then converted, may thus have different
13 13 # hashes for changesets that are otherwise identical.
14 14 #
15 15 # * Using "--config convert.hg.saverev=true" will make the source
16 16 # identifier to be stored in the converted revision. This will cause
17 17 # the converted revision to have a different identity than the
18 18 # source.
19 19
20 20
21 21 import os, time, cStringIO
22 22 from mercurial.i18n import _
23 23 from mercurial.node import bin, hex, nullid
24 24 from mercurial import hg, util, context, bookmarks, error, scmutil
25 25
26 26 from common import NoRepo, commit, converter_source, converter_sink
27 27
28 28 import re
29 29 sha1re = re.compile(r'\b[0-9a-f]{6,40}\b')
30 30
31 31 class mercurial_sink(converter_sink):
32 32 def __init__(self, ui, path):
33 33 converter_sink.__init__(self, ui, path)
34 34 self.branchnames = ui.configbool('convert', 'hg.usebranchnames', True)
35 35 self.clonebranches = ui.configbool('convert', 'hg.clonebranches', False)
36 36 self.tagsbranch = ui.config('convert', 'hg.tagsbranch', 'default')
37 37 self.lastbranch = None
38 38 if os.path.isdir(path) and len(os.listdir(path)) > 0:
39 39 try:
40 40 self.repo = hg.repository(self.ui, path)
41 41 if not self.repo.local():
42 42 raise NoRepo(_('%s is not a local Mercurial repository')
43 43 % path)
44 44 except error.RepoError, err:
45 45 ui.traceback()
46 46 raise NoRepo(err.args[0])
47 47 else:
48 48 try:
49 49 ui.status(_('initializing destination %s repository\n') % path)
50 50 self.repo = hg.repository(self.ui, path, create=True)
51 51 if not self.repo.local():
52 52 raise NoRepo(_('%s is not a local Mercurial repository')
53 53 % path)
54 54 self.created.append(path)
55 55 except error.RepoError:
56 56 ui.traceback()
57 57 raise NoRepo(_("could not create hg repository %s as sink")
58 58 % path)
59 59 self.lock = None
60 60 self.wlock = None
61 61 self.filemapmode = False
62 62
63 63 def before(self):
64 64 self.ui.debug('run hg sink pre-conversion action\n')
65 65 self.wlock = self.repo.wlock()
66 66 self.lock = self.repo.lock()
67 67
68 68 def after(self):
69 69 self.ui.debug('run hg sink post-conversion action\n')
70 70 if self.lock:
71 71 self.lock.release()
72 72 if self.wlock:
73 73 self.wlock.release()
74 74
75 75 def revmapfile(self):
76 76 return self.repo.join("shamap")
77 77
78 78 def authorfile(self):
79 79 return self.repo.join("authormap")
80 80
81 81 def setbranch(self, branch, pbranches):
82 82 if not self.clonebranches:
83 83 return
84 84
85 85 setbranch = (branch != self.lastbranch)
86 86 self.lastbranch = branch
87 87 if not branch:
88 88 branch = 'default'
89 89 pbranches = [(b[0], b[1] and b[1] or 'default') for b in pbranches]
90 90 pbranch = pbranches and pbranches[0][1] or 'default'
91 91
92 92 branchpath = os.path.join(self.path, branch)
93 93 if setbranch:
94 94 self.after()
95 95 try:
96 96 self.repo = hg.repository(self.ui, branchpath)
97 97 except Exception:
98 98 self.repo = hg.repository(self.ui, branchpath, create=True)
99 99 self.before()
100 100
101 101 # pbranches may bring revisions from other branches (merge parents)
102 102 # Make sure we have them, or pull them.
103 103 missings = {}
104 104 for b in pbranches:
105 105 try:
106 106 self.repo.lookup(b[0])
107 107 except Exception:
108 108 missings.setdefault(b[1], []).append(b[0])
109 109
110 110 if missings:
111 111 self.after()
112 112 for pbranch, heads in sorted(missings.iteritems()):
113 113 pbranchpath = os.path.join(self.path, pbranch)
114 114 prepo = hg.peer(self.ui, {}, pbranchpath)
115 115 self.ui.note(_('pulling from %s into %s\n') % (pbranch, branch))
116 116 self.repo.pull(prepo, [prepo.lookup(h) for h in heads])
117 117 self.before()
118 118
119 119 def _rewritetags(self, source, revmap, data):
120 120 fp = cStringIO.StringIO()
121 121 for line in data.splitlines():
122 122 s = line.split(' ', 1)
123 123 if len(s) != 2:
124 124 continue
125 125 revid = revmap.get(source.lookuprev(s[0]))
126 126 if not revid:
127 127 continue
128 128 fp.write('%s %s\n' % (revid, s[1]))
129 129 return fp.getvalue()
130 130
131 131 def putcommit(self, files, copies, parents, commit, source, revmap):
132 132
133 133 files = dict(files)
134 134 def getfilectx(repo, memctx, f):
135 135 v = files[f]
136 136 data, mode = source.getfile(f, v)
137 137 if f == '.hgtags':
138 138 data = self._rewritetags(source, revmap, data)
139 139 return context.memfilectx(self.repo, f, data, 'l' in mode,
140 140 'x' in mode, copies.get(f))
141 141
142 142 pl = []
143 143 for p in parents:
144 144 if p not in pl:
145 145 pl.append(p)
146 146 parents = pl
147 147 nparents = len(parents)
148 148 if self.filemapmode and nparents == 1:
149 149 m1node = self.repo.changelog.read(bin(parents[0]))[0]
150 150 parent = parents[0]
151 151
152 152 if len(parents) < 2:
153 153 parents.append(nullid)
154 154 if len(parents) < 2:
155 155 parents.append(nullid)
156 156 p2 = parents.pop(0)
157 157
158 158 text = commit.desc
159 159
160 160 sha1s = re.findall(sha1re, text)
161 161 for sha1 in sha1s:
162 162 oldrev = source.lookuprev(sha1)
163 163 newrev = revmap.get(oldrev)
164 164 if newrev is not None:
165 165 text = text.replace(sha1, newrev[:len(sha1)])
166 166
167 167 extra = commit.extra.copy()
168
169 for label in ('source', 'transplant_source', 'rebase_source'):
170 node = extra.get(label)
171
172 if node is None:
173 continue
174
175 # Only transplant stores its reference in binary
176 if label == 'transplant_source':
177 node = hex(node)
178
179 newrev = revmap.get(node)
180 if newrev is not None:
181 if label == 'transplant_source':
182 newrev = bin(newrev)
183
184 extra[label] = newrev
185
168 186 if self.branchnames and commit.branch:
169 187 extra['branch'] = commit.branch
170 188 if commit.rev:
171 189 extra['convert_revision'] = commit.rev
172 190
173 191 while parents:
174 192 p1 = p2
175 193 p2 = parents.pop(0)
176 194 ctx = context.memctx(self.repo, (p1, p2), text, files.keys(),
177 195 getfilectx, commit.author, commit.date, extra)
178 196 self.repo.commitctx(ctx)
179 197 text = "(octopus merge fixup)\n"
180 198 p2 = hex(self.repo.changelog.tip())
181 199
182 200 if self.filemapmode and nparents == 1:
183 201 man = self.repo.manifest
184 202 mnode = self.repo.changelog.read(bin(p2))[0]
185 203 closed = 'close' in commit.extra
186 204 if not closed and not man.cmp(m1node, man.revision(mnode)):
187 205 self.ui.status(_("filtering out empty revision\n"))
188 206 self.repo.rollback(force=True)
189 207 return parent
190 208 return p2
191 209
192 210 def puttags(self, tags):
193 211 try:
194 212 parentctx = self.repo[self.tagsbranch]
195 213 tagparent = parentctx.node()
196 214 except error.RepoError:
197 215 parentctx = None
198 216 tagparent = nullid
199 217
200 218 oldlines = set()
201 219 for branch, heads in self.repo.branchmap().iteritems():
202 220 for h in heads:
203 221 if '.hgtags' in self.repo[h]:
204 222 oldlines.update(
205 223 set(self.repo[h]['.hgtags'].data().splitlines(True)))
206 224 oldlines = sorted(list(oldlines))
207 225
208 226 newlines = sorted([("%s %s\n" % (tags[tag], tag)) for tag in tags])
209 227 if newlines == oldlines:
210 228 return None, None
211 229
212 230 # if the old and new tags match, then there is nothing to update
213 231 oldtags = set()
214 232 newtags = set()
215 233 for line in oldlines:
216 234 s = line.strip().split(' ', 1)
217 235 if len(s) != 2:
218 236 continue
219 237 oldtags.add(s[1])
220 238 for line in newlines:
221 239 s = line.strip().split(' ', 1)
222 240 if len(s) != 2:
223 241 continue
224 242 if s[1] not in oldtags:
225 243 newtags.add(s[1].strip())
226 244
227 245 if not newtags:
228 246 return None, None
229 247
230 248 data = "".join(newlines)
231 249 def getfilectx(repo, memctx, f):
232 250 return context.memfilectx(repo, f, data, False, False, None)
233 251
234 252 self.ui.status(_("updating tags\n"))
235 253 date = "%s 0" % int(time.mktime(time.gmtime()))
236 254 extra = {'branch': self.tagsbranch}
237 255 ctx = context.memctx(self.repo, (tagparent, None), "update tags",
238 256 [".hgtags"], getfilectx, "convert-repo", date,
239 257 extra)
240 258 self.repo.commitctx(ctx)
241 259 return hex(self.repo.changelog.tip()), hex(tagparent)
242 260
243 261 def setfilemapmode(self, active):
244 262 self.filemapmode = active
245 263
246 264 def putbookmarks(self, updatedbookmark):
247 265 if not len(updatedbookmark):
248 266 return
249 267
250 268 self.ui.status(_("updating bookmarks\n"))
251 269 destmarks = self.repo._bookmarks
252 270 for bookmark in updatedbookmark:
253 271 destmarks[bookmark] = bin(updatedbookmark[bookmark])
254 272 destmarks.write()
255 273
256 274 def hascommitfrommap(self, rev):
257 275 # the exact semantics of clonebranches is unclear so we can't say no
258 276 return rev in self.repo or self.clonebranches
259 277
260 278 def hascommitforsplicemap(self, rev):
261 279 if rev not in self.repo and self.clonebranches:
262 280 raise util.Abort(_('revision %s not found in destination '
263 281 'repository (lookups with clonebranches=true '
264 282 'are not implemented)') % rev)
265 283 return rev in self.repo
266 284
267 285 class mercurial_source(converter_source):
268 286 def __init__(self, ui, path, rev=None):
269 287 converter_source.__init__(self, ui, path, rev)
270 288 self.ignoreerrors = ui.configbool('convert', 'hg.ignoreerrors', False)
271 289 self.ignored = set()
272 290 self.saverev = ui.configbool('convert', 'hg.saverev', False)
273 291 try:
274 292 self.repo = hg.repository(self.ui, path)
275 293 # try to provoke an exception if this isn't really a hg
276 294 # repo, but some other bogus compatible-looking url
277 295 if not self.repo.local():
278 296 raise error.RepoError
279 297 except error.RepoError:
280 298 ui.traceback()
281 299 raise NoRepo(_("%s is not a local Mercurial repository") % path)
282 300 self.lastrev = None
283 301 self.lastctx = None
284 302 self._changescache = None
285 303 self.convertfp = None
286 304 # Restrict converted revisions to startrev descendants
287 305 startnode = ui.config('convert', 'hg.startrev')
288 306 hgrevs = ui.config('convert', 'hg.revs')
289 307 if hgrevs is None:
290 308 if startnode is not None:
291 309 try:
292 310 startnode = self.repo.lookup(startnode)
293 311 except error.RepoError:
294 312 raise util.Abort(_('%s is not a valid start revision')
295 313 % startnode)
296 314 startrev = self.repo.changelog.rev(startnode)
297 315 children = {startnode: 1}
298 316 for r in self.repo.changelog.descendants([startrev]):
299 317 children[self.repo.changelog.node(r)] = 1
300 318 self.keep = children.__contains__
301 319 else:
302 320 self.keep = util.always
303 321 if rev:
304 322 self._heads = [self.repo[rev].node()]
305 323 else:
306 324 self._heads = self.repo.heads()
307 325 else:
308 326 if rev or startnode is not None:
309 327 raise util.Abort(_('hg.revs cannot be combined with '
310 328 'hg.startrev or --rev'))
311 329 nodes = set()
312 330 parents = set()
313 331 for r in scmutil.revrange(self.repo, [hgrevs]):
314 332 ctx = self.repo[r]
315 333 nodes.add(ctx.node())
316 334 parents.update(p.node() for p in ctx.parents())
317 335 self.keep = nodes.__contains__
318 336 self._heads = nodes - parents
319 337
320 338 def changectx(self, rev):
321 339 if self.lastrev != rev:
322 340 self.lastctx = self.repo[rev]
323 341 self.lastrev = rev
324 342 return self.lastctx
325 343
326 344 def parents(self, ctx):
327 345 return [p for p in ctx.parents() if p and self.keep(p.node())]
328 346
329 347 def getheads(self):
330 348 return [hex(h) for h in self._heads if self.keep(h)]
331 349
332 350 def getfile(self, name, rev):
333 351 try:
334 352 fctx = self.changectx(rev)[name]
335 353 return fctx.data(), fctx.flags()
336 354 except error.LookupError, err:
337 355 raise IOError(err)
338 356
339 357 def getchanges(self, rev):
340 358 ctx = self.changectx(rev)
341 359 parents = self.parents(ctx)
342 360 if not parents:
343 361 files = sorted(ctx.manifest())
344 362 # getcopies() is not needed for roots, but it is a simple way to
345 363 # detect missing revlogs and abort on errors or populate
346 364 # self.ignored
347 365 self.getcopies(ctx, parents, files)
348 366 return [(f, rev) for f in files if f not in self.ignored], {}
349 367 if self._changescache and self._changescache[0] == rev:
350 368 m, a, r = self._changescache[1]
351 369 else:
352 370 m, a, r = self.repo.status(parents[0].node(), ctx.node())[:3]
353 371 # getcopies() detects missing revlogs early, run it before
354 372 # filtering the changes.
355 373 copies = self.getcopies(ctx, parents, m + a)
356 374 changes = [(name, rev) for name in m + a + r
357 375 if name not in self.ignored]
358 376 return sorted(changes), copies
359 377
360 378 def getcopies(self, ctx, parents, files):
361 379 copies = {}
362 380 for name in files:
363 381 if name in self.ignored:
364 382 continue
365 383 try:
366 384 copysource, _copynode = ctx.filectx(name).renamed()
367 385 if copysource in self.ignored:
368 386 continue
369 387 # Ignore copy sources not in parent revisions
370 388 found = False
371 389 for p in parents:
372 390 if copysource in p:
373 391 found = True
374 392 break
375 393 if not found:
376 394 continue
377 395 copies[name] = copysource
378 396 except TypeError:
379 397 pass
380 398 except error.LookupError, e:
381 399 if not self.ignoreerrors:
382 400 raise
383 401 self.ignored.add(name)
384 402 self.ui.warn(_('ignoring: %s\n') % e)
385 403 return copies
386 404
387 405 def getcommit(self, rev):
388 406 ctx = self.changectx(rev)
389 407 parents = [p.hex() for p in self.parents(ctx)]
390 408 if self.saverev:
391 409 crev = rev
392 410 else:
393 411 crev = None
394 412 return commit(author=ctx.user(),
395 413 date=util.datestr(ctx.date(), '%Y-%m-%d %H:%M:%S %1%2'),
396 414 desc=ctx.description(), rev=crev, parents=parents,
397 415 branch=ctx.branch(), extra=ctx.extra(),
398 416 sortkey=ctx.rev())
399 417
400 418 def gettags(self):
401 419 # This will get written to .hgtags, filter non global tags out.
402 420 tags = [t for t in self.repo.tagslist()
403 421 if self.repo.tagtype(t[0]) == 'global']
404 422 return dict([(name, hex(node)) for name, node in tags
405 423 if self.keep(node)])
406 424
407 425 def getchangedfiles(self, rev, i):
408 426 ctx = self.changectx(rev)
409 427 parents = self.parents(ctx)
410 428 if not parents and i is None:
411 429 i = 0
412 430 changes = [], ctx.manifest().keys(), []
413 431 else:
414 432 i = i or 0
415 433 changes = self.repo.status(parents[i].node(), ctx.node())[:3]
416 434 changes = [[f for f in l if f not in self.ignored] for l in changes]
417 435
418 436 if i == 0:
419 437 self._changescache = (rev, changes)
420 438
421 439 return changes[0] + changes[1] + changes[2]
422 440
423 441 def converted(self, rev, destrev):
424 442 if self.convertfp is None:
425 443 self.convertfp = open(self.repo.join('shamap'), 'a')
426 444 self.convertfp.write('%s %s\n' % (destrev, rev))
427 445 self.convertfp.flush()
428 446
429 447 def before(self):
430 448 self.ui.debug('run hg source pre-conversion action\n')
431 449
432 450 def after(self):
433 451 self.ui.debug('run hg source post-conversion action\n')
434 452
435 453 def hasnativeorder(self):
436 454 return True
437 455
438 456 def hasnativeclose(self):
439 457 return True
440 458
441 459 def lookuprev(self, rev):
442 460 try:
443 461 return hex(self.repo.lookup(rev))
444 462 except error.RepoError:
445 463 return None
446 464
447 465 def getbookmarks(self):
448 466 return bookmarks.listbookmarks(self.repo)
449 467
450 468 def checkrevformat(self, revstr, mapname='splicemap'):
451 469 """ Mercurial, revision string is a 40 byte hex """
452 470 self.checkhexformat(revstr, mapname)
@@ -1,602 +1,633 b''
1 1 Create a repo with some stuff in it:
2 2
3 3 $ hg init a
4 4 $ cd a
5 5 $ echo a > a
6 6 $ echo a > d
7 7 $ echo a > e
8 8 $ hg ci -qAm0
9 9 $ echo b > a
10 10 $ hg ci -m1 -u bar
11 11 $ hg mv a b
12 12 $ hg ci -m2
13 13 $ hg cp b c
14 14 $ hg ci -m3 -u baz
15 15 $ echo b > d
16 16 $ echo f > e
17 17 $ hg ci -m4
18 18 $ hg up -q 3
19 19 $ echo b > e
20 20 $ hg branch -q stable
21 21 $ hg ci -m5
22 22 $ hg merge -q default --tool internal:local
23 23 $ hg branch -q default
24 24 $ hg ci -m6
25 25 $ hg phase --public 3
26 26 $ hg phase --force --secret 6
27 27
28 28 $ hg log -G --template '{author}@{rev}.{phase}: {desc}\n'
29 29 @ test@6.secret: 6
30 30 |\
31 31 | o test@5.draft: 5
32 32 | |
33 33 o | test@4.draft: 4
34 34 |/
35 35 o baz@3.public: 3
36 36 |
37 37 o test@2.public: 2
38 38 |
39 39 o bar@1.public: 1
40 40 |
41 41 o test@0.public: 0
42 42
43 43
44 44 Need to specify a rev:
45 45
46 46 $ hg graft
47 47 abort: no revisions specified
48 48 [255]
49 49
50 50 Can't graft ancestor:
51 51
52 52 $ hg graft 1 2
53 53 skipping ancestor revision 1
54 54 skipping ancestor revision 2
55 55 [255]
56 56
57 57 Specify revisions with -r:
58 58
59 59 $ hg graft -r 1 -r 2
60 60 skipping ancestor revision 1
61 61 skipping ancestor revision 2
62 62 [255]
63 63
64 64 $ hg graft -r 1 2
65 65 skipping ancestor revision 2
66 66 skipping ancestor revision 1
67 67 [255]
68 68
69 69 Can't graft with dirty wd:
70 70
71 71 $ hg up -q 0
72 72 $ echo foo > a
73 73 $ hg graft 1
74 74 abort: uncommitted changes
75 75 [255]
76 76 $ hg revert a
77 77
78 78 Graft a rename:
79 79 (this also tests that editor is invoked if '--edit' is specified)
80 80
81 81 $ hg status --rev "2^1" --rev 2
82 82 A b
83 83 R a
84 84 $ HGEDITOR=cat hg graft 2 -u foo --edit
85 85 grafting revision 2
86 86 merging a and b to b
87 87 2
88 88
89 89
90 90 HG: Enter commit message. Lines beginning with 'HG:' are removed.
91 91 HG: Leave message empty to abort commit.
92 92 HG: --
93 93 HG: user: foo
94 94 HG: branch 'default'
95 95 HG: changed b
96 96 HG: removed a
97 97 $ hg export tip --git
98 98 # HG changeset patch
99 99 # User foo
100 100 # Date 0 0
101 101 # Thu Jan 01 00:00:00 1970 +0000
102 102 # Node ID ef0ef43d49e79e81ddafdc7997401ba0041efc82
103 103 # Parent 68795b066622ca79a25816a662041d8f78f3cd9e
104 104 2
105 105
106 106 diff --git a/a b/b
107 107 rename from a
108 108 rename to b
109 109
110 110 Look for extra:source
111 111
112 112 $ hg log --debug -r tip
113 113 changeset: 7:ef0ef43d49e79e81ddafdc7997401ba0041efc82
114 114 tag: tip
115 115 phase: draft
116 116 parent: 0:68795b066622ca79a25816a662041d8f78f3cd9e
117 117 parent: -1:0000000000000000000000000000000000000000
118 118 manifest: 7:e59b6b228f9cbf9903d5e9abf996e083a1f533eb
119 119 user: foo
120 120 date: Thu Jan 01 00:00:00 1970 +0000
121 121 files+: b
122 122 files-: a
123 123 extra: branch=default
124 124 extra: source=5c095ad7e90f871700f02dd1fa5012cb4498a2d4
125 125 description:
126 126 2
127 127
128 128
129 129
130 130 Graft out of order, skipping a merge and a duplicate
131 131 (this also tests that editor is not invoked if '--edit' is not specified)
132 132
133 133 $ hg graft 1 5 4 3 'merge()' 2 -n
134 134 skipping ungraftable merge revision 6
135 135 skipping revision 2 (already grafted to 7)
136 136 grafting revision 1
137 137 grafting revision 5
138 138 grafting revision 4
139 139 grafting revision 3
140 140
141 141 $ HGEDITOR=cat hg graft 1 5 4 3 'merge()' 2 --debug
142 142 skipping ungraftable merge revision 6
143 143 scanning for duplicate grafts
144 144 skipping revision 2 (already grafted to 7)
145 145 grafting revision 1
146 146 searching for copies back to rev 1
147 147 unmatched files in local:
148 148 b
149 149 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
150 150 src: 'a' -> dst: 'b' *
151 151 checking for directory renames
152 152 resolving manifests
153 153 branchmerge: True, force: True, partial: False
154 154 ancestor: 68795b066622, local: ef0ef43d49e7+, remote: 5d205f8b35b6
155 155 preserving b for resolve of b
156 156 b: local copied/moved from a -> m
157 157 updating: b 1/1 files (100.00%)
158 158 picked tool 'internal:merge' for b (binary False symlink False)
159 159 merging b and a to b
160 160 my b@ef0ef43d49e7+ other a@5d205f8b35b6 ancestor a@68795b066622
161 161 premerge successful
162 162 b
163 163 grafting revision 5
164 164 searching for copies back to rev 1
165 165 resolving manifests
166 166 branchmerge: True, force: True, partial: False
167 167 ancestor: 4c60f11aa304, local: 6b9e5368ca4e+, remote: 97f8bfe72746
168 168 e: remote is newer -> g
169 169 getting e
170 170 updating: e 1/1 files (100.00%)
171 171 b: keep -> k
172 172 e
173 173 grafting revision 4
174 174 searching for copies back to rev 1
175 175 resolving manifests
176 176 branchmerge: True, force: True, partial: False
177 177 ancestor: 4c60f11aa304, local: 1905859650ec+, remote: 9c233e8e184d
178 178 preserving e for resolve of e
179 179 d: remote is newer -> g
180 180 getting d
181 181 updating: d 1/2 files (50.00%)
182 182 b: keep -> k
183 183 e: versions differ -> m
184 184 updating: e 2/2 files (100.00%)
185 185 picked tool 'internal:merge' for e (binary False symlink False)
186 186 merging e
187 187 my e@1905859650ec+ other e@9c233e8e184d ancestor e@68795b066622
188 188 warning: conflicts during merge.
189 189 merging e incomplete! (edit conflicts, then use 'hg resolve --mark')
190 190 abort: unresolved conflicts, can't continue
191 191 (use hg resolve and hg graft --continue)
192 192 [255]
193 193
194 194 Commit while interrupted should fail:
195 195
196 196 $ hg ci -m 'commit interrupted graft'
197 197 abort: graft in progress
198 198 (use 'hg graft --continue' or 'hg update' to abort)
199 199 [255]
200 200
201 201 Abort the graft and try committing:
202 202
203 203 $ hg up -C .
204 204 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
205 205 $ echo c >> e
206 206 $ hg ci -mtest
207 207
208 208 $ hg strip . --config extensions.mq=
209 209 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
210 210 saved backup bundle to $TESTTMP/a/.hg/strip-backup/*-backup.hg (glob)
211 211
212 212 Graft again:
213 213
214 214 $ hg graft 1 5 4 3 'merge()' 2
215 215 skipping ungraftable merge revision 6
216 216 skipping revision 2 (already grafted to 7)
217 217 skipping revision 1 (already grafted to 8)
218 218 skipping revision 5 (already grafted to 9)
219 219 grafting revision 4
220 220 merging e
221 221 warning: conflicts during merge.
222 222 merging e incomplete! (edit conflicts, then use 'hg resolve --mark')
223 223 abort: unresolved conflicts, can't continue
224 224 (use hg resolve and hg graft --continue)
225 225 [255]
226 226
227 227 Continue without resolve should fail:
228 228
229 229 $ hg graft -c
230 230 grafting revision 4
231 231 abort: unresolved merge conflicts (see hg help resolve)
232 232 [255]
233 233
234 234 Fix up:
235 235
236 236 $ echo b > e
237 237 $ hg resolve -m e
238 238 no more unresolved files
239 239
240 240 Continue with a revision should fail:
241 241
242 242 $ hg graft -c 6
243 243 abort: can't specify --continue and revisions
244 244 [255]
245 245
246 246 $ hg graft -c -r 6
247 247 abort: can't specify --continue and revisions
248 248 [255]
249 249
250 250 Continue for real, clobber usernames
251 251
252 252 $ hg graft -c -U
253 253 grafting revision 4
254 254 grafting revision 3
255 255
256 256 Compare with original:
257 257
258 258 $ hg diff -r 6
259 259 $ hg status --rev 0:. -C
260 260 M d
261 261 M e
262 262 A b
263 263 a
264 264 A c
265 265 a
266 266 R a
267 267
268 268 View graph:
269 269
270 270 $ hg log -G --template '{author}@{rev}.{phase}: {desc}\n'
271 271 @ test@11.draft: 3
272 272 |
273 273 o test@10.draft: 4
274 274 |
275 275 o test@9.draft: 5
276 276 |
277 277 o bar@8.draft: 1
278 278 |
279 279 o foo@7.draft: 2
280 280 |
281 281 | o test@6.secret: 6
282 282 | |\
283 283 | | o test@5.draft: 5
284 284 | | |
285 285 | o | test@4.draft: 4
286 286 | |/
287 287 | o baz@3.public: 3
288 288 | |
289 289 | o test@2.public: 2
290 290 | |
291 291 | o bar@1.public: 1
292 292 |/
293 293 o test@0.public: 0
294 294
295 295 Graft again onto another branch should preserve the original source
296 296 $ hg up -q 0
297 297 $ echo 'g'>g
298 298 $ hg add g
299 299 $ hg ci -m 7
300 300 created new head
301 301 $ hg graft 7
302 302 grafting revision 7
303 303
304 304 $ hg log -r 7 --template '{rev}:{node}\n'
305 305 7:ef0ef43d49e79e81ddafdc7997401ba0041efc82
306 306 $ hg log -r 2 --template '{rev}:{node}\n'
307 307 2:5c095ad7e90f871700f02dd1fa5012cb4498a2d4
308 308
309 309 $ hg log --debug -r tip
310 310 changeset: 13:9db0f28fd3747e92c57d015f53b5593aeec53c2d
311 311 tag: tip
312 312 phase: draft
313 313 parent: 12:b592ea63bb0c19a6c5c44685ee29a2284f9f1b8f
314 314 parent: -1:0000000000000000000000000000000000000000
315 315 manifest: 13:dc313617b8c32457c0d589e0dbbedfe71f3cd637
316 316 user: foo
317 317 date: Thu Jan 01 00:00:00 1970 +0000
318 318 files+: b
319 319 files-: a
320 320 extra: branch=default
321 321 extra: source=5c095ad7e90f871700f02dd1fa5012cb4498a2d4
322 322 description:
323 323 2
324 324
325 325
326 326 Disallow grafting an already grafted cset onto its original branch
327 327 $ hg up -q 6
328 328 $ hg graft 7
329 329 skipping already grafted revision 7 (was grafted from 2)
330 330 [255]
331 331
332 332 Disallow grafting already grafted csets with the same origin onto each other
333 333 $ hg up -q 13
334 334 $ hg graft 2
335 335 skipping revision 2 (already grafted to 13)
336 336 [255]
337 337 $ hg graft 7
338 338 skipping already grafted revision 7 (13 also has origin 2)
339 339 [255]
340 340
341 341 $ hg up -q 7
342 342 $ hg graft 2
343 343 skipping revision 2 (already grafted to 7)
344 344 [255]
345 345 $ hg graft tip
346 346 skipping already grafted revision 13 (7 also has origin 2)
347 347 [255]
348 348
349 349 Graft with --log
350 350
351 351 $ hg up -Cq 1
352 352 $ hg graft 3 --log -u foo
353 353 grafting revision 3
354 354 warning: can't find ancestor for 'c' copied from 'b'!
355 355 $ hg log --template '{rev} {parents} {desc}\n' -r tip
356 356 14 1:5d205f8b35b6 3
357 357 (grafted from 4c60f11aa304a54ae1c199feb94e7fc771e51ed8)
358 358
359 359 Resolve conflicted graft
360 360 $ hg up -q 0
361 361 $ echo b > a
362 362 $ hg ci -m 8
363 363 created new head
364 364 $ echo a > a
365 365 $ hg ci -m 9
366 366 $ hg graft 1 --tool internal:fail
367 367 grafting revision 1
368 368 abort: unresolved conflicts, can't continue
369 369 (use hg resolve and hg graft --continue)
370 370 [255]
371 371 $ hg resolve --all
372 372 merging a
373 373 no more unresolved files
374 374 $ hg graft -c
375 375 grafting revision 1
376 376 $ hg export tip --git
377 377 # HG changeset patch
378 378 # User bar
379 379 # Date 0 0
380 380 # Thu Jan 01 00:00:00 1970 +0000
381 381 # Node ID 64ecd9071ce83c6e62f538d8ce7709d53f32ebf7
382 382 # Parent 4bdb9a9d0b84ffee1d30f0dfc7744cade17aa19c
383 383 1
384 384
385 385 diff --git a/a b/a
386 386 --- a/a
387 387 +++ b/a
388 388 @@ -1,1 +1,1 @@
389 389 -a
390 390 +b
391 391
392 392 Resolve conflicted graft with rename
393 393 $ echo c > a
394 394 $ hg ci -m 10
395 395 $ hg graft 2 --tool internal:fail
396 396 grafting revision 2
397 397 abort: unresolved conflicts, can't continue
398 398 (use hg resolve and hg graft --continue)
399 399 [255]
400 400 $ hg resolve --all
401 401 merging a and b to b
402 402 no more unresolved files
403 403 $ hg graft -c
404 404 grafting revision 2
405 405 $ hg export tip --git
406 406 # HG changeset patch
407 407 # User test
408 408 # Date 0 0
409 409 # Thu Jan 01 00:00:00 1970 +0000
410 410 # Node ID 2e80e1351d6ed50302fe1e05f8bd1d4d412b6e11
411 411 # Parent e5a51ae854a8bbaaf25cc5c6a57ff46042dadbb4
412 412 2
413 413
414 414 diff --git a/a b/b
415 415 rename from a
416 416 rename to b
417 417
418 418 Test simple origin(), with and without args
419 419 $ hg log -r 'origin()'
420 420 changeset: 1:5d205f8b35b6
421 421 user: bar
422 422 date: Thu Jan 01 00:00:00 1970 +0000
423 423 summary: 1
424 424
425 425 changeset: 2:5c095ad7e90f
426 426 user: test
427 427 date: Thu Jan 01 00:00:00 1970 +0000
428 428 summary: 2
429 429
430 430 changeset: 3:4c60f11aa304
431 431 user: baz
432 432 date: Thu Jan 01 00:00:00 1970 +0000
433 433 summary: 3
434 434
435 435 changeset: 4:9c233e8e184d
436 436 user: test
437 437 date: Thu Jan 01 00:00:00 1970 +0000
438 438 summary: 4
439 439
440 440 changeset: 5:97f8bfe72746
441 441 branch: stable
442 442 parent: 3:4c60f11aa304
443 443 user: test
444 444 date: Thu Jan 01 00:00:00 1970 +0000
445 445 summary: 5
446 446
447 447 $ hg log -r 'origin(7)'
448 448 changeset: 2:5c095ad7e90f
449 449 user: test
450 450 date: Thu Jan 01 00:00:00 1970 +0000
451 451 summary: 2
452 452
453 453 Now transplant a graft to test following through copies
454 454 $ hg up -q 0
455 455 $ hg branch -q dev
456 456 $ hg ci -qm "dev branch"
457 457 $ hg --config extensions.transplant= transplant -q 7
458 458 $ hg log -r 'origin(.)'
459 459 changeset: 2:5c095ad7e90f
460 460 user: test
461 461 date: Thu Jan 01 00:00:00 1970 +0000
462 462 summary: 2
463 463
464 Test that the graft and transplant markers in extra are converted, allowing
465 origin() to still work. Note that these recheck the immediately preceeding two
466 tests.
467 $ hg --quiet --config extensions.convert= --config convert.hg.saverev=True convert . ../converted
468
469 The graft case
470 $ hg -R ../converted log -r 7 --template "{rev}: {node}\n{join(extras, '\n')}\n"
471 7: 7ae846e9111fc8f57745634250c7b9ac0a60689b
472 branch=default
473 convert_revision=ef0ef43d49e79e81ddafdc7997401ba0041efc82
474 source=e0213322b2c1a5d5d236c74e79666441bee67a7d
475 $ hg -R ../converted log -r 'origin(7)'
476 changeset: 2:e0213322b2c1
477 user: test
478 date: Thu Jan 01 00:00:00 1970 +0000
479 summary: 2
480
481 The transplant case
482 $ hg -R ../converted log -r tip --template "{rev}: {node}\n{join(extras, '\n')}\n"
483 21: fbb6c5cc81002f2b4b49c9d731404688bcae5ade
484 branch=dev
485 convert_revision=7e61b508e709a11d28194a5359bc3532d910af21
486 transplant_source=z\xe8F\xe9\x11\x1f\xc8\xf5wEcBP\xc7\xb9\xac (esc)
487 `h\x9b (esc)
488 $ hg -R ../converted log -r 'origin(tip)'
489 changeset: 2:e0213322b2c1
490 user: test
491 date: Thu Jan 01 00:00:00 1970 +0000
492 summary: 2
493
494
464 495 Test simple destination
465 496 $ hg log -r 'destination()'
466 497 changeset: 7:ef0ef43d49e7
467 498 parent: 0:68795b066622
468 499 user: foo
469 500 date: Thu Jan 01 00:00:00 1970 +0000
470 501 summary: 2
471 502
472 503 changeset: 8:6b9e5368ca4e
473 504 user: bar
474 505 date: Thu Jan 01 00:00:00 1970 +0000
475 506 summary: 1
476 507
477 508 changeset: 9:1905859650ec
478 509 user: test
479 510 date: Thu Jan 01 00:00:00 1970 +0000
480 511 summary: 5
481 512
482 513 changeset: 10:52dc0b4c6907
483 514 user: test
484 515 date: Thu Jan 01 00:00:00 1970 +0000
485 516 summary: 4
486 517
487 518 changeset: 11:882b35362a6b
488 519 user: test
489 520 date: Thu Jan 01 00:00:00 1970 +0000
490 521 summary: 3
491 522
492 523 changeset: 13:9db0f28fd374
493 524 user: foo
494 525 date: Thu Jan 01 00:00:00 1970 +0000
495 526 summary: 2
496 527
497 528 changeset: 14:f64defefacee
498 529 parent: 1:5d205f8b35b6
499 530 user: foo
500 531 date: Thu Jan 01 00:00:00 1970 +0000
501 532 summary: 3
502 533
503 534 changeset: 17:64ecd9071ce8
504 535 user: bar
505 536 date: Thu Jan 01 00:00:00 1970 +0000
506 537 summary: 1
507 538
508 539 changeset: 19:2e80e1351d6e
509 540 user: test
510 541 date: Thu Jan 01 00:00:00 1970 +0000
511 542 summary: 2
512 543
513 544 changeset: 21:7e61b508e709
514 545 branch: dev
515 546 tag: tip
516 547 user: foo
517 548 date: Thu Jan 01 00:00:00 1970 +0000
518 549 summary: 2
519 550
520 551 $ hg log -r 'destination(2)'
521 552 changeset: 7:ef0ef43d49e7
522 553 parent: 0:68795b066622
523 554 user: foo
524 555 date: Thu Jan 01 00:00:00 1970 +0000
525 556 summary: 2
526 557
527 558 changeset: 13:9db0f28fd374
528 559 user: foo
529 560 date: Thu Jan 01 00:00:00 1970 +0000
530 561 summary: 2
531 562
532 563 changeset: 19:2e80e1351d6e
533 564 user: test
534 565 date: Thu Jan 01 00:00:00 1970 +0000
535 566 summary: 2
536 567
537 568 changeset: 21:7e61b508e709
538 569 branch: dev
539 570 tag: tip
540 571 user: foo
541 572 date: Thu Jan 01 00:00:00 1970 +0000
542 573 summary: 2
543 574
544 575 Transplants of grafts can find a destination...
545 576 $ hg log -r 'destination(7)'
546 577 changeset: 21:7e61b508e709
547 578 branch: dev
548 579 tag: tip
549 580 user: foo
550 581 date: Thu Jan 01 00:00:00 1970 +0000
551 582 summary: 2
552 583
553 584 ... grafts of grafts unfortunately can't
554 585 $ hg graft -q 13
555 586 $ hg log -r 'destination(13)'
556 587 All copies of a cset
557 588 $ hg log -r 'origin(13) or destination(origin(13))'
558 589 changeset: 2:5c095ad7e90f
559 590 user: test
560 591 date: Thu Jan 01 00:00:00 1970 +0000
561 592 summary: 2
562 593
563 594 changeset: 7:ef0ef43d49e7
564 595 parent: 0:68795b066622
565 596 user: foo
566 597 date: Thu Jan 01 00:00:00 1970 +0000
567 598 summary: 2
568 599
569 600 changeset: 13:9db0f28fd374
570 601 user: foo
571 602 date: Thu Jan 01 00:00:00 1970 +0000
572 603 summary: 2
573 604
574 605 changeset: 19:2e80e1351d6e
575 606 user: test
576 607 date: Thu Jan 01 00:00:00 1970 +0000
577 608 summary: 2
578 609
579 610 changeset: 21:7e61b508e709
580 611 branch: dev
581 612 user: foo
582 613 date: Thu Jan 01 00:00:00 1970 +0000
583 614 summary: 2
584 615
585 616 changeset: 22:1313d0a825e2
586 617 branch: dev
587 618 tag: tip
588 619 user: foo
589 620 date: Thu Jan 01 00:00:00 1970 +0000
590 621 summary: 2
591 622
592 623
593 624 graft works on complex revset
594 625
595 626 $ hg graft 'origin(13) or destination(origin(13))'
596 627 skipping ancestor revision 21
597 628 skipping ancestor revision 22
598 629 skipping revision 2 (already grafted to 22)
599 630 grafting revision 7
600 631 grafting revision 13
601 632 grafting revision 19
602 633 merging b
General Comments 0
You need to be logged in to leave comments. Login now