mercurial/commit.py
changeset 45204 ce9ee81df9ff
parent 45203 ae5c1a3bc339
child 45223 d056a131c93f
equal deleted inserted replaced
45203:ae5c1a3bc339 45204:ce9ee81df9ff
     9 import weakref
     9 import weakref
    10 
    10 
    11 from .i18n import _
    11 from .i18n import _
    12 from .node import (
    12 from .node import (
    13     hex,
    13     hex,
       
    14     nullid,
    14     nullrev,
    15     nullrev,
    15 )
    16 )
    16 
    17 
    17 from . import (
    18 from . import (
       
    19     context,
       
    20     mergestate,
    18     metadata,
    21     metadata,
    19     phases,
    22     phases,
    20     scmutil,
    23     scmutil,
    21     subrepoutil,
    24     subrepoutil,
    22 )
    25 )
    96                     fctx = ctx[f]
    99                     fctx = ctx[f]
    97                     if fctx is None:
   100                     if fctx is None:
    98                         removed.append(f)
   101                         removed.append(f)
    99                     else:
   102                     else:
   100                         added.append(f)
   103                         added.append(f)
   101                         m[f], is_touched = repo._filecommit(
   104                         m[f], is_touched = _filecommit(
   102                             fctx, m1, m2, linkrev, trp, writefilecopymeta,
   105                             repo, fctx, m1, m2, linkrev, trp, writefilecopymeta,
   103                         )
   106                         )
   104                         if is_touched:
   107                         if is_touched:
   105                             touched.append(f)
   108                             touched.append(f)
   106                             if writechangesetcopy and is_touched == 'added':
   109                             if writechangesetcopy and is_touched == 'added':
   107                                 filesadded.append(f)
   110                                 filesadded.append(f)
   211             # be compliant anyway
   214             # be compliant anyway
   212             #
   215             #
   213             # if minimal phase was 0 we don't need to retract anything
   216             # if minimal phase was 0 we don't need to retract anything
   214             phases.registernew(repo, tr, targetphase, [n])
   217             phases.registernew(repo, tr, targetphase, [n])
   215         return n
   218         return n
       
   219 
       
   220 
       
   221 def _filecommit(
       
   222     repo, fctx, manifest1, manifest2, linkrev, tr, includecopymeta,
       
   223 ):
       
   224     """
       
   225     commit an individual file as part of a larger transaction
       
   226 
       
   227     input:
       
   228 
       
   229         fctx:       a file context with the content we are trying to commit
       
   230         manifest1:  manifest of changeset first parent
       
   231         manifest2:  manifest of changeset second parent
       
   232         linkrev:    revision number of the changeset being created
       
   233         tr:         current transation
       
   234         individual: boolean, set to False to skip storing the copy data
       
   235                     (only used by the Google specific feature of using
       
   236                     changeset extra as copy source of truth).
       
   237 
       
   238     output: (filenode, touched)
       
   239 
       
   240         filenode: the filenode that should be used by this changeset
       
   241         touched:  one of: None, 'added' or 'modified'
       
   242     """
       
   243 
       
   244     fname = fctx.path()
       
   245     fparent1 = manifest1.get(fname, nullid)
       
   246     fparent2 = manifest2.get(fname, nullid)
       
   247     touched = None
       
   248     if fparent1 == fparent2 == nullid:
       
   249         touched = 'added'
       
   250 
       
   251     if isinstance(fctx, context.filectx):
       
   252         # This block fast path most comparisons which are usually done. It
       
   253         # assumes that bare filectx is used and no merge happened, hence no
       
   254         # need to create a new file revision in this case.
       
   255         node = fctx.filenode()
       
   256         if node in [fparent1, fparent2]:
       
   257             repo.ui.debug(b'reusing %s filelog entry\n' % fname)
       
   258             if (
       
   259                 fparent1 != nullid and manifest1.flags(fname) != fctx.flags()
       
   260             ) or (
       
   261                 fparent2 != nullid and manifest2.flags(fname) != fctx.flags()
       
   262             ):
       
   263                 touched = 'modified'
       
   264             return node, touched
       
   265 
       
   266     flog = repo.file(fname)
       
   267     meta = {}
       
   268     cfname = fctx.copysource()
       
   269     fnode = None
       
   270 
       
   271     if cfname and cfname != fname:
       
   272         # Mark the new revision of this file as a copy of another
       
   273         # file.  This copy data will effectively act as a parent
       
   274         # of this new revision.  If this is a merge, the first
       
   275         # parent will be the nullid (meaning "look up the copy data")
       
   276         # and the second one will be the other parent.  For example:
       
   277         #
       
   278         # 0 --- 1 --- 3   rev1 changes file foo
       
   279         #   \       /     rev2 renames foo to bar and changes it
       
   280         #    \- 2 -/      rev3 should have bar with all changes and
       
   281         #                      should record that bar descends from
       
   282         #                      bar in rev2 and foo in rev1
       
   283         #
       
   284         # this allows this merge to succeed:
       
   285         #
       
   286         # 0 --- 1 --- 3   rev4 reverts the content change from rev2
       
   287         #   \       /     merging rev3 and rev4 should use bar@rev2
       
   288         #    \- 2 --- 4        as the merge base
       
   289         #
       
   290 
       
   291         cnode = manifest1.get(cfname)
       
   292         newfparent = fparent2
       
   293 
       
   294         if manifest2:  # branch merge
       
   295             if fparent2 == nullid or cnode is None:  # copied on remote side
       
   296                 if cfname in manifest2:
       
   297                     cnode = manifest2[cfname]
       
   298                     newfparent = fparent1
       
   299 
       
   300         # Here, we used to search backwards through history to try to find
       
   301         # where the file copy came from if the source of a copy was not in
       
   302         # the parent directory. However, this doesn't actually make sense to
       
   303         # do (what does a copy from something not in your working copy even
       
   304         # mean?) and it causes bugs (eg, issue4476). Instead, we will warn
       
   305         # the user that copy information was dropped, so if they didn't
       
   306         # expect this outcome it can be fixed, but this is the correct
       
   307         # behavior in this circumstance.
       
   308 
       
   309         if cnode:
       
   310             repo.ui.debug(b" %s: copy %s:%s\n" % (fname, cfname, hex(cnode)))
       
   311             if includecopymeta:
       
   312                 meta[b"copy"] = cfname
       
   313                 meta[b"copyrev"] = hex(cnode)
       
   314             fparent1, fparent2 = nullid, newfparent
       
   315         else:
       
   316             repo.ui.warn(
       
   317                 _(
       
   318                     b"warning: can't find ancestor for '%s' "
       
   319                     b"copied from '%s'!\n"
       
   320                 )
       
   321                 % (fname, cfname)
       
   322             )
       
   323 
       
   324     elif fparent1 == nullid:
       
   325         fparent1, fparent2 = fparent2, nullid
       
   326     elif fparent2 != nullid:
       
   327         # is one parent an ancestor of the other?
       
   328         fparentancestors = flog.commonancestorsheads(fparent1, fparent2)
       
   329         if fparent1 in fparentancestors:
       
   330             fparent1, fparent2 = fparent2, nullid
       
   331         elif fparent2 in fparentancestors:
       
   332             fparent2 = nullid
       
   333         elif not fparentancestors:
       
   334             # TODO: this whole if-else might be simplified much more
       
   335             ms = mergestate.mergestate.read(repo)
       
   336             if (
       
   337                 fname in ms
       
   338                 and ms[fname] == mergestate.MERGE_RECORD_MERGED_OTHER
       
   339             ):
       
   340                 fparent1, fparent2 = fparent2, nullid
       
   341 
       
   342     # is the file changed?
       
   343     text = fctx.data()
       
   344     if fparent2 != nullid or meta or flog.cmp(fparent1, text):
       
   345         if touched is None:  # do not overwrite added
       
   346             touched = 'modified'
       
   347         fnode = flog.add(text, meta, tr, linkrev, fparent1, fparent2)
       
   348     # are just the flags changed during merge?
       
   349     elif fname in manifest1 and manifest1.flags(fname) != fctx.flags():
       
   350         touched = 'modified'
       
   351         fnode = fparent1
       
   352     else:
       
   353         fnode = fparent1
       
   354     return fnode, touched