histedit: make histedit aware of obsolescense not stored in state (issue4800)

Before this change, when histedit exited to interactive session (during edit
command for example), user could introduce obsolescence markers that would not
be known to histedit. For example, user could've amended one of the commits.
The fact of this amendment would not be stored in histedit's state file
and later, when histedit would try to process all the replacements,
one of the final successors (in histedit's opinion) would turn out to be hidden.
This behavior is described in issue4800. This commit fixes it.
This commit is contained in:
Kostia Balytskyi 2016-02-23 21:38:36 +00:00
parent a05047be24
commit 4b4261dc2f
2 changed files with 81 additions and 1 deletions

View File

@ -1386,13 +1386,48 @@ def verifyactions(actions, state, ctxs):
hint=_('use "drop %s" to discard, see also: '
'"hg help -e histedit.config"') % missing[0][:12])
def adjustreplacementsfrommarkers(repo, oldreplacements):
"""Adjust replacements from obsolescense markers
Replacements structure is originally generated based on
histedit's state and does not account for changes that are
not recorded there. This function fixes that by adding
data read from obsolescense markers"""
if not obsolete.isenabled(repo, obsolete.createmarkersopt):
return oldreplacements
unfi = repo.unfiltered()
newreplacements = list(oldreplacements)
oldsuccs = [r[1] for r in oldreplacements]
# successors that have already been added to succstocheck once
seensuccs = set().union(*oldsuccs) # create a set from an iterable of tuples
succstocheck = list(seensuccs)
while succstocheck:
n = succstocheck.pop()
try:
ctx = unfi[n]
except error.RepoError:
# XXX node unknown locally, we should properly follow marker
newreplacements.append((n, ()))
continue
for marker in obsolete.successormarkers(ctx):
nsuccs = marker.succnodes()
newreplacements.append((n, nsuccs))
for nsucc in nsuccs:
if nsucc not in seensuccs:
seensuccs.add(nsucc)
succstocheck.append(nsucc)
return newreplacements
def processreplacement(state):
"""process the list of replacements to return
1) the final mapping between original and created nodes
2) the list of temporary node created by histedit
3) the list of new commit created by histedit"""
replacements = state.replacements
replacements = adjustreplacementsfrommarkers(state.repo, state.replacements)
allsuccs = set()
replaced = set()
fullmapping = {}

View File

@ -14,6 +14,51 @@ Enable obsolete
> rebase=
> EOF
Test that histedit learns about obsolescence not stored in histedit state
$ hg init boo
$ cd boo
$ echo a > a
$ hg ci -Am a
adding a
$ echo a > b
$ echo a > c
$ echo a > c
$ hg ci -Am b
adding b
adding c
$ echo a > d
$ hg ci -Am c
adding d
$ echo "pick `hg log -r 0 -T '{node|short}'`" > plan
$ echo "pick `hg log -r 2 -T '{node|short}'`" >> plan
$ echo "edit `hg log -r 1 -T '{node|short}'`" >> plan
$ hg histedit -r 'all()' --commands plan
Editing (1b2d564fad96), you may commit or record as needed now.
(hg histedit --continue to resume)
[1]
$ hg st
A b
A c
? plan
$ hg commit --amend b
$ hg histedit --continue
$ hg log -G
@ 6:46abc7c4d873 b
|
o 5:49d44ab2be1b c
|
o 0:cb9a9f314b8b a
$ hg debugobsolete
e72d22b19f8ecf4150ab4f91d0973fd9955d3ddf 49d44ab2be1b67a79127568a67c9c99430633b48 0 (*) {'user': 'test'} (glob)
3e30a45cf2f719e96ab3922dfe039cfd047956ce 0 {e72d22b19f8ecf4150ab4f91d0973fd9955d3ddf} (*) {'user': 'test'} (glob)
1b2d564fad96311b45362f17c2aa855150efb35f 46abc7c4d8738e8563e577f7889e1b6db3da4199 0 (*) {'user': 'test'} (glob)
114f4176969ef342759a8a57e6bccefc4234829b 49d44ab2be1b67a79127568a67c9c99430633b48 0 (*) {'user': 'test'} (glob)
$ cd ..
Base setup for the rest of the testing
======================================
$ hg init base
$ cd base