evolve: close transaction if conflict is detected in relocate (issue4966)
Before this patch, transaction is aborted, if conflict is detected at
merging while "hg evolve".
Since 8f2ff40fe9c9 (or 3.6) of Mercurial, aborting transaction
discards all dirstate changes inside transaction scope for
"transactional dirstate" (see below wiki page for detail about it).
https://mercurial.selenic.com/wiki/DirstateTransactionPlan
Therefore, just aborting transaction causes unchanged (and unexpected)
dirstate, even though subsequent commands require dirstate changes
while "hg evolve".
To keep dirstate changes while "hg evolve", this patch closes current
running transaction, if conflict is detected in relocate(), even
though exception is raised as usual.
Even though "save dirstate and restore it after aborting transaction"
like shelve._aborttransaction() of Mercurial can also solve this
issue, this patch chose closing transaction for similarity with
failure for conflict at "hg unshelve". In addition to it, closing
transaction can keep any previous (implicit) changes.
In newly added test, there is an additional ancestor revision, which
"will be evolved safely". It is used to examine whether failure for
conflict doesn't discard already relocated revision(s) while "hg
evolve".
It is fact for current implementation that "hg evolve" relocates each
revisions in separated transactions and already relocated ones are
never discarded, even if subsequent relocation fails. Though, this
examination is useful to detect unintentional regression in the
future.
# Extension which prevent changeset to be turn public by push operation
#
# Copyright 2011 Logilab SA <contact@logilab.fr>
#
# This software may be used and distributed according to the terms of the
# GNU General Public License version 2 or any later version.
from mercurial import extensions, util
from mercurial import discovery
def checkpublish(orig, repo, remote, outgoing, *args):
# is remote publishing?
publish = True
if 'phases' in remote.listkeys('namespaces'):
remotephases = remote.listkeys('phases')
publish = remotephases.get('publishing', False)
npublish = 0
if publish:
for rev in outgoing.missing:
if repo[rev].phase():
npublish += 1
if npublish:
repo.ui.warn("Push would publish %s changesets" % npublish)
ret = orig(repo, remote, outgoing, *args)
if npublish:
raise util.Abort("Publishing push forbiden",
hint="Use `hg phase -p <rev>` to manually publish them")
return ret
def uisetup(ui):
extensions.wrapfunction(discovery, 'checkheads', checkpublish)