[PATCH 1 of 1 v3] localrepo: add commitworkingctx() wrapper

Dan Villiom Podlaski Christiansen danchr at gmail.com
Sun Jan 8 08:12:54 CST 2012


# HG changeset patch
# User Dan Villiom Podlaski Christiansen <danchr at gmail.com>
# Date 1326030361 -3600
# Node ID 21b96e13ad7914599b08e0240dde79b98628161c
# Parent  28986dd00d65a45c6d3fc791af0aca07391820e2
localrepo: add commitworkingctx() wrapper

This wrapper allows extensions to safely wrap commitctx() for
user-initiated commits only.

Fundamentally, there are two kinds of client of the commitctx() API:
- localrepository.commit().
- conversion extensions such as convert, hgsubversion and hg-git.

When an extension wraps or specialises commitctx() instead of
commit(), it's often because it needs to access the context being
committed. However, commitctx() is the sole API for adding changesets
to the repository, and for this reason, it is also used by the
conversion extensions. The needs of these extensions typically
conflict with wrapping commitctx(), as it is important that their
contexts are committed unmodified, as-is to the repository.

To solve this conflict, either the wrapping extensions or the
converting extensions must be changed.

In favor of changing the wrapping extensions is the fact that wrapping
is usually done in a single source location. Retaining compatibility
with earlier versions of Mercurial can be achieved with the following
small hack:

  if getattr(localrepository, 'commitworkingctx', None) is None:
    wrappedrepo.commitctx = wrappedrepo.commitworkingctx
    del wrappedrepo.commitworkingctx

(I'm assuming the extension uses subclassing to extend the repository
class.)

In favor of changing the conversion extensions is the fact that there
are relatively few of them. All of them are likely to be maintained
actively, and have a somewhat complex code-base. The latter, however,
speaks against changing them. Although they could well use a
compatibility function to conditionally call commitctx() or
e.g. rawcommitctx(), the changes are likely to be much more
invasive.

For reference, hgsubversion calls commitctx() in 27 source locations,
of which 21 are in its tests. Both the convert and hg-git extensions
call it in two source locations, each.

diff --git a/mercurial/localrepo.py b/mercurial/localrepo.py
--- a/mercurial/localrepo.py
+++ b/mercurial/localrepo.py
@@ -1167,7 +1167,7 @@ class localrepository(repo.repository):
             hookp1, hookp2 = hex(p1), (p2 != nullid and hex(p2) or '')
             try:
                 self.hook("precommit", throw=True, parent1=hookp1, parent2=hookp2)
-                ret = self.commitctx(cctx, True)
+                ret = self.usercommitctx(cctx, True)
             except:
                 if edited:
                     self.ui.write(
@@ -1188,9 +1188,23 @@ class localrepository(repo.repository):
         self.hook("commit", node=hex(ret), parent1=hookp1, parent2=hookp2)
         return ret
 
+    def usercommitctx(self, ctx, error=False):
+        """
+        Wrapper for commitctx() method indicating a user-initiated commit.
+
+        This method is primarily intended for wrapping or specializing.
+        Extensions can safely wrap it, without interfering with other uses of
+        commitctx(), such as for conversion extensions.
+        """
+        return self.commitctx(ctx, error)
+
     def commitctx(self, ctx, error=False):
         """Add a new revision to current repository.
         Revision information is passed via the context argument.
+
+        Please note that this method shouldn't be wrapped or specialized by
+        extensions. This method may be used part of a conversion where it's
+        important that `ctx' is committed to the repository as-is.
         """
 
         tr = lock = None


More information about the Mercurial-devel mailing list