hg unshelve error handling - an example

Faheem Mitha faheem at faheem.info
Wed May 12 13:45:35 UTC 2021


I recently unshelved in a working directory with the wrong
parent. This is quite easy to do. But it's not so easy to recover
from. The scripts below illustrate the situation I accidentally
created.

The Expect script `shelveinterrupt.exp` calls the shell script
`shelveinterrupt.sh`.

What happened here is that a patch to the file 'foo' was unshelved in
a working directory with a parent that doesn't contain 'foo'. So of
course this caused a problem when trying to merge the patch.

Suppose one does a Ctrl-C on the following dialog.

     file 'foo' was deleted in local [working-copy] but was modified in
     other [shelve].  You can use (c)hanged version, leave (d)eleted,
     or leave (u)nresolved.  What do you want to do? interrupted!

This results in a state of conflict.

     hg status

     # The repository is in an unfinished *update* state.

     # Unresolved merge conflicts:
     #
     #     foo
     #
     # To mark files as resolved:  hg resolve --mark FILE

     # To continue:    hg update .

It requires some effort to return the repository to a sane state.

As can be seen below, one needs to do the following, in that order.

     hg resolve -m foo
     hg update
     hg unshelve # respond with "u"
     hg abort

This is perhaps not an entirely obvious sequence of operations. Also,
I believe `hg abort` is relatively new. I'm also not clear why `hg
update` is necessary in this case, as it doesn't actually do anything,
as far as I can tell.

Perhaps Ctrl-C could leave the repository in a better state?

And is there a slicker/better way to fix this situation?

I have written before that I wished that `hg shelve` was a bit more
error-proof, and knowing which parent it belonged to would be a good
start. Or if that was not possible, perhaps it could keep track of
branch or topic name and give a warning if they differ on unshelve?
See for example https://bz.mercurial-scm.org/show_bug.cgi?id=6123.

Regards, Faheem Mitha

#############################
shelveinterrupt.sh
#############################
#!/bin/bash

rm -rf test-shelveinterrupt
hg init test-shelveinterrupt
cd test-shelveinterrupt

hg topics foo
echo "First line of foo" >> foo
hg add foo
hg ci -m "First line of foo" foo

echo "Second line of foo" >> foo
hg shelve

hg up null

hg topics bar
echo "First line of bar" >> bar
hg add bar
hg ci -m "First line of bar" bar

hg unshelve
#############################

#############################
shelveinterrupt.exp
#############################
#!/usr/bin/expect -f
#exp_internal 1

puts "RUNNING SHELVEINTERRUPT.SH SCRIPT"
spawn ./shelveinterrupt.sh
puts "INTERRUPTING PROMPT"
expect "What do you want to do?"
send -- ""
expect eof

cd test-shelveinterrupt
puts "CALLING HG STATUS"
exec hg status >@ stdout 2>@ stderr
puts "CALLING HG RESOLVE -M FOO"
exec hg resolve -m foo >@ stdout 2>@ stderr
puts "CALLING HG UPDATE"
exec hg update >@ stdout 2>@ stderr

puts "CALLING HG UNSHELVE"

spawn hg unshelve

puts "SENDING U TO THE PROMPT"
expect "What do you want to do?"
send -- "u\r"
expect eof
puts "CALLING HG ABORT"
exec hg abort >@ stdout 2>@ stderr
##################################

Output of: TERM=dumb ./shelveinterrupt.exp &> shelveinterrupt.out

##############################################
RUNNING SHELVEINTERRUPT.SH SCRIPT
spawn ./shelveinterrupt.sh
INTERRUPTING PROMPT
marked working directory as topic: foo
active topic 'foo' grew its first changeset
(see 'hg help topics' for more information)
shelved as default
1 files updated, 0 files merged, 0 files removed, 0 files unresolved
0 files updated, 0 files merged, 1 files removed, 0 files unresolved
marked working directory as topic: bar
active topic 'bar' grew its first changeset
(see 'hg help topics' for more information)
unshelving change 'default'
rebasing shelved changes
file 'foo' was deleted in local [working-copy] but was modified in other [shelve].
You can use (c)hanged version, leave (d)eleted, or leave (u)nresolved.
What do you want to do? interrupted!
CALLING HG STATUS
# The repository is in an unfinished *update* state.

# Unresolved merge conflicts:
#
#     foo
#
# To mark files as resolved:  hg resolve --mark FILE

# To continue:    hg update .

CALLING HG RESOLVE -M FOO
(no more unresolved files)
CALLING HG UPDATE
0 files updated, 0 files merged, 0 files removed, 0 files unresolved
CALLING HG UNSHELVE
spawn hg unshelve
SENDING U TO THE PROMPT
unshelving change 'default'
rebasing shelved changes
file 'foo' was deleted in local [working-copy] but was modified in other [shelve].
You can use (c)hanged version, leave (d)eleted, or leave (u)nresolved.
What do you want to do? u
unresolved conflicts (see 'hg resolve', then 'hg unshelve --continue')
CALLING HG ABORT
unshelve of 'default' aborted


More information about the Mercurial mailing list