Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can I fix an incorrect file move in Mercurial much later?

We've all been there at some point -- you move a file using something other than Mercurial's hg move command and commit and push your changes only to realize your mistake much later. Is there any way to later inform Mercurial that there is a relationship between the "old" path and the "new" path?

I've tried playing with --after but that just seems to error out unless you happen to catch the problem before committing the problem changeset:

  > hg move OLD_PATH NEW_PATH --after
  OLD_PATH: The system cannot find the file specified
  abort: no files to copy

In my particular situation, this change was committed by a coworker across a messy private branch (or two) and then merged to our default branch and pushed to our main repo, so it's already spread out to the entire team.

Any ideas?

like image 950
Chris Phillips Avatar asked Sep 10 '15 19:09

Chris Phillips


People also ask

How to revert a changeset in Mercurial?

To revert a file to a specific changeset, use hg revert -r CHANGESET FILENAME . This will revert the file without committing it.

How do you revert the last push in Heartgold?

hg rollback reverts the last transaction, so you'd be left with unfinished merge, which you have to use hg update -C to get out. If you don't want *b (you have it in another clone), then enable the built-in MQ extension and run hg strip -r <*b> . It will get rid of *b and *merge.

What does hg rollback do?

The hg backout command lets you “undo” the effects of an entire changeset in an automated fashion. Because Mercurial's history is immutable, this command does not get rid of the changeset you want to undo. Instead, it creates a new changeset that reverses the effect of the to-be-undone changeset.


1 Answers

First of all, are you sure that the rename wasn't detected? Mercurial will detect moves where the file remains unchanged (i.e. where it wasn't a rename + edit) by default and record it as a rename. Verify that it wasn't actually detected with hg log -f NEW_PATH.

If it wasn't recorded as a rename, do a branch off the old commit with a proper rename and merge:

hg update -r ORIGINAL_COMMIT    # Go to the commit before the rename
hg move OLD_PATH NEW_PATH       # Record the rename
hg commit                       # Commit the rename
hg update default               # or whatever your main branch is
hg merge -r tip --tool :other   # '-r tip' is technically optional
hg commit                       # Commit the rename to the main branch
hg log -f NEW_PATH              # Verify that it worked

Note that --tool :other will prioritize the "other" branch (i.e. the new commit) and learn the proper history from there.


Postscript: It is also possible to alter the history after the fact, but for that you need the evolve extension. With that, you can alter a commit after the fact (with hg amend or hg commit --amend) and propagate the change through your history (with hg evolve --all) [1]. However, other members of your team may also need the evolve extension; plus, the shared repository that you push to/pull from needs to be set up for it (as a non-publishing repository). While this is possible, the setup effort, and the training your team may need to become proficient may not be worth it; also, other Mercurial-related tools may not understand it and become confused by it. In short: this is currently an advanced option that you should only use with full understanding and if the alternatives are insufficient.

[1] Note that hg evolve will not actually modify history, but create a new alternate history, related to the original one via so-called obsolescence markers. The old history can be viewed with hg log --hidden (or other commands with the --hidden flag).

like image 133
Reimer Behrends Avatar answered Sep 24 '22 02:09

Reimer Behrends