[Updated] [+++- ] D8567: mergestate: implement trivial in-memory mergestate
durin42 (Augie Fackler)
phabricator at mercurial-scm.org
Thu May 28 21:34:04 UTC 2020
durin42 updated this revision to Diff 21519.
REPOSITORY
rHG Mercurial
CHANGES SINCE LAST UPDATE
https://phab.mercurial-scm.org/D8567?vs=21518&id=21519
BRANCH
default
CHANGES SINCE LAST ACTION
https://phab.mercurial-scm.org/D8567/new/
REVISION DETAIL
https://phab.mercurial-scm.org/D8567
AFFECTED FILES
mercurial/mergestate.py
CHANGE DETAILS
diff --git a/mercurial/mergestate.py b/mercurial/mergestate.py
--- a/mercurial/mergestate.py
+++ b/mercurial/mergestate.py
@@ -92,7 +92,40 @@
}
-class mergestate(object):
+class _basemergestate(object):
+ def __init__(self, repo):
+ self._repo = repo
+ self._readmergedriver = None
+
+ @util.propertycache
+ def mergedriver(self):
+ # protect against the following:
+ # - A configures a malicious merge driver in their hgrc, then
+ # pauses the merge
+ # - A edits their hgrc to remove references to the merge driver
+ # - A gives a copy of their entire repo, including .hg, to B
+ # - B inspects .hgrc and finds it to be clean
+ # - B then continues the merge and the malicious merge driver
+ # gets invoked
+ configmergedriver = self._repo.ui.config(
+ b'experimental', b'mergedriver'
+ )
+ if (
+ self._readmergedriver is not None
+ and self._readmergedriver != configmergedriver
+ ):
+ raise error.ConfigError(
+ _(b"merge driver changed since merge started"),
+ hint=_(b"revert merge driver change or abort merge"),
+ )
+
+ return configmergedriver
+
+ def reset(self, node=None, other=None, labels=None):
+ self._readmergedriver = None
+
+
+class mergestate(_basemergestate):
'''track 3-way merge state of individual files
The merge state is stored on disk when needed. Two files are used: one with
@@ -161,11 +194,12 @@
"""Initialize the merge state.
Do not use this directly! Instead call read() or clean()."""
- self._repo = repo
+ super(mergestate, self).__init__(repo)
self._dirty = False
self._labels = None
def reset(self, node=None, other=None, labels=None):
+ super(mergestate, self).reset(node=node, other=other, labels=labels)
self._state = {}
self._stateextras = {}
self._local = None
@@ -177,7 +211,6 @@
if node:
self._local = node
self._other = other
- self._readmergedriver = None
if self.mergedriver:
self._mdstate = MERGE_DRIVER_STATE_SUCCESS
else:
@@ -362,30 +395,6 @@
return records
@util.propertycache
- def mergedriver(self):
- # protect against the following:
- # - A configures a malicious merge driver in their hgrc, then
- # pauses the merge
- # - A edits their hgrc to remove references to the merge driver
- # - A gives a copy of their entire repo, including .hg, to B
- # - B inspects .hgrc and finds it to be clean
- # - B then continues the merge and the malicious merge driver
- # gets invoked
- configmergedriver = self._repo.ui.config(
- b'experimental', b'mergedriver'
- )
- if (
- self._readmergedriver is not None
- and self._readmergedriver != configmergedriver
- ):
- raise error.ConfigError(
- _(b"merge driver changed since merge started"),
- hint=_(b"revert merge driver change or abort merge"),
- )
-
- return configmergedriver
-
- @util.propertycache
def local(self):
if self._local is None:
msg = b"local accessed but self._local isn't set"
@@ -858,3 +867,113 @@
repo.dirstate.copy(f0, f)
else:
repo.dirstate.normal(f)
+
+
+class memmergestate(_basemergestate):
+ def __init__(self, repo, ctx):
+ super(memmergestate, self).__init__(repo)
+ self._basectx = ctx
+ self.reset()
+ self._ancestor_filectxs = {}
+
+ def add(self, fcl, fco, fca, fd):
+ """add a new (potentially?) conflicting file to the merge state"""
+ self._conflicts.add(fcl.path())
+ # TODO(augie): the rebase codepath depends on non-implicit
+ # ancestor. I think we should fix things so that ancestor can
+ # be passed in to reset().
+ #
+ # Also, you may be tempted to guard this line by
+ #
+ # if fca.node() != nullid:
+ #
+ # but you'd be misled: this angers some low levels of the
+ # merge code and it seems the only way to trigger the issue is
+ # the merge code.
+ self._ancestor_filectxs[fcl.path()] = fca
+
+ # Since memmergestate isn't mutable yet, these are all trivial
+ # implementations used by the "happy path" in merge code.
+ def reset(self, node=None, other=None, labels=None):
+ super(memmergestate, self).reset(node=node, other=other, labels=labels)
+ self._local = node
+ self._other = other
+ if self._local is not None:
+ try:
+ self._ancestor_ctx = next(
+ self._repo.set(b'ancestor(%ln)', (node, other))
+ )
+ except StopIteration:
+ # This will almost certainly be an issue, but if
+ # callers consistently pass a filectx in add() when we
+ # can't figure out the ancestor this won't matter. It
+ # would be a better API for reset() to take an
+ # ancestor ctx, but that's a broader cleanup.
+ self._ancestor_ctx = None
+ self._labels = labels
+ self._conflicts = set()
+
+ def commit(self):
+ if self._conflicts:
+ error.InMemoryMergeConflictsError(
+ 'cannot commit memmergestate with conflicts, have %d conflicts'
+ % self.unresolvedcount()
+ )
+
+ def counts(self):
+ return 0, 0, 0
+
+ def unresolvedcount(self):
+ return len(self._conflicts)
+
+ def actions(self):
+ return {}
+
+ @property
+ def mergedriver(self):
+ md = super(memmergestate, self).mergedriver
+ if md:
+ raise error.InMemoryMergeConflictsError(
+ b"in-memory merge does not support mergedriver"
+ )
+ return md
+
+ def addmergedother(self, path):
+ # I'm very dubious this is right.
+ pass
+
+ def preresolve(self, dfile, wctx):
+ return self._resolve(True, dfile, wctx)
+
+ def resolve(self, dfile, wctx):
+ return self._resolve(False, dfile, wctx)[1]
+
+ def _resolve(self, preresolve, dfile, wctx):
+ # TODO: clean up _filectxorabsent API?
+ fcd = _filectxorabsent(
+ nullhex if dfile not in wctx else None, wctx, dfile
+ )
+ octx = self._repo[self._other]
+ fco = _filectxorabsent(
+ nullhex if dfile not in octx else None, octx, dfile
+ )
+ actx = self._ancestor_ctx
+ if dfile in self._ancestor_filectxs:
+ fca = self._ancestor_filectxs[dfile]
+ else:
+ fca = _filectxorabsent(
+ nullhex if dfile not in actx else None, actx, dfile
+ )
+ fn = filemerge.premerge if preresolve else filemerge.filemerge
+ complete, mergeret, deleted = fn(
+ self._repo,
+ wctx,
+ self._local,
+ dfile, # orig
+ fcd,
+ fco,
+ fca,
+ labels=self._labels,
+ )
+ del deleted # unused
+ return complete, mergeret
To: durin42, #hg-reviewers
Cc: martinvonz, mercurial-patches
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.mercurial-scm.org/pipermail/mercurial-patches/attachments/20200528/1a507c5b/attachment-0002.html>
More information about the Mercurial-patches
mailing list