Show More
@@ -373,57 +373,6 b' def _computenonoverlap(repo, c1, c2, add' | |||
|
373 | 373 | |
|
374 | 374 | return u1, u2 |
|
375 | 375 | |
|
376 | def _makegetfctx(ctx): | |
|
377 | """return a 'getfctx' function suitable for _checkcopies usage | |
|
378 | ||
|
379 | We have to re-setup the function building 'filectx' for each | |
|
380 | '_checkcopies' to ensure the linkrev adjustment is properly setup for | |
|
381 | each. Linkrev adjustment is important to avoid bug in rename | |
|
382 | detection. Moreover, having a proper '_ancestrycontext' setup ensures | |
|
383 | the performance impact of this adjustment is kept limited. Without it, | |
|
384 | each file could do a full dag traversal making the time complexity of | |
|
385 | the operation explode (see issue4537). | |
|
386 | ||
|
387 | This function exists here mostly to limit the impact on stable. Feel | |
|
388 | free to refactor on default. | |
|
389 | """ | |
|
390 | rev = ctx.rev() | |
|
391 | repo = ctx._repo | |
|
392 | ac = getattr(ctx, '_ancestrycontext', None) | |
|
393 | if ac is None: | |
|
394 | revs = [rev] | |
|
395 | if rev is None: | |
|
396 | revs = [p.rev() for p in ctx.parents()] | |
|
397 | ac = repo.changelog.ancestors(revs, inclusive=True) | |
|
398 | ctx._ancestrycontext = ac | |
|
399 | def makectx(f, n): | |
|
400 | if n in node.wdirfilenodeids: # in a working context? | |
|
401 | if ctx.rev() is None: | |
|
402 | return ctx.filectx(f) | |
|
403 | return repo[None][f] | |
|
404 | fctx = repo.filectx(f, fileid=n) | |
|
405 | # setup only needed for filectx not create from a changectx | |
|
406 | fctx._ancestrycontext = ac | |
|
407 | fctx._descendantrev = rev | |
|
408 | return fctx | |
|
409 | return util.lrucachefunc(makectx) | |
|
410 | ||
|
411 | def _combinecopies(copyfrom, copyto, finalcopy, diverge, incompletediverge): | |
|
412 | """combine partial copy paths""" | |
|
413 | remainder = {} | |
|
414 | for f in copyfrom: | |
|
415 | if f in copyto: | |
|
416 | finalcopy[copyto[f]] = copyfrom[f] | |
|
417 | del copyto[f] | |
|
418 | for f in incompletediverge: | |
|
419 | assert f not in diverge | |
|
420 | ic = incompletediverge[f] | |
|
421 | if ic[0] in copyto: | |
|
422 | diverge[f] = [copyto[ic[0]], ic[1]] | |
|
423 | else: | |
|
424 | remainder[f] = ic | |
|
425 | return remainder | |
|
426 | ||
|
427 | 376 | def mergecopies(repo, c1, c2, base): |
|
428 | 377 | """ |
|
429 | 378 | Finds moves and copies between context c1 and c2 that are relevant for |
@@ -518,6 +467,23 b' def _isfullcopytraceable(repo, c1, base)' | |||
|
518 | 467 | return commits < sourcecommitlimit |
|
519 | 468 | return False |
|
520 | 469 | |
|
470 | def _checksinglesidecopies(src, dsts1, m1, m2, mb, c2, base, | |
|
471 | copy, renamedelete): | |
|
472 | if src not in m2: | |
|
473 | # deleted on side 2 | |
|
474 | if src not in m1: | |
|
475 | # renamed on side 1, deleted on side 2 | |
|
476 | renamedelete[src] = dsts1 | |
|
477 | elif m2[src] != mb[src]: | |
|
478 | if not _related(c2[src], base[src]): | |
|
479 | return | |
|
480 | # modified on side 2 | |
|
481 | for dst in dsts1: | |
|
482 | if dst not in m2: | |
|
483 | # dst not added on side 2 (handle as regular | |
|
484 | # "both created" case in manifestmerge otherwise) | |
|
485 | copy[dst] = src | |
|
486 | ||
|
521 | 487 | def _fullcopytracing(repo, c1, c2, base): |
|
522 | 488 | """ The full copytracing algorithm which finds all the new files that were |
|
523 | 489 | added from merge base up to the top commit and for each file it checks if |
@@ -526,168 +492,73 b' def _fullcopytracing(repo, c1, c2, base)' | |||
|
526 | 492 | This is pretty slow when a lot of changesets are involved but will track all |
|
527 | 493 | the copies. |
|
528 | 494 | """ |
|
529 | # In certain scenarios (e.g. graft, update or rebase), base can be | |
|
530 | # overridden We still need to know a real common ancestor in this case We | |
|
531 | # can't just compute _c1.ancestor(_c2) and compare it to ca, because there | |
|
532 | # can be multiple common ancestors, e.g. in case of bidmerge. Because our | |
|
533 | # caller may not know if the revision passed in lieu of the CA is a genuine | |
|
534 | # common ancestor or not without explicitly checking it, it's better to | |
|
535 | # determine that here. | |
|
536 | # | |
|
537 | # base.isancestorof(wc) is False, work around that | |
|
538 | _c1 = c1.p1() if c1.rev() is None else c1 | |
|
539 | _c2 = c2.p1() if c2.rev() is None else c2 | |
|
540 | # an endpoint is "dirty" if it isn't a descendant of the merge base | |
|
541 | # if we have a dirty endpoint, we need to trigger graft logic, and also | |
|
542 | # keep track of which endpoint is dirty | |
|
543 | dirtyc1 = not base.isancestorof(_c1) | |
|
544 | dirtyc2 = not base.isancestorof(_c2) | |
|
545 | graft = dirtyc1 or dirtyc2 | |
|
546 | tca = base | |
|
547 | if graft: | |
|
548 | tca = _c1.ancestor(_c2) | |
|
549 | ||
|
550 | limit = _findlimit(repo, c1, c2) | |
|
551 | ||
|
552 | 495 | m1 = c1.manifest() |
|
553 | 496 | m2 = c2.manifest() |
|
554 | 497 | mb = base.manifest() |
|
555 | 498 | |
|
556 | # gather data from _checkcopies: | |
|
557 | # - diverge = record all diverges in this dict | |
|
558 | # - copy = record all non-divergent copies in this dict | |
|
559 | # - fullcopy = record all copies in this dict | |
|
560 | # - incomplete = record non-divergent partial copies here | |
|
561 | # - incompletediverge = record divergent partial copies here | |
|
562 | diverge = {} # divergence data is shared | |
|
563 | incompletediverge = {} | |
|
564 | data1 = {'copy': {}, | |
|
565 | 'fullcopy': {}, | |
|
566 | 'incomplete': {}, | |
|
567 | 'diverge': diverge, | |
|
568 | 'incompletediverge': incompletediverge, | |
|
569 | } | |
|
570 | data2 = {'copy': {}, | |
|
571 | 'fullcopy': {}, | |
|
572 | 'incomplete': {}, | |
|
573 | 'diverge': diverge, | |
|
574 | 'incompletediverge': incompletediverge, | |
|
575 | } | |
|
499 | copies1 = pathcopies(base, c1) | |
|
500 | copies2 = pathcopies(base, c2) | |
|
501 | ||
|
502 | inversecopies1 = {} | |
|
503 | inversecopies2 = {} | |
|
504 | for dst, src in copies1.items(): | |
|
505 | inversecopies1.setdefault(src, []).append(dst) | |
|
506 | for dst, src in copies2.items(): | |
|
507 | inversecopies2.setdefault(src, []).append(dst) | |
|
508 | ||
|
509 | copy = {} | |
|
510 | diverge = {} | |
|
511 | renamedelete = {} | |
|
512 | allsources = set(inversecopies1) | set(inversecopies2) | |
|
513 | for src in allsources: | |
|
514 | dsts1 = inversecopies1.get(src) | |
|
515 | dsts2 = inversecopies2.get(src) | |
|
516 | if dsts1 and dsts2: | |
|
517 | # copied/renamed on both sides | |
|
518 | if src not in m1 and src not in m2: | |
|
519 | # renamed on both sides | |
|
520 | dsts1 = set(dsts1) | |
|
521 | dsts2 = set(dsts2) | |
|
522 | # If there's some overlap in the rename destinations, we | |
|
523 | # consider it not divergent. For example, if side 1 copies 'a' | |
|
524 | # to 'b' and 'c' and deletes 'a', and side 2 copies 'a' to 'c' | |
|
525 | # and 'd' and deletes 'a'. | |
|
526 | if dsts1 & dsts2: | |
|
527 | for dst in (dsts1 & dsts2): | |
|
528 | copy[dst] = src | |
|
529 | else: | |
|
530 | diverge[src] = sorted(dsts1 | dsts2) | |
|
531 | elif src in m1 and src in m2: | |
|
532 | # copied on both sides | |
|
533 | dsts1 = set(dsts1) | |
|
534 | dsts2 = set(dsts2) | |
|
535 | for dst in (dsts1 & dsts2): | |
|
536 | copy[dst] = src | |
|
537 | # TODO: Handle cases where it was renamed on one side and copied | |
|
538 | # on the other side | |
|
539 | elif dsts1: | |
|
540 | # copied/renamed only on side 1 | |
|
541 | _checksinglesidecopies(src, dsts1, m1, m2, mb, c2, base, | |
|
542 | copy, renamedelete) | |
|
543 | elif dsts2: | |
|
544 | # copied/renamed only on side 2 | |
|
545 | _checksinglesidecopies(src, dsts2, m2, m1, mb, c1, base, | |
|
546 | copy, renamedelete) | |
|
547 | ||
|
548 | renamedeleteset = set() | |
|
549 | divergeset = set() | |
|
550 | for src, dsts in diverge.items(): | |
|
551 | divergeset.update(dsts) | |
|
552 | for src, dsts in renamedelete.items(): | |
|
553 | renamedeleteset.update(dsts) | |
|
576 | 554 | |
|
577 | 555 | # find interesting file sets from manifests |
|
578 | 556 | addedinm1 = m1.filesnotin(mb, repo.narrowmatch()) |
|
579 | 557 | addedinm2 = m2.filesnotin(mb, repo.narrowmatch()) |
|
580 | bothnew = sorted(addedinm1 & addedinm2) | |
|
581 | if tca == base: | |
|
582 | # unmatched file from base | |
|
583 | u1r, u2r = _computenonoverlap(repo, c1, c2, addedinm1, addedinm2) | |
|
584 | u1u, u2u = u1r, u2r | |
|
585 | else: | |
|
586 | # unmatched file from base (DAG rotation in the graft case) | |
|
587 | u1r, u2r = _computenonoverlap(repo, c1, c2, addedinm1, addedinm2) | |
|
588 | # unmatched file from topological common ancestors (no DAG rotation) | |
|
589 | # need to recompute this for directory move handling when grafting | |
|
590 | mta = tca.manifest() | |
|
591 | u1u, u2u = _computenonoverlap(repo, c1, c2, | |
|
592 | m1.filesnotin(mta, repo.narrowmatch()), | |
|
593 | m2.filesnotin(mta, repo.narrowmatch()), | |
|
594 | debug=False) | |
|
595 | ||
|
596 | for f in u1u: | |
|
597 | _checkcopies(c1, c2, f, base, tca, dirtyc1, limit, data1) | |
|
598 | ||
|
599 | for f in u2u: | |
|
600 | _checkcopies(c2, c1, f, base, tca, dirtyc2, limit, data2) | |
|
601 | ||
|
602 | copy = dict(data1['copy']) | |
|
603 | copy.update(data2['copy']) | |
|
604 | fullcopy = dict(data1['fullcopy']) | |
|
605 | fullcopy.update(data2['fullcopy']) | |
|
606 | ||
|
607 | if dirtyc1: | |
|
608 | _combinecopies(data2['incomplete'], data1['incomplete'], copy, diverge, | |
|
609 | incompletediverge) | |
|
610 | if dirtyc2: | |
|
611 | _combinecopies(data1['incomplete'], data2['incomplete'], copy, diverge, | |
|
612 | incompletediverge) | |
|
613 | ||
|
614 | renamedelete = {} | |
|
615 | renamedeleteset = set() | |
|
616 | divergeset = set() | |
|
617 | for of, fl in list(diverge.items()): | |
|
618 | if len(fl) == 1 or of in c1 or of in c2: | |
|
619 | del diverge[of] # not actually divergent, or not a rename | |
|
620 | if of not in c1 and of not in c2: | |
|
621 | # renamed on one side, deleted on the other side, but filter | |
|
622 | # out files that have been renamed and then deleted | |
|
623 | renamedelete[of] = [f for f in fl if f in c1 or f in c2] | |
|
624 | renamedeleteset.update(fl) # reverse map for below | |
|
625 | else: | |
|
626 | divergeset.update(fl) # reverse map for below | |
|
558 | u1, u2 = _computenonoverlap(repo, c1, c2, addedinm1, addedinm2) | |
|
627 | 559 | |
|
628 | bothdiverge = {} | |
|
629 | bothincompletediverge = {} | |
|
630 | remainder = {} | |
|
631 | both1 = {'copy': {}, | |
|
632 | 'fullcopy': {}, | |
|
633 | 'incomplete': {}, | |
|
634 | 'diverge': bothdiverge, | |
|
635 | 'incompletediverge': bothincompletediverge | |
|
636 | } | |
|
637 | both2 = {'copy': {}, | |
|
638 | 'fullcopy': {}, | |
|
639 | 'incomplete': {}, | |
|
640 | 'diverge': bothdiverge, | |
|
641 | 'incompletediverge': bothincompletediverge | |
|
642 | } | |
|
643 | for f in bothnew: | |
|
644 | _checkcopies(c1, c2, f, base, tca, dirtyc1, limit, both1) | |
|
645 | _checkcopies(c2, c1, f, base, tca, dirtyc2, limit, both2) | |
|
646 | if dirtyc1 and dirtyc2: | |
|
647 | remainder = _combinecopies(both2['incomplete'], both1['incomplete'], | |
|
648 | copy, bothdiverge, bothincompletediverge) | |
|
649 | remainder1 = _combinecopies(both1['incomplete'], both2['incomplete'], | |
|
650 | copy, bothdiverge, bothincompletediverge) | |
|
651 | remainder.update(remainder1) | |
|
652 | elif dirtyc1: | |
|
653 | # incomplete copies may only be found on the "dirty" side for bothnew | |
|
654 | assert not both2['incomplete'] | |
|
655 | remainder = _combinecopies({}, both1['incomplete'], copy, bothdiverge, | |
|
656 | bothincompletediverge) | |
|
657 | elif dirtyc2: | |
|
658 | assert not both1['incomplete'] | |
|
659 | remainder = _combinecopies({}, both2['incomplete'], copy, bothdiverge, | |
|
660 | bothincompletediverge) | |
|
661 | else: | |
|
662 | # incomplete copies and divergences can't happen outside grafts | |
|
663 | assert not both1['incomplete'] | |
|
664 | assert not both2['incomplete'] | |
|
665 | assert not bothincompletediverge | |
|
666 | for f in remainder: | |
|
667 | assert f not in bothdiverge | |
|
668 | ic = remainder[f] | |
|
669 | if ic[0] in (m1 if dirtyc1 else m2): | |
|
670 | # backed-out rename on one side, but watch out for deleted files | |
|
671 | bothdiverge[f] = ic | |
|
672 | for of, fl in bothdiverge.items(): | |
|
673 | if len(fl) == 2 and fl[0] == fl[1]: | |
|
674 | copy[fl[0]] = of # not actually divergent, just matching renames | |
|
675 | ||
|
676 | # Sometimes we get invalid copies here (the "and not remotebase" in | |
|
677 | # _checkcopies() seems suspicious). Filter them out. | |
|
678 | for dst, src in fullcopy.copy().items(): | |
|
679 | if src not in mb: | |
|
680 | del fullcopy[dst] | |
|
681 | # Sometimes we forget to add entries from "copy" to "fullcopy", so fix | |
|
682 | # that up here | |
|
683 | for dst, src in copy.items(): | |
|
684 | fullcopy[dst] = src | |
|
685 | # Sometimes we forget to add entries from "diverge" to "fullcopy", so fix | |
|
686 | # that up here | |
|
687 | for src, dsts in diverge.items(): | |
|
688 | for dst in dsts: | |
|
689 | fullcopy[dst] = src | |
|
690 | ||
|
560 | fullcopy = copies1.copy() | |
|
561 | fullcopy.update(copies2) | |
|
691 | 562 | if not fullcopy: |
|
692 | 563 | return copy, {}, diverge, renamedelete, {} |
|
693 | 564 | |
@@ -752,7 +623,7 b' def _fullcopytracing(repo, c1, c2, base)' | |||
|
752 | 623 | |
|
753 | 624 | movewithdir = {} |
|
754 | 625 | # check unaccounted nonoverlapping files against directory moves |
|
755 |
for f in u1 |
|
|
626 | for f in u1 + u2: | |
|
756 | 627 | if f not in fullcopy: |
|
757 | 628 | for d in dirmove: |
|
758 | 629 | if f.startswith(d): |
@@ -899,99 +770,6 b' def _related(f1, f2):' | |||
|
899 | 770 | except StopIteration: |
|
900 | 771 | return False |
|
901 | 772 | |
|
902 | def _checkcopies(srcctx, dstctx, f, base, tca, remotebase, limit, data): | |
|
903 | """ | |
|
904 | check possible copies of f from msrc to mdst | |
|
905 | ||
|
906 | srcctx = starting context for f in msrc | |
|
907 | dstctx = destination context for f in mdst | |
|
908 | f = the filename to check (as in msrc) | |
|
909 | base = the changectx used as a merge base | |
|
910 | tca = topological common ancestor for graft-like scenarios | |
|
911 | remotebase = True if base is outside tca::srcctx, False otherwise | |
|
912 | limit = the rev number to not search beyond | |
|
913 | data = dictionary of dictionary to store copy data. (see mergecopies) | |
|
914 | ||
|
915 | note: limit is only an optimization, and provides no guarantee that | |
|
916 | irrelevant revisions will not be visited | |
|
917 | there is no easy way to make this algorithm stop in a guaranteed way | |
|
918 | once it "goes behind a certain revision". | |
|
919 | """ | |
|
920 | ||
|
921 | msrc = srcctx.manifest() | |
|
922 | mdst = dstctx.manifest() | |
|
923 | mb = base.manifest() | |
|
924 | mta = tca.manifest() | |
|
925 | # Might be true if this call is about finding backward renames, | |
|
926 | # This happens in the case of grafts because the DAG is then rotated. | |
|
927 | # If the file exists in both the base and the source, we are not looking | |
|
928 | # for a rename on the source side, but on the part of the DAG that is | |
|
929 | # traversed backwards. | |
|
930 | # | |
|
931 | # In the case there is both backward and forward renames (before and after | |
|
932 | # the base) this is more complicated as we must detect a divergence. | |
|
933 | # We use 'backwards = False' in that case. | |
|
934 | backwards = not remotebase and base != tca and f in mb | |
|
935 | getsrcfctx = _makegetfctx(srcctx) | |
|
936 | getdstfctx = _makegetfctx(dstctx) | |
|
937 | ||
|
938 | if msrc[f] == mb.get(f) and not remotebase: | |
|
939 | # Nothing to merge | |
|
940 | return | |
|
941 | ||
|
942 | of = None | |
|
943 | seen = {f} | |
|
944 | for oc in getsrcfctx(f, msrc[f]).ancestors(): | |
|
945 | of = oc.path() | |
|
946 | if of in seen: | |
|
947 | # check limit late - grab last rename before | |
|
948 | if oc.linkrev() < limit: | |
|
949 | break | |
|
950 | continue | |
|
951 | seen.add(of) | |
|
952 | ||
|
953 | # remember for dir rename detection | |
|
954 | if backwards: | |
|
955 | data['fullcopy'][of] = f # grafting backwards through renames | |
|
956 | else: | |
|
957 | data['fullcopy'][f] = of | |
|
958 | if of not in mdst: | |
|
959 | continue # no match, keep looking | |
|
960 | if mdst[of] == mb.get(of): | |
|
961 | return # no merge needed, quit early | |
|
962 | c2 = getdstfctx(of, mdst[of]) | |
|
963 | # c2 might be a plain new file on added on destination side that is | |
|
964 | # unrelated to the droids we are looking for. | |
|
965 | cr = _related(oc, c2) | |
|
966 | if cr and (of == f or of == c2.path()): # non-divergent | |
|
967 | if backwards: | |
|
968 | data['copy'][of] = f | |
|
969 | elif of in mb: | |
|
970 | data['copy'][f] = of | |
|
971 | elif remotebase: # special case: a <- b <- a -> b "ping-pong" rename | |
|
972 | data['copy'][of] = f | |
|
973 | del data['fullcopy'][f] | |
|
974 | data['fullcopy'][of] = f | |
|
975 | else: # divergence w.r.t. graft CA on one side of topological CA | |
|
976 | for sf in seen: | |
|
977 | if sf in mb: | |
|
978 | assert sf not in data['diverge'] | |
|
979 | data['diverge'][sf] = [f, of] | |
|
980 | break | |
|
981 | return | |
|
982 | ||
|
983 | if of in mta: | |
|
984 | if backwards or remotebase: | |
|
985 | data['incomplete'][of] = f | |
|
986 | else: | |
|
987 | for sf in seen: | |
|
988 | if sf in mb: | |
|
989 | if tca == base: | |
|
990 | data['diverge'].setdefault(sf, []).append(f) | |
|
991 | else: | |
|
992 | data['incompletediverge'][sf] = [of, f] | |
|
993 | return | |
|
994 | ||
|
995 | 773 | def duplicatecopies(repo, wctx, rev, fromrev, skiprev=None): |
|
996 | 774 | """reproduce copies from fromrev to rev in the dirstate |
|
997 | 775 |
@@ -273,37 +273,10 b' annotate after merge with -l' | |||
|
273 | 273 | > EOF |
|
274 | 274 | $ hg ci -mc -d '3 0' |
|
275 | 275 | created new head |
|
276 | BROKEN: 'a' was copied to 'b' on both sides. We should not get a merge conflict here | |
|
277 | 276 | $ hg merge |
|
278 | 277 | merging b |
|
279 | warning: conflicts while merging b! (edit, then use 'hg resolve --mark') | |
|
280 | 0 files updated, 0 files merged, 0 files removed, 1 files unresolved | |
|
281 | use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon | |
|
282 | [1] | |
|
283 | $ cat b | |
|
284 | <<<<<<< working copy: b80e3e32f75a - test: c | |
|
285 | a | |
|
286 | z | |
|
287 | a | |
|
288 | ||||||| base | |
|
289 | ======= | |
|
290 | a | |
|
291 | a | |
|
292 | a | |
|
293 | b4 | |
|
294 | c | |
|
295 | b5 | |
|
296 | >>>>>>> merge rev: 64afcdf8e29e - test: mergeb | |
|
297 | $ cat <<EOF > b | |
|
298 | > a | |
|
299 | > z | |
|
300 | > a | |
|
301 | > b4 | |
|
302 | > c | |
|
303 | > b5 | |
|
304 | > EOF | |
|
305 | $ hg resolve --mark -q | |
|
306 | $ rm b.orig | |
|
278 | 0 files updated, 1 files merged, 0 files removed, 0 files unresolved | |
|
279 | (branch merge, don't forget to commit) | |
|
307 | 280 | $ echo d >> b |
|
308 | 281 |
$ |
|
309 | 282 |
@@ -787,12 +787,11 b' Amend a merge changeset (with renames du' | |||
|
787 | 787 | Update to p1 with 'aaa' modified. 'aaa' was renamed from 'aa' in p2. 'aa' exists |
|
788 | 788 | in p1 too, but it was recorded as copied from p2. |
|
789 | 789 | $ echo modified >> aaa |
|
790 | BROKEN: should not be follow the rename back to 'aa' here, since the rename | |
|
791 | happened compared to p2 | |
|
792 | 790 | $ hg co -m '.^' -t :merge3 |
|
793 | merging aaa and aa to aa | |
|
794 | warning: conflicts while merging aa! (edit, then use 'hg resolve --mark') | |
|
795 | 0 files updated, 0 files merged, 1 files removed, 1 files unresolved | |
|
791 | file 'aaa' was deleted in other [destination] but was modified in local [working copy]. | |
|
792 | What do you want to do? | |
|
793 | use (c)hanged version, (d)elete, or leave (u)nresolved? u | |
|
794 | 1 files updated, 0 files merged, 1 files removed, 1 files unresolved | |
|
796 | 795 | use 'hg resolve' to retry unresolved file merges |
|
797 | 796 | [1] |
|
798 | 797 | $ hg co -C tip |
@@ -549,9 +549,6 b' test reflect that for this particular ca' | |||
|
549 | 549 | |
|
550 | 550 | Grafting revision 4 on top of revision 2, showing that it respect the rename: |
|
551 | 551 | |
|
552 | TODO: Make this work with copy info in changesets (probably by writing a | |
|
553 | changeset-centric version of copies.mergecopies()) | |
|
554 | #if no-changeset | |
|
555 | 552 | $ hg up 2 -q |
|
556 | 553 | $ hg graft -r 4 --base 3 --hidden |
|
557 | 554 | grafting 4:af28412ec03c "added d, modified b" (tip) |
@@ -560,15 +557,14 b' changeset-centric version of copies.merg' | |||
|
560 | 557 | $ hg l -l1 -p |
|
561 | 558 | @ 5 added d, modified b |
|
562 | 559 | | b1 |
|
563 | ~ diff -r 5a4825cc2926 -r 94a2f1a0e8e2 b1 | |
|
560 | ~ diff -r 5a4825cc2926 -r 94a2f1a0e8e2 b1 (no-changeset !) | |
|
561 | ~ diff -r f5474f5023a8 -r ef7c02d69f3d b1 (changeset !) | |
|
564 | 562 | --- a/b1 Thu Jan 01 00:00:00 1970 +0000 |
|
565 | 563 | +++ b/b1 Thu Jan 01 00:00:00 1970 +0000 |
|
566 | 564 | @@ -1,1 +1,2 @@ |
|
567 | 565 | b |
|
568 | 566 | +baba |
|
569 | 567 | |
|
570 | #endif | |
|
571 | ||
|
572 | 568 | Test to make sure that fullcopytracing algorithm don't fail when both the merging csets are dirty |
|
573 | 569 | (a dirty cset is one who is not the descendant of merge base) |
|
574 | 570 | ------------------------------------------------------------------------------------------------- |
@@ -273,37 +273,10 b' annotate after merge with -l' | |||
|
273 | 273 | > EOF |
|
274 | 274 | $ hg ci -mc -d '3 0' |
|
275 | 275 | created new head |
|
276 | BROKEN: 'a' was copied to 'b' on both sides. We should not get a merge conflict here | |
|
277 | 276 | $ hg merge |
|
278 | 277 | merging b |
|
279 | warning: conflicts while merging b! (edit, then use 'hg resolve --mark') | |
|
280 | 0 files updated, 0 files merged, 0 files removed, 1 files unresolved | |
|
281 | use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon | |
|
282 | [1] | |
|
283 | $ cat b | |
|
284 | <<<<<<< working copy: b80e3e32f75a - test: c | |
|
285 | a | |
|
286 | z | |
|
287 | a | |
|
288 | ||||||| base | |
|
289 | ======= | |
|
290 | a | |
|
291 | a | |
|
292 | a | |
|
293 | b4 | |
|
294 | c | |
|
295 | b5 | |
|
296 | >>>>>>> merge rev: 64afcdf8e29e - test: mergeb | |
|
297 | $ cat <<EOF > b | |
|
298 | > a | |
|
299 | > z | |
|
300 | > a | |
|
301 | > b4 | |
|
302 | > c | |
|
303 | > b5 | |
|
304 | > EOF | |
|
305 | $ hg resolve --mark -q | |
|
306 | $ rm b.orig | |
|
278 | 0 files updated, 1 files merged, 0 files removed, 0 files unresolved | |
|
279 | (branch merge, don't forget to commit) | |
|
307 | 280 | $ echo d >> b |
|
308 | 281 |
$ |
|
309 | 282 |
@@ -75,6 +75,8 b' Specifying child as --base revision fail' | |||
|
75 | 75 | |
|
76 | 76 | $ hg graft -r 2 --base 3 |
|
77 | 77 | grafting 2:5c095ad7e90f "2" |
|
78 | note: possible conflict - c was deleted and renamed to: | |
|
79 | a | |
|
78 | 80 | note: graft of 2:5c095ad7e90f created no changes to commit |
|
79 | 81 | |
|
80 | 82 | Can't continue without starting: |
@@ -220,6 +222,9 b' Graft out of order, skipping a merge and' | |||
|
220 | 222 | committing changelog |
|
221 | 223 | updating the branch cache |
|
222 | 224 | grafting 5:97f8bfe72746 "5" |
|
225 | all copies found (* = to merge, ! = divergent, % = renamed and deleted): | |
|
226 | src: 'c' -> dst: 'b' | |
|
227 | checking for directory renames | |
|
223 | 228 | resolving manifests |
|
224 | 229 | branchmerge: True, force: True, partial: False |
|
225 | 230 | ancestor: 4c60f11aa304, local: 6b9e5368ca4e+, remote: 97f8bfe72746 |
@@ -233,6 +238,9 b' Graft out of order, skipping a merge and' | |||
|
233 | 238 | $ HGEDITOR=cat hg graft 4 3 --log --debug |
|
234 | 239 | scanning for duplicate grafts |
|
235 | 240 | grafting 4:9c233e8e184d "4" |
|
241 | all copies found (* = to merge, ! = divergent, % = renamed and deleted): | |
|
242 | src: 'c' -> dst: 'b' | |
|
243 | checking for directory renames | |
|
236 | 244 | resolving manifests |
|
237 | 245 | branchmerge: True, force: True, partial: False |
|
238 | 246 | ancestor: 4c60f11aa304, local: 1905859650ec+, remote: 9c233e8e184d |
@@ -1129,7 +1137,6 b' and A.3 with a local content change to b' | |||
|
1129 | 1137 | grafting 2:f58c7e2b28fa "C0" |
|
1130 | 1138 | merging f1e and f1b to f1e |
|
1131 | 1139 | merging f2a and f2c to f2c |
|
1132 | merging f5b and f5a to f5a | |
|
1133 | 1140 | |
|
1134 | 1141 | Test the cases A.1 (f4x) and A.7 (f3x). |
|
1135 | 1142 |
@@ -1688,13 +1688,8 b' Check debug output for copy tracing' | |||
|
1688 | 1688 | Check that merging across the rename works |
|
1689 | 1689 | |
|
1690 | 1690 | $ echo modified >> renamed |
|
1691 | BROKEN: This should propagate the change to 'f' | |
|
1692 | 1691 | $ hg co -m 4 |
|
1693 | file 'renamed' was deleted in other [destination] but was modified in local [working copy]. | |
|
1694 | What do you want to do? | |
|
1695 | use (c)hanged version, (d)elete, or leave (u)nresolved? u | |
|
1696 | 1 files updated, 0 files merged, 0 files removed, 1 files unresolved | |
|
1697 | use 'hg resolve' to retry unresolved file merges | |
|
1698 | [1] | |
|
1692 | merging renamed and f to f | |
|
1693 | 0 files updated, 1 files merged, 0 files removed, 0 files unresolved | |
|
1699 | 1694 | |
|
1700 | 1695 | $ cd .. |
@@ -433,6 +433,9 b' m "um a c" "um x c" " " "10 do merg' | |||
|
433 | 433 | -------------- |
|
434 | 434 | test L:nc a b R:up b W: - 12 merge b no ancestor |
|
435 | 435 | -------------- |
|
436 | all copies found (* = to merge, ! = divergent, % = renamed and deleted): | |
|
437 | src: 'a' -> dst: 'b' | |
|
438 | checking for directory renames | |
|
436 | 439 | resolving manifests |
|
437 | 440 | branchmerge: True, force: False, partial: False |
|
438 | 441 | ancestor: 924404dff337, local: 86a2aa42fc76+, remote: af30c7647fc7 |
@@ -469,6 +472,9 b' m "um a c" "um x c" " " "10 do merg' | |||
|
469 | 472 | -------------- |
|
470 | 473 | test L:up b R:nm a b W: - 13 merge b no ancestor |
|
471 | 474 | -------------- |
|
475 | all copies found (* = to merge, ! = divergent, % = renamed and deleted): | |
|
476 | src: 'a' -> dst: 'b' | |
|
477 | checking for directory renames | |
|
472 | 478 | resolving manifests |
|
473 | 479 | branchmerge: True, force: False, partial: False |
|
474 | 480 | ancestor: 924404dff337, local: 59318016310c+, remote: bdb19105162a |
@@ -506,6 +512,9 b' m "um a c" "um x c" " " "10 do merg' | |||
|
506 | 512 | -------------- |
|
507 | 513 | test L:nc a b R:up a b W: - 14 merge b no ancestor |
|
508 | 514 | -------------- |
|
515 | all copies found (* = to merge, ! = divergent, % = renamed and deleted): | |
|
516 | src: 'a' -> dst: 'b' | |
|
517 | checking for directory renames | |
|
509 | 518 | resolving manifests |
|
510 | 519 | branchmerge: True, force: False, partial: False |
|
511 | 520 | ancestor: 924404dff337, local: 86a2aa42fc76+, remote: 8dbce441892a |
@@ -543,6 +552,9 b' m "um a c" "um x c" " " "10 do merg' | |||
|
543 | 552 | -------------- |
|
544 | 553 | test L:up b R:nm a b W: - 15 merge b no ancestor, remove a |
|
545 | 554 | -------------- |
|
555 | all copies found (* = to merge, ! = divergent, % = renamed and deleted): | |
|
556 | src: 'a' -> dst: 'b' | |
|
557 | checking for directory renames | |
|
546 | 558 | resolving manifests |
|
547 | 559 | branchmerge: True, force: False, partial: False |
|
548 | 560 | ancestor: 924404dff337, local: 59318016310c+, remote: bdb19105162a |
@@ -580,6 +592,9 b' m "um a c" "um x c" " " "10 do merg' | |||
|
580 | 592 | -------------- |
|
581 | 593 | test L:nc a b R:up a b W: - 16 get a, merge b no ancestor |
|
582 | 594 | -------------- |
|
595 | all copies found (* = to merge, ! = divergent, % = renamed and deleted): | |
|
596 | src: 'a' -> dst: 'b' | |
|
597 | checking for directory renames | |
|
583 | 598 | resolving manifests |
|
584 | 599 | branchmerge: True, force: False, partial: False |
|
585 | 600 | ancestor: 924404dff337, local: 86a2aa42fc76+, remote: 8dbce441892a |
@@ -617,6 +632,9 b' m "um a c" "um x c" " " "10 do merg' | |||
|
617 | 632 | -------------- |
|
618 | 633 | test L:up a b R:nc a b W: - 17 keep a, merge b no ancestor |
|
619 | 634 | -------------- |
|
635 | all copies found (* = to merge, ! = divergent, % = renamed and deleted): | |
|
636 | src: 'a' -> dst: 'b' | |
|
637 | checking for directory renames | |
|
620 | 638 | resolving manifests |
|
621 | 639 | branchmerge: True, force: False, partial: False |
|
622 | 640 | ancestor: 924404dff337, local: 0b76e65c8289+, remote: 4ce40f5aca24 |
@@ -653,6 +671,9 b' m "um a c" "um x c" " " "10 do merg' | |||
|
653 | 671 | -------------- |
|
654 | 672 | test L:nm a b R:up a b W: - 18 merge b no ancestor |
|
655 | 673 | -------------- |
|
674 | all copies found (* = to merge, ! = divergent, % = renamed and deleted): | |
|
675 | src: 'a' -> dst: 'b' | |
|
676 | checking for directory renames | |
|
656 | 677 | resolving manifests |
|
657 | 678 | branchmerge: True, force: False, partial: False |
|
658 | 679 | ancestor: 924404dff337, local: 02963e448370+, remote: 8dbce441892a |
@@ -695,6 +716,9 b' m "um a c" "um x c" " " "10 do merg' | |||
|
695 | 716 | -------------- |
|
696 | 717 | test L:up a b R:nm a b W: - 19 merge b no ancestor, prompt remove a |
|
697 | 718 | -------------- |
|
719 | all copies found (* = to merge, ! = divergent, % = renamed and deleted): | |
|
720 | src: 'a' -> dst: 'b' | |
|
721 | checking for directory renames | |
|
698 | 722 | resolving manifests |
|
699 | 723 | branchmerge: True, force: False, partial: False |
|
700 | 724 | ancestor: 924404dff337, local: 0b76e65c8289+, remote: bdb19105162a |
General Comments 0
You need to be logged in to leave comments.
Login now