I have a file before.txt, which I want to split into two smaller files. What I should have done was

$ hg cp before.txt part1.txt # Then edit part1.txt
$ hg mv before.txt part2.txt # Then edit part2.txt
$ hg commit

Then, both part1.txt and part2.txt will have before.txt as part of their history, so the diff will show as deleting parts of a larger file, rather than deleting one file and creating a new one with some of its contents.

However, what I actually did was

$ cp before.txt part1.txt # Then edit part1.txt
$ hg mv before.txt part2.txt # Then edit part2.txt
$ hg commit

So before.txt is only present in the history of one of my two files. If I hadn't run hg commit, it seems clear to me that I could solve my problem with

$ hg cp --after before.txt part1.txt

or something similar to that. And I haven't pushed this commit upstream, so I should be able to edit it however I like. But I can't figure out how to do it. When I run that hg cp, I see:

$ hg cp --after before.txt part1.txt
before.txt: No such file or directory
before.txt: No such file or directory
abort: no files to copy

This makes sense: it can't record that edit as part of a new commit, because the source file doesn't exist. I want to record it as part of the previous commit, but I don't know how to do that except by recording it as part of a new commit and then amending it into the previous commit.

2 Answers

2
DaveInCaz On Best Solutions

Here's one way to fix that situation:

$ hg shelve       # get any miscellaneous, unrelated changes out of the way
$ hg up <parent of revision with the mistake in it>

$ hg cp before.txt part1.txt 
$ hg mv before.txt part2.txt 

$ hg revert -r <revision with the mistake in it> --all
$ hg commit
$ hg strip <revision with the mistake in it>

(I didn't actually try all these commands, hopefully no typos!)

The first step is optional depending on the state of your working directories.

Now part1.txt and part2.txt should have the correct contents. The use of revert was just to save having to manually re-edit the file changes. But you could also just redo it manually if that seems easier.

The use of revert to pull into the working folder the effects of another changeset is a trick I use a lot. Its like a manual way of amending which gives you total flexibility. But it only works well when the revision you are reverting your working copy to is closely related to the revision which is the parent of the working copy. Otherwise it will create numerous nuisance changes.

1
arhak On

based on @DaveInCaz answer, here is a MCVE

mkdir tmpdir
cd tmpdir
hg init

echo line1 > before.txt
echo line2 >> before.txt
hg add before.txt
hg commit -m "my before"

cp before.txt part1.txt
hg add part1.txt
hg mv before.txt part2.txt
echo line1 > part1.txt
echo line2 > part2.txt
hg commit -m "my bad"


hg shelve
hg up -r -2

hg cp before.txt part1.txt
hg cp before.txt part2.txt
hg revert -r -1 --all
hg commit -m "my good"

hg strip -r -2

some remarks:

  • twice hg cp, because the later revert will delete the file before.txt for us, otherwise it would complain about missing it
  • revert --all at least in my version it requires to specify what is being reverted
  • the hg shelve is to be safe, before switching over to a different revision
  • the hg up -r -2 could have -C since previous shelve made us safe, that way you can retry final steps with different approaches and see what suits you better