mercurial/merge.py
changeset 4674 723e0ddb6ada
parent 4635 63b9d2deed48
child 4682 dc5920ea12f8
equal deleted inserted replaced
4673:d8442fc0da8d 4674:723e0ddb6ada
   153         old.sort()
   153         old.sort()
   154         return old
   154         return old
   155 
   155 
   156     copy = {}
   156     copy = {}
   157     fullcopy = {}
   157     fullcopy = {}
       
   158     diverge = {}
   158 
   159 
   159     def checkcopies(c, man):
   160     def checkcopies(c, man):
   160         '''check possible copies for filectx c'''
   161         '''check possible copies for filectx c'''
   161         for of in findold(c):
   162         for of in findold(c):
       
   163             fullcopy[c.path()] = of # remember for dir rename detection
   162             if of not in man: # original file not in other manifest?
   164             if of not in man: # original file not in other manifest?
       
   165                 if of in ma:
       
   166                     diverge.setdefault(of, []).append(c.path())
   163                 continue
   167                 continue
   164             c2 = ctx(of, man[of])
   168             c2 = ctx(of, man[of])
   165             ca = c.ancestor(c2)
   169             ca = c.ancestor(c2)
   166             if not ca: # unrelated?
   170             if not ca: # unrelated?
   167                 continue
   171                 continue
   168             # named changed on only one side?
   172             # named changed on only one side?
   169             if ca.path() == c.path() or ca.path() == c2.path():
   173             if ca.path() == c.path() or ca.path() == c2.path():
   170                 fullcopy[c.path()] = of # remember for dir rename detection
       
   171                 if c == ca or c2 == ca: # no merge needed, ignore copy
   174                 if c == ca or c2 == ca: # no merge needed, ignore copy
   172                     continue
   175                     continue
   173                 copy[c.path()] = of
   176                 copy[c.path()] = of
   174 
   177 
   175     if not repo.ui.configbool("merge", "followcopies", True):
   178     if not repo.ui.configbool("merge", "followcopies", True):
   176         return {}
   179         return {}, {}
   177 
   180 
   178     # avoid silly behavior for update from empty dir
   181     # avoid silly behavior for update from empty dir
   179     if not m1 or not m2 or not ma:
   182     if not m1 or not m2 or not ma:
   180         return {}
   183         return {}, {}
   181 
   184 
   182     u1 = nonoverlap(m1, m2, ma)
   185     u1 = nonoverlap(m1, m2, ma)
   183     u2 = nonoverlap(m2, m1, ma)
   186     u2 = nonoverlap(m2, m1, ma)
   184 
   187 
   185     for f in u1:
   188     for f in u1:
   186         checkcopies(ctx(f, m1[f]), m2)
   189         checkcopies(ctx(f, m1[f]), m2)
   187 
   190 
   188     for f in u2:
   191     for f in u2:
   189         checkcopies(ctx(f, m2[f]), m1)
   192         checkcopies(ctx(f, m2[f]), m1)
   190 
   193 
       
   194     d2 = {}
       
   195     for of, fl in diverge.items():
       
   196         for f in fl:
       
   197             fo = list(fl)
       
   198             fo.remove(f)
       
   199             d2[f] = (of, fo)
       
   200     #diverge = d2
       
   201 
   191     if not fullcopy or not repo.ui.configbool("merge", "followdirs", True):
   202     if not fullcopy or not repo.ui.configbool("merge", "followdirs", True):
   192         return copy
   203         return copy, diverge
   193 
   204 
   194     # generate a directory move map
   205     # generate a directory move map
   195     d1, d2 = dirs(m1), dirs(m2)
   206     d1, d2 = dirs(m1), dirs(m2)
   196     invalid = {}
   207     invalid = {}
   197     dirmove = {}
   208     dirmove = {}
   221             del dirmove[i]
   232             del dirmove[i]
   222 
   233 
   223     del d1, d2, invalid
   234     del d1, d2, invalid
   224 
   235 
   225     if not dirmove:
   236     if not dirmove:
   226         return copy
   237         return copy, diverge
   227 
   238 
   228     # check unaccounted nonoverlapping files against directory moves
   239     # check unaccounted nonoverlapping files against directory moves
   229     for f in u1 + u2:
   240     for f in u1 + u2:
   230         if f not in fullcopy:
   241         if f not in fullcopy:
   231             for d in dirmove:
   242             for d in dirmove:
   232                 if f.startswith(d):
   243                 if f.startswith(d):
   233                     # new file added in a directory that was moved, move it
   244                     # new file added in a directory that was moved, move it
   234                     copy[f] = dirmove[d] + f[len(d):]
   245                     copy[f] = dirmove[d] + f[len(d):]
   235                     break
   246                     break
   236 
   247 
   237     return copy
   248     return copy, diverge
   238 
   249 
   239 def manifestmerge(repo, p1, p2, pa, overwrite, partial):
   250 def manifestmerge(repo, p1, p2, pa, overwrite, partial):
   240     """
   251     """
   241     Merge p1 and p2 with ancestor ma and generate merge action list
   252     Merge p1 and p2 with ancestor ma and generate merge action list
   242 
   253 
   252     m2 = p2.manifest()
   263     m2 = p2.manifest()
   253     ma = pa.manifest()
   264     ma = pa.manifest()
   254     backwards = (pa == p2)
   265     backwards = (pa == p2)
   255     action = []
   266     action = []
   256     copy = {}
   267     copy = {}
       
   268     diverge = {}
   257 
   269 
   258     def fmerge(f, f2=None, fa=None):
   270     def fmerge(f, f2=None, fa=None):
   259         """merge flags"""
   271         """merge flags"""
   260         if not f2:
   272         if not f2:
   261             f2 = f
   273             f2 = f
   271     def act(msg, m, f, *args):
   283     def act(msg, m, f, *args):
   272         repo.ui.debug(" %s: %s -> %s\n" % (f, msg, m))
   284         repo.ui.debug(" %s: %s -> %s\n" % (f, msg, m))
   273         action.append((f, m) + args)
   285         action.append((f, m) + args)
   274 
   286 
   275     if not (backwards or overwrite):
   287     if not (backwards or overwrite):
   276         copy = findcopies(repo, m1, m2, ma, pa.rev())
   288         copy, diverge = findcopies(repo, m1, m2, ma, pa.rev())
       
   289 
       
   290     for of, fl in diverge.items():
       
   291         act("divergent renames", "dr", of, fl)
       
   292 
   277     copied = dict.fromkeys(copy.values())
   293     copied = dict.fromkeys(copy.values())
   278 
   294 
   279     # Compare manifests
   295     # Compare manifests
   280     for f, n in m1.iteritems():
   296     for f, n in m1.iteritems():
   281         if partial and not partial(f):
   297         if partial and not partial(f):
   408             if f2:
   424             if f2:
   409                 repo.ui.note(_("getting %s to %s\n") % (f2, fd))
   425                 repo.ui.note(_("getting %s to %s\n") % (f2, fd))
   410                 t = mctx.filectx(f2).data()
   426                 t = mctx.filectx(f2).data()
   411                 repo.wwrite(fd, t, flags)
   427                 repo.wwrite(fd, t, flags)
   412             updated += 1
   428             updated += 1
       
   429         elif m == "dr": # divergent renames
       
   430             fl = a[2]
       
   431             repo.ui.warn("warning: detected divergent renames of %s to:\n" % f)
       
   432             for nf in fl:
       
   433                 repo.ui.warn(" %s\n" % nf)
   413         elif m == "e": # exec
   434         elif m == "e": # exec
   414             flags = a[2]
   435             flags = a[2]
   415             util.set_exec(repo.wjoin(f), flags)
   436             util.set_exec(repo.wjoin(f), flags)
   416 
   437 
   417     return updated, merged, removed, unresolved
   438     return updated, merged, removed, unresolved