Show More
@@ -373,57 +373,6 b' def _computenonoverlap(repo, c1, c2, add' | |||||
373 |
|
373 | |||
374 | return u1, u2 |
|
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 | def mergecopies(repo, c1, c2, base): |
|
376 | def mergecopies(repo, c1, c2, base): | |
428 | """ |
|
377 | """ | |
429 | Finds moves and copies between context c1 and c2 that are relevant for |
|
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 | return commits < sourcecommitlimit |
|
467 | return commits < sourcecommitlimit | |
519 | return False |
|
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 | def _fullcopytracing(repo, c1, c2, base): |
|
487 | def _fullcopytracing(repo, c1, c2, base): | |
522 | """ The full copytracing algorithm which finds all the new files that were |
|
488 | """ The full copytracing algorithm which finds all the new files that were | |
523 | added from merge base up to the top commit and for each file it checks if |
|
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 | This is pretty slow when a lot of changesets are involved but will track all |
|
492 | This is pretty slow when a lot of changesets are involved but will track all | |
527 | the copies. |
|
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 | m1 = c1.manifest() |
|
495 | m1 = c1.manifest() | |
553 | m2 = c2.manifest() |
|
496 | m2 = c2.manifest() | |
554 | mb = base.manifest() |
|
497 | mb = base.manifest() | |
555 |
|
498 | |||
556 | # gather data from _checkcopies: |
|
499 | copies1 = pathcopies(base, c1) | |
557 | # - diverge = record all diverges in this dict |
|
500 | copies2 = pathcopies(base, c2) | |
558 | # - copy = record all non-divergent copies in this dict |
|
501 | ||
559 | # - fullcopy = record all copies in this dict |
|
502 | inversecopies1 = {} | |
560 | # - incomplete = record non-divergent partial copies here |
|
503 | inversecopies2 = {} | |
561 | # - incompletediverge = record divergent partial copies here |
|
504 | for dst, src in copies1.items(): | |
562 | diverge = {} # divergence data is shared |
|
505 | inversecopies1.setdefault(src, []).append(dst) | |
563 | incompletediverge = {} |
|
506 | for dst, src in copies2.items(): | |
564 | data1 = {'copy': {}, |
|
507 | inversecopies2.setdefault(src, []).append(dst) | |
565 | 'fullcopy': {}, |
|
508 | ||
566 | 'incomplete': {}, |
|
509 | copy = {} | |
567 | 'diverge': diverge, |
|
510 | diverge = {} | |
568 | 'incompletediverge': incompletediverge, |
|
511 | renamedelete = {} | |
569 | } |
|
512 | allsources = set(inversecopies1) | set(inversecopies2) | |
570 | data2 = {'copy': {}, |
|
513 | for src in allsources: | |
571 | 'fullcopy': {}, |
|
514 | dsts1 = inversecopies1.get(src) | |
572 | 'incomplete': {}, |
|
515 | dsts2 = inversecopies2.get(src) | |
573 | 'diverge': diverge, |
|
516 | if dsts1 and dsts2: | |
574 | 'incompletediverge': incompletediverge, |
|
517 | # copied/renamed on both sides | |
575 | } |
|
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 | # find interesting file sets from manifests |
|
555 | # find interesting file sets from manifests | |
578 | addedinm1 = m1.filesnotin(mb, repo.narrowmatch()) |
|
556 | addedinm1 = m1.filesnotin(mb, repo.narrowmatch()) | |
579 | addedinm2 = m2.filesnotin(mb, repo.narrowmatch()) |
|
557 | addedinm2 = m2.filesnotin(mb, repo.narrowmatch()) | |
580 | bothnew = sorted(addedinm1 & addedinm2) |
|
558 | u1, u2 = _computenonoverlap(repo, c1, c2, 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 |
|
|||
627 |
|
559 | |||
628 | bothdiverge = {} |
|
560 | fullcopy = copies1.copy() | |
629 | bothincompletediverge = {} |
|
561 | fullcopy.update(copies2) | |
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 |
|
||||
691 | if not fullcopy: |
|
562 | if not fullcopy: | |
692 | return copy, {}, diverge, renamedelete, {} |
|
563 | return copy, {}, diverge, renamedelete, {} | |
693 |
|
564 | |||
@@ -752,7 +623,7 b' def _fullcopytracing(repo, c1, c2, base)' | |||||
752 |
|
623 | |||
753 | movewithdir = {} |
|
624 | movewithdir = {} | |
754 | # check unaccounted nonoverlapping files against directory moves |
|
625 | # check unaccounted nonoverlapping files against directory moves | |
755 |
for f in u1 |
|
626 | for f in u1 + u2: | |
756 | if f not in fullcopy: |
|
627 | if f not in fullcopy: | |
757 | for d in dirmove: |
|
628 | for d in dirmove: | |
758 | if f.startswith(d): |
|
629 | if f.startswith(d): | |
@@ -899,99 +770,6 b' def _related(f1, f2):' | |||||
899 | except StopIteration: |
|
770 | except StopIteration: | |
900 | return False |
|
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 | def duplicatecopies(repo, wctx, rev, fromrev, skiprev=None): |
|
773 | def duplicatecopies(repo, wctx, rev, fromrev, skiprev=None): | |
996 | """reproduce copies from fromrev to rev in the dirstate |
|
774 | """reproduce copies from fromrev to rev in the dirstate | |
997 |
|
775 |
@@ -273,37 +273,10 b' annotate after merge with -l' | |||||
273 | > EOF |
|
273 | > EOF | |
274 | $ hg ci -mc -d '3 0' |
|
274 | $ hg ci -mc -d '3 0' | |
275 | created new head |
|
275 | created new head | |
276 | BROKEN: 'a' was copied to 'b' on both sides. We should not get a merge conflict here |
|
|||
277 | $ hg merge |
|
276 | $ hg merge | |
278 | merging b |
|
277 | merging b | |
279 | warning: conflicts while merging b! (edit, then use 'hg resolve --mark') |
|
278 | 0 files updated, 1 files merged, 0 files removed, 0 files unresolved | |
280 | 0 files updated, 0 files merged, 0 files removed, 1 files unresolved |
|
279 | (branch merge, don't forget to commit) | |
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 |
|
|||
307 | $ echo d >> b |
|
280 | $ echo d >> b | |
308 |
$ |
|
281 | $ hg ci -mmerge2 -d '4 0' | |
309 |
|
282 |
@@ -787,12 +787,11 b' Amend a merge changeset (with renames du' | |||||
787 | Update to p1 with 'aaa' modified. 'aaa' was renamed from 'aa' in p2. 'aa' exists |
|
787 | Update to p1 with 'aaa' modified. 'aaa' was renamed from 'aa' in p2. 'aa' exists | |
788 | in p1 too, but it was recorded as copied from p2. |
|
788 | in p1 too, but it was recorded as copied from p2. | |
789 | $ echo modified >> aaa |
|
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 | $ hg co -m '.^' -t :merge3 |
|
790 | $ hg co -m '.^' -t :merge3 | |
793 | merging aaa and aa to aa |
|
791 | file 'aaa' was deleted in other [destination] but was modified in local [working copy]. | |
794 | warning: conflicts while merging aa! (edit, then use 'hg resolve --mark') |
|
792 | What do you want to do? | |
795 | 0 files updated, 0 files merged, 1 files removed, 1 files unresolved |
|
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 | use 'hg resolve' to retry unresolved file merges |
|
795 | use 'hg resolve' to retry unresolved file merges | |
797 | [1] |
|
796 | [1] | |
798 | $ hg co -C tip |
|
797 | $ hg co -C tip |
@@ -549,9 +549,6 b' test reflect that for this particular ca' | |||||
549 |
|
549 | |||
550 | Grafting revision 4 on top of revision 2, showing that it respect the rename: |
|
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 | $ hg up 2 -q |
|
552 | $ hg up 2 -q | |
556 | $ hg graft -r 4 --base 3 --hidden |
|
553 | $ hg graft -r 4 --base 3 --hidden | |
557 | grafting 4:af28412ec03c "added d, modified b" (tip) |
|
554 | grafting 4:af28412ec03c "added d, modified b" (tip) | |
@@ -560,15 +557,14 b' changeset-centric version of copies.merg' | |||||
560 | $ hg l -l1 -p |
|
557 | $ hg l -l1 -p | |
561 | @ 5 added d, modified b |
|
558 | @ 5 added d, modified b | |
562 | | b1 |
|
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 | --- a/b1 Thu Jan 01 00:00:00 1970 +0000 |
|
562 | --- a/b1 Thu Jan 01 00:00:00 1970 +0000 | |
565 | +++ b/b1 Thu Jan 01 00:00:00 1970 +0000 |
|
563 | +++ b/b1 Thu Jan 01 00:00:00 1970 +0000 | |
566 | @@ -1,1 +1,2 @@ |
|
564 | @@ -1,1 +1,2 @@ | |
567 | b |
|
565 | b | |
568 | +baba |
|
566 | +baba | |
569 |
|
567 | |||
570 | #endif |
|
|||
571 |
|
||||
572 | Test to make sure that fullcopytracing algorithm don't fail when both the merging csets are dirty |
|
568 | Test to make sure that fullcopytracing algorithm don't fail when both the merging csets are dirty | |
573 | (a dirty cset is one who is not the descendant of merge base) |
|
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 | > EOF |
|
273 | > EOF | |
274 | $ hg ci -mc -d '3 0' |
|
274 | $ hg ci -mc -d '3 0' | |
275 | created new head |
|
275 | created new head | |
276 | BROKEN: 'a' was copied to 'b' on both sides. We should not get a merge conflict here |
|
|||
277 | $ hg merge |
|
276 | $ hg merge | |
278 | merging b |
|
277 | merging b | |
279 | warning: conflicts while merging b! (edit, then use 'hg resolve --mark') |
|
278 | 0 files updated, 1 files merged, 0 files removed, 0 files unresolved | |
280 | 0 files updated, 0 files merged, 0 files removed, 1 files unresolved |
|
279 | (branch merge, don't forget to commit) | |
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 |
|
|||
307 | $ echo d >> b |
|
280 | $ echo d >> b | |
308 |
$ |
|
281 | $ hg ci -mmerge2 -d '4 0' | |
309 |
|
282 |
@@ -75,6 +75,8 b' Specifying child as --base revision fail' | |||||
75 |
|
75 | |||
76 | $ hg graft -r 2 --base 3 |
|
76 | $ hg graft -r 2 --base 3 | |
77 | grafting 2:5c095ad7e90f "2" |
|
77 | grafting 2:5c095ad7e90f "2" | |
|
78 | note: possible conflict - c was deleted and renamed to: | |||
|
79 | a | |||
78 | note: graft of 2:5c095ad7e90f created no changes to commit |
|
80 | note: graft of 2:5c095ad7e90f created no changes to commit | |
79 |
|
81 | |||
80 | Can't continue without starting: |
|
82 | Can't continue without starting: | |
@@ -220,6 +222,9 b' Graft out of order, skipping a merge and' | |||||
220 | committing changelog |
|
222 | committing changelog | |
221 | updating the branch cache |
|
223 | updating the branch cache | |
222 | grafting 5:97f8bfe72746 "5" |
|
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 | resolving manifests |
|
228 | resolving manifests | |
224 | branchmerge: True, force: True, partial: False |
|
229 | branchmerge: True, force: True, partial: False | |
225 | ancestor: 4c60f11aa304, local: 6b9e5368ca4e+, remote: 97f8bfe72746 |
|
230 | ancestor: 4c60f11aa304, local: 6b9e5368ca4e+, remote: 97f8bfe72746 | |
@@ -233,6 +238,9 b' Graft out of order, skipping a merge and' | |||||
233 | $ HGEDITOR=cat hg graft 4 3 --log --debug |
|
238 | $ HGEDITOR=cat hg graft 4 3 --log --debug | |
234 | scanning for duplicate grafts |
|
239 | scanning for duplicate grafts | |
235 | grafting 4:9c233e8e184d "4" |
|
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 | resolving manifests |
|
244 | resolving manifests | |
237 | branchmerge: True, force: True, partial: False |
|
245 | branchmerge: True, force: True, partial: False | |
238 | ancestor: 4c60f11aa304, local: 1905859650ec+, remote: 9c233e8e184d |
|
246 | ancestor: 4c60f11aa304, local: 1905859650ec+, remote: 9c233e8e184d | |
@@ -1129,7 +1137,6 b' and A.3 with a local content change to b' | |||||
1129 | grafting 2:f58c7e2b28fa "C0" |
|
1137 | grafting 2:f58c7e2b28fa "C0" | |
1130 | merging f1e and f1b to f1e |
|
1138 | merging f1e and f1b to f1e | |
1131 | merging f2a and f2c to f2c |
|
1139 | merging f2a and f2c to f2c | |
1132 | merging f5b and f5a to f5a |
|
|||
1133 |
|
1140 | |||
1134 | Test the cases A.1 (f4x) and A.7 (f3x). |
|
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 | Check that merging across the rename works |
|
1688 | Check that merging across the rename works | |
1689 |
|
1689 | |||
1690 | $ echo modified >> renamed |
|
1690 | $ echo modified >> renamed | |
1691 | BROKEN: This should propagate the change to 'f' |
|
|||
1692 | $ hg co -m 4 |
|
1691 | $ hg co -m 4 | |
1693 | file 'renamed' was deleted in other [destination] but was modified in local [working copy]. |
|
1692 | merging renamed and f to f | |
1694 | What do you want to do? |
|
1693 | 0 files updated, 1 files merged, 0 files removed, 0 files unresolved | |
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] |
|
|||
1699 |
|
1694 | |||
1700 | $ cd .. |
|
1695 | $ cd .. |
@@ -433,6 +433,9 b' m "um a c" "um x c" " " "10 do merg' | |||||
433 | -------------- |
|
433 | -------------- | |
434 | test L:nc a b R:up b W: - 12 merge b no ancestor |
|
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 | resolving manifests |
|
439 | resolving manifests | |
437 | branchmerge: True, force: False, partial: False |
|
440 | branchmerge: True, force: False, partial: False | |
438 | ancestor: 924404dff337, local: 86a2aa42fc76+, remote: af30c7647fc7 |
|
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 | test L:up b R:nm a b W: - 13 merge b no ancestor |
|
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 | resolving manifests |
|
478 | resolving manifests | |
473 | branchmerge: True, force: False, partial: False |
|
479 | branchmerge: True, force: False, partial: False | |
474 | ancestor: 924404dff337, local: 59318016310c+, remote: bdb19105162a |
|
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 | test L:nc a b R:up a b W: - 14 merge b no ancestor |
|
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 | resolving manifests |
|
518 | resolving manifests | |
510 | branchmerge: True, force: False, partial: False |
|
519 | branchmerge: True, force: False, partial: False | |
511 | ancestor: 924404dff337, local: 86a2aa42fc76+, remote: 8dbce441892a |
|
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 | test L:up b R:nm a b W: - 15 merge b no ancestor, remove a |
|
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 | resolving manifests |
|
558 | resolving manifests | |
547 | branchmerge: True, force: False, partial: False |
|
559 | branchmerge: True, force: False, partial: False | |
548 | ancestor: 924404dff337, local: 59318016310c+, remote: bdb19105162a |
|
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 | test L:nc a b R:up a b W: - 16 get a, merge b no ancestor |
|
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 | resolving manifests |
|
598 | resolving manifests | |
584 | branchmerge: True, force: False, partial: False |
|
599 | branchmerge: True, force: False, partial: False | |
585 | ancestor: 924404dff337, local: 86a2aa42fc76+, remote: 8dbce441892a |
|
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 | test L:up a b R:nc a b W: - 17 keep a, merge b no ancestor |
|
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 | resolving manifests |
|
638 | resolving manifests | |
621 | branchmerge: True, force: False, partial: False |
|
639 | branchmerge: True, force: False, partial: False | |
622 | ancestor: 924404dff337, local: 0b76e65c8289+, remote: 4ce40f5aca24 |
|
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 | test L:nm a b R:up a b W: - 18 merge b no ancestor |
|
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 | resolving manifests |
|
677 | resolving manifests | |
657 | branchmerge: True, force: False, partial: False |
|
678 | branchmerge: True, force: False, partial: False | |
658 | ancestor: 924404dff337, local: 02963e448370+, remote: 8dbce441892a |
|
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 | test L:up a b R:nm a b W: - 19 merge b no ancestor, prompt remove a |
|
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 | resolving manifests |
|
722 | resolving manifests | |
699 | branchmerge: True, force: False, partial: False |
|
723 | branchmerge: True, force: False, partial: False | |
700 | ancestor: 924404dff337, local: 0b76e65c8289+, remote: bdb19105162a |
|
724 | ancestor: 924404dff337, local: 0b76e65c8289+, remote: bdb19105162a |
General Comments 0
You need to be logged in to leave comments.
Login now