##// END OF EJS Templates
mergecopies: invoke _computenonoverlap for both base and tca during merges...
Gábor Stefanik -
r30197:0accd5a5 default
parent child Browse files
Show More
@@ -1,618 +1,630
1 1 # copies.py - copy detection for Mercurial
2 2 #
3 3 # Copyright 2008 Matt Mackall <mpm@selenic.com>
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 from __future__ import absolute_import
9 9
10 10 import heapq
11 11
12 12 from . import (
13 13 node,
14 14 pathutil,
15 15 scmutil,
16 16 util,
17 17 )
18 18
19 19 def _findlimit(repo, a, b):
20 20 """
21 21 Find the last revision that needs to be checked to ensure that a full
22 22 transitive closure for file copies can be properly calculated.
23 23 Generally, this means finding the earliest revision number that's an
24 24 ancestor of a or b but not both, except when a or b is a direct descendent
25 25 of the other, in which case we can return the minimum revnum of a and b.
26 26 None if no such revision exists.
27 27 """
28 28
29 29 # basic idea:
30 30 # - mark a and b with different sides
31 31 # - if a parent's children are all on the same side, the parent is
32 32 # on that side, otherwise it is on no side
33 33 # - walk the graph in topological order with the help of a heap;
34 34 # - add unseen parents to side map
35 35 # - clear side of any parent that has children on different sides
36 36 # - track number of interesting revs that might still be on a side
37 37 # - track the lowest interesting rev seen
38 38 # - quit when interesting revs is zero
39 39
40 40 cl = repo.changelog
41 41 working = len(cl) # pseudo rev for the working directory
42 42 if a is None:
43 43 a = working
44 44 if b is None:
45 45 b = working
46 46
47 47 side = {a: -1, b: 1}
48 48 visit = [-a, -b]
49 49 heapq.heapify(visit)
50 50 interesting = len(visit)
51 51 hascommonancestor = False
52 52 limit = working
53 53
54 54 while interesting:
55 55 r = -heapq.heappop(visit)
56 56 if r == working:
57 57 parents = [cl.rev(p) for p in repo.dirstate.parents()]
58 58 else:
59 59 parents = cl.parentrevs(r)
60 60 for p in parents:
61 61 if p < 0:
62 62 continue
63 63 if p not in side:
64 64 # first time we see p; add it to visit
65 65 side[p] = side[r]
66 66 if side[p]:
67 67 interesting += 1
68 68 heapq.heappush(visit, -p)
69 69 elif side[p] and side[p] != side[r]:
70 70 # p was interesting but now we know better
71 71 side[p] = 0
72 72 interesting -= 1
73 73 hascommonancestor = True
74 74 if side[r]:
75 75 limit = r # lowest rev visited
76 76 interesting -= 1
77 77
78 78 if not hascommonancestor:
79 79 return None
80 80
81 81 # Consider the following flow (see test-commit-amend.t under issue4405):
82 82 # 1/ File 'a0' committed
83 83 # 2/ File renamed from 'a0' to 'a1' in a new commit (call it 'a1')
84 84 # 3/ Move back to first commit
85 85 # 4/ Create a new commit via revert to contents of 'a1' (call it 'a1-amend')
86 86 # 5/ Rename file from 'a1' to 'a2' and commit --amend 'a1-msg'
87 87 #
88 88 # During the amend in step five, we will be in this state:
89 89 #
90 90 # @ 3 temporary amend commit for a1-amend
91 91 # |
92 92 # o 2 a1-amend
93 93 # |
94 94 # | o 1 a1
95 95 # |/
96 96 # o 0 a0
97 97 #
98 98 # When _findlimit is called, a and b are revs 3 and 0, so limit will be 2,
99 99 # yet the filelog has the copy information in rev 1 and we will not look
100 100 # back far enough unless we also look at the a and b as candidates.
101 101 # This only occurs when a is a descendent of b or visa-versa.
102 102 return min(limit, a, b)
103 103
104 104 def _chain(src, dst, a, b):
105 105 '''chain two sets of copies a->b'''
106 106 t = a.copy()
107 107 for k, v in b.iteritems():
108 108 if v in t:
109 109 # found a chain
110 110 if t[v] != k:
111 111 # file wasn't renamed back to itself
112 112 t[k] = t[v]
113 113 if v not in dst:
114 114 # chain was a rename, not a copy
115 115 del t[v]
116 116 if v in src:
117 117 # file is a copy of an existing file
118 118 t[k] = v
119 119
120 120 # remove criss-crossed copies
121 121 for k, v in t.items():
122 122 if k in src and v in dst:
123 123 del t[k]
124 124
125 125 return t
126 126
127 127 def _tracefile(fctx, am, limit=-1):
128 128 '''return file context that is the ancestor of fctx present in ancestor
129 129 manifest am, stopping after the first ancestor lower than limit'''
130 130
131 131 for f in fctx.ancestors():
132 132 if am.get(f.path(), None) == f.filenode():
133 133 return f
134 134 if limit >= 0 and f.linkrev() < limit and f.rev() < limit:
135 135 return None
136 136
137 137 def _dirstatecopies(d):
138 138 ds = d._repo.dirstate
139 139 c = ds.copies().copy()
140 140 for k in c.keys():
141 141 if ds[k] not in 'anm':
142 142 del c[k]
143 143 return c
144 144
145 145 def _computeforwardmissing(a, b, match=None):
146 146 """Computes which files are in b but not a.
147 147 This is its own function so extensions can easily wrap this call to see what
148 148 files _forwardcopies is about to process.
149 149 """
150 150 ma = a.manifest()
151 151 mb = b.manifest()
152 152 if match:
153 153 ma = ma.matches(match)
154 154 mb = mb.matches(match)
155 155 return mb.filesnotin(ma)
156 156
157 157 def _forwardcopies(a, b, match=None):
158 158 '''find {dst@b: src@a} copy mapping where a is an ancestor of b'''
159 159
160 160 # check for working copy
161 161 w = None
162 162 if b.rev() is None:
163 163 w = b
164 164 b = w.p1()
165 165 if a == b:
166 166 # short-circuit to avoid issues with merge states
167 167 return _dirstatecopies(w)
168 168
169 169 # files might have to be traced back to the fctx parent of the last
170 170 # one-side-only changeset, but not further back than that
171 171 limit = _findlimit(a._repo, a.rev(), b.rev())
172 172 if limit is None:
173 173 limit = -1
174 174 am = a.manifest()
175 175
176 176 # find where new files came from
177 177 # we currently don't try to find where old files went, too expensive
178 178 # this means we can miss a case like 'hg rm b; hg cp a b'
179 179 cm = {}
180 180
181 181 # Computing the forward missing is quite expensive on large manifests, since
182 182 # it compares the entire manifests. We can optimize it in the common use
183 183 # case of computing what copies are in a commit versus its parent (like
184 184 # during a rebase or histedit). Note, we exclude merge commits from this
185 185 # optimization, since the ctx.files() for a merge commit is not correct for
186 186 # this comparison.
187 187 forwardmissingmatch = match
188 188 if not match and b.p1() == a and b.p2().node() == node.nullid:
189 189 forwardmissingmatch = scmutil.matchfiles(a._repo, b.files())
190 190 missing = _computeforwardmissing(a, b, match=forwardmissingmatch)
191 191
192 192 ancestrycontext = a._repo.changelog.ancestors([b.rev()], inclusive=True)
193 193 for f in missing:
194 194 fctx = b[f]
195 195 fctx._ancestrycontext = ancestrycontext
196 196 ofctx = _tracefile(fctx, am, limit)
197 197 if ofctx:
198 198 cm[f] = ofctx.path()
199 199
200 200 # combine copies from dirstate if necessary
201 201 if w is not None:
202 202 cm = _chain(a, w, cm, _dirstatecopies(w))
203 203
204 204 return cm
205 205
206 206 def _backwardrenames(a, b):
207 207 if a._repo.ui.configbool('experimental', 'disablecopytrace'):
208 208 return {}
209 209
210 210 # Even though we're not taking copies into account, 1:n rename situations
211 211 # can still exist (e.g. hg cp a b; hg mv a c). In those cases we
212 212 # arbitrarily pick one of the renames.
213 213 f = _forwardcopies(b, a)
214 214 r = {}
215 215 for k, v in sorted(f.iteritems()):
216 216 # remove copies
217 217 if v in a:
218 218 continue
219 219 r[v] = k
220 220 return r
221 221
222 222 def pathcopies(x, y, match=None):
223 223 '''find {dst@y: src@x} copy mapping for directed compare'''
224 224 if x == y or not x or not y:
225 225 return {}
226 226 a = y.ancestor(x)
227 227 if a == x:
228 228 return _forwardcopies(x, y, match=match)
229 229 if a == y:
230 230 return _backwardrenames(x, y)
231 231 return _chain(x, y, _backwardrenames(x, a),
232 232 _forwardcopies(a, y, match=match))
233 233
234 234 def _computenonoverlap(repo, c1, c2, addedinm1, addedinm2, baselabel=''):
235 235 """Computes, based on addedinm1 and addedinm2, the files exclusive to c1
236 236 and c2. This is its own function so extensions can easily wrap this call
237 237 to see what files mergecopies is about to process.
238 238
239 239 Even though c1 and c2 are not used in this function, they are useful in
240 240 other extensions for being able to read the file nodes of the changed files.
241 241
242 242 "baselabel" can be passed to help distinguish the multiple computations
243 243 done in the graft case.
244 244 """
245 245 u1 = sorted(addedinm1 - addedinm2)
246 246 u2 = sorted(addedinm2 - addedinm1)
247 247
248 248 header = " unmatched files in %s"
249 249 if baselabel:
250 250 header += ' (from %s)' % baselabel
251 251 if u1:
252 252 repo.ui.debug("%s:\n %s\n" % (header % 'local', "\n ".join(u1)))
253 253 if u2:
254 254 repo.ui.debug("%s:\n %s\n" % (header % 'other', "\n ".join(u2)))
255 255 return u1, u2
256 256
257 257 def _makegetfctx(ctx):
258 258 """return a 'getfctx' function suitable for _checkcopies usage
259 259
260 260 We have to re-setup the function building 'filectx' for each
261 261 '_checkcopies' to ensure the linkrev adjustment is properly setup for
262 262 each. Linkrev adjustment is important to avoid bug in rename
263 263 detection. Moreover, having a proper '_ancestrycontext' setup ensures
264 264 the performance impact of this adjustment is kept limited. Without it,
265 265 each file could do a full dag traversal making the time complexity of
266 266 the operation explode (see issue4537).
267 267
268 268 This function exists here mostly to limit the impact on stable. Feel
269 269 free to refactor on default.
270 270 """
271 271 rev = ctx.rev()
272 272 repo = ctx._repo
273 273 ac = getattr(ctx, '_ancestrycontext', None)
274 274 if ac is None:
275 275 revs = [rev]
276 276 if rev is None:
277 277 revs = [p.rev() for p in ctx.parents()]
278 278 ac = repo.changelog.ancestors(revs, inclusive=True)
279 279 ctx._ancestrycontext = ac
280 280 def makectx(f, n):
281 281 if len(n) != 20: # in a working context?
282 282 if ctx.rev() is None:
283 283 return ctx.filectx(f)
284 284 return repo[None][f]
285 285 fctx = repo.filectx(f, fileid=n)
286 286 # setup only needed for filectx not create from a changectx
287 287 fctx._ancestrycontext = ac
288 288 fctx._descendantrev = rev
289 289 return fctx
290 290 return util.lrucachefunc(makectx)
291 291
292 292 def mergecopies(repo, c1, c2, base):
293 293 """
294 294 Find moves and copies between context c1 and c2 that are relevant
295 295 for merging. 'base' will be used as the merge base.
296 296
297 297 Returns four dicts: "copy", "movewithdir", "diverge", and
298 298 "renamedelete".
299 299
300 300 "copy" is a mapping from destination name -> source name,
301 301 where source is in c1 and destination is in c2 or vice-versa.
302 302
303 303 "movewithdir" is a mapping from source name -> destination name,
304 304 where the file at source present in one context but not the other
305 305 needs to be moved to destination by the merge process, because the
306 306 other context moved the directory it is in.
307 307
308 308 "diverge" is a mapping of source name -> list of destination names
309 309 for divergent renames.
310 310
311 311 "renamedelete" is a mapping of source name -> list of destination
312 312 names for files deleted in c1 that were renamed in c2 or vice-versa.
313 313 """
314 314 # avoid silly behavior for update from empty dir
315 315 if not c1 or not c2 or c1 == c2:
316 316 return {}, {}, {}, {}
317 317
318 318 # avoid silly behavior for parent -> working dir
319 319 if c2.node() is None and c1.node() == repo.dirstate.p1():
320 320 return repo.dirstate.copies(), {}, {}, {}
321 321
322 322 # Copy trace disabling is explicitly below the node == p1 logic above
323 323 # because the logic above is required for a simple copy to be kept across a
324 324 # rebase.
325 325 if repo.ui.configbool('experimental', 'disablecopytrace'):
326 326 return {}, {}, {}, {}
327 327
328 328 # In certain scenarios (e.g. graft, update or rebase), base can be
329 329 # overridden We still need to know a real common ancestor in this case We
330 330 # can't just compute _c1.ancestor(_c2) and compare it to ca, because there
331 331 # can be multiple common ancestors, e.g. in case of bidmerge. Because our
332 332 # caller may not know if the revision passed in lieu of the CA is a genuine
333 333 # common ancestor or not without explicitly checking it, it's better to
334 334 # determine that here.
335 335 #
336 336 # base.descendant(wc) and base.descendant(base) are False, work around that
337 337 _c1 = c1.p1() if c1.rev() is None else c1
338 338 _c2 = c2.p1() if c2.rev() is None else c2
339 339 # an endpoint is "dirty" if it isn't a descendant of the merge base
340 340 # if we have a dirty endpoint, we need to trigger graft logic, and also
341 341 # keep track of which endpoint is dirty
342 342 dirtyc1 = not (base == _c1 or base.descendant(_c1))
343 343 dirtyc2 = not (base== _c2 or base.descendant(_c2))
344 344 graft = dirtyc1 or dirtyc2
345 345 tca = base
346 346 if graft:
347 347 tca = _c1.ancestor(_c2)
348 348
349 349 limit = _findlimit(repo, c1.rev(), c2.rev())
350 350 if limit is None:
351 351 # no common ancestor, no copies
352 352 return {}, {}, {}, {}
353 353 repo.ui.debug(" searching for copies back to rev %d\n" % limit)
354 354
355 355 m1 = c1.manifest()
356 356 m2 = c2.manifest()
357 357 mb = base.manifest()
358 358
359 359 # gather data from _checkcopies:
360 360 # - diverge = record all diverges in this dict
361 361 # - copy = record all non-divergent copies in this dict
362 362 # - fullcopy = record all copies in this dict
363 363 diverge = {} # divergence data is shared
364 364 data1 = {'copy': {},
365 365 'fullcopy': {},
366 366 'diverge': diverge,
367 367 }
368 368 data2 = {'copy': {},
369 369 'fullcopy': {},
370 370 'diverge': diverge,
371 371 }
372 372
373 373 # find interesting file sets from manifests
374 374 addedinm1 = m1.filesnotin(mb)
375 375 addedinm2 = m2.filesnotin(mb)
376 bothnew = sorted(addedinm1 & addedinm2)
377 if tca == base:
378 # unmatched file from base
376 379 u1r, u2r = _computenonoverlap(repo, c1, c2, addedinm1, addedinm2)
377 380 u1u, u2u = u1r, u2r
378 bothnew = sorted(addedinm1 & addedinm2)
381 else:
382 # unmatched file from base (DAG rotation in the graft case)
383 u1r, u2r = _computenonoverlap(repo, c1, c2, addedinm1, addedinm2,
384 baselabel='base')
385 # unmatched file from topological common ancestors (no DAG rotation)
386 # need to recompute this for directory move handling when grafting
387 mta = tca.manifest()
388 u1u, u2u = _computenonoverlap(repo, c1, c2, m1.filesnotin(mta),
389 m2.filesnotin(mta),
390 baselabel='topological common ancestor')
379 391
380 392 for f in u1u:
381 393 _checkcopies(c1, f, m1, m2, base, tca, limit, data1)
382 394
383 395 for f in u2u:
384 396 _checkcopies(c2, f, m2, m1, base, tca, limit, data2)
385 397
386 398 copy = dict(data1['copy'].items() + data2['copy'].items())
387 399 fullcopy = dict(data1['fullcopy'].items() + data2['fullcopy'].items())
388 400
389 401 renamedelete = {}
390 402 renamedeleteset = set()
391 403 divergeset = set()
392 404 for of, fl in diverge.items():
393 405 if len(fl) == 1 or of in c1 or of in c2:
394 406 del diverge[of] # not actually divergent, or not a rename
395 407 if of not in c1 and of not in c2:
396 408 # renamed on one side, deleted on the other side, but filter
397 409 # out files that have been renamed and then deleted
398 410 renamedelete[of] = [f for f in fl if f in c1 or f in c2]
399 411 renamedeleteset.update(fl) # reverse map for below
400 412 else:
401 413 divergeset.update(fl) # reverse map for below
402 414
403 415 if bothnew:
404 416 repo.ui.debug(" unmatched files new in both:\n %s\n"
405 417 % "\n ".join(bothnew))
406 418 bothdiverge = {}
407 419 bothdata = {'copy': {},
408 420 'fullcopy': {},
409 421 'diverge': bothdiverge,
410 422 }
411 423 for f in bothnew:
412 424 _checkcopies(c1, f, m1, m2, base, tca, limit, bothdata)
413 425 _checkcopies(c2, f, m2, m1, base, tca, limit, bothdata)
414 426 for of, fl in bothdiverge.items():
415 427 if len(fl) == 2 and fl[0] == fl[1]:
416 428 copy[fl[0]] = of # not actually divergent, just matching renames
417 429
418 430 if fullcopy and repo.ui.debugflag:
419 431 repo.ui.debug(" all copies found (* = to merge, ! = divergent, "
420 432 "% = renamed and deleted):\n")
421 433 for f in sorted(fullcopy):
422 434 note = ""
423 435 if f in copy:
424 436 note += "*"
425 437 if f in divergeset:
426 438 note += "!"
427 439 if f in renamedeleteset:
428 440 note += "%"
429 441 repo.ui.debug(" src: '%s' -> dst: '%s' %s\n" % (fullcopy[f], f,
430 442 note))
431 443 del divergeset
432 444
433 445 if not fullcopy:
434 446 return copy, {}, diverge, renamedelete
435 447
436 448 repo.ui.debug(" checking for directory renames\n")
437 449
438 450 # generate a directory move map
439 451 d1, d2 = c1.dirs(), c2.dirs()
440 452 # Hack for adding '', which is not otherwise added, to d1 and d2
441 453 d1.addpath('/')
442 454 d2.addpath('/')
443 455 invalid = set()
444 456 dirmove = {}
445 457
446 458 # examine each file copy for a potential directory move, which is
447 459 # when all the files in a directory are moved to a new directory
448 460 for dst, src in fullcopy.iteritems():
449 461 dsrc, ddst = pathutil.dirname(src), pathutil.dirname(dst)
450 462 if dsrc in invalid:
451 463 # already seen to be uninteresting
452 464 continue
453 465 elif dsrc in d1 and ddst in d1:
454 466 # directory wasn't entirely moved locally
455 467 invalid.add(dsrc + "/")
456 468 elif dsrc in d2 and ddst in d2:
457 469 # directory wasn't entirely moved remotely
458 470 invalid.add(dsrc + "/")
459 471 elif dsrc + "/" in dirmove and dirmove[dsrc + "/"] != ddst + "/":
460 472 # files from the same directory moved to two different places
461 473 invalid.add(dsrc + "/")
462 474 else:
463 475 # looks good so far
464 476 dirmove[dsrc + "/"] = ddst + "/"
465 477
466 478 for i in invalid:
467 479 if i in dirmove:
468 480 del dirmove[i]
469 481 del d1, d2, invalid
470 482
471 483 if not dirmove:
472 484 return copy, {}, diverge, renamedelete
473 485
474 486 for d in dirmove:
475 487 repo.ui.debug(" discovered dir src: '%s' -> dst: '%s'\n" %
476 488 (d, dirmove[d]))
477 489
478 490 movewithdir = {}
479 491 # check unaccounted nonoverlapping files against directory moves
480 492 for f in u1r + u2r:
481 493 if f not in fullcopy:
482 494 for d in dirmove:
483 495 if f.startswith(d):
484 496 # new file added in a directory that was moved, move it
485 497 df = dirmove[d] + f[len(d):]
486 498 if df not in copy:
487 499 movewithdir[f] = df
488 500 repo.ui.debug((" pending file src: '%s' -> "
489 501 "dst: '%s'\n") % (f, df))
490 502 break
491 503
492 504 return copy, movewithdir, diverge, renamedelete
493 505
494 506 def _related(f1, f2, limit):
495 507 """return True if f1 and f2 filectx have a common ancestor
496 508
497 509 Walk back to common ancestor to see if the two files originate
498 510 from the same file. Since workingfilectx's rev() is None it messes
499 511 up the integer comparison logic, hence the pre-step check for
500 512 None (f1 and f2 can only be workingfilectx's initially).
501 513 """
502 514
503 515 if f1 == f2:
504 516 return f1 # a match
505 517
506 518 g1, g2 = f1.ancestors(), f2.ancestors()
507 519 try:
508 520 f1r, f2r = f1.linkrev(), f2.linkrev()
509 521
510 522 if f1r is None:
511 523 f1 = next(g1)
512 524 if f2r is None:
513 525 f2 = next(g2)
514 526
515 527 while True:
516 528 f1r, f2r = f1.linkrev(), f2.linkrev()
517 529 if f1r > f2r:
518 530 f1 = next(g1)
519 531 elif f2r > f1r:
520 532 f2 = next(g2)
521 533 elif f1 == f2:
522 534 return f1 # a match
523 535 elif f1r == f2r or f1r < limit or f2r < limit:
524 536 return False # copy no longer relevant
525 537 except StopIteration:
526 538 return False
527 539
528 540 def _checkcopies(ctx, f, m1, m2, base, tca, limit, data):
529 541 """
530 542 check possible copies of f from m1 to m2
531 543
532 544 ctx = starting context for f in m1
533 545 f = the filename to check (as in m1)
534 546 m1 = the source manifest
535 547 m2 = the destination manifest
536 548 base = the changectx used as a merge base
537 549 tca = topological common ancestor for graft-like scenarios
538 550 limit = the rev number to not search beyond
539 551 data = dictionary of dictionary to store copy data. (see mergecopies)
540 552
541 553 note: limit is only an optimization, and there is no guarantee that
542 554 irrelevant revisions will not be limited
543 555 there is no easy way to make this algorithm stop in a guaranteed way
544 556 once it "goes behind a certain revision".
545 557 """
546 558
547 559 mb = base.manifest()
548 560 # Might be true if this call is about finding backward renames,
549 561 # This happens in the case of grafts because the DAG is then rotated.
550 562 # If the file exists in both the base and the source, we are not looking
551 563 # for a rename on the source side, but on the part of the DAG that is
552 564 # traversed backwards.
553 565 #
554 566 # In the case there is both backward and forward renames (before and after
555 567 # the base) this is more complicated as we must detect a divergence. This
556 568 # is currently broken and hopefully some later code update will make that
557 569 # work (we use 'backwards = False' in that case)
558 570 backwards = base != tca and f in mb
559 571 getfctx = _makegetfctx(ctx)
560 572
561 573 of = None
562 574 seen = set([f])
563 575 for oc in getfctx(f, m1[f]).ancestors():
564 576 ocr = oc.linkrev()
565 577 of = oc.path()
566 578 if of in seen:
567 579 # check limit late - grab last rename before
568 580 if ocr < limit:
569 581 break
570 582 continue
571 583 seen.add(of)
572 584
573 585 # remember for dir rename detection
574 586 if backwards:
575 587 data['fullcopy'][of] = f # grafting backwards through renames
576 588 else:
577 589 data['fullcopy'][f] = of
578 590 if of not in m2:
579 591 continue # no match, keep looking
580 592 if m2[of] == mb.get(of):
581 593 return # no merge needed, quit early
582 594 c2 = getfctx(of, m2[of])
583 595 # c2 might be a plain new file on added on destination side that is
584 596 # unrelated to the droids we are looking for.
585 597 cr = _related(oc, c2, tca.rev())
586 598 if cr and (of == f or of == c2.path()): # non-divergent
587 599 if backwards:
588 600 data['copy'][of] = f
589 601 elif of in mb:
590 602 data['copy'][f] = of
591 603 return
592 604
593 605 if of in mb:
594 606 data['diverge'].setdefault(of, []).append(f)
595 607
596 608 def duplicatecopies(repo, rev, fromrev, skiprev=None):
597 609 '''reproduce copies from fromrev to rev in the dirstate
598 610
599 611 If skiprev is specified, it's a revision that should be used to
600 612 filter copy records. Any copies that occur between fromrev and
601 613 skiprev will not be duplicated, even if they appear in the set of
602 614 copies between fromrev and rev.
603 615 '''
604 616 exclude = {}
605 617 if (skiprev is not None and
606 618 not repo.ui.configbool('experimental', 'disablecopytrace')):
607 619 # disablecopytrace skips this line, but not the entire function because
608 620 # the line below is O(size of the repo) during a rebase, while the rest
609 621 # of the function is much faster (and is required for carrying copy
610 622 # metadata across the rebase anyway).
611 623 exclude = pathcopies(repo[fromrev], repo[skiprev])
612 624 for dst, src in pathcopies(repo[fromrev], repo[rev]).iteritems():
613 625 # copies.pathcopies returns backward renames, so dst might not
614 626 # actually be in the dirstate
615 627 if dst in exclude:
616 628 continue
617 629 if repo.dirstate[dst] in "nma":
618 630 repo.dirstate.copy(src, dst)
@@ -1,1302 +1,1333
1 1 $ cat >> $HGRCPATH <<EOF
2 2 > [extdiff]
3 3 > # for portability:
4 4 > pdiff = sh "$RUNTESTDIR/pdiff"
5 5 > EOF
6 6
7 7 Create a repo with some stuff in it:
8 8
9 9 $ hg init a
10 10 $ cd a
11 11 $ echo a > a
12 12 $ echo a > d
13 13 $ echo a > e
14 14 $ hg ci -qAm0
15 15 $ echo b > a
16 16 $ hg ci -m1 -u bar
17 17 $ hg mv a b
18 18 $ hg ci -m2
19 19 $ hg cp b c
20 20 $ hg ci -m3 -u baz
21 21 $ echo b > d
22 22 $ echo f > e
23 23 $ hg ci -m4
24 24 $ hg up -q 3
25 25 $ echo b > e
26 26 $ hg branch -q stable
27 27 $ hg ci -m5
28 28 $ hg merge -q default --tool internal:local
29 29 $ hg branch -q default
30 30 $ hg ci -m6
31 31 $ hg phase --public 3
32 32 $ hg phase --force --secret 6
33 33
34 34 $ hg log -G --template '{author}@{rev}.{phase}: {desc}\n'
35 35 @ test@6.secret: 6
36 36 |\
37 37 | o test@5.draft: 5
38 38 | |
39 39 o | test@4.draft: 4
40 40 |/
41 41 o baz@3.public: 3
42 42 |
43 43 o test@2.public: 2
44 44 |
45 45 o bar@1.public: 1
46 46 |
47 47 o test@0.public: 0
48 48
49 49 Can't continue without starting:
50 50
51 51 $ hg rm -q e
52 52 $ hg graft --continue
53 53 abort: no graft in progress
54 54 [255]
55 55 $ hg revert -r . -q e
56 56
57 57 Need to specify a rev:
58 58
59 59 $ hg graft
60 60 abort: no revisions specified
61 61 [255]
62 62
63 63 Can't graft ancestor:
64 64
65 65 $ hg graft 1 2
66 66 skipping ancestor revision 1:5d205f8b35b6
67 67 skipping ancestor revision 2:5c095ad7e90f
68 68 [255]
69 69
70 70 Specify revisions with -r:
71 71
72 72 $ hg graft -r 1 -r 2
73 73 skipping ancestor revision 1:5d205f8b35b6
74 74 skipping ancestor revision 2:5c095ad7e90f
75 75 [255]
76 76
77 77 $ hg graft -r 1 2
78 78 warning: inconsistent use of --rev might give unexpected revision ordering!
79 79 skipping ancestor revision 2:5c095ad7e90f
80 80 skipping ancestor revision 1:5d205f8b35b6
81 81 [255]
82 82
83 83 Can't graft with dirty wd:
84 84
85 85 $ hg up -q 0
86 86 $ echo foo > a
87 87 $ hg graft 1
88 88 abort: uncommitted changes
89 89 [255]
90 90 $ hg revert a
91 91
92 92 Graft a rename:
93 93 (this also tests that editor is invoked if '--edit' is specified)
94 94
95 95 $ hg status --rev "2^1" --rev 2
96 96 A b
97 97 R a
98 98 $ HGEDITOR=cat hg graft 2 -u foo --edit
99 99 grafting 2:5c095ad7e90f "2"
100 100 merging a and b to b
101 101 2
102 102
103 103
104 104 HG: Enter commit message. Lines beginning with 'HG:' are removed.
105 105 HG: Leave message empty to abort commit.
106 106 HG: --
107 107 HG: user: foo
108 108 HG: branch 'default'
109 109 HG: added b
110 110 HG: removed a
111 111 $ hg export tip --git
112 112 # HG changeset patch
113 113 # User foo
114 114 # Date 0 0
115 115 # Thu Jan 01 00:00:00 1970 +0000
116 116 # Node ID ef0ef43d49e79e81ddafdc7997401ba0041efc82
117 117 # Parent 68795b066622ca79a25816a662041d8f78f3cd9e
118 118 2
119 119
120 120 diff --git a/a b/b
121 121 rename from a
122 122 rename to b
123 123
124 124 Look for extra:source
125 125
126 126 $ hg log --debug -r tip
127 127 changeset: 7:ef0ef43d49e79e81ddafdc7997401ba0041efc82
128 128 tag: tip
129 129 phase: draft
130 130 parent: 0:68795b066622ca79a25816a662041d8f78f3cd9e
131 131 parent: -1:0000000000000000000000000000000000000000
132 132 manifest: 7:e59b6b228f9cbf9903d5e9abf996e083a1f533eb
133 133 user: foo
134 134 date: Thu Jan 01 00:00:00 1970 +0000
135 135 files+: b
136 136 files-: a
137 137 extra: branch=default
138 138 extra: source=5c095ad7e90f871700f02dd1fa5012cb4498a2d4
139 139 description:
140 140 2
141 141
142 142
143 143
144 144 Graft out of order, skipping a merge and a duplicate
145 145 (this also tests that editor is not invoked if '--edit' is not specified)
146 146
147 147 $ hg graft 1 5 4 3 'merge()' 2 -n
148 148 skipping ungraftable merge revision 6
149 149 skipping revision 2:5c095ad7e90f (already grafted to 7:ef0ef43d49e7)
150 150 grafting 1:5d205f8b35b6 "1"
151 151 grafting 5:97f8bfe72746 "5"
152 152 grafting 4:9c233e8e184d "4"
153 153 grafting 3:4c60f11aa304 "3"
154 154
155 155 $ HGEDITOR=cat hg graft 1 5 'merge()' 2 --debug
156 156 skipping ungraftable merge revision 6
157 157 scanning for duplicate grafts
158 158 skipping revision 2:5c095ad7e90f (already grafted to 7:ef0ef43d49e7)
159 159 grafting 1:5d205f8b35b6 "1"
160 160 searching for copies back to rev 1
161 161 unmatched files in local:
162 162 b
163 163 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
164 164 src: 'a' -> dst: 'b' *
165 165 checking for directory renames
166 166 resolving manifests
167 167 branchmerge: True, force: True, partial: False
168 168 ancestor: 68795b066622, local: ef0ef43d49e7+, remote: 5d205f8b35b6
169 169 preserving b for resolve of b
170 170 starting 4 threads for background file closing (?)
171 171 b: local copied/moved from a -> m (premerge)
172 172 picked tool ':merge' for b (binary False symlink False changedelete False)
173 173 merging b and a to b
174 174 my b@ef0ef43d49e7+ other a@5d205f8b35b6 ancestor a@68795b066622
175 175 premerge successful
176 176 committing files:
177 177 b
178 178 committing manifest
179 179 committing changelog
180 180 grafting 5:97f8bfe72746 "5"
181 181 searching for copies back to rev 1
182 unmatched files in other (from topological common ancestor):
183 c
184 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
185 src: 'c' -> dst: 'b' *
186 checking for directory renames
182 187 resolving manifests
183 188 branchmerge: True, force: True, partial: False
184 189 ancestor: 4c60f11aa304, local: 6b9e5368ca4e+, remote: 97f8bfe72746
185 190 e: remote is newer -> g
186 191 getting e
187 192 b: remote unchanged -> k
188 193 committing files:
189 194 e
190 195 committing manifest
191 196 committing changelog
192 197 $ HGEDITOR=cat hg graft 4 3 --log --debug
193 198 scanning for duplicate grafts
194 199 grafting 4:9c233e8e184d "4"
195 200 searching for copies back to rev 1
201 unmatched files in other (from topological common ancestor):
202 c
203 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
204 src: 'c' -> dst: 'b' *
205 checking for directory renames
196 206 resolving manifests
197 207 branchmerge: True, force: True, partial: False
198 208 ancestor: 4c60f11aa304, local: 1905859650ec+, remote: 9c233e8e184d
199 209 preserving e for resolve of e
200 210 d: remote is newer -> g
201 211 getting d
202 212 b: remote unchanged -> k
203 213 e: versions differ -> m (premerge)
204 214 picked tool ':merge' for e (binary False symlink False changedelete False)
205 215 merging e
206 216 my e@1905859650ec+ other e@9c233e8e184d ancestor e@4c60f11aa304
207 217 e: versions differ -> m (merge)
208 218 picked tool ':merge' for e (binary False symlink False changedelete False)
209 219 my e@1905859650ec+ other e@9c233e8e184d ancestor e@4c60f11aa304
210 220 warning: conflicts while merging e! (edit, then use 'hg resolve --mark')
211 221 abort: unresolved conflicts, can't continue
212 222 (use 'hg resolve' and 'hg graft --continue --log')
213 223 [255]
214 224
215 225 Summary should mention graft:
216 226
217 227 $ hg summary |grep graft
218 228 commit: 2 modified, 2 unknown, 1 unresolved (graft in progress)
219 229
220 230 Commit while interrupted should fail:
221 231
222 232 $ hg ci -m 'commit interrupted graft'
223 233 abort: graft in progress
224 234 (use 'hg graft --continue' or 'hg update' to abort)
225 235 [255]
226 236
227 237 Abort the graft and try committing:
228 238
229 239 $ hg up -C .
230 240 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
231 241 $ echo c >> e
232 242 $ hg ci -mtest
233 243
234 244 $ hg strip . --config extensions.strip=
235 245 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
236 246 saved backup bundle to $TESTTMP/a/.hg/strip-backup/*-backup.hg (glob)
237 247
238 248 Graft again:
239 249
240 250 $ hg graft 1 5 4 3 'merge()' 2
241 251 skipping ungraftable merge revision 6
242 252 skipping revision 2:5c095ad7e90f (already grafted to 7:ef0ef43d49e7)
243 253 skipping revision 1:5d205f8b35b6 (already grafted to 8:6b9e5368ca4e)
244 254 skipping revision 5:97f8bfe72746 (already grafted to 9:1905859650ec)
245 255 grafting 4:9c233e8e184d "4"
246 256 merging e
247 257 warning: conflicts while merging e! (edit, then use 'hg resolve --mark')
248 258 abort: unresolved conflicts, can't continue
249 259 (use 'hg resolve' and 'hg graft --continue')
250 260 [255]
251 261
252 262 Continue without resolve should fail:
253 263
254 264 $ hg graft -c
255 265 grafting 4:9c233e8e184d "4"
256 266 abort: unresolved merge conflicts (see 'hg help resolve')
257 267 [255]
258 268
259 269 Fix up:
260 270
261 271 $ echo b > e
262 272 $ hg resolve -m e
263 273 (no more unresolved files)
264 274 continue: hg graft --continue
265 275
266 276 Continue with a revision should fail:
267 277
268 278 $ hg graft -c 6
269 279 abort: can't specify --continue and revisions
270 280 [255]
271 281
272 282 $ hg graft -c -r 6
273 283 abort: can't specify --continue and revisions
274 284 [255]
275 285
276 286 Continue for real, clobber usernames
277 287
278 288 $ hg graft -c -U
279 289 grafting 4:9c233e8e184d "4"
280 290 grafting 3:4c60f11aa304 "3"
281 291
282 292 Compare with original:
283 293
284 294 $ hg diff -r 6
285 295 $ hg status --rev 0:. -C
286 296 M d
287 297 M e
288 298 A b
289 299 a
290 300 A c
291 301 a
292 302 R a
293 303
294 304 View graph:
295 305
296 306 $ hg log -G --template '{author}@{rev}.{phase}: {desc}\n'
297 307 @ test@11.draft: 3
298 308 |
299 309 o test@10.draft: 4
300 310 |
301 311 o test@9.draft: 5
302 312 |
303 313 o bar@8.draft: 1
304 314 |
305 315 o foo@7.draft: 2
306 316 |
307 317 | o test@6.secret: 6
308 318 | |\
309 319 | | o test@5.draft: 5
310 320 | | |
311 321 | o | test@4.draft: 4
312 322 | |/
313 323 | o baz@3.public: 3
314 324 | |
315 325 | o test@2.public: 2
316 326 | |
317 327 | o bar@1.public: 1
318 328 |/
319 329 o test@0.public: 0
320 330
321 331 Graft again onto another branch should preserve the original source
322 332 $ hg up -q 0
323 333 $ echo 'g'>g
324 334 $ hg add g
325 335 $ hg ci -m 7
326 336 created new head
327 337 $ hg graft 7
328 338 grafting 7:ef0ef43d49e7 "2"
329 339
330 340 $ hg log -r 7 --template '{rev}:{node}\n'
331 341 7:ef0ef43d49e79e81ddafdc7997401ba0041efc82
332 342 $ hg log -r 2 --template '{rev}:{node}\n'
333 343 2:5c095ad7e90f871700f02dd1fa5012cb4498a2d4
334 344
335 345 $ hg log --debug -r tip
336 346 changeset: 13:7a4785234d87ec1aa420ed6b11afe40fa73e12a9
337 347 tag: tip
338 348 phase: draft
339 349 parent: 12:b592ea63bb0c19a6c5c44685ee29a2284f9f1b8f
340 350 parent: -1:0000000000000000000000000000000000000000
341 351 manifest: 13:dc313617b8c32457c0d589e0dbbedfe71f3cd637
342 352 user: foo
343 353 date: Thu Jan 01 00:00:00 1970 +0000
344 354 files+: b
345 355 files-: a
346 356 extra: branch=default
347 357 extra: intermediate-source=ef0ef43d49e79e81ddafdc7997401ba0041efc82
348 358 extra: source=5c095ad7e90f871700f02dd1fa5012cb4498a2d4
349 359 description:
350 360 2
351 361
352 362
353 363 Disallow grafting an already grafted cset onto its original branch
354 364 $ hg up -q 6
355 365 $ hg graft 7
356 366 skipping already grafted revision 7:ef0ef43d49e7 (was grafted from 2:5c095ad7e90f)
357 367 [255]
358 368
359 369 $ hg pdiff --config extensions.extdiff= --patch -r 2 -r 13
360 370 --- */hg-5c095ad7e90f.patch * (glob)
361 371 +++ */hg-7a4785234d87.patch * (glob)
362 372 @@ -1,18 +1,18 @@
363 373 # HG changeset patch
364 374 -# User test
365 375 +# User foo
366 376 # Date 0 0
367 377 # Thu Jan 01 00:00:00 1970 +0000
368 378 -# Node ID 5c095ad7e90f871700f02dd1fa5012cb4498a2d4
369 379 -# Parent 5d205f8b35b66bc36375c9534ffd3237730e8f04
370 380 +# Node ID 7a4785234d87ec1aa420ed6b11afe40fa73e12a9
371 381 +# Parent b592ea63bb0c19a6c5c44685ee29a2284f9f1b8f
372 382 2
373 383
374 384 -diff -r 5d205f8b35b6 -r 5c095ad7e90f a
375 385 +diff -r b592ea63bb0c -r 7a4785234d87 a
376 386 --- a/a Thu Jan 01 00:00:00 1970 +0000
377 387 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
378 388 @@ -1,1 +0,0 @@
379 389 --b
380 390 -diff -r 5d205f8b35b6 -r 5c095ad7e90f b
381 391 +-a
382 392 +diff -r b592ea63bb0c -r 7a4785234d87 b
383 393 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
384 394 +++ b/b Thu Jan 01 00:00:00 1970 +0000
385 395 @@ -0,0 +1,1 @@
386 396 -+b
387 397 ++a
388 398 [1]
389 399
390 400 $ hg pdiff --config extensions.extdiff= --patch -r 2 -r 13 -X .
391 401 --- */hg-5c095ad7e90f.patch * (glob)
392 402 +++ */hg-7a4785234d87.patch * (glob)
393 403 @@ -1,8 +1,8 @@
394 404 # HG changeset patch
395 405 -# User test
396 406 +# User foo
397 407 # Date 0 0
398 408 # Thu Jan 01 00:00:00 1970 +0000
399 409 -# Node ID 5c095ad7e90f871700f02dd1fa5012cb4498a2d4
400 410 -# Parent 5d205f8b35b66bc36375c9534ffd3237730e8f04
401 411 +# Node ID 7a4785234d87ec1aa420ed6b11afe40fa73e12a9
402 412 +# Parent b592ea63bb0c19a6c5c44685ee29a2284f9f1b8f
403 413 2
404 414
405 415 [1]
406 416
407 417 Disallow grafting already grafted csets with the same origin onto each other
408 418 $ hg up -q 13
409 419 $ hg graft 2
410 420 skipping revision 2:5c095ad7e90f (already grafted to 13:7a4785234d87)
411 421 [255]
412 422 $ hg graft 7
413 423 skipping already grafted revision 7:ef0ef43d49e7 (13:7a4785234d87 also has origin 2:5c095ad7e90f)
414 424 [255]
415 425
416 426 $ hg up -q 7
417 427 $ hg graft 2
418 428 skipping revision 2:5c095ad7e90f (already grafted to 7:ef0ef43d49e7)
419 429 [255]
420 430 $ hg graft tip
421 431 skipping already grafted revision 13:7a4785234d87 (7:ef0ef43d49e7 also has origin 2:5c095ad7e90f)
422 432 [255]
423 433
424 434 Graft with --log
425 435
426 436 $ hg up -Cq 1
427 437 $ hg graft 3 --log -u foo
428 438 grafting 3:4c60f11aa304 "3"
429 439 warning: can't find ancestor for 'c' copied from 'b'!
430 440 $ hg log --template '{rev}:{node|short} {parents} {desc}\n' -r tip
431 441 14:0c921c65ef1e 1:5d205f8b35b6 3
432 442 (grafted from 4c60f11aa304a54ae1c199feb94e7fc771e51ed8)
433 443
434 444 Resolve conflicted graft
435 445 $ hg up -q 0
436 446 $ echo b > a
437 447 $ hg ci -m 8
438 448 created new head
439 449 $ echo c > a
440 450 $ hg ci -m 9
441 451 $ hg graft 1 --tool internal:fail
442 452 grafting 1:5d205f8b35b6 "1"
443 453 abort: unresolved conflicts, can't continue
444 454 (use 'hg resolve' and 'hg graft --continue')
445 455 [255]
446 456 $ hg resolve --all
447 457 merging a
448 458 warning: conflicts while merging a! (edit, then use 'hg resolve --mark')
449 459 [1]
450 460 $ cat a
451 461 <<<<<<< local: aaa4406d4f0a - test: 9
452 462 c
453 463 =======
454 464 b
455 465 >>>>>>> graft: 5d205f8b35b6 - bar: 1
456 466 $ echo b > a
457 467 $ hg resolve -m a
458 468 (no more unresolved files)
459 469 continue: hg graft --continue
460 470 $ hg graft -c
461 471 grafting 1:5d205f8b35b6 "1"
462 472 $ hg export tip --git
463 473 # HG changeset patch
464 474 # User bar
465 475 # Date 0 0
466 476 # Thu Jan 01 00:00:00 1970 +0000
467 477 # Node ID f67661df0c4804d301f064f332b57e7d5ddaf2be
468 478 # Parent aaa4406d4f0ae9befd6e58c82ec63706460cbca6
469 479 1
470 480
471 481 diff --git a/a b/a
472 482 --- a/a
473 483 +++ b/a
474 484 @@ -1,1 +1,1 @@
475 485 -c
476 486 +b
477 487
478 488 Resolve conflicted graft with rename
479 489 $ echo c > a
480 490 $ hg ci -m 10
481 491 $ hg graft 2 --tool internal:fail
482 492 grafting 2:5c095ad7e90f "2"
483 493 abort: unresolved conflicts, can't continue
484 494 (use 'hg resolve' and 'hg graft --continue')
485 495 [255]
486 496 $ hg resolve --all
487 497 merging a and b to b
488 498 (no more unresolved files)
489 499 continue: hg graft --continue
490 500 $ hg graft -c
491 501 grafting 2:5c095ad7e90f "2"
492 502 $ hg export tip --git
493 503 # HG changeset patch
494 504 # User test
495 505 # Date 0 0
496 506 # Thu Jan 01 00:00:00 1970 +0000
497 507 # Node ID 9627f653b421c61fc1ea4c4e366745070fa3d2bc
498 508 # Parent ee295f490a40b97f3d18dd4c4f1c8936c233b612
499 509 2
500 510
501 511 diff --git a/a b/b
502 512 rename from a
503 513 rename to b
504 514
505 515 Test simple origin(), with and without args
506 516 $ hg log -r 'origin()'
507 517 changeset: 1:5d205f8b35b6
508 518 user: bar
509 519 date: Thu Jan 01 00:00:00 1970 +0000
510 520 summary: 1
511 521
512 522 changeset: 2:5c095ad7e90f
513 523 user: test
514 524 date: Thu Jan 01 00:00:00 1970 +0000
515 525 summary: 2
516 526
517 527 changeset: 3:4c60f11aa304
518 528 user: baz
519 529 date: Thu Jan 01 00:00:00 1970 +0000
520 530 summary: 3
521 531
522 532 changeset: 4:9c233e8e184d
523 533 user: test
524 534 date: Thu Jan 01 00:00:00 1970 +0000
525 535 summary: 4
526 536
527 537 changeset: 5:97f8bfe72746
528 538 branch: stable
529 539 parent: 3:4c60f11aa304
530 540 user: test
531 541 date: Thu Jan 01 00:00:00 1970 +0000
532 542 summary: 5
533 543
534 544 $ hg log -r 'origin(7)'
535 545 changeset: 2:5c095ad7e90f
536 546 user: test
537 547 date: Thu Jan 01 00:00:00 1970 +0000
538 548 summary: 2
539 549
540 550 Now transplant a graft to test following through copies
541 551 $ hg up -q 0
542 552 $ hg branch -q dev
543 553 $ hg ci -qm "dev branch"
544 554 $ hg --config extensions.transplant= transplant -q 7
545 555 $ hg log -r 'origin(.)'
546 556 changeset: 2:5c095ad7e90f
547 557 user: test
548 558 date: Thu Jan 01 00:00:00 1970 +0000
549 559 summary: 2
550 560
551 561 Test that the graft and transplant markers in extra are converted, allowing
552 562 origin() to still work. Note that these recheck the immediately preceeding two
553 563 tests.
554 564 $ hg --quiet --config extensions.convert= --config convert.hg.saverev=True convert . ../converted
555 565
556 566 The graft case
557 567 $ hg -R ../converted log -r 7 --template "{rev}: {node}\n{join(extras, '\n')}\n"
558 568 7: 7ae846e9111fc8f57745634250c7b9ac0a60689b
559 569 branch=default
560 570 convert_revision=ef0ef43d49e79e81ddafdc7997401ba0041efc82
561 571 source=e0213322b2c1a5d5d236c74e79666441bee67a7d
562 572 $ hg -R ../converted log -r 'origin(7)'
563 573 changeset: 2:e0213322b2c1
564 574 user: test
565 575 date: Thu Jan 01 00:00:00 1970 +0000
566 576 summary: 2
567 577
568 578 Test that template correctly expands more than one 'extra' (issue4362), and that
569 579 'intermediate-source' is converted.
570 580 $ hg -R ../converted log -r 13 --template "{extras % ' Extra: {extra}\n'}"
571 581 Extra: branch=default
572 582 Extra: convert_revision=7a4785234d87ec1aa420ed6b11afe40fa73e12a9
573 583 Extra: intermediate-source=7ae846e9111fc8f57745634250c7b9ac0a60689b
574 584 Extra: source=e0213322b2c1a5d5d236c74e79666441bee67a7d
575 585
576 586 The transplant case
577 587 $ hg -R ../converted log -r tip --template "{rev}: {node}\n{join(extras, '\n')}\n"
578 588 21: fbb6c5cc81002f2b4b49c9d731404688bcae5ade
579 589 branch=dev
580 590 convert_revision=7e61b508e709a11d28194a5359bc3532d910af21
581 591 transplant_source=z\xe8F\xe9\x11\x1f\xc8\xf5wEcBP\xc7\xb9\xac (esc)
582 592 `h\x9b (esc)
583 593 $ hg -R ../converted log -r 'origin(tip)'
584 594 changeset: 2:e0213322b2c1
585 595 user: test
586 596 date: Thu Jan 01 00:00:00 1970 +0000
587 597 summary: 2
588 598
589 599
590 600 Test simple destination
591 601 $ hg log -r 'destination()'
592 602 changeset: 7:ef0ef43d49e7
593 603 parent: 0:68795b066622
594 604 user: foo
595 605 date: Thu Jan 01 00:00:00 1970 +0000
596 606 summary: 2
597 607
598 608 changeset: 8:6b9e5368ca4e
599 609 user: bar
600 610 date: Thu Jan 01 00:00:00 1970 +0000
601 611 summary: 1
602 612
603 613 changeset: 9:1905859650ec
604 614 user: test
605 615 date: Thu Jan 01 00:00:00 1970 +0000
606 616 summary: 5
607 617
608 618 changeset: 10:52dc0b4c6907
609 619 user: test
610 620 date: Thu Jan 01 00:00:00 1970 +0000
611 621 summary: 4
612 622
613 623 changeset: 11:882b35362a6b
614 624 user: test
615 625 date: Thu Jan 01 00:00:00 1970 +0000
616 626 summary: 3
617 627
618 628 changeset: 13:7a4785234d87
619 629 user: foo
620 630 date: Thu Jan 01 00:00:00 1970 +0000
621 631 summary: 2
622 632
623 633 changeset: 14:0c921c65ef1e
624 634 parent: 1:5d205f8b35b6
625 635 user: foo
626 636 date: Thu Jan 01 00:00:00 1970 +0000
627 637 summary: 3
628 638
629 639 changeset: 17:f67661df0c48
630 640 user: bar
631 641 date: Thu Jan 01 00:00:00 1970 +0000
632 642 summary: 1
633 643
634 644 changeset: 19:9627f653b421
635 645 user: test
636 646 date: Thu Jan 01 00:00:00 1970 +0000
637 647 summary: 2
638 648
639 649 changeset: 21:7e61b508e709
640 650 branch: dev
641 651 tag: tip
642 652 user: foo
643 653 date: Thu Jan 01 00:00:00 1970 +0000
644 654 summary: 2
645 655
646 656 $ hg log -r 'destination(2)'
647 657 changeset: 7:ef0ef43d49e7
648 658 parent: 0:68795b066622
649 659 user: foo
650 660 date: Thu Jan 01 00:00:00 1970 +0000
651 661 summary: 2
652 662
653 663 changeset: 13:7a4785234d87
654 664 user: foo
655 665 date: Thu Jan 01 00:00:00 1970 +0000
656 666 summary: 2
657 667
658 668 changeset: 19:9627f653b421
659 669 user: test
660 670 date: Thu Jan 01 00:00:00 1970 +0000
661 671 summary: 2
662 672
663 673 changeset: 21:7e61b508e709
664 674 branch: dev
665 675 tag: tip
666 676 user: foo
667 677 date: Thu Jan 01 00:00:00 1970 +0000
668 678 summary: 2
669 679
670 680 Transplants of grafts can find a destination...
671 681 $ hg log -r 'destination(7)'
672 682 changeset: 21:7e61b508e709
673 683 branch: dev
674 684 tag: tip
675 685 user: foo
676 686 date: Thu Jan 01 00:00:00 1970 +0000
677 687 summary: 2
678 688
679 689 ... grafts of grafts unfortunately can't
680 690 $ hg graft -q 13
681 691 warning: can't find ancestor for 'b' copied from 'a'!
682 692 $ hg log -r 'destination(13)'
683 693 All copies of a cset
684 694 $ hg log -r 'origin(13) or destination(origin(13))'
685 695 changeset: 2:5c095ad7e90f
686 696 user: test
687 697 date: Thu Jan 01 00:00:00 1970 +0000
688 698 summary: 2
689 699
690 700 changeset: 7:ef0ef43d49e7
691 701 parent: 0:68795b066622
692 702 user: foo
693 703 date: Thu Jan 01 00:00:00 1970 +0000
694 704 summary: 2
695 705
696 706 changeset: 13:7a4785234d87
697 707 user: foo
698 708 date: Thu Jan 01 00:00:00 1970 +0000
699 709 summary: 2
700 710
701 711 changeset: 19:9627f653b421
702 712 user: test
703 713 date: Thu Jan 01 00:00:00 1970 +0000
704 714 summary: 2
705 715
706 716 changeset: 21:7e61b508e709
707 717 branch: dev
708 718 user: foo
709 719 date: Thu Jan 01 00:00:00 1970 +0000
710 720 summary: 2
711 721
712 722 changeset: 22:d1cb6591fa4b
713 723 branch: dev
714 724 tag: tip
715 725 user: foo
716 726 date: Thu Jan 01 00:00:00 1970 +0000
717 727 summary: 2
718 728
719 729
720 730 graft works on complex revset
721 731
722 732 $ hg graft 'origin(13) or destination(origin(13))'
723 733 skipping ancestor revision 21:7e61b508e709
724 734 skipping ancestor revision 22:d1cb6591fa4b
725 735 skipping revision 2:5c095ad7e90f (already grafted to 22:d1cb6591fa4b)
726 736 grafting 7:ef0ef43d49e7 "2"
727 737 warning: can't find ancestor for 'b' copied from 'a'!
728 738 grafting 13:7a4785234d87 "2"
729 739 warning: can't find ancestor for 'b' copied from 'a'!
730 740 grafting 19:9627f653b421 "2"
731 741 merging b
732 742 warning: can't find ancestor for 'b' copied from 'a'!
733 743
734 744 graft with --force (still doesn't graft merges)
735 745
736 746 $ hg graft 19 0 6
737 747 skipping ungraftable merge revision 6
738 748 skipping ancestor revision 0:68795b066622
739 749 skipping already grafted revision 19:9627f653b421 (22:d1cb6591fa4b also has origin 2:5c095ad7e90f)
740 750 [255]
741 751 $ hg graft 19 0 6 --force
742 752 skipping ungraftable merge revision 6
743 753 grafting 19:9627f653b421 "2"
744 754 merging b
745 755 warning: can't find ancestor for 'b' copied from 'a'!
746 756 grafting 0:68795b066622 "0"
747 757
748 758 graft --force after backout
749 759
750 760 $ echo abc > a
751 761 $ hg ci -m 28
752 762 $ hg backout 28
753 763 reverting a
754 764 changeset 29:53177ba928f6 backs out changeset 28:50a516bb8b57
755 765 $ hg graft 28
756 766 skipping ancestor revision 28:50a516bb8b57
757 767 [255]
758 768 $ hg graft 28 --force
759 769 grafting 28:50a516bb8b57 "28"
760 770 merging a
761 771 $ cat a
762 772 abc
763 773
764 774 graft --continue after --force
765 775
766 776 $ echo def > a
767 777 $ hg ci -m 31
768 778 $ hg graft 28 --force --tool internal:fail
769 779 grafting 28:50a516bb8b57 "28"
770 780 abort: unresolved conflicts, can't continue
771 781 (use 'hg resolve' and 'hg graft --continue')
772 782 [255]
773 783 $ hg resolve --all
774 784 merging a
775 785 warning: conflicts while merging a! (edit, then use 'hg resolve --mark')
776 786 [1]
777 787 $ echo abc > a
778 788 $ hg resolve -m a
779 789 (no more unresolved files)
780 790 continue: hg graft --continue
781 791 $ hg graft -c
782 792 grafting 28:50a516bb8b57 "28"
783 793 $ cat a
784 794 abc
785 795
786 796 Continue testing same origin policy, using revision numbers from test above
787 797 but do some destructive editing of the repo:
788 798
789 799 $ hg up -qC 7
790 800 $ hg tag -l -r 13 tmp
791 801 $ hg --config extensions.strip= strip 2
792 802 saved backup bundle to $TESTTMP/a/.hg/strip-backup/5c095ad7e90f-d323a1e4-backup.hg (glob)
793 803 $ hg graft tmp
794 804 skipping already grafted revision 8:7a4785234d87 (2:ef0ef43d49e7 also has unknown origin 5c095ad7e90f)
795 805 [255]
796 806
797 807 Empty graft
798 808
799 809 $ hg up -qr 26
800 810 $ hg tag -f something
801 811 $ hg graft -qr 27
802 812 $ hg graft -f 27
803 813 grafting 27:ed6c7e54e319 "28"
804 814 note: graft of 27:ed6c7e54e319 created no changes to commit
805 815
806 816 $ cd ..
807 817
808 818 Graft to duplicate a commit
809 819
810 820 $ hg init graftsibling
811 821 $ cd graftsibling
812 822 $ touch a
813 823 $ hg commit -qAm a
814 824 $ touch b
815 825 $ hg commit -qAm b
816 826 $ hg log -G -T '{rev}\n'
817 827 @ 1
818 828 |
819 829 o 0
820 830
821 831 $ hg up -q 0
822 832 $ hg graft -r 1
823 833 grafting 1:0e067c57feba "b" (tip)
824 834 $ hg log -G -T '{rev}\n'
825 835 @ 2
826 836 |
827 837 | o 1
828 838 |/
829 839 o 0
830 840
831 841 Graft to duplicate a commit twice
832 842
833 843 $ hg up -q 0
834 844 $ hg graft -r 2
835 845 grafting 2:044ec77f6389 "b" (tip)
836 846 $ hg log -G -T '{rev}\n'
837 847 @ 3
838 848 |
839 849 | o 2
840 850 |/
841 851 | o 1
842 852 |/
843 853 o 0
844 854
845 855 Graft from behind a move or rename
846 856 ==================================
847 857
848 858 NOTE: This is affected by issue5343, and will need updating when it's fixed
849 859
850 860 Possible cases during a regular graft (when ca is between cta and c2):
851 861
852 862 name | c1<-cta | cta<->ca | ca->c2
853 863 A.0 | | |
854 864 A.1 | X | |
855 865 A.2 | | X |
856 866 A.3 | | | X
857 867 A.4 | X | X |
858 868 A.5 | X | | X
859 869 A.6 | | X | X
860 870 A.7 | X | X | X
861 871
862 872 A.0 is trivial, and doesn't need copy tracking.
863 873 For A.1, a forward rename is recorded in the c1 pass, to be followed later.
864 874 In A.2, the rename is recorded in the c2 pass and followed backwards.
865 875 A.3 is recorded in the c2 pass as a forward rename to be duplicated on target.
866 876 In A.4, both passes of checkcopies record incomplete renames, which are
867 877 then joined in mergecopies to record a rename to be followed.
868 878 In A.5 and A.7, the c1 pass records an incomplete rename, while the c2 pass
869 879 records an incomplete divergence. The incomplete rename is then joined to the
870 880 appropriate side of the incomplete divergence, and the result is recorded as a
871 881 divergence. The code doesn't distinguish at all between these two cases, since
872 882 the end result of them is the same: an incomplete divergence joined with an
873 883 incomplete rename into a divergence.
874 884 Finally, A.6 records a divergence entirely in the c2 pass.
875 885
876 886 A.4 has a degenerate case a<-b<-a->a, where checkcopies isn't needed at all.
877 887 A.5 has a special case a<-b<-b->a, which is treated like a<-b->a in a merge.
878 888 A.6 has a special case a<-a<-b->a. Here, checkcopies will find a spurious
879 889 incomplete divergence, which is in fact complete. This is handled later in
880 890 mergecopies.
881 891 A.7 has 4 special cases: a<-b<-a->b (the "ping-pong" case), a<-b<-c->b,
882 892 a<-b<-a->c and a<-b<-c->a. Of these, only the "ping-pong" case is interesting,
883 893 the others are fairly trivial (a<-b<-c->b and a<-b<-a->c proceed like the base
884 894 case, a<-b<-c->a is treated the same as a<-b<-b->a).
885 895
886 896 f5a therefore tests the "ping-pong" rename case, where a file is renamed to the
887 897 same name on both branches, then the rename is backed out on one branch, and
888 898 the backout is grafted to the other branch. This creates a challenging rename
889 899 sequence of a<-b<-a->b in the graft target, topological CA, graft CA and graft
890 900 source, respectively. Since rename detection will run on the c1 side for such a
891 901 sequence (as for technical reasons, we split the c1 and c2 sides not at the
892 902 graft CA, but rather at the topological CA), it will pick up a false rename,
893 903 and cause a spurious merge conflict. This false rename is always exactly the
894 904 reverse of the true rename that would be detected on the c2 side, so we can
895 905 correct for it by detecting this condition and reversing as necessary.
896 906
897 907 First, set up the repository with commits to be grafted
898 908
899 909 $ hg init ../graftmove
900 910 $ cd ../graftmove
901 911 $ echo c1a > f1a
902 912 $ echo c2a > f2a
903 913 $ echo c3a > f3a
904 914 $ echo c4a > f4a
905 915 $ echo c5a > f5a
906 916 $ hg ci -qAm A0
907 917 $ hg mv f1a f1b
908 918 $ hg mv f3a f3b
909 919 $ hg mv f5a f5b
910 920 $ hg ci -qAm B0
911 921 $ echo c1c > f1b
912 922 $ hg mv f2a f2c
913 923 $ hg mv f5b f5a
914 924 $ echo c5c > f5a
915 925 $ hg ci -qAm C0
916 926 $ hg mv f3b f3d
917 927 $ echo c4d > f4a
918 928 $ hg ci -qAm D0
919 929 $ hg log -G
920 930 @ changeset: 3:b69f5839d2d9
921 931 | tag: tip
922 932 | user: test
923 933 | date: Thu Jan 01 00:00:00 1970 +0000
924 934 | summary: D0
925 935 |
926 936 o changeset: 2:f58c7e2b28fa
927 937 | user: test
928 938 | date: Thu Jan 01 00:00:00 1970 +0000
929 939 | summary: C0
930 940 |
931 941 o changeset: 1:3d7bba921b5d
932 942 | user: test
933 943 | date: Thu Jan 01 00:00:00 1970 +0000
934 944 | summary: B0
935 945 |
936 946 o changeset: 0:11f7a1b56675
937 947 user: test
938 948 date: Thu Jan 01 00:00:00 1970 +0000
939 949 summary: A0
940 950
941 951
942 952 Test the cases A.2 (f1x), A.3 (f2x) and a special case of A.6 (f5x) where the
943 953 two renames actually converge to the same name (thus no actual divergence).
944 954
945 955 $ hg up -q 'desc("A0")'
946 956 $ HGEDITOR="echo C1 >" hg graft -r 'desc("C0")' --edit
947 957 grafting 2:f58c7e2b28fa "C0"
958 merging f1a and f1b to f1a
948 959 merging f5a
949 960 warning: conflicts while merging f5a! (edit, then use 'hg resolve --mark')
950 961 abort: unresolved conflicts, can't continue
951 962 (use 'hg resolve' and 'hg graft --continue')
952 963 [255]
953 964 $ hg resolve f5a -t ':other' # XXX work around failure
954 965 (no more unresolved files)
955 966 continue: hg graft --continue
956 967 $ hg graft --continue # XXX work around failure
957 968 grafting 2:f58c7e2b28fa "C0"
958 969 warning: can't find ancestor for 'f5a' copied from 'f5b'!
959 970 $ hg status --change .
971 M f1a
960 972 M f5a
961 A f1b
962 973 A f2c
963 974 R f2a
964 975 $ hg cat f1a
965 c1a
976 c1c
966 977 $ hg cat f1b
967 c1c
978 f1b: no such file in rev 43e4b415492d
979 [1]
968 980
969 981 Test the cases A.0 (f4x) and A.6 (f3x)
970 982
971 983 $ HGEDITOR="echo D1 >" hg graft -r 'desc("D0")' --edit
972 984 grafting 3:b69f5839d2d9 "D0"
973 985 warning: can't find ancestor for 'f3d' copied from 'f3b'!
974 986
975 987 Set up the repository for some further tests
976 988
977 989 $ hg up -q "min(desc("A0"))"
978 990 $ hg mv f1a f1e
979 991 $ echo c2e > f2a
980 992 $ hg mv f3a f3e
981 993 $ hg mv f4a f4e
982 994 $ hg mv f5a f5b
983 995 $ hg ci -qAm "E0"
984 996 $ hg log -G
985 997 @ changeset: 6:ebba59d1fb02
986 998 | tag: tip
987 999 | parent: 0:11f7a1b56675
988 1000 | user: test
989 1001 | date: Thu Jan 01 00:00:00 1970 +0000
990 1002 | summary: E0
991 1003 |
992 | o changeset: 5:573bb6b4b56d
1004 | o changeset: 5:4f4ba7a6e606
993 1005 | | user: test
994 1006 | | date: Thu Jan 01 00:00:00 1970 +0000
995 1007 | | summary: D1
996 1008 | |
997 | o changeset: 4:af23416e619b
1009 | o changeset: 4:43e4b415492d
998 1010 |/ parent: 0:11f7a1b56675
999 1011 | user: test
1000 1012 | date: Thu Jan 01 00:00:00 1970 +0000
1001 1013 | summary: C0
1002 1014 |
1003 1015 | o changeset: 3:b69f5839d2d9
1004 1016 | | user: test
1005 1017 | | date: Thu Jan 01 00:00:00 1970 +0000
1006 1018 | | summary: D0
1007 1019 | |
1008 1020 | o changeset: 2:f58c7e2b28fa
1009 1021 | | user: test
1010 1022 | | date: Thu Jan 01 00:00:00 1970 +0000
1011 1023 | | summary: C0
1012 1024 | |
1013 1025 | o changeset: 1:3d7bba921b5d
1014 1026 |/ user: test
1015 1027 | date: Thu Jan 01 00:00:00 1970 +0000
1016 1028 | summary: B0
1017 1029 |
1018 1030 o changeset: 0:11f7a1b56675
1019 1031 user: test
1020 1032 date: Thu Jan 01 00:00:00 1970 +0000
1021 1033 summary: A0
1022 1034
1023 1035
1024 1036 Test the cases A.4 (f1x), the "ping-pong" special case of A.7 (f5x),
1025 1037 and A.3 with a local content change to be preserved (f2x).
1026 1038
1027 1039 $ HGEDITOR="echo C2 >" hg graft -r 'desc("C0")' --edit
1028 1040 grafting 2:f58c7e2b28fa "C0"
1029 1041 other [graft] changed f1b which local [local] deleted
1030 1042 use (c)hanged version, leave (d)eleted, or leave (u)nresolved? u
1031 1043 merging f2a and f2c to f2c
1044 merging f5b and f5a to f5a
1032 1045 abort: unresolved conflicts, can't continue
1033 1046 (use 'hg resolve' and 'hg graft --continue')
1034 1047 [255]
1035 1048 $ hg resolve f1b -t ':other' # XXX work around failure
1036 1049 (no more unresolved files)
1037 1050 continue: hg graft --continue
1038 1051 $ hg graft --continue # XXX work around failure
1039 1052 grafting 2:f58c7e2b28fa "C0"
1040 grafting 4:af23416e619b "C0"
1053 grafting 4:43e4b415492d "C0"
1054 merging f1e and f1a to f1e
1041 1055 merging f2c
1042 1056 warning: can't find ancestor for 'f2c' copied from 'f2a'!
1043 1057
1044 1058 Test the cases A.1 (f4x) and A.7 (f3x).
1045 1059
1046 1060 $ HGEDITOR="echo D2 >" hg graft -r 'desc("D0")' --edit
1047 1061 grafting 3:b69f5839d2d9 "D0"
1048 1062 merging f4e and f4a to f4e
1049 1063 warning: can't find ancestor for 'f3d' copied from 'f3b'!
1050 1064
1051 1065 Check the results of the grafts tested
1052 1066
1053 1067 $ hg log -CGv --patch --git
1054 @ changeset: 9:6d76b84e6e84
1068 @ changeset: 9:100f4d78e056
1055 1069 | tag: tip
1056 1070 | user: test
1057 1071 | date: Thu Jan 01 00:00:00 1970 +0000
1058 1072 | files: f3d f4e
1059 1073 | description:
1060 1074 | D2
1061 1075 |
1062 1076 |
1063 1077 | diff --git a/f3d b/f3d
1064 1078 | new file mode 100644
1065 1079 | --- /dev/null
1066 1080 | +++ b/f3d
1067 1081 | @@ -0,0 +1,1 @@
1068 1082 | +c3a
1069 1083 | diff --git a/f4e b/f4e
1070 1084 | --- a/f4e
1071 1085 | +++ b/f4e
1072 1086 | @@ -1,1 +1,1 @@
1073 1087 | -c4a
1074 1088 | +c4d
1075 1089 |
1076 o changeset: 8:3079ba7d03f0
1090 o changeset: 8:84915a7da133
1077 1091 | user: test
1078 1092 | date: Thu Jan 01 00:00:00 1970 +0000
1093 | files: f1e f5a.orig
1079 1094 | description:
1080 1095 | C0
1081 1096 |
1082 1097 |
1098 | diff --git a/f1e b/f1e
1099 | --- a/f1e
1100 | +++ b/f1e
1101 | @@ -1,1 +1,1 @@
1102 | -c1a
1103 | +c1c
1104 | diff --git a/f5a.orig b/f5a.orig
1105 | deleted file mode 100644
1106 | --- a/f5a.orig
1107 | +++ /dev/null
1108 | @@ -1,5 +0,0 @@
1109 | -<<<<<<< local: 11f7a1b56675 - test: A0
1110 | -c5a
1111 | -=======
1112 | -c5c
1113 | ->>>>>>> graft: f58c7e2b28fa - test: C0
1083 1114 |
1084 1115 o changeset: 7:dc778749ee9a
1085 1116 | user: test
1086 1117 | date: Thu Jan 01 00:00:00 1970 +0000
1087 1118 | files: f1b f2a f2c f5a f5b
1088 1119 | copies: f2c (f2a) f5a (f5b)
1089 1120 | description:
1090 1121 | C0
1091 1122 |
1092 1123 |
1093 1124 | diff --git a/f1b b/f1b
1094 1125 | new file mode 100644
1095 1126 | --- /dev/null
1096 1127 | +++ b/f1b
1097 1128 | @@ -0,0 +1,1 @@
1098 1129 | +c1c
1099 1130 | diff --git a/f2a b/f2c
1100 1131 | rename from f2a
1101 1132 | rename to f2c
1102 1133 | diff --git a/f5b b/f5a
1103 1134 | rename from f5b
1104 1135 | rename to f5a
1105 1136 | --- a/f5b
1106 1137 | +++ b/f5a
1107 1138 | @@ -1,1 +1,1 @@
1108 1139 | -c5a
1109 1140 | +c5c
1110 1141 |
1111 1142 o changeset: 6:ebba59d1fb02
1112 1143 | parent: 0:11f7a1b56675
1113 1144 | user: test
1114 1145 | date: Thu Jan 01 00:00:00 1970 +0000
1115 1146 | files: f1a f1e f2a f3a f3e f4a f4e f5a f5a.orig f5b
1116 1147 | copies: f1e (f1a) f3e (f3a) f4e (f4a) f5b (f5a)
1117 1148 | description:
1118 1149 | E0
1119 1150 |
1120 1151 |
1121 1152 | diff --git a/f1a b/f1e
1122 1153 | rename from f1a
1123 1154 | rename to f1e
1124 1155 | diff --git a/f2a b/f2a
1125 1156 | --- a/f2a
1126 1157 | +++ b/f2a
1127 1158 | @@ -1,1 +1,1 @@
1128 1159 | -c2a
1129 1160 | +c2e
1130 1161 | diff --git a/f3a b/f3e
1131 1162 | rename from f3a
1132 1163 | rename to f3e
1133 1164 | diff --git a/f4a b/f4e
1134 1165 | rename from f4a
1135 1166 | rename to f4e
1136 1167 | diff --git a/f5a.orig b/f5a.orig
1137 1168 | new file mode 100644
1138 1169 | --- /dev/null
1139 1170 | +++ b/f5a.orig
1140 1171 | @@ -0,0 +1,5 @@
1141 1172 | +<<<<<<< local: 11f7a1b56675 - test: A0
1142 1173 | +c5a
1143 1174 | +=======
1144 1175 | +c5c
1145 1176 | +>>>>>>> graft: f58c7e2b28fa - test: C0
1146 1177 | diff --git a/f5a b/f5b
1147 1178 | rename from f5a
1148 1179 | rename to f5b
1149 1180 |
1150 | o changeset: 5:573bb6b4b56d
1181 | o changeset: 5:4f4ba7a6e606
1151 1182 | | user: test
1152 1183 | | date: Thu Jan 01 00:00:00 1970 +0000
1153 1184 | | files: f3d f4a
1154 1185 | | description:
1155 1186 | | D1
1156 1187 | |
1157 1188 | |
1158 1189 | | diff --git a/f3d b/f3d
1159 1190 | | new file mode 100644
1160 1191 | | --- /dev/null
1161 1192 | | +++ b/f3d
1162 1193 | | @@ -0,0 +1,1 @@
1163 1194 | | +c3a
1164 1195 | | diff --git a/f4a b/f4a
1165 1196 | | --- a/f4a
1166 1197 | | +++ b/f4a
1167 1198 | | @@ -1,1 +1,1 @@
1168 1199 | | -c4a
1169 1200 | | +c4d
1170 1201 | |
1171 | o changeset: 4:af23416e619b
1202 | o changeset: 4:43e4b415492d
1172 1203 |/ parent: 0:11f7a1b56675
1173 1204 | user: test
1174 1205 | date: Thu Jan 01 00:00:00 1970 +0000
1175 | files: f1b f2a f2c f5a
1206 | files: f1a f2a f2c f5a
1176 1207 | copies: f2c (f2a)
1177 1208 | description:
1178 1209 | C0
1179 1210 |
1180 1211 |
1181 | diff --git a/f1b b/f1b
1182 | new file mode 100644
1183 | --- /dev/null
1184 | +++ b/f1b
1185 | @@ -0,0 +1,1 @@
1212 | diff --git a/f1a b/f1a
1213 | --- a/f1a
1214 | +++ b/f1a
1215 | @@ -1,1 +1,1 @@
1216 | -c1a
1186 1217 | +c1c
1187 1218 | diff --git a/f2a b/f2c
1188 1219 | rename from f2a
1189 1220 | rename to f2c
1190 1221 | diff --git a/f5a b/f5a
1191 1222 | --- a/f5a
1192 1223 | +++ b/f5a
1193 1224 | @@ -1,1 +1,1 @@
1194 1225 | -c5a
1195 1226 | +c5c
1196 1227 |
1197 1228 | o changeset: 3:b69f5839d2d9
1198 1229 | | user: test
1199 1230 | | date: Thu Jan 01 00:00:00 1970 +0000
1200 1231 | | files: f3b f3d f4a
1201 1232 | | copies: f3d (f3b)
1202 1233 | | description:
1203 1234 | | D0
1204 1235 | |
1205 1236 | |
1206 1237 | | diff --git a/f3b b/f3d
1207 1238 | | rename from f3b
1208 1239 | | rename to f3d
1209 1240 | | diff --git a/f4a b/f4a
1210 1241 | | --- a/f4a
1211 1242 | | +++ b/f4a
1212 1243 | | @@ -1,1 +1,1 @@
1213 1244 | | -c4a
1214 1245 | | +c4d
1215 1246 | |
1216 1247 | o changeset: 2:f58c7e2b28fa
1217 1248 | | user: test
1218 1249 | | date: Thu Jan 01 00:00:00 1970 +0000
1219 1250 | | files: f1b f2a f2c f5a f5b
1220 1251 | | copies: f2c (f2a) f5a (f5b)
1221 1252 | | description:
1222 1253 | | C0
1223 1254 | |
1224 1255 | |
1225 1256 | | diff --git a/f1b b/f1b
1226 1257 | | --- a/f1b
1227 1258 | | +++ b/f1b
1228 1259 | | @@ -1,1 +1,1 @@
1229 1260 | | -c1a
1230 1261 | | +c1c
1231 1262 | | diff --git a/f2a b/f2c
1232 1263 | | rename from f2a
1233 1264 | | rename to f2c
1234 1265 | | diff --git a/f5b b/f5a
1235 1266 | | rename from f5b
1236 1267 | | rename to f5a
1237 1268 | | --- a/f5b
1238 1269 | | +++ b/f5a
1239 1270 | | @@ -1,1 +1,1 @@
1240 1271 | | -c5a
1241 1272 | | +c5c
1242 1273 | |
1243 1274 | o changeset: 1:3d7bba921b5d
1244 1275 |/ user: test
1245 1276 | date: Thu Jan 01 00:00:00 1970 +0000
1246 1277 | files: f1a f1b f3a f3b f5a f5b
1247 1278 | copies: f1b (f1a) f3b (f3a) f5b (f5a)
1248 1279 | description:
1249 1280 | B0
1250 1281 |
1251 1282 |
1252 1283 | diff --git a/f1a b/f1b
1253 1284 | rename from f1a
1254 1285 | rename to f1b
1255 1286 | diff --git a/f3a b/f3b
1256 1287 | rename from f3a
1257 1288 | rename to f3b
1258 1289 | diff --git a/f5a b/f5b
1259 1290 | rename from f5a
1260 1291 | rename to f5b
1261 1292 |
1262 1293 o changeset: 0:11f7a1b56675
1263 1294 user: test
1264 1295 date: Thu Jan 01 00:00:00 1970 +0000
1265 1296 files: f1a f2a f3a f4a f5a
1266 1297 description:
1267 1298 A0
1268 1299
1269 1300
1270 1301 diff --git a/f1a b/f1a
1271 1302 new file mode 100644
1272 1303 --- /dev/null
1273 1304 +++ b/f1a
1274 1305 @@ -0,0 +1,1 @@
1275 1306 +c1a
1276 1307 diff --git a/f2a b/f2a
1277 1308 new file mode 100644
1278 1309 --- /dev/null
1279 1310 +++ b/f2a
1280 1311 @@ -0,0 +1,1 @@
1281 1312 +c2a
1282 1313 diff --git a/f3a b/f3a
1283 1314 new file mode 100644
1284 1315 --- /dev/null
1285 1316 +++ b/f3a
1286 1317 @@ -0,0 +1,1 @@
1287 1318 +c3a
1288 1319 diff --git a/f4a b/f4a
1289 1320 new file mode 100644
1290 1321 --- /dev/null
1291 1322 +++ b/f4a
1292 1323 @@ -0,0 +1,1 @@
1293 1324 +c4a
1294 1325 diff --git a/f5a b/f5a
1295 1326 new file mode 100644
1296 1327 --- /dev/null
1297 1328 +++ b/f5a
1298 1329 @@ -0,0 +1,1 @@
1299 1330 +c5a
1300 1331
1301 1332 $ hg cat f2c
1302 1333 c2e
@@ -1,359 +1,363
1 1 $ cat >> $HGRCPATH <<EOF
2 2 > [format]
3 3 > usegeneraldelta=yes
4 4 > [extensions]
5 5 > rebase=
6 6 >
7 7 > [phases]
8 8 > publish=False
9 9 >
10 10 > [alias]
11 11 > tglog = log -G --template "{rev}:{phase} '{desc}' {branches} {bookmarks}\n"
12 12 > EOF
13 13
14 14 $ hg init a
15 15 $ cd a
16 16 $ echo c1 >common
17 17 $ hg add common
18 18 $ hg ci -m C1
19 19
20 20 $ echo c2 >>common
21 21 $ hg ci -m C2
22 22
23 23 $ echo c3 >>common
24 24 $ hg ci -m C3
25 25
26 26 $ hg up -q -C 1
27 27
28 28 $ echo l1 >>extra
29 29 $ hg add extra
30 30 $ hg ci -m L1
31 31 created new head
32 32
33 33 $ sed -e 's/c2/l2/' common > common.new
34 34 $ mv common.new common
35 35 $ hg ci -m L2
36 36
37 37 $ echo l3 >> extra2
38 38 $ hg add extra2
39 39 $ hg ci -m L3
40 40 $ hg bookmark mybook
41 41
42 42 $ hg phase --force --secret 4
43 43
44 44 $ hg tglog
45 45 @ 5:secret 'L3' mybook
46 46 |
47 47 o 4:secret 'L2'
48 48 |
49 49 o 3:draft 'L1'
50 50 |
51 51 | o 2:draft 'C3'
52 52 |/
53 53 o 1:draft 'C2'
54 54 |
55 55 o 0:draft 'C1'
56 56
57 57 Try to call --continue:
58 58
59 59 $ hg rebase --continue
60 60 abort: no rebase in progress
61 61 [255]
62 62
63 63 Conflicting rebase:
64 64
65 65 $ hg rebase -s 3 -d 2
66 66 rebasing 3:3163e20567cc "L1"
67 67 rebasing 4:46f0b057b5c0 "L2"
68 68 merging common
69 69 warning: conflicts while merging common! (edit, then use 'hg resolve --mark')
70 70 unresolved conflicts (see hg resolve, then hg rebase --continue)
71 71 [1]
72 72
73 73 Try to continue without solving the conflict:
74 74
75 75 $ hg rebase --continue
76 76 already rebased 3:3163e20567cc "L1" as 3e046f2ecedb
77 77 rebasing 4:46f0b057b5c0 "L2"
78 78 abort: unresolved merge conflicts (see 'hg help resolve')
79 79 [255]
80 80
81 81 Conclude rebase:
82 82
83 83 $ echo 'resolved merge' >common
84 84 $ hg resolve -m common
85 85 (no more unresolved files)
86 86 continue: hg rebase --continue
87 87 $ hg rebase --continue
88 88 already rebased 3:3163e20567cc "L1" as 3e046f2ecedb
89 89 rebasing 4:46f0b057b5c0 "L2"
90 90 rebasing 5:8029388f38dc "L3" (mybook)
91 91 saved backup bundle to $TESTTMP/a/.hg/strip-backup/3163e20567cc-5ca4656e-backup.hg (glob)
92 92
93 93 $ hg tglog
94 94 @ 5:secret 'L3' mybook
95 95 |
96 96 o 4:secret 'L2'
97 97 |
98 98 o 3:draft 'L1'
99 99 |
100 100 o 2:draft 'C3'
101 101 |
102 102 o 1:draft 'C2'
103 103 |
104 104 o 0:draft 'C1'
105 105
106 106 Check correctness:
107 107
108 108 $ hg cat -r 0 common
109 109 c1
110 110
111 111 $ hg cat -r 1 common
112 112 c1
113 113 c2
114 114
115 115 $ hg cat -r 2 common
116 116 c1
117 117 c2
118 118 c3
119 119
120 120 $ hg cat -r 3 common
121 121 c1
122 122 c2
123 123 c3
124 124
125 125 $ hg cat -r 4 common
126 126 resolved merge
127 127
128 128 $ hg cat -r 5 common
129 129 resolved merge
130 130
131 131 Bookmark stays active after --continue
132 132 $ hg bookmarks
133 133 * mybook 5:d67b21408fc0
134 134
135 135 $ cd ..
136 136
137 137 Check that the right ancestors is used while rebasing a merge (issue4041)
138 138
139 139 $ hg clone "$TESTDIR/bundles/issue4041.hg" issue4041
140 140 requesting all changes
141 141 adding changesets
142 142 adding manifests
143 143 adding file changes
144 144 added 11 changesets with 8 changes to 3 files (+1 heads)
145 145 updating to branch default
146 146 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
147 147 $ cd issue4041
148 148 $ hg log -G
149 149 o changeset: 10:2f2496ddf49d
150 150 |\ branch: f1
151 151 | | tag: tip
152 152 | | parent: 7:4c9fbe56a16f
153 153 | | parent: 9:e31216eec445
154 154 | | user: szhang
155 155 | | date: Thu Sep 05 12:59:39 2013 -0400
156 156 | | summary: merge
157 157 | |
158 158 | o changeset: 9:e31216eec445
159 159 | | branch: f1
160 160 | | user: szhang
161 161 | | date: Thu Sep 05 12:59:10 2013 -0400
162 162 | | summary: more changes to f1
163 163 | |
164 164 | o changeset: 8:8e4e2c1a07ae
165 165 | |\ branch: f1
166 166 | | | parent: 2:4bc80088dc6b
167 167 | | | parent: 6:400110238667
168 168 | | | user: szhang
169 169 | | | date: Thu Sep 05 12:57:59 2013 -0400
170 170 | | | summary: bad merge
171 171 | | |
172 172 o | | changeset: 7:4c9fbe56a16f
173 173 |/ / branch: f1
174 174 | | parent: 2:4bc80088dc6b
175 175 | | user: szhang
176 176 | | date: Thu Sep 05 12:54:00 2013 -0400
177 177 | | summary: changed f1
178 178 | |
179 179 | o changeset: 6:400110238667
180 180 | | branch: f2
181 181 | | parent: 4:12e8ec6bb010
182 182 | | user: szhang
183 183 | | date: Tue Sep 03 13:58:02 2013 -0400
184 184 | | summary: changed f2 on f2
185 185 | |
186 186 | | @ changeset: 5:d79e2059b5c0
187 187 | | | parent: 3:8a951942e016
188 188 | | | user: szhang
189 189 | | | date: Tue Sep 03 13:57:39 2013 -0400
190 190 | | | summary: changed f2 on default
191 191 | | |
192 192 | o | changeset: 4:12e8ec6bb010
193 193 | |/ branch: f2
194 194 | | user: szhang
195 195 | | date: Tue Sep 03 13:57:18 2013 -0400
196 196 | | summary: created f2 branch
197 197 | |
198 198 | o changeset: 3:8a951942e016
199 199 | | parent: 0:24797d4f68de
200 200 | | user: szhang
201 201 | | date: Tue Sep 03 13:57:11 2013 -0400
202 202 | | summary: added f2.txt
203 203 | |
204 204 o | changeset: 2:4bc80088dc6b
205 205 | | branch: f1
206 206 | | user: szhang
207 207 | | date: Tue Sep 03 13:56:20 2013 -0400
208 208 | | summary: added f1.txt
209 209 | |
210 210 o | changeset: 1:ef53c9e6b608
211 211 |/ branch: f1
212 212 | user: szhang
213 213 | date: Tue Sep 03 13:55:26 2013 -0400
214 214 | summary: created f1 branch
215 215 |
216 216 o changeset: 0:24797d4f68de
217 217 user: szhang
218 218 date: Tue Sep 03 13:55:08 2013 -0400
219 219 summary: added default.txt
220 220
221 221 $ hg rebase -s9 -d2 --debug # use debug to really check merge base used
222 222 rebase onto 4bc80088dc6b starting from e31216eec445
223 223 ignoring null merge rebase of 3
224 224 ignoring null merge rebase of 4
225 225 ignoring null merge rebase of 6
226 226 ignoring null merge rebase of 8
227 227 rebasing 9:e31216eec445 "more changes to f1"
228 228 future parents are 2 and -1
229 229 rebase status stored
230 230 update to 2:4bc80088dc6b
231 231 resolving manifests
232 232 branchmerge: False, force: True, partial: False
233 233 ancestor: d79e2059b5c0+, local: d79e2059b5c0+, remote: 4bc80088dc6b
234 234 f2.txt: other deleted -> r
235 235 removing f2.txt
236 236 f1.txt: remote created -> g
237 237 getting f1.txt
238 238 merge against 9:e31216eec445
239 239 detach base 8:8e4e2c1a07ae
240 240 searching for copies back to rev 3
241 unmatched files in other (from topological common ancestor):
242 f2.txt
241 243 resolving manifests
242 244 branchmerge: True, force: True, partial: False
243 245 ancestor: 8e4e2c1a07ae, local: 4bc80088dc6b+, remote: e31216eec445
244 246 f1.txt: remote is newer -> g
245 247 getting f1.txt
246 248 committing files:
247 249 f1.txt
248 250 committing manifest
249 251 committing changelog
250 252 rebased as 19c888675e13
251 253 rebasing 10:2f2496ddf49d "merge" (tip)
252 254 future parents are 11 and 7
253 255 rebase status stored
254 256 already in target
255 257 merge against 10:2f2496ddf49d
256 258 detach base 9:e31216eec445
257 259 searching for copies back to rev 3
260 unmatched files in other (from topological common ancestor):
261 f2.txt
258 262 resolving manifests
259 263 branchmerge: True, force: True, partial: False
260 264 ancestor: e31216eec445, local: 19c888675e13+, remote: 2f2496ddf49d
261 265 f1.txt: remote is newer -> g
262 266 getting f1.txt
263 267 committing files:
264 268 f1.txt
265 269 committing manifest
266 270 committing changelog
267 271 rebased as 2a7f09cac94c
268 272 rebase merging completed
269 273 update back to initial working directory parent
270 274 resolving manifests
271 275 branchmerge: False, force: False, partial: False
272 276 ancestor: 2a7f09cac94c, local: 2a7f09cac94c+, remote: d79e2059b5c0
273 277 f1.txt: other deleted -> r
274 278 removing f1.txt
275 279 f2.txt: remote created -> g
276 280 getting f2.txt
277 281 2 changesets found
278 282 list of changesets:
279 283 e31216eec445e44352c5f01588856059466a24c9
280 284 2f2496ddf49d69b5ef23ad8cf9fb2e0e4faf0ac2
281 285 bundle2-output-bundle: "HG20", (1 params) 1 parts total
282 286 bundle2-output-part: "changegroup" (params: 1 mandatory 1 advisory) streamed payload
283 287 saved backup bundle to $TESTTMP/issue4041/.hg/strip-backup/e31216eec445-15f7a814-backup.hg (glob)
284 288 3 changesets found
285 289 list of changesets:
286 290 4c9fbe56a16f30c0d5dcc40ec1a97bbe3325209c
287 291 19c888675e133ab5dff84516926a65672eaf04d9
288 292 2a7f09cac94c7f4b73ebd5cd1a62d3b2e8e336bf
289 293 bundle2-output-bundle: "HG20", 1 parts total
290 294 bundle2-output-part: "changegroup" (params: 1 mandatory 1 advisory) streamed payload
291 295 adding branch
292 296 bundle2-input-bundle: with-transaction
293 297 bundle2-input-part: "changegroup" (params: 1 mandatory 1 advisory) supported
294 298 adding changesets
295 299 add changeset 4c9fbe56a16f
296 300 add changeset 19c888675e13
297 301 add changeset 2a7f09cac94c
298 302 adding manifests
299 303 adding file changes
300 304 adding f1.txt revisions
301 305 added 2 changesets with 2 changes to 1 files
302 306 bundle2-input-part: total payload size 1713
303 307 bundle2-input-bundle: 0 parts total
304 308 invalid branchheads cache (served): tip differs
305 309 history modification detected - truncating revision branch cache to revision 9
306 310 rebase completed
307 311 truncating cache/rbc-revs-v1 to 72
308 312
309 313 Test minimization of merge conflicts
310 314 $ hg up -q null
311 315 $ echo a > a
312 316 $ hg add a
313 317 $ hg commit -q -m 'a'
314 318 $ echo b >> a
315 319 $ hg commit -q -m 'ab'
316 320 $ hg bookmark ab
317 321 $ hg up -q '.^'
318 322 $ echo b >> a
319 323 $ echo c >> a
320 324 $ hg commit -q -m 'abc'
321 325 $ hg rebase -s 7bc217434fc1 -d ab --keep
322 326 rebasing 13:7bc217434fc1 "abc" (tip)
323 327 merging a
324 328 warning: conflicts while merging a! (edit, then use 'hg resolve --mark')
325 329 unresolved conflicts (see hg resolve, then hg rebase --continue)
326 330 [1]
327 331 $ hg diff
328 332 diff -r 328e4ab1f7cc a
329 333 --- a/a Thu Jan 01 00:00:00 1970 +0000
330 334 +++ b/a * (glob)
331 335 @@ -1,2 +1,6 @@
332 336 a
333 337 b
334 338 +<<<<<<< dest: 328e4ab1f7cc ab - test: ab
335 339 +=======
336 340 +c
337 341 +>>>>>>> source: 7bc217434fc1 - test: abc
338 342 $ hg rebase --abort
339 343 rebase aborted
340 344 $ hg up -q -C 7bc217434fc1
341 345 $ hg rebase -s . -d ab --keep -t internal:merge3
342 346 rebasing 13:7bc217434fc1 "abc" (tip)
343 347 merging a
344 348 warning: conflicts while merging a! (edit, then use 'hg resolve --mark')
345 349 unresolved conflicts (see hg resolve, then hg rebase --continue)
346 350 [1]
347 351 $ hg diff
348 352 diff -r 328e4ab1f7cc a
349 353 --- a/a Thu Jan 01 00:00:00 1970 +0000
350 354 +++ b/a * (glob)
351 355 @@ -1,2 +1,8 @@
352 356 a
353 357 +<<<<<<< dest: 328e4ab1f7cc ab - test: ab
354 358 b
355 359 +||||||| base
356 360 +=======
357 361 +b
358 362 +c
359 363 +>>>>>>> source: 7bc217434fc1 - test: abc
General Comments 0
You need to be logged in to leave comments. Login now