Peter Toft
Peter Toft

Reputation: 707

Mercurial: "undoing" two or more commits

In How do I do a pristine checkout with mercurial? Martin Geisler discuss how to remove already Mercurial commit'ed files using:

hg strip "outgoing()"

But what if I I want to keep my added files which went into "outgoing()" - example:

Two users a and b — starting on the same changeset

User a:

echo "A" > A.txt; hg ci -M -m ""; hg push

User b (forgets to run hg pull -u):

echo "B" > B.txt; hg ci -M -m "" B.txt;
echo "C" > C.txt; hg ci -M -m "" C.txt;

If user b run hg strip "outgoing()" then B.txt and C.txt are lost. hg rollback is not an option since there are two commits.

Can user b revert his files as "locally added - nontracked", then do hg pull -u, which gets A.txt, then handle the add/commit/push for B.txt and C.txt later?

Martin Geisler answered this earlier in the mentioned thread (a comment which I deleted and moved here:

hg update "p1(min(outgoing()))"
hg revert --all --rev tip 
hg strip "outgoing()"
hg pull -u

Now user c can finalize his work in the new files B.txt and C.txt and commit+push those.

Other ways to do this?

Upvotes: 1

Views: 1608

Answers (2)

Mark Tolonen
Mark Tolonen

Reputation: 177396

Another option is the rebase extension. With your scenario:

  1. A and B start with the same history.
  2. A commits and pushs a change.
  3. B commits two changes, but can't push because of A's commit.
  4. B pulls A's change.
  5. B runs hg rebase and pushes.

Before rebase:

Common ---------------------------- A (tip)
     \
      B1 - B2 (working parent)

After:

Common - A - B1 - B2 (tip, working parent)

Upvotes: 1

Ned Deily
Ned Deily

Reputation: 85025

You could but, by doing so, you are working against one of the biggest features of a DVCS like mercurial, that is, to easily and reliably handle the merging of multiple lines of development as in your case. If user b's goal is to have a line of development with all three changes applied, then the standard way to do that in hg would be to just go ahead and do an hg pull -u which will create a new head containing the change(s) from user a (and any other changes pushed to repo used for pulling) and then use hg merge to merge the two heads, the head containing user b's two change sets and the other containing user a's change set (as pulled). In a simple case like this one with no overlapping changes, hg should do all the right things by default.

$ hg pull -u
[...]
added 1 changesets with 1 changes to 1 files (+1 heads)
not updating: crosses branches (merge branches or update --check to force update)
$ hg merge
1 files updated, 0 files merged, 0 files removed, 0 files unresolved
(branch merge, don't forget to commit)
$ hg ci -m "merge"

If there were conflicts between the two heads (i.e. both users committed changes to the same files), there might need to be conflict resolution editing as part of the merge; hg will tell you if that is the case.

Upvotes: 2

Related Questions