Mercurial > public > mercurial-scm > hg-stable
comparison mercurial/merge.py @ 23637:13f53a2aa342
merge: write manifestmerge() using dictionary with entry per file
In the same vein as 38e55e55ae4d (largefiles: rewrite merge code using
dictionary with entry per file, 2014-12-09), rewrite manifestmerge()
itself as dictionary with the filename as key. This will let us
simplify some of the other code in merge.py and eventually drop the
conversion in the largefiles code.
No difference in speed could be detected (well within the noise level
when run in Mozilla repo).
author | Martin von Zweigbergk <martinvonz@google.com> |
---|---|
date | Mon, 08 Dec 2014 13:24:10 -0800 |
parents | 7cc0fb0080b6 |
children | 09be050ca98c |
comparison
equal
deleted
inserted
replaced
23636:ab3b8d8fd2a0 | 23637:13f53a2aa342 |
---|---|
373 branchmerge and force are as passed in to update | 373 branchmerge and force are as passed in to update |
374 partial = function to filter file lists | 374 partial = function to filter file lists |
375 acceptremote = accept the incoming changes without prompting | 375 acceptremote = accept the incoming changes without prompting |
376 """ | 376 """ |
377 | 377 |
378 actions = dict((m, []) for m in 'a f g cd dc r dm dg m e k'.split()) | |
379 copy, movewithdir, diverge, renamedelete = {}, {}, {}, {} | 378 copy, movewithdir, diverge, renamedelete = {}, {}, {}, {} |
380 | 379 |
381 # manifests fetched in order are going to be faster, so prime the caches | 380 # manifests fetched in order are going to be faster, so prime the caches |
382 [x.manifest() for x in | 381 [x.manifest() for x in |
383 sorted(wctx.parents() + [p2, pa], key=lambda x: x.rev())] | 382 sorted(wctx.parents() + [p2, pa], key=lambda x: x.rev())] |
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 |