mercurial/copies.py
changeset 44758 45f3f35cefe7
parent 44276 30862e226339
child 44916 61719b9658b1
equal deleted inserted replaced
44757:f365dfede78f 44758:45f3f35cefe7
   181     * p1: revision number of first parent
   181     * p1: revision number of first parent
   182     * p2: revision number of first parent
   182     * p2: revision number of first parent
   183     * p1copies: mapping of copies from p1
   183     * p1copies: mapping of copies from p1
   184     * p2copies: mapping of copies from p2
   184     * p2copies: mapping of copies from p2
   185     * removed: a list of removed files
   185     * removed: a list of removed files
       
   186     * ismerged: a callback to know if file was merged in that revision
   186     """
   187     """
   187     cl = repo.changelog
   188     cl = repo.changelog
   188     parents = cl.parentrevs
   189     parents = cl.parentrevs
       
   190 
       
   191     def get_ismerged(rev):
       
   192         ctx = repo[rev]
       
   193 
       
   194         def ismerged(path):
       
   195             if path not in ctx.files():
       
   196                 return False
       
   197             fctx = ctx[path]
       
   198             parents = fctx._filelog.parents(fctx._filenode)
       
   199             nb_parents = 0
       
   200             for n in parents:
       
   201                 if n != node.nullid:
       
   202                     nb_parents += 1
       
   203             return nb_parents >= 2
       
   204 
       
   205         return ismerged
   189 
   206 
   190     if repo.filecopiesmode == b'changeset-sidedata':
   207     if repo.filecopiesmode == b'changeset-sidedata':
   191         changelogrevision = cl.changelogrevision
   208         changelogrevision = cl.changelogrevision
   192         flags = cl.flags
   209         flags = cl.flags
   193 
   210 
   216         # time to save memory.
   233         # time to save memory.
   217         merge_caches = {}
   234         merge_caches = {}
   218 
   235 
   219         def revinfo(rev):
   236         def revinfo(rev):
   220             p1, p2 = parents(rev)
   237             p1, p2 = parents(rev)
       
   238             value = None
   221             if flags(rev) & REVIDX_SIDEDATA:
   239             if flags(rev) & REVIDX_SIDEDATA:
   222                 e = merge_caches.pop(rev, None)
   240                 e = merge_caches.pop(rev, None)
   223                 if e is not None:
   241                 if e is not None:
   224                     return e
   242                     return e
   225                 c = changelogrevision(rev)
   243                 c = changelogrevision(rev)
   226                 p1copies = c.p1copies
   244                 p1copies = c.p1copies
   227                 p2copies = c.p2copies
   245                 p2copies = c.p2copies
   228                 removed = c.filesremoved
   246                 removed = c.filesremoved
   229                 if p1 != node.nullrev and p2 != node.nullrev:
   247                 if p1 != node.nullrev and p2 != node.nullrev:
   230                     # XXX some case we over cache, IGNORE
   248                     # XXX some case we over cache, IGNORE
   231                     merge_caches[rev] = (p1, p2, p1copies, p2copies, removed)
   249                     value = merge_caches[rev] = (
       
   250                         p1,
       
   251                         p2,
       
   252                         p1copies,
       
   253                         p2copies,
       
   254                         removed,
       
   255                         get_ismerged(rev),
       
   256                     )
   232             else:
   257             else:
   233                 p1copies = {}
   258                 p1copies = {}
   234                 p2copies = {}
   259                 p2copies = {}
   235                 removed = []
   260                 removed = []
   236             return p1, p2, p1copies, p2copies, removed
   261 
       
   262             if value is None:
       
   263                 value = (p1, p2, p1copies, p2copies, removed, get_ismerged(rev))
       
   264             return value
   237 
   265 
   238     else:
   266     else:
   239 
   267 
   240         def revinfo(rev):
   268         def revinfo(rev):
   241             p1, p2 = parents(rev)
   269             p1, p2 = parents(rev)
   242             ctx = repo[rev]
   270             ctx = repo[rev]
   243             p1copies, p2copies = ctx._copies
   271             p1copies, p2copies = ctx._copies
   244             removed = ctx.filesremoved()
   272             removed = ctx.filesremoved()
   245             return p1, p2, p1copies, p2copies, removed
   273             return p1, p2, p1copies, p2copies, removed, get_ismerged(rev)
   246 
   274 
   247     return revinfo
   275     return revinfo
   248 
   276 
   249 
   277 
   250 def _changesetforwardcopies(a, b, match):
   278 def _changesetforwardcopies(a, b, match):
   254     repo = a.repo().unfiltered()
   282     repo = a.repo().unfiltered()
   255     children = {}
   283     children = {}
   256     revinfo = _revinfogetter(repo)
   284     revinfo = _revinfogetter(repo)
   257 
   285 
   258     cl = repo.changelog
   286     cl = repo.changelog
       
   287     isancestor = cl.isancestorrev  # XXX we should had chaching to this.
   259     missingrevs = cl.findmissingrevs(common=[a.rev()], heads=[b.rev()])
   288     missingrevs = cl.findmissingrevs(common=[a.rev()], heads=[b.rev()])
   260     mrset = set(missingrevs)
   289     mrset = set(missingrevs)
   261     roots = set()
   290     roots = set()
   262     for r in missingrevs:
   291     for r in missingrevs:
   263         for p in cl.parentrevs(r):
   292         for p in cl.parentrevs(r):
   281     iterrevs = set(from_head)
   310     iterrevs = set(from_head)
   282     iterrevs &= mrset
   311     iterrevs &= mrset
   283     iterrevs.update(roots)
   312     iterrevs.update(roots)
   284     iterrevs.remove(b.rev())
   313     iterrevs.remove(b.rev())
   285     revs = sorted(iterrevs)
   314     revs = sorted(iterrevs)
   286     return _combinechangesetcopies(revs, children, b.rev(), revinfo, match)
   315     return _combinechangesetcopies(
   287 
   316         revs, children, b.rev(), revinfo, match, isancestor
   288 
   317     )
   289 def _combinechangesetcopies(revs, children, targetrev, revinfo, match):
   318 
       
   319 
       
   320 def _combinechangesetcopies(
       
   321     revs, children, targetrev, revinfo, match, isancestor
       
   322 ):
   290     """combine the copies information for each item of iterrevs
   323     """combine the copies information for each item of iterrevs
   291 
   324 
   292     revs: sorted iterable of revision to visit
   325     revs: sorted iterable of revision to visit
   293     children: a {parent: [children]} mapping.
   326     children: a {parent: [children]} mapping.
   294     targetrev: the final copies destination revision (not in iterrevs)
   327     targetrev: the final copies destination revision (not in iterrevs)
   303         copies = all_copies.pop(r, None)
   336         copies = all_copies.pop(r, None)
   304         if copies is None:
   337         if copies is None:
   305             # this is a root
   338             # this is a root
   306             copies = {}
   339             copies = {}
   307         for i, c in enumerate(children[r]):
   340         for i, c in enumerate(children[r]):
   308             p1, p2, p1copies, p2copies, removed = revinfo(c)
   341             p1, p2, p1copies, p2copies, removed, ismerged = revinfo(c)
   309             if r == p1:
   342             if r == p1:
   310                 parent = 1
   343                 parent = 1
   311                 childcopies = p1copies
   344                 childcopies = p1copies
   312             else:
   345             else:
   313                 assert r == p2
   346                 assert r == p2
   317                 childcopies = {
   350                 childcopies = {
   318                     dst: src for dst, src in childcopies.items() if match(dst)
   351                     dst: src for dst, src in childcopies.items() if match(dst)
   319                 }
   352                 }
   320             newcopies = copies
   353             newcopies = copies
   321             if childcopies:
   354             if childcopies:
   322                 newcopies = _chain(newcopies, childcopies)
   355                 newcopies = copies.copy()
   323                 # _chain makes a copies, we can avoid doing so in some
   356                 for dest, source in pycompat.iteritems(childcopies):
   324                 # simple/linear cases.
   357                     prev = copies.get(source)
       
   358                     if prev is not None and prev[1] is not None:
       
   359                         source = prev[1]
       
   360                     newcopies[dest] = (c, source)
   325                 assert newcopies is not copies
   361                 assert newcopies is not copies
   326             for f in removed:
   362             for f in removed:
   327                 if f in newcopies:
   363                 if f in newcopies:
   328                     if newcopies is copies:
   364                     if newcopies is copies:
   329                         # copy on write to avoid affecting potential other
   365                         # copy on write to avoid affecting potential other
   330                         # branches.  when there are no other branches, this
   366                         # branches.  when there are no other branches, this
   331                         # could be avoided.
   367                         # could be avoided.
   332                         newcopies = copies.copy()
   368                         newcopies = copies.copy()
   333                     del newcopies[f]
   369                     newcopies[f] = (c, None)
   334             othercopies = all_copies.get(c)
   370             othercopies = all_copies.get(c)
   335             if othercopies is None:
   371             if othercopies is None:
   336                 all_copies[c] = newcopies
   372                 all_copies[c] = newcopies
   337             else:
   373             else:
   338                 # we are the second parent to work on c, we need to merge our
   374                 # we are the second parent to work on c, we need to merge our
   339                 # work with the other.
   375                 # work with the other.
   340                 #
   376                 #
   341                 # Unlike when copies are stored in the filelog, we consider
       
   342                 # it a copy even if the destination already existed on the
       
   343                 # other branch. It's simply too expensive to check if the
       
   344                 # file existed in the manifest.
       
   345                 #
       
   346                 # In case of conflict, parent 1 take precedence over parent 2.
   377                 # In case of conflict, parent 1 take precedence over parent 2.
   347                 # This is an arbitrary choice made anew when implementing
   378                 # This is an arbitrary choice made anew when implementing
   348                 # changeset based copies. It was made without regards with
   379                 # changeset based copies. It was made without regards with
   349                 # potential filelog related behavior.
   380                 # potential filelog related behavior.
   350                 if parent == 1:
   381                 if parent == 1:
   351                     othercopies.update(newcopies)
   382                     _merge_copies_dict(
       
   383                         othercopies, newcopies, isancestor, ismerged
       
   384                     )
   352                 else:
   385                 else:
   353                     newcopies.update(othercopies)
   386                     _merge_copies_dict(
       
   387                         newcopies, othercopies, isancestor, ismerged
       
   388                     )
   354                     all_copies[c] = newcopies
   389                     all_copies[c] = newcopies
   355     return all_copies[targetrev]
   390 
       
   391     final_copies = {}
       
   392     for dest, (tt, source) in all_copies[targetrev].items():
       
   393         if source is not None:
       
   394             final_copies[dest] = source
       
   395     return final_copies
       
   396 
       
   397 
       
   398 def _merge_copies_dict(minor, major, isancestor, ismerged):
       
   399     """merge two copies-mapping together, minor and major
       
   400 
       
   401     In case of conflict, value from "major" will be picked.
       
   402 
       
   403     - `isancestors(low_rev, high_rev)`: callable return True if `low_rev` is an
       
   404                                         ancestors of `high_rev`,
       
   405 
       
   406     - `ismerged(path)`: callable return True if `path` have been merged in the
       
   407                         current revision,
       
   408     """
       
   409     for dest, value in major.items():
       
   410         other = minor.get(dest)
       
   411         if other is None:
       
   412             minor[dest] = value
       
   413         else:
       
   414             new_tt = value[0]
       
   415             other_tt = other[0]
       
   416             if value[1] == other[1]:
       
   417                 continue
       
   418             # content from "major" wins, unless it is older
       
   419             # than the branch point or there is a merge
       
   420             if (
       
   421                 new_tt == other_tt
       
   422                 or not isancestor(new_tt, other_tt)
       
   423                 or ismerged(dest)
       
   424             ):
       
   425                 minor[dest] = value
   356 
   426 
   357 
   427 
   358 def _forwardcopies(a, b, base=None, match=None):
   428 def _forwardcopies(a, b, base=None, match=None):
   359     """find {dst@b: src@a} copy mapping where a is an ancestor of b"""
   429     """find {dst@b: src@a} copy mapping where a is an ancestor of b"""
   360 
   430