mercurial/commit.py
changeset 45203 ae5c1a3bc339
child 45204 ce9ee81df9ff
equal deleted inserted replaced
45202:31393ec06cef 45203:ae5c1a3bc339
       
     1 # commit.py - fonction to perform commit
       
     2 #
       
     3 # This software may be used and distributed according to the terms of the
       
     4 # GNU General Public License version 2 or any later version.
       
     5 
       
     6 from __future__ import absolute_import
       
     7 
       
     8 import errno
       
     9 import weakref
       
    10 
       
    11 from .i18n import _
       
    12 from .node import (
       
    13     hex,
       
    14     nullrev,
       
    15 )
       
    16 
       
    17 from . import (
       
    18     metadata,
       
    19     phases,
       
    20     scmutil,
       
    21     subrepoutil,
       
    22 )
       
    23 
       
    24 
       
    25 def commitctx(repo, ctx, error=False, origctx=None):
       
    26     """Add a new revision to the target repository.
       
    27     Revision information is passed via the context argument.
       
    28 
       
    29     ctx.files() should list all files involved in this commit, i.e.
       
    30     modified/added/removed files. On merge, it may be wider than the
       
    31     ctx.files() to be committed, since any file nodes derived directly
       
    32     from p1 or p2 are excluded from the committed ctx.files().
       
    33 
       
    34     origctx is for convert to work around the problem that bug
       
    35     fixes to the files list in changesets change hashes. For
       
    36     convert to be the identity, it can pass an origctx and this
       
    37     function will use the same files list when it makes sense to
       
    38     do so.
       
    39     """
       
    40     repo = repo.unfiltered()
       
    41 
       
    42     p1, p2 = ctx.p1(), ctx.p2()
       
    43     user = ctx.user()
       
    44 
       
    45     if repo.filecopiesmode == b'changeset-sidedata':
       
    46         writechangesetcopy = True
       
    47         writefilecopymeta = True
       
    48         writecopiesto = None
       
    49     else:
       
    50         writecopiesto = repo.ui.config(b'experimental', b'copies.write-to')
       
    51         writefilecopymeta = writecopiesto != b'changeset-only'
       
    52         writechangesetcopy = writecopiesto in (
       
    53             b'changeset-only',
       
    54             b'compatibility',
       
    55         )
       
    56     p1copies, p2copies = None, None
       
    57     if writechangesetcopy:
       
    58         p1copies = ctx.p1copies()
       
    59         p2copies = ctx.p2copies()
       
    60     filesadded, filesremoved = None, None
       
    61     with repo.lock(), repo.transaction(b"commit") as tr:
       
    62         trp = weakref.proxy(tr)
       
    63 
       
    64         if ctx.manifestnode():
       
    65             # reuse an existing manifest revision
       
    66             repo.ui.debug(b'reusing known manifest\n')
       
    67             mn = ctx.manifestnode()
       
    68             files = ctx.files()
       
    69             if writechangesetcopy:
       
    70                 filesadded = ctx.filesadded()
       
    71                 filesremoved = ctx.filesremoved()
       
    72         elif not ctx.files():
       
    73             repo.ui.debug(b'reusing manifest from p1 (no file change)\n')
       
    74             mn = p1.manifestnode()
       
    75             files = []
       
    76         else:
       
    77             m1ctx = p1.manifestctx()
       
    78             m2ctx = p2.manifestctx()
       
    79             mctx = m1ctx.copy()
       
    80 
       
    81             m = mctx.read()
       
    82             m1 = m1ctx.read()
       
    83             m2 = m2ctx.read()
       
    84 
       
    85             # check in files
       
    86             added = []
       
    87             filesadded = []
       
    88             removed = list(ctx.removed())
       
    89             touched = []
       
    90             linkrev = len(repo)
       
    91             repo.ui.note(_(b"committing files:\n"))
       
    92             uipathfn = scmutil.getuipathfn(repo)
       
    93             for f in sorted(ctx.modified() + ctx.added()):
       
    94                 repo.ui.note(uipathfn(f) + b"\n")
       
    95                 try:
       
    96                     fctx = ctx[f]
       
    97                     if fctx is None:
       
    98                         removed.append(f)
       
    99                     else:
       
   100                         added.append(f)
       
   101                         m[f], is_touched = repo._filecommit(
       
   102                             fctx, m1, m2, linkrev, trp, writefilecopymeta,
       
   103                         )
       
   104                         if is_touched:
       
   105                             touched.append(f)
       
   106                             if writechangesetcopy and is_touched == 'added':
       
   107                                 filesadded.append(f)
       
   108                         m.setflag(f, fctx.flags())
       
   109                 except OSError:
       
   110                     repo.ui.warn(_(b"trouble committing %s!\n") % uipathfn(f))
       
   111                     raise
       
   112                 except IOError as inst:
       
   113                     errcode = getattr(inst, 'errno', errno.ENOENT)
       
   114                     if error or errcode and errcode != errno.ENOENT:
       
   115                         repo.ui.warn(
       
   116                             _(b"trouble committing %s!\n") % uipathfn(f)
       
   117                         )
       
   118                     raise
       
   119 
       
   120             # update manifest
       
   121             removed = [f for f in removed if f in m1 or f in m2]
       
   122             drop = sorted([f for f in removed if f in m])
       
   123             for f in drop:
       
   124                 del m[f]
       
   125             if p2.rev() != nullrev:
       
   126                 rf = metadata.get_removal_filter(ctx, (p1, p2, m1, m2))
       
   127                 removed = [f for f in removed if not rf(f)]
       
   128 
       
   129             touched.extend(removed)
       
   130 
       
   131             if writechangesetcopy:
       
   132                 filesremoved = removed
       
   133 
       
   134             files = touched
       
   135             md = None
       
   136             if not files:
       
   137                 # if no "files" actually changed in terms of the changelog,
       
   138                 # try hard to detect unmodified manifest entry so that the
       
   139                 # exact same commit can be reproduced later on convert.
       
   140                 md = m1.diff(m, scmutil.matchfiles(repo, ctx.files()))
       
   141             if not files and md:
       
   142                 repo.ui.debug(
       
   143                     b'not reusing manifest (no file change in '
       
   144                     b'changelog, but manifest differs)\n'
       
   145                 )
       
   146             if files or md:
       
   147                 repo.ui.note(_(b"committing manifest\n"))
       
   148                 # we're using narrowmatch here since it's already applied at
       
   149                 # other stages (such as dirstate.walk), so we're already
       
   150                 # ignoring things outside of narrowspec in most cases. The
       
   151                 # one case where we might have files outside the narrowspec
       
   152                 # at this point is merges, and we already error out in the
       
   153                 # case where the merge has files outside of the narrowspec,
       
   154                 # so this is safe.
       
   155                 mn = mctx.write(
       
   156                     trp,
       
   157                     linkrev,
       
   158                     p1.manifestnode(),
       
   159                     p2.manifestnode(),
       
   160                     added,
       
   161                     drop,
       
   162                     match=repo.narrowmatch(),
       
   163                 )
       
   164             else:
       
   165                 repo.ui.debug(
       
   166                     b'reusing manifest from p1 (listed files '
       
   167                     b'actually unchanged)\n'
       
   168                 )
       
   169                 mn = p1.manifestnode()
       
   170 
       
   171         if writecopiesto == b'changeset-only':
       
   172             # If writing only to changeset extras, use None to indicate that
       
   173             # no entry should be written. If writing to both, write an empty
       
   174             # entry to prevent the reader from falling back to reading
       
   175             # filelogs.
       
   176             p1copies = p1copies or None
       
   177             p2copies = p2copies or None
       
   178             filesadded = filesadded or None
       
   179             filesremoved = filesremoved or None
       
   180 
       
   181         if origctx and origctx.manifestnode() == mn:
       
   182             files = origctx.files()
       
   183 
       
   184         # update changelog
       
   185         repo.ui.note(_(b"committing changelog\n"))
       
   186         repo.changelog.delayupdate(tr)
       
   187         n = repo.changelog.add(
       
   188             mn,
       
   189             files,
       
   190             ctx.description(),
       
   191             trp,
       
   192             p1.node(),
       
   193             p2.node(),
       
   194             user,
       
   195             ctx.date(),
       
   196             ctx.extra().copy(),
       
   197             p1copies,
       
   198             p2copies,
       
   199             filesadded,
       
   200             filesremoved,
       
   201         )
       
   202         xp1, xp2 = p1.hex(), p2 and p2.hex() or b''
       
   203         repo.hook(
       
   204             b'pretxncommit', throw=True, node=hex(n), parent1=xp1, parent2=xp2,
       
   205         )
       
   206         # set the new commit is proper phase
       
   207         targetphase = subrepoutil.newcommitphase(repo.ui, ctx)
       
   208         if targetphase:
       
   209             # retract boundary do not alter parent changeset.
       
   210             # if a parent have higher the resulting phase will
       
   211             # be compliant anyway
       
   212             #
       
   213             # if minimal phase was 0 we don't need to retract anything
       
   214             phases.registernew(repo, tr, targetphase, [n])
       
   215         return n