404 |
403 |
405 aborts = [] |
404 aborts = [] |
406 # Compare manifests |
405 # Compare manifests |
407 diff = m1.diff(m2) |
406 diff = m1.diff(m2) |
408 |
407 |
|
408 actions = {} |
409 for f, ((n1, fl1), (n2, fl2)) in diff.iteritems(): |
409 for f, ((n1, fl1), (n2, fl2)) in diff.iteritems(): |
410 if partial and not partial(f): |
410 if partial and not partial(f): |
411 continue |
411 continue |
412 if n1 and n2: # file exists on both local and remote side |
412 if n1 and n2: # file exists on both local and remote side |
413 if f not in ma: |
413 if f not in ma: |
414 fa = copy.get(f, None) |
414 fa = copy.get(f, None) |
415 if fa is not None: |
415 if fa is not None: |
416 actions['m'].append((f, (f, f, fa, False, pa.node()), |
416 actions[f] = ('m', (f, f, fa, False, pa.node()), |
417 "both renamed from " + fa)) |
417 "both renamed from " + fa) |
418 else: |
418 else: |
419 actions['m'].append((f, (f, f, None, False, pa.node()), |
419 actions[f] = ('m', (f, f, None, False, pa.node()), |
420 "both created")) |
420 "both created") |
421 else: |
421 else: |
422 a = ma[f] |
422 a = ma[f] |
423 fla = ma.flags(f) |
423 fla = ma.flags(f) |
424 nol = 'l' not in fl1 + fl2 + fla |
424 nol = 'l' not in fl1 + fl2 + fla |
425 if n2 == a and fl2 == fla: |
425 if n2 == a and fl2 == fla: |
426 actions['k'].append((f, (), "remote unchanged")) |
426 actions[f] = ('k' , (), "remote unchanged") |
427 elif n1 == a and fl1 == fla: # local unchanged - use remote |
427 elif n1 == a and fl1 == fla: # local unchanged - use remote |
428 if n1 == n2: # optimization: keep local content |
428 if n1 == n2: # optimization: keep local content |
429 actions['e'].append((f, (fl2,), "update permissions")) |
429 actions[f] = ('e', (fl2,), "update permissions") |
430 else: |
430 else: |
431 actions['g'].append((f, (fl2,), "remote is newer")) |
431 actions[f] = ('g', (fl2,), "remote is newer") |
432 elif nol and n2 == a: # remote only changed 'x' |
432 elif nol and n2 == a: # remote only changed 'x' |
433 actions['e'].append((f, (fl2,), "update permissions")) |
433 actions[f] = ('e', (fl2,), "update permissions") |
434 elif nol and n1 == a: # local only changed 'x' |
434 elif nol and n1 == a: # local only changed 'x' |
435 actions['g'].append((f, (fl1,), "remote is newer")) |
435 actions[f] = ('g', (fl1,), "remote is newer") |
436 else: # both changed something |
436 else: # both changed something |
437 actions['m'].append((f, (f, f, f, False, pa.node()), |
437 actions[f] = ('m', (f, f, f, False, pa.node()), |
438 "versions differ")) |
438 "versions differ") |
439 elif n1: # file exists only on local side |
439 elif n1: # file exists only on local side |
440 if f in copied: |
440 if f in copied: |
441 pass # we'll deal with it on m2 side |
441 pass # we'll deal with it on m2 side |
442 elif f in movewithdir: # directory rename, move local |
442 elif f in movewithdir: # directory rename, move local |
443 f2 = movewithdir[f] |
443 f2 = movewithdir[f] |
444 if f2 in m2: |
444 if f2 in m2: |
445 actions['m'].append((f2, (f, f2, None, True, pa.node()), |
445 actions[f2] = ('m', (f, f2, None, True, pa.node()), |
446 "remote directory rename, both created")) |
446 "remote directory rename, both created") |
447 else: |
447 else: |
448 actions['dm'].append((f2, (f, fl1), |
448 actions[f2] = ('dm', (f, fl1), |
449 "remote directory rename - move from " + f)) |
449 "remote directory rename - move from " + f) |
450 elif f in copy: |
450 elif f in copy: |
451 f2 = copy[f] |
451 f2 = copy[f] |
452 actions['m'].append((f, (f, f2, f2, False, pa.node()), |
452 actions[f] = ('m', (f, f2, f2, False, pa.node()), |
453 "local copied/moved from " + f2)) |
453 "local copied/moved from " + f2) |
454 elif f in ma: # clean, a different, no remote |
454 elif f in ma: # clean, a different, no remote |
455 if n1 != ma[f]: |
455 if n1 != ma[f]: |
456 if acceptremote: |
456 if acceptremote: |
457 actions['r'].append((f, None, "remote delete")) |
457 actions[f] = ('r', None, "remote delete") |
458 else: |
458 else: |
459 actions['cd'].append((f, None, |
459 actions[f] = ('cd', None, "prompt changed/deleted") |
460 "prompt changed/deleted")) |
|
461 elif n1[20:] == 'a': |
460 elif n1[20:] == 'a': |
462 # This extra 'a' is added by working copy manifest to mark |
461 # This extra 'a' is added by working copy manifest to mark |
463 # the file as locally added. We should forget it instead of |
462 # the file as locally added. We should forget it instead of |
464 # deleting it. |
463 # deleting it. |
465 actions['f'].append((f, None, "remote deleted")) |
464 actions[f] = ('f', None, "remote deleted") |
466 else: |
465 else: |
467 actions['r'].append((f, None, "other deleted")) |
466 actions[f] = ('r', None, "other deleted") |
468 elif n2: # file exists only on remote side |
467 elif n2: # file exists only on remote side |
469 if f in copied: |
468 if f in copied: |
470 pass # we'll deal with it on m1 side |
469 pass # we'll deal with it on m1 side |
471 elif f in movewithdir: |
470 elif f in movewithdir: |
472 f2 = movewithdir[f] |
471 f2 = movewithdir[f] |
473 if f2 in m1: |
472 if f2 in m1: |
474 actions['m'].append((f2, (f2, f, None, False, pa.node()), |
473 actions[f2] = ('m', (f2, f, None, False, pa.node()), |
475 "local directory rename, both created")) |
474 "local directory rename, both created") |
476 else: |
475 else: |
477 actions['dg'].append((f2, (f, fl2), |
476 actions[f2] = ('dg', (f, fl2), |
478 "local directory rename - get from " + f)) |
477 "local directory rename - get from " + f) |
479 elif f in copy: |
478 elif f in copy: |
480 f2 = copy[f] |
479 f2 = copy[f] |
481 if f2 in m2: |
480 if f2 in m2: |
482 actions['m'].append((f, (f2, f, f2, False, pa.node()), |
481 actions[f] = ('m', (f2, f, f2, False, pa.node()), |
483 "remote copied from " + f2)) |
482 "remote copied from " + f2) |
484 else: |
483 else: |
485 actions['m'].append((f, (f2, f, f2, True, pa.node()), |
484 actions[f] = ('m', (f2, f, f2, True, pa.node()), |
486 "remote moved from " + f2)) |
485 "remote moved from " + f2) |
487 elif f not in ma: |
486 elif f not in ma: |
488 # local unknown, remote created: the logic is described by the |
487 # local unknown, remote created: the logic is described by the |
489 # following table: |
488 # following table: |
490 # |
489 # |
491 # force branchmerge different | action |
490 # force branchmerge different | action |
496 # y y y | merge |
495 # y y y | merge |
497 # |
496 # |
498 # Checking whether the files are different is expensive, so we |
497 # Checking whether the files are different is expensive, so we |
499 # don't do that when we can avoid it. |
498 # don't do that when we can avoid it. |
500 if force and not branchmerge: |
499 if force and not branchmerge: |
501 actions['g'].append((f, (fl2,), "remote created")) |
500 actions[f] = ('g', (fl2,), "remote created") |
502 else: |
501 else: |
503 different = _checkunknownfile(repo, wctx, p2, f) |
502 different = _checkunknownfile(repo, wctx, p2, f) |
504 if force and branchmerge and different: |
503 if force and branchmerge and different: |
505 actions['m'].append((f, (f, f, None, False, pa.node()), |
504 actions[f] = ('m', (f, f, None, False, pa.node()), |
506 "remote differs from untracked local")) |
505 "remote differs from untracked local") |
507 elif not force and different: |
506 elif not force and different: |
508 aborts.append((f, 'ud')) |
507 aborts.append((f, 'ud')) |
509 else: |
508 else: |
510 actions['g'].append((f, (fl2,), "remote created")) |
509 actions[f] = ('g', (fl2,), "remote created") |
511 elif n2 != ma[f]: |
510 elif n2 != ma[f]: |
512 different = _checkunknownfile(repo, wctx, p2, f) |
511 different = _checkunknownfile(repo, wctx, p2, f) |
513 if not force and different: |
512 if not force and different: |
514 aborts.append((f, 'ud')) |
513 aborts.append((f, 'ud')) |
515 else: |
514 else: |
516 if acceptremote: |
515 if acceptremote: |
517 actions['g'].append((f, (fl2,), "remote recreating")) |
516 actions[f] = ('g', (fl2,), "remote recreating") |
518 else: |
517 else: |
519 actions['dc'].append((f, (fl2,), |
518 actions[f] = ('dc', (fl2,), "prompt deleted/changed") |
520 "prompt deleted/changed")) |
|
521 |
519 |
522 for f, m in sorted(aborts): |
520 for f, m in sorted(aborts): |
523 if m == 'ud': |
521 if m == 'ud': |
524 repo.ui.warn(_("%s: untracked file differs\n") % f) |
522 repo.ui.warn(_("%s: untracked file differs\n") % f) |
525 else: assert False, m |
523 else: assert False, m |
526 if aborts: |
524 if aborts: |
527 raise util.Abort(_("untracked files in working directory differ " |
525 raise util.Abort(_("untracked files in working directory differ " |
528 "from files in requested revision")) |
526 "from files in requested revision")) |
|
527 |
|
528 # Convert to dictionary-of-lists format |
|
529 actionbyfile = actions |
|
530 actions = dict((m, []) for m in 'a f g cd dc r dm dg m e k'.split()) |
|
531 for f, (m, args, msg) in actionbyfile.iteritems(): |
|
532 actions[m].append((f, args, msg)) |
529 |
533 |
530 return actions, diverge, renamedelete |
534 return actions, diverge, renamedelete |
531 |
535 |
532 def _resolvetrivial(repo, wctx, mctx, ancestor, actions): |
536 def _resolvetrivial(repo, wctx, mctx, ancestor, actions): |
533 """Resolves false conflicts where the nodeid changed but the content |
537 """Resolves false conflicts where the nodeid changed but the content |