Mercurial: Move a modified file between commits

Viewed 32

Let's say I have the following commit history:

A - newest
B
C - oldest

I have changed a file foo in B, but I want to move the change from B to A.

How can I do that with hg?

2 Answers

There are several different ways that I can think of. Both have some moderate user requirements (please make sure you have backed up or pushed to a non-publishing repository first).

1 ) Use histedit and amend (requires that each extensions be enabled).

- copy file you wish to move to a safe location
- run hg histedit and edit changeset b and restore file to its original state
- run hg histedit --continue to commit changes.
- copy file from safe location to repository
- run hg amend to append file to changeset A

2 ) Use split and histedit.

- run hg split -r b

    - split out all but the file you wish to move into a new changeset

    - create a new changeset onto of that containing the fie (give it a temporary description)

- run hg histedit

    - move the temp change above A

    - roll the temp change into A

3 ) Use hg-evolve uncommit / amend. While this is a somewhat advanced method, I much prefer this one myself.

- run hg update B

- run hg uncommit and select the file you wish to move.

- run hg evolve to correct the stack

- run hg update A

    Note: if you get a warning about needing to use --clean use hg shelve before
            running the update followed by hg unshelve afterwords.

- run hg amend to add file to change A

4 ) Use hg uncommit all contents of changesets A and B and then recommit using hg commit -i to reassemble the changesets with the desired content.

There are likely a number of other ways, but this is what came to me.

Here's a simplistic but functional approach, maybe for people not very experienced with some of the more advanced HG commands:

  1. Make sure your working directory is clean (no changes).

  2. hg update -r C

  3. hg revert -r B

  4. hg commit ... as needed, but leave out the change destined for A. This creates B'.

  5. hg shelve ... store away the uncommitted changes

  6. hg revert -r A

  7. hg unshelve ... restore uncommitted changes, merging them into the workding directory

  8. hg commit ... creates C'.

  9. hg strip -r B ... (optional!)

(where it says -r A that means use the revision # corresponding to A)

Explanation: we just create two (or more) entirely new changesets and then delete the originals (which is optional).

We're using revert basically to mean "copy from some changeset to the working directory" which I find is very handy in many circumstances.

After these operations -- but prior to using strip -- the history would look like:

   *
   |
A  A'
|  |
B  B'
|/
C

and strip will just delete A, B.

Related