mercurial/commit.py
changeset 45239 ae5c1a3bc339
child 45240 ce9ee81df9ff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mercurial/commit.py	Mon Jul 06 23:14:52 2020 +0200
@@ -0,0 +1,215 @@
+# commit.py - fonction to perform commit
+#
+# This software may be used and distributed according to the terms of the
+# GNU General Public License version 2 or any later version.
+
+from __future__ import absolute_import
+
+import errno
+import weakref
+
+from .i18n import _
+from .node import (
+    hex,
+    nullrev,
+)
+
+from . import (
+    metadata,
+    phases,
+    scmutil,
+    subrepoutil,
+)
+
+
+def commitctx(repo, ctx, error=False, origctx=None):
+    """Add a new revision to the target repository.
+    Revision information is passed via the context argument.
+
+    ctx.files() should list all files involved in this commit, i.e.
+    modified/added/removed files. On merge, it may be wider than the
+    ctx.files() to be committed, since any file nodes derived directly
+    from p1 or p2 are excluded from the committed ctx.files().
+
+    origctx is for convert to work around the problem that bug
+    fixes to the files list in changesets change hashes. For
+    convert to be the identity, it can pass an origctx and this
+    function will use the same files list when it makes sense to
+    do so.
+    """
+    repo = repo.unfiltered()
+
+    p1, p2 = ctx.p1(), ctx.p2()
+    user = ctx.user()
+
+    if repo.filecopiesmode == b'changeset-sidedata':
+        writechangesetcopy = True
+        writefilecopymeta = True
+        writecopiesto = None
+    else:
+        writecopiesto = repo.ui.config(b'experimental', b'copies.write-to')
+        writefilecopymeta = writecopiesto != b'changeset-only'
+        writechangesetcopy = writecopiesto in (
+            b'changeset-only',
+            b'compatibility',
+        )
+    p1copies, p2copies = None, None
+    if writechangesetcopy:
+        p1copies = ctx.p1copies()
+        p2copies = ctx.p2copies()
+    filesadded, filesremoved = None, None
+    with repo.lock(), repo.transaction(b"commit") as tr:
+        trp = weakref.proxy(tr)
+
+        if ctx.manifestnode():
+            # reuse an existing manifest revision
+            repo.ui.debug(b'reusing known manifest\n')
+            mn = ctx.manifestnode()
+            files = ctx.files()
+            if writechangesetcopy:
+                filesadded = ctx.filesadded()
+                filesremoved = ctx.filesremoved()
+        elif not ctx.files():
+            repo.ui.debug(b'reusing manifest from p1 (no file change)\n')
+            mn = p1.manifestnode()
+            files = []
+        else:
+            m1ctx = p1.manifestctx()
+            m2ctx = p2.manifestctx()
+            mctx = m1ctx.copy()
+
+            m = mctx.read()
+            m1 = m1ctx.read()
+            m2 = m2ctx.read()
+
+            # check in files
+            added = []
+            filesadded = []
+            removed = list(ctx.removed())
+            touched = []
+            linkrev = len(repo)
+            repo.ui.note(_(b"committing files:\n"))
+            uipathfn = scmutil.getuipathfn(repo)
+            for f in sorted(ctx.modified() + ctx.added()):
+                repo.ui.note(uipathfn(f) + b"\n")
+                try:
+                    fctx = ctx[f]
+                    if fctx is None:
+                        removed.append(f)
+                    else:
+                        added.append(f)
+                        m[f], is_touched = repo._filecommit(
+                            fctx, m1, m2, linkrev, trp, writefilecopymeta,
+                        )
+                        if is_touched:
+                            touched.append(f)
+                            if writechangesetcopy and is_touched == 'added':
+                                filesadded.append(f)
+                        m.setflag(f, fctx.flags())
+                except OSError:
+                    repo.ui.warn(_(b"trouble committing %s!\n") % uipathfn(f))
+                    raise
+                except IOError as inst:
+                    errcode = getattr(inst, 'errno', errno.ENOENT)
+                    if error or errcode and errcode != errno.ENOENT:
+                        repo.ui.warn(
+                            _(b"trouble committing %s!\n") % uipathfn(f)
+                        )
+                    raise
+
+            # update manifest
+            removed = [f for f in removed if f in m1 or f in m2]
+            drop = sorted([f for f in removed if f in m])
+            for f in drop:
+                del m[f]
+            if p2.rev() != nullrev:
+                rf = metadata.get_removal_filter(ctx, (p1, p2, m1, m2))
+                removed = [f for f in removed if not rf(f)]
+
+            touched.extend(removed)
+
+            if writechangesetcopy:
+                filesremoved = removed
+
+            files = touched
+            md = None
+            if not files:
+                # if no "files" actually changed in terms of the changelog,
+                # try hard to detect unmodified manifest entry so that the
+                # exact same commit can be reproduced later on convert.
+                md = m1.diff(m, scmutil.matchfiles(repo, ctx.files()))
+            if not files and md:
+                repo.ui.debug(
+                    b'not reusing manifest (no file change in '
+                    b'changelog, but manifest differs)\n'
+                )
+            if files or md:
+                repo.ui.note(_(b"committing manifest\n"))
+                # we're using narrowmatch here since it's already applied at
+                # other stages (such as dirstate.walk), so we're already
+                # ignoring things outside of narrowspec in most cases. The
+                # one case where we might have files outside the narrowspec
+                # at this point is merges, and we already error out in the
+                # case where the merge has files outside of the narrowspec,
+                # so this is safe.
+                mn = mctx.write(
+                    trp,
+                    linkrev,
+                    p1.manifestnode(),
+                    p2.manifestnode(),
+                    added,
+                    drop,
+                    match=repo.narrowmatch(),
+                )
+            else:
+                repo.ui.debug(
+                    b'reusing manifest from p1 (listed files '
+                    b'actually unchanged)\n'
+                )
+                mn = p1.manifestnode()
+
+        if writecopiesto == b'changeset-only':
+            # If writing only to changeset extras, use None to indicate that
+            # no entry should be written. If writing to both, write an empty
+            # entry to prevent the reader from falling back to reading
+            # filelogs.
+            p1copies = p1copies or None
+            p2copies = p2copies or None
+            filesadded = filesadded or None
+            filesremoved = filesremoved or None
+
+        if origctx and origctx.manifestnode() == mn:
+            files = origctx.files()
+
+        # update changelog
+        repo.ui.note(_(b"committing changelog\n"))
+        repo.changelog.delayupdate(tr)
+        n = repo.changelog.add(
+            mn,
+            files,
+            ctx.description(),
+            trp,
+            p1.node(),
+            p2.node(),
+            user,
+            ctx.date(),
+            ctx.extra().copy(),
+            p1copies,
+            p2copies,
+            filesadded,
+            filesremoved,
+        )
+        xp1, xp2 = p1.hex(), p2 and p2.hex() or b''
+        repo.hook(
+            b'pretxncommit', throw=True, node=hex(n), parent1=xp1, parent2=xp2,
+        )
+        # set the new commit is proper phase
+        targetphase = subrepoutil.newcommitphase(repo.ui, ctx)
+        if targetphase:
+            # retract boundary do not alter parent changeset.
+            # if a parent have higher the resulting phase will
+            # be compliant anyway
+            #
+            # if minimal phase was 0 we don't need to retract anything
+            phases.registernew(repo, tr, targetphase, [n])
+        return n