rewriteutil.py
125 lines
| 3.9 KiB
| text/x-python
|
PythonLexer
/ mercurial / rewriteutil.py
|
r35242 | # rewriteutil.py - utility functions for rewriting changesets | ||
# | ||||
# Copyright 2017 Octobus <contact@octobus.net> | ||||
# | ||||
# 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 | ||||
|
r45993 | import re | ||
|
r35243 | from .i18n import _ | ||
|
r35242 | from . import ( | ||
|
r35243 | error, | ||
node, | ||||
|
r35242 | obsolete, | ||
|
r45993 | obsutil, | ||
|
r35242 | revset, | ||
|
r45993 | scmutil, | ||
|
r35242 | ) | ||
|
r43345 | |||
|
r45996 | NODE_RE = re.compile(br'\b[0-9a-f]{6,64}\b') | ||
|
r45993 | |||
|
r43347 | def precheck(repo, revs, action=b'rewrite'): | ||
|
r35243 | """check if revs can be rewritten | ||
action is used to control the error message. | ||||
Make sure this function is called after taking the lock. | ||||
""" | ||||
if node.nullrev in revs: | ||||
|
r43347 | msg = _(b"cannot %s null changeset") % action | ||
hint = _(b"no changeset checked out") | ||||
|
r35243 | raise error.Abort(msg, hint=hint) | ||
if len(repo[None].parents()) > 1: | ||||
|
r43347 | raise error.Abort(_(b"cannot %s while merging") % action) | ||
|
r35243 | |||
|
r43347 | publicrevs = repo.revs(b'%ld and public()', revs) | ||
|
r35243 | if publicrevs: | ||
|
r43347 | msg = _(b"cannot %s public changesets") % action | ||
hint = _(b"see 'hg help phases' for details") | ||||
|
r35243 | raise error.Abort(msg, hint=hint) | ||
newunstable = disallowednewunstable(repo, revs) | ||||
if newunstable: | ||||
|
r43347 | raise error.Abort(_(b"cannot %s changeset with children") % action) | ||
|
r35243 | |||
|
r43345 | |||
|
r35242 | def disallowednewunstable(repo, revs): | ||
"""Checks whether editing the revs will create new unstable changesets and | ||||
are we allowed to create them. | ||||
To allow new unstable changesets, set the config: | ||||
`experimental.evolution.allowunstable=True` | ||||
""" | ||||
allowunstable = obsolete.isenabled(repo, obsolete.allowunstableopt) | ||||
if allowunstable: | ||||
return revset.baseset() | ||||
|
r43347 | return repo.revs(b"(%ld::) - %ld", revs, revs) | ||
|
r45682 | |||
def skip_empty_successor(ui, command): | ||||
empty_successor = ui.config(b'rewrite', b'empty-successor') | ||||
if empty_successor == b'skip': | ||||
return True | ||||
elif empty_successor == b'keep': | ||||
return False | ||||
else: | ||||
raise error.ConfigError( | ||||
_( | ||||
b"%s doesn't know how to handle config " | ||||
b"rewrite.empty-successor=%s (only 'skip' and 'keep' are " | ||||
b"supported)" | ||||
) | ||||
% (command, empty_successor) | ||||
) | ||||
|
r45993 | |||
|
r45994 | def update_hash_refs(repo, commitmsg, pending=None): | ||
|
r45993 | """Replace all obsolete commit hashes in the message with the current hash. | ||
If the obsolete commit was split or is divergent, the hash is not replaced | ||||
as there's no way to know which successor to choose. | ||||
|
r45994 | |||
For commands that update a series of commits in the current transaction, the | ||||
new obsolete markers can be considered by setting ``pending`` to a mapping | ||||
of ``pending[oldnode] = [successor_node1, successor_node2,..]``. | ||||
|
r45993 | """ | ||
|
r45994 | if not pending: | ||
pending = {} | ||||
|
r45993 | cache = {} | ||
|
r45996 | hashes = re.findall(NODE_RE, commitmsg) | ||
|
r45993 | unfi = repo.unfiltered() | ||
|
r45996 | for h in hashes: | ||
fullnode = scmutil.resolvehexnodeidprefix(unfi, h) | ||||
|
r45993 | if fullnode is None: | ||
continue | ||||
ctx = unfi[fullnode] | ||||
if not ctx.obsolete(): | ||||
|
r45994 | successors = pending.get(fullnode) | ||
if successors is None: | ||||
continue | ||||
# obsutil.successorssets() returns a list of list of nodes | ||||
successors = [successors] | ||||
else: | ||||
successors = obsutil.successorssets(repo, ctx.node(), cache=cache) | ||||
|
r45993 | |||
# We can't make any assumptions about how to update the hash if the | ||||
# cset in question was split or diverged. | ||||
if len(successors) == 1 and len(successors[0]) == 1: | ||||
|
r45996 | newhash = node.hex(successors[0][0]) | ||
commitmsg = commitmsg.replace(h, newhash[: len(h)]) | ||||
|
r45993 | else: | ||
repo.ui.note( | ||||
_( | ||||
b'The stale commit message reference to %s could ' | ||||
b'not be updated\n' | ||||
) | ||||
|
r45996 | % h | ||
|
r45993 | ) | ||
return commitmsg | ||||