329 If we're merging, and the other revision has removed a file |
329 If we're merging, and the other revision has removed a file |
330 that is not present in the working directory, we need to mark it |
330 that is not present in the working directory, we need to mark it |
331 as removed. |
331 as removed. |
332 """ |
332 """ |
333 |
333 |
334 actions = [] |
334 ractions = [] |
335 state = branchmerge and 'r' or 'f' |
335 factions = xactions = [] |
|
336 if branchmerge: |
|
337 xactions = ractions |
336 for f in wctx.deleted(): |
338 for f in wctx.deleted(): |
337 if f not in mctx: |
339 if f not in mctx: |
338 actions.append((f, state, None, "forget deleted")) |
340 xactions.append((f, None, "forget deleted")) |
339 |
341 |
340 if not branchmerge: |
342 if not branchmerge: |
341 for f in wctx.removed(): |
343 for f in wctx.removed(): |
342 if f not in mctx: |
344 if f not in mctx: |
343 actions.append((f, "f", None, "forget removed")) |
345 factions.append((f, None, "forget removed")) |
344 |
346 |
345 return actions |
347 return ractions, factions |
346 |
348 |
347 def _checkcollision(repo, wmf, actions): |
349 def _checkcollision(repo, wmf, actions): |
348 # build provisional merged manifest up |
350 # build provisional merged manifest up |
349 pmmf = set(wmf) |
351 pmmf = set(wmf) |
350 |
352 |
351 def addop(f, args): |
353 if actions: |
352 pmmf.add(f) |
354 # k, dr, e and rd are no-op |
353 def removeop(f, args): |
355 for m in 'a', 'f', 'g', 'cd', 'dc': |
354 pmmf.discard(f) |
356 for f, args, msg in actions[m]: |
355 def nop(f, args): |
357 pmmf.add(f) |
356 pass |
358 for f, args, msg in actions['r']: |
357 |
359 pmmf.discard(f) |
358 def renamemoveop(f, args): |
360 for f, args, msg in actions['dm']: |
359 f2, flags = args |
361 f2, flags = args |
360 pmmf.discard(f2) |
362 pmmf.discard(f2) |
361 pmmf.add(f) |
363 pmmf.add(f) |
362 def renamegetop(f, args): |
364 for f, args, msg in actions['dg']: |
363 f2, flags = args |
365 f2, flags = args |
364 pmmf.add(f) |
366 pmmf.add(f) |
365 def mergeop(f, args): |
367 for f, args, msg in actions['m']: |
366 f1, f2, fa, move, anc = args |
368 f1, f2, fa, move, anc = args |
367 if move: |
369 if move: |
368 pmmf.discard(f1) |
370 pmmf.discard(f1) |
369 pmmf.add(f) |
371 pmmf.add(f) |
370 |
|
371 opmap = { |
|
372 "a": addop, |
|
373 "dm": renamemoveop, |
|
374 "dg": renamegetop, |
|
375 "dr": nop, |
|
376 "e": nop, |
|
377 "k": nop, |
|
378 "f": addop, # untracked file should be kept in working directory |
|
379 "g": addop, |
|
380 "m": mergeop, |
|
381 "r": removeop, |
|
382 "rd": nop, |
|
383 "cd": addop, |
|
384 "dc": addop, |
|
385 } |
|
386 for f, m, args, msg in actions: |
|
387 op = opmap.get(m) |
|
388 assert op, m |
|
389 op(f, args) |
|
390 |
372 |
391 # check case-folding collision in provisional merged manifest |
373 # check case-folding collision in provisional merged manifest |
392 foldmap = {} |
374 foldmap = {} |
393 for f in sorted(pmmf): |
375 for f in sorted(pmmf): |
394 fold = util.normcase(f) |
376 fold = util.normcase(f) |
405 branchmerge and force are as passed in to update |
387 branchmerge and force are as passed in to update |
406 partial = function to filter file lists |
388 partial = function to filter file lists |
407 acceptremote = accept the incoming changes without prompting |
389 acceptremote = accept the incoming changes without prompting |
408 """ |
390 """ |
409 |
391 |
410 actions, copy, movewithdir = [], {}, {} |
392 actions = dict((m, []) for m in 'a f g cd dc r dm dg m dr e rd k'.split()) |
|
393 copy, movewithdir = {}, {} |
411 |
394 |
412 # manifests fetched in order are going to be faster, so prime the caches |
395 # manifests fetched in order are going to be faster, so prime the caches |
413 [x.manifest() for x in |
396 [x.manifest() for x in |
414 sorted(wctx.parents() + [p2, pa], key=lambda x: x.rev())] |
397 sorted(wctx.parents() + [p2, pa], key=lambda x: x.rev())] |
415 |
398 |
416 if followcopies: |
399 if followcopies: |
417 ret = copies.mergecopies(repo, wctx, p2, pa) |
400 ret = copies.mergecopies(repo, wctx, p2, pa) |
418 copy, movewithdir, diverge, renamedelete = ret |
401 copy, movewithdir, diverge, renamedelete = ret |
419 for of, fl in diverge.iteritems(): |
402 for of, fl in diverge.iteritems(): |
420 actions.append((of, "dr", (fl,), "divergent renames")) |
403 actions['dr'].append((of, (fl,), "divergent renames")) |
421 for of, fl in renamedelete.iteritems(): |
404 for of, fl in renamedelete.iteritems(): |
422 actions.append((of, "rd", (fl,), "rename and delete")) |
405 actions['rd'].append((of, (fl,), "rename and delete")) |
423 |
406 |
424 repo.ui.note(_("resolving manifests\n")) |
407 repo.ui.note(_("resolving manifests\n")) |
425 repo.ui.debug(" branchmerge: %s, force: %s, partial: %s\n" |
408 repo.ui.debug(" branchmerge: %s, force: %s, partial: %s\n" |
426 % (bool(branchmerge), bool(force), bool(partial))) |
409 % (bool(branchmerge), bool(force), bool(partial))) |
427 repo.ui.debug(" ancestor: %s, local: %s, remote: %s\n" % (pa, wctx, p2)) |
410 repo.ui.debug(" ancestor: %s, local: %s, remote: %s\n" % (pa, wctx, p2)) |
469 # Note: f as default is wrong - we can't really make a 3-way |
452 # Note: f as default is wrong - we can't really make a 3-way |
470 # merge without an ancestor file. |
453 # merge without an ancestor file. |
471 fla = ma.flags(fa) |
454 fla = ma.flags(fa) |
472 nol = 'l' not in fl1 + fl2 + fla |
455 nol = 'l' not in fl1 + fl2 + fla |
473 if n2 == a and fl2 == fla: |
456 if n2 == a and fl2 == fla: |
474 actions.append((f, "k", (), "keep")) # remote unchanged |
457 actions['k'].append((f, (), "keep")) # remote unchanged |
475 elif n1 == a and fl1 == fla: # local unchanged - use remote |
458 elif n1 == a and fl1 == fla: # local unchanged - use remote |
476 if n1 == n2: # optimization: keep local content |
459 if n1 == n2: # optimization: keep local content |
477 actions.append((f, "e", (fl2,), "update permissions")) |
460 actions['e'].append((f, (fl2,), "update permissions")) |
478 else: |
461 else: |
479 actions.append((f, "g", (fl2,), "remote is newer")) |
462 actions['g'].append((f, (fl2,), "remote is newer")) |
480 elif nol and n2 == a: # remote only changed 'x' |
463 elif nol and n2 == a: # remote only changed 'x' |
481 actions.append((f, "e", (fl2,), "update permissions")) |
464 actions['e'].append((f, (fl2,), "update permissions")) |
482 elif nol and n1 == a: # local only changed 'x' |
465 elif nol and n1 == a: # local only changed 'x' |
483 actions.append((f, "g", (fl1,), "remote is newer")) |
466 actions['g'].append((f, (fl1,), "remote is newer")) |
484 else: # both changed something |
467 else: # both changed something |
485 actions.append((f, "m", (f, f, fa, False, pa.node()), |
468 actions['m'].append((f, (f, f, fa, False, pa.node()), |
486 "versions differ")) |
469 "versions differ")) |
487 elif f in copied: # files we'll deal with on m2 side |
470 elif f in copied: # files we'll deal with on m2 side |
488 pass |
471 pass |
489 elif n1 and f in movewithdir: # directory rename, move local |
472 elif n1 and f in movewithdir: # directory rename, move local |
490 f2 = movewithdir[f] |
473 f2 = movewithdir[f] |
491 actions.append((f2, "dm", (f, fl1), |
474 actions['dm'].append((f2, (f, fl1), |
492 "remote directory rename - move from " + f)) |
475 "remote directory rename - move from " + f)) |
493 elif n1 and f in copy: |
476 elif n1 and f in copy: |
494 f2 = copy[f] |
477 f2 = copy[f] |
495 actions.append((f, "m", (f, f2, f2, False, pa.node()), |
478 actions['m'].append((f, (f, f2, f2, False, pa.node()), |
496 "local copied/moved from " + f2)) |
479 "local copied/moved from " + f2)) |
497 elif n1 and f in ma: # clean, a different, no remote |
480 elif n1 and f in ma: # clean, a different, no remote |
498 if n1 != ma[f]: |
481 if n1 != ma[f]: |
499 if acceptremote: |
482 if acceptremote: |
500 actions.append((f, "r", None, "remote delete")) |
483 actions['r'].append((f, None, "remote delete")) |
501 else: |
484 else: |
502 actions.append((f, "cd", None, "prompt changed/deleted")) |
485 actions['cd'].append((f, None, "prompt changed/deleted")) |
503 elif n1[20:] == "a": # added, no remote |
486 elif n1[20:] == "a": # added, no remote |
504 actions.append((f, "f", None, "remote deleted")) |
487 actions['f'].append((f, None, "remote deleted")) |
505 else: |
488 else: |
506 actions.append((f, "r", None, "other deleted")) |
489 actions['r'].append((f, None, "other deleted")) |
507 elif n2 and f in movewithdir: |
490 elif n2 and f in movewithdir: |
508 f2 = movewithdir[f] |
491 f2 = movewithdir[f] |
509 actions.append((f2, "dg", (f, fl2), |
492 actions['dg'].append((f2, (f, fl2), |
510 "local directory rename - get from " + f)) |
493 "local directory rename - get from " + f)) |
511 elif n2 and f in copy: |
494 elif n2 and f in copy: |
512 f2 = copy[f] |
495 f2 = copy[f] |
513 if f2 in m2: |
496 if f2 in m2: |
514 actions.append((f, "m", (f2, f, f2, False, pa.node()), |
497 actions['m'].append((f, (f2, f, f2, False, pa.node()), |
515 "remote copied from " + f2)) |
498 "remote copied from " + f2)) |
516 else: |
499 else: |
517 actions.append((f, "m", (f2, f, f2, True, pa.node()), |
500 actions['m'].append((f, (f2, f, f2, True, pa.node()), |
518 "remote moved from " + f2)) |
501 "remote moved from " + f2)) |
519 elif n2 and f not in ma: |
502 elif n2 and f not in ma: |
520 # local unknown, remote created: the logic is described by the |
503 # local unknown, remote created: the logic is described by the |
521 # following table: |
504 # following table: |
522 # |
505 # |
528 # y y y | merge |
511 # y y y | merge |
529 # |
512 # |
530 # Checking whether the files are different is expensive, so we |
513 # Checking whether the files are different is expensive, so we |
531 # don't do that when we can avoid it. |
514 # don't do that when we can avoid it. |
532 if force and not branchmerge: |
515 if force and not branchmerge: |
533 actions.append((f, "g", (fl2,), "remote created")) |
516 actions['g'].append((f, (fl2,), "remote created")) |
534 else: |
517 else: |
535 different = _checkunknownfile(repo, wctx, p2, f) |
518 different = _checkunknownfile(repo, wctx, p2, f) |
536 if force and branchmerge and different: |
519 if force and branchmerge and different: |
537 # FIXME: This is wrong - f is not in ma ... |
520 # FIXME: This is wrong - f is not in ma ... |
538 actions.append((f, "m", (f, f, f, False, pa.node()), |
521 actions['m'].append((f, (f, f, f, False, pa.node()), |
539 "remote differs from untracked local")) |
522 "remote differs from untracked local")) |
540 elif not force and different: |
523 elif not force and different: |
541 aborts.append((f, "ud")) |
524 aborts.append((f, "ud")) |
542 else: |
525 else: |
543 actions.append((f, "g", (fl2,), "remote created")) |
526 actions['g'].append((f, (fl2,), "remote created")) |
544 elif n2 and n2 != ma[f]: |
527 elif n2 and n2 != ma[f]: |
545 different = _checkunknownfile(repo, wctx, p2, f) |
528 different = _checkunknownfile(repo, wctx, p2, f) |
546 if not force and different: |
529 if not force and different: |
547 aborts.append((f, "ud")) |
530 aborts.append((f, "ud")) |
548 else: |
531 else: |
549 # if different: old untracked f may be overwritten and lost |
532 # if different: old untracked f may be overwritten and lost |
550 if acceptremote: |
533 if acceptremote: |
551 actions.append((f, "g", (m2.flags(f),), |
534 actions['g'].append((f, (m2.flags(f),), |
552 "remote recreating")) |
535 "remote recreating")) |
553 else: |
536 else: |
554 actions.append((f, "dc", (m2.flags(f),), |
537 actions['dc'].append((f, (m2.flags(f),), |
555 "prompt deleted/changed")) |
538 "prompt deleted/changed")) |
556 |
539 |
557 for f, m in sorted(aborts): |
540 for f, m in sorted(aborts): |
558 if m == "ud": |
541 if m == "ud": |
559 repo.ui.warn(_("%s: untracked file differs\n") % f) |
542 repo.ui.warn(_("%s: untracked file differs\n") % f) |
675 if os.path.lexists(repo.wjoin(f)): |
652 if os.path.lexists(repo.wjoin(f)): |
676 repo.ui.debug("removing %s\n" % f) |
653 repo.ui.debug("removing %s\n" % f) |
677 audit(f) |
654 audit(f) |
678 util.unlinkpath(repo.wjoin(f)) |
655 util.unlinkpath(repo.wjoin(f)) |
679 |
656 |
680 numupdates = len([a for a in actions if a[1] != 'k']) |
657 numupdates = sum(len(l) for m, l in actions.items() if m != 'k') |
681 workeractions = [a for a in actions if a[1] in 'gr'] |
658 |
682 updateactions = [a for a in workeractions if a[1] == 'g'] |
659 if [a for a in actions['r'] if a[0] == '.hgsubstate']: |
683 updated = len(updateactions) |
|
684 removeactions = [a for a in workeractions if a[1] == 'r'] |
|
685 removed = len(removeactions) |
|
686 actions = [a for a in actions if a[1] not in 'gr'] |
|
687 |
|
688 hgsub = [a[1] for a in workeractions if a[0] == '.hgsubstate'] |
|
689 if hgsub and hgsub[0] == 'r': |
|
690 subrepo.submerge(repo, wctx, mctx, wctx, overwrite) |
660 subrepo.submerge(repo, wctx, mctx, wctx, overwrite) |
691 |
661 |
692 # remove in parallel (must come first) |
662 # remove in parallel (must come first) |
693 z = 0 |
663 z = 0 |
694 prog = worker.worker(repo.ui, 0.001, batchremove, (repo,), removeactions) |
664 prog = worker.worker(repo.ui, 0.001, batchremove, (repo,), actions['r']) |
695 for i, item in prog: |
665 for i, item in prog: |
696 z += i |
666 z += i |
697 progress(_updating, z, item=item, total=numupdates, unit=_files) |
667 progress(_updating, z, item=item, total=numupdates, unit=_files) |
|
668 removed = len(actions['r']) |
698 |
669 |
699 # get in parallel |
670 # get in parallel |
700 prog = worker.worker(repo.ui, 0.001, batchget, (repo, mctx), updateactions) |
671 prog = worker.worker(repo.ui, 0.001, batchget, (repo, mctx), actions['g']) |
701 for i, item in prog: |
672 for i, item in prog: |
702 z += i |
673 z += i |
703 progress(_updating, z, item=item, total=numupdates, unit=_files) |
674 progress(_updating, z, item=item, total=numupdates, unit=_files) |
704 |
675 updated = len(actions['g']) |
705 if hgsub and hgsub[0] == 'g': |
676 |
|
677 if [a for a in actions['g'] if a[0] == '.hgsubstate']: |
706 subrepo.submerge(repo, wctx, mctx, wctx, overwrite) |
678 subrepo.submerge(repo, wctx, mctx, wctx, overwrite) |
707 |
679 |
708 for f, m, args, msg in actions: |
680 if True: |
709 |
681 |
710 # forget (manifest only, just log it) (must come first) |
682 # forget (manifest only, just log it) (must come first) |
711 if m == "f": |
683 for f, args, msg in actions['f']: |
712 repo.ui.debug(" %s: %s -> f\n" % (f, msg)) |
684 repo.ui.debug(" %s: %s -> f\n" % (f, msg)) |
713 z += 1 |
685 z += 1 |
714 progress(_updating, z, item=f, total=numupdates, unit=_files) |
686 progress(_updating, z, item=f, total=numupdates, unit=_files) |
715 |
687 |
716 # re-add (manifest only, just log it) |
688 # re-add (manifest only, just log it) |
717 elif m == "a": |
689 for f, args, msg in actions['a']: |
718 repo.ui.debug(" %s: %s -> a\n" % (f, msg)) |
690 repo.ui.debug(" %s: %s -> a\n" % (f, msg)) |
719 z += 1 |
691 z += 1 |
720 progress(_updating, z, item=f, total=numupdates, unit=_files) |
692 progress(_updating, z, item=f, total=numupdates, unit=_files) |
721 |
693 |
722 # keep (noop, just log it) |
694 # keep (noop, just log it) |
723 elif m == "k": |
695 for f, args, msg in actions['k']: |
724 repo.ui.debug(" %s: %s -> k\n" % (f, msg)) |
696 repo.ui.debug(" %s: %s -> k\n" % (f, msg)) |
725 # no progress |
697 # no progress |
726 |
698 |
727 # merge |
699 # merge |
728 elif m == "m": |
700 for f, args, msg in actions['m']: |
729 repo.ui.debug(" %s: %s -> m\n" % (f, msg)) |
701 repo.ui.debug(" %s: %s -> m\n" % (f, msg)) |
730 z += 1 |
702 z += 1 |
731 progress(_updating, z, item=f, total=numupdates, unit=_files) |
703 progress(_updating, z, item=f, total=numupdates, unit=_files) |
732 f1, f2, fa, move, anc = args |
704 f1, f2, fa, move, anc = args |
733 if f == '.hgsubstate': # subrepo states need updating |
705 if f == '.hgsubstate': # subrepo states need updating |
755 repo.wwrite(f, wctx.filectx(f0).data(), flags) |
727 repo.wwrite(f, wctx.filectx(f0).data(), flags) |
756 util.unlinkpath(repo.wjoin(f0)) |
728 util.unlinkpath(repo.wjoin(f0)) |
757 updated += 1 |
729 updated += 1 |
758 |
730 |
759 # local directory rename, get |
731 # local directory rename, get |
760 elif m == "dg": |
732 for f, args, msg in actions['dg']: |
761 repo.ui.debug(" %s: %s -> dg\n" % (f, msg)) |
733 repo.ui.debug(" %s: %s -> dg\n" % (f, msg)) |
762 z += 1 |
734 z += 1 |
763 progress(_updating, z, item=f, total=numupdates, unit=_files) |
735 progress(_updating, z, item=f, total=numupdates, unit=_files) |
764 f0, flags = args |
736 f0, flags = args |
765 repo.ui.note(_("getting %s to %s\n") % (f0, f)) |
737 repo.ui.note(_("getting %s to %s\n") % (f0, f)) |
766 repo.wwrite(f, mctx.filectx(f0).data(), flags) |
738 repo.wwrite(f, mctx.filectx(f0).data(), flags) |
767 updated += 1 |
739 updated += 1 |
768 |
740 |
769 # divergent renames |
741 # divergent renames |
770 elif m == "dr": |
742 for f, args, msg in actions['dr']: |
771 repo.ui.debug(" %s: %s -> dr\n" % (f, msg)) |
743 repo.ui.debug(" %s: %s -> dr\n" % (f, msg)) |
772 z += 1 |
744 z += 1 |
773 progress(_updating, z, item=f, total=numupdates, unit=_files) |
745 progress(_updating, z, item=f, total=numupdates, unit=_files) |
774 fl, = args |
746 fl, = args |
775 repo.ui.warn(_("note: possible conflict - %s was renamed " |
747 repo.ui.warn(_("note: possible conflict - %s was renamed " |
776 "multiple times to:\n") % f) |
748 "multiple times to:\n") % f) |
777 for nf in fl: |
749 for nf in fl: |
778 repo.ui.warn(" %s\n" % nf) |
750 repo.ui.warn(" %s\n" % nf) |
779 |
751 |
780 # rename and delete |
752 # rename and delete |
781 elif m == "rd": |
753 for f, args, msg in actions['rd']: |
782 repo.ui.debug(" %s: %s -> rd\n" % (f, msg)) |
754 repo.ui.debug(" %s: %s -> rd\n" % (f, msg)) |
783 z += 1 |
755 z += 1 |
784 progress(_updating, z, item=f, total=numupdates, unit=_files) |
756 progress(_updating, z, item=f, total=numupdates, unit=_files) |
785 fl, = args |
757 fl, = args |
786 repo.ui.warn(_("note: possible conflict - %s was deleted " |
758 repo.ui.warn(_("note: possible conflict - %s was deleted " |
787 "and renamed to:\n") % f) |
759 "and renamed to:\n") % f) |
788 for nf in fl: |
760 for nf in fl: |
789 repo.ui.warn(" %s\n" % nf) |
761 repo.ui.warn(" %s\n" % nf) |
790 |
762 |
791 # exec |
763 # exec |
792 elif m == "e": |
764 for f, args, msg in actions['e']: |
793 repo.ui.debug(" %s: %s -> e\n" % (f, msg)) |
765 repo.ui.debug(" %s: %s -> e\n" % (f, msg)) |
794 z += 1 |
766 z += 1 |
795 progress(_updating, z, item=f, total=numupdates, unit=_files) |
767 progress(_updating, z, item=f, total=numupdates, unit=_files) |
796 flags, = args |
768 flags, = args |
797 audit(f) |
769 audit(f) |
816 repo.ui.status( |
788 repo.ui.status( |
817 _("note: merging %s and %s using bids from ancestors %s\n") % |
789 _("note: merging %s and %s using bids from ancestors %s\n") % |
818 (wctx, mctx, _(' and ').join(str(anc) for anc in ancestors))) |
790 (wctx, mctx, _(' and ').join(str(anc) for anc in ancestors))) |
819 |
791 |
820 # Call for bids |
792 # Call for bids |
821 fbids = {} # mapping filename to list af action bids |
793 fbids = {} # mapping filename to bids (action method to list af actions) |
822 for ancestor in ancestors: |
794 for ancestor in ancestors: |
823 repo.ui.note(_('\ncalculating bids for ancestor %s\n') % ancestor) |
795 repo.ui.note(_('\ncalculating bids for ancestor %s\n') % ancestor) |
824 actions = manifestmerge(repo, wctx, mctx, ancestor, |
796 actions = manifestmerge(repo, wctx, mctx, ancestor, |
825 branchmerge, force, |
797 branchmerge, force, |
826 partial, acceptremote, followcopies) |
798 partial, acceptremote, followcopies) |
827 for a in sorted(actions, key=lambda a: (a[1], a)): |
799 for m, l in sorted(actions.items()): |
828 f, m, args, msg = a |
800 for a in l: |
829 repo.ui.debug(' %s: %s -> %s\n' % (f, msg, m)) |
801 f, args, msg = a |
830 if f in fbids: |
802 repo.ui.debug(' %s: %s -> %s\n' % (f, msg, m)) |
831 fbids[f].append(a) |
803 if f in fbids: |
832 else: |
804 d = fbids[f] |
833 fbids[f] = [a] |
805 if m in d: |
|
806 d[m].append(a) |
|
807 else: |
|
808 d[m] = [a] |
|
809 else: |
|
810 fbids[f] = {m: [a]} |
834 |
811 |
835 # Pick the best bid for each file |
812 # Pick the best bid for each file |
836 repo.ui.note(_('\nauction for merging merge bids\n')) |
813 repo.ui.note(_('\nauction for merging merge bids\n')) |
837 actions = [] |
814 actions = dict((m, []) for m in actions.keys()) |
838 for f, bidsl in sorted(fbids.items()): |
815 for f, bids in sorted(fbids.items()): |
|
816 # bids is a mapping from action method to list af actions |
839 # Consensus? |
817 # Consensus? |
840 a0 = bidsl[0] |
818 if len(bids) == 1: # all bids are the same kind of method |
841 if util.all(a == a0 for a in bidsl[1:]): # len(bidsl) is > 1 |
819 m, l = bids.items()[0] |
842 repo.ui.note(" %s: consensus for %s\n" % (f, a0[1])) |
820 if util.all(a == l[0] for a in l[1:]): # len(bids) is > 1 |
843 actions.append(a0) |
821 repo.ui.note(" %s: consensus for %s\n" % (f, m)) |
844 continue |
822 actions[m].append(l[0]) |
845 # Group bids by kind of action |
823 continue |
846 bids = {} |
|
847 for a in bidsl: |
|
848 m = a[1] |
|
849 if m in bids: |
|
850 bids[m].append(a) |
|
851 else: |
|
852 bids[m] = [a] |
|
853 # If keep is an option, just do it. |
824 # If keep is an option, just do it. |
854 if "k" in bids: |
825 if "k" in bids: |
855 repo.ui.note(" %s: picking 'keep' action\n" % f) |
826 repo.ui.note(" %s: picking 'keep' action\n" % f) |
856 actions.append(bids["k"][0]) |
827 actions['k'].append(bids["k"][0]) |
857 continue |
828 continue |
858 # If all gets agree [how could they not?], just do it. |
829 # If there are gets and they all agree [how could they not?], do it. |
859 if "g" in bids: |
830 if "g" in bids: |
860 ga0 = bids["g"][0] |
831 ga0 = bids["g"][0] |
861 if util.all(a == ga0 for a in bids["g"][1:]): |
832 if util.all(a == ga0 for a in bids["g"][1:]): |
862 repo.ui.note(" %s: picking 'get' action\n" % f) |
833 repo.ui.note(" %s: picking 'get' action\n" % f) |
863 actions.append(ga0) |
834 actions['g'].append(ga0) |
864 continue |
835 continue |
865 # TODO: Consider other simple actions such as mode changes |
836 # TODO: Consider other simple actions such as mode changes |
866 # Handle inefficient democrazy. |
837 # Handle inefficient democrazy. |
867 repo.ui.note(_(' %s: multiple bids for merge action:\n') % f) |
838 repo.ui.note(_(' %s: multiple bids for merge action:\n') % f) |
868 for _f, m, args, msg in bidsl: |
839 for m, l in sorted(bids.items()): |
869 repo.ui.note(' %s -> %s\n' % (msg, m)) |
840 for _f, args, msg in l: |
|
841 repo.ui.note(' %s -> %s\n' % (msg, m)) |
870 # Pick random action. TODO: Instead, prompt user when resolving |
842 # Pick random action. TODO: Instead, prompt user when resolving |
871 a0 = bidsl[0] |
843 m, l = bids.items()[0] |
872 repo.ui.warn(_(' %s: ambiguous merge - picked %s action\n') % |
844 repo.ui.warn(_(' %s: ambiguous merge - picked %s action\n') % |
873 (f, a0[1])) |
845 (f, m)) |
874 actions.append(a0) |
846 actions[m].append(l[0]) |
875 continue |
847 continue |
876 repo.ui.note(_('end of auction\n\n')) |
848 repo.ui.note(_('end of auction\n\n')) |
877 |
849 |
878 # Filter out prompts. |
|
879 newactions, prompts = [], [] |
|
880 for a in actions: |
|
881 if a[1] in ("cd", "dc"): |
|
882 prompts.append(a) |
|
883 else: |
|
884 newactions.append(a) |
|
885 # Prompt and create actions. TODO: Move this towards resolve phase. |
850 # Prompt and create actions. TODO: Move this towards resolve phase. |
886 for f, m, args, msg in sorted(prompts): |
851 if True: |
887 if m == "cd": |
852 for f, args, msg in actions['cd']: |
888 if repo.ui.promptchoice( |
853 if repo.ui.promptchoice( |
889 _("local changed %s which remote deleted\n" |
854 _("local changed %s which remote deleted\n" |
890 "use (c)hanged version or (d)elete?" |
855 "use (c)hanged version or (d)elete?" |
891 "$$ &Changed $$ &Delete") % f, 0): |
856 "$$ &Changed $$ &Delete") % f, 0): |
892 newactions.append((f, "r", None, "prompt delete")) |
857 actions['r'].append((f, None, "prompt delete")) |
893 else: |
858 else: |
894 newactions.append((f, "a", None, "prompt keep")) |
859 actions['a'].append((f, None, "prompt keep")) |
895 elif m == "dc": |
860 del actions['cd'][:] |
|
861 |
|
862 for f, args, msg in actions['dc']: |
896 flags, = args |
863 flags, = args |
897 if repo.ui.promptchoice( |
864 if repo.ui.promptchoice( |
898 _("remote changed %s which local deleted\n" |
865 _("remote changed %s which local deleted\n" |
899 "use (c)hanged version or leave (d)eleted?" |
866 "use (c)hanged version or leave (d)eleted?" |
900 "$$ &Changed $$ &Deleted") % f, 0) == 0: |
867 "$$ &Changed $$ &Deleted") % f, 0) == 0: |
901 newactions.append((f, "g", (flags,), "prompt recreating")) |
868 actions['g'].append((f, (flags,), "prompt recreating")) |
902 else: assert False, m |
869 del actions['dc'][:] |
903 |
870 |
904 if wctx.rev() is None: |
871 if wctx.rev() is None: |
905 newactions += _forgetremoved(wctx, mctx, branchmerge) |
872 ractions, factions = _forgetremoved(wctx, mctx, branchmerge) |
906 |
873 actions['r'].extend(ractions) |
907 return newactions |
874 actions['f'].extend(factions) |
|
875 |
|
876 return actions |
908 |
877 |
909 def recordupdates(repo, actions, branchmerge): |
878 def recordupdates(repo, actions, branchmerge): |
910 "record merge actions to the dirstate" |
879 "record merge actions to the dirstate" |
911 |
880 if True: |
912 for f, m, args, msg in actions: |
|
913 |
|
914 # remove (must come first) |
881 # remove (must come first) |
915 if m == "r": # remove |
882 for f, args, msg in actions['r']: |
916 if branchmerge: |
883 if branchmerge: |
917 repo.dirstate.remove(f) |
884 repo.dirstate.remove(f) |
918 else: |
885 else: |
919 repo.dirstate.drop(f) |
886 repo.dirstate.drop(f) |
920 |
887 |
921 # forget (must come first) |
888 # forget (must come first) |
922 elif m == "f": |
889 for f, args, msg in actions['f']: |
923 repo.dirstate.drop(f) |
890 repo.dirstate.drop(f) |
924 |
891 |
925 # re-add |
892 # re-add |
926 elif m == "a": |
893 for f, args, msg in actions['a']: |
927 if not branchmerge: |
894 if not branchmerge: |
928 repo.dirstate.add(f) |
895 repo.dirstate.add(f) |
929 |
896 |
930 # exec change |
897 # exec change |
931 elif m == "e": |
898 for f, args, msg in actions['e']: |
932 repo.dirstate.normallookup(f) |
899 repo.dirstate.normallookup(f) |
933 |
900 |
934 # keep |
901 # keep |
935 elif m == "k": |
902 for f, args, msg in actions['k']: |
936 pass |
903 pass |
937 |
904 |
938 # get |
905 # get |
939 elif m == "g": |
906 for f, args, msg in actions['g']: |
940 if branchmerge: |
907 if branchmerge: |
941 repo.dirstate.otherparent(f) |
908 repo.dirstate.otherparent(f) |
942 else: |
909 else: |
943 repo.dirstate.normal(f) |
910 repo.dirstate.normal(f) |
944 |
911 |
945 # merge |
912 # merge |
946 elif m == "m": |
913 for f, args, msg in actions['m']: |
947 f1, f2, fa, move, anc = args |
914 f1, f2, fa, move, anc = args |
948 if branchmerge: |
915 if branchmerge: |
949 # We've done a branch merge, mark this file as merged |
916 # We've done a branch merge, mark this file as merged |
950 # so that we properly record the merger later |
917 # so that we properly record the merger later |
951 repo.dirstate.merge(f) |
918 repo.dirstate.merge(f) |