diff mercurial/localrepo.py @ 39111:46da52f4b820

commit: try hard to reuse p1 manifest if nothing changed This is all for commit reproducibility on "hg convert". With this change, p1 manifest is reused if ctx.files() *to be committed* is empty, and if new manifest entry is identical to p1. This is important property for "hg convert" since memctx.files() built from a convert source may be either a) more narrowed thanks to a committed ctx.files() which provides more accurate status, or b) containing redundant files because of sloppy filtering on e.g. octopus merge.
author Yuya Nishihara <yuya@tcha.org>
date Sat, 07 Jul 2018 22:40:39 +0900
parents a915db9a5e77
children 7a9f15ed3b96
line wrap: on
line diff
--- a/mercurial/localrepo.py	Sun Aug 12 18:44:42 2018 +0900
+++ b/mercurial/localrepo.py	Sat Jul 07 22:40:39 2018 +0900
@@ -2039,6 +2039,11 @@
     def commitctx(self, ctx, error=False):
         """Add a new revision to current 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().
         """
 
         tr = None
@@ -2091,15 +2096,29 @@
                         raise
 
                 # update manifest
-                self.ui.note(_("committing manifest\n"))
                 removed = [f for f in sorted(removed) if f in m1 or f in m2]
                 drop = [f for f in removed if f in m]
                 for f in drop:
                     del m[f]
-                mn = mctx.write(trp, linkrev,
-                                p1.manifestnode(), p2.manifestnode(),
-                                added, drop)
                 files = changed + removed
+                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(self, ctx.files()))
+                if not files and md:
+                    self.ui.debug('not reusing manifest (no file change in '
+                                  'changelog, but manifest differs)\n')
+                if files or md:
+                    self.ui.note(_("committing manifest\n"))
+                    mn = mctx.write(trp, linkrev,
+                                    p1.manifestnode(), p2.manifestnode(),
+                                    added, drop)
+                else:
+                    self.ui.debug('reusing manifest form p1 (listed files '
+                                  'actually unchanged)\n')
+                    mn = p1.manifestnode()
             else:
                 self.ui.debug('reusing manifest from p1 (no file change)\n')
                 mn = p1.manifestnode()