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