mercurial/merge.py
changeset 23637 13f53a2aa342
parent 23544 7cc0fb0080b6
child 23638 09be050ca98c
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