views:

142

answers:

3

I made a big oops, and could use some help undoing it.

We have two repositories-a fairly stable repository, and a repository we're working on changes in. I just made a defect fix in our stable repository, and was moving it up to the working repository. I pulled from the stable repository, merged, then accidentally pushed to the stable repository.

The stable repository now looks like this:

*merge
| \
|  \
|   *b
*a  |
|  / 
*c

where a is the commit that should be the tip of the stable repository, b is all the stuff that we've done in the development repository, and c is the point we branched the development repository.

How do I go about making it back to:

*a
|
*c

(I know I can't really make changes go away, I'm just looking for a functional structure...)

I've read some things that make me think that hg backout is the command I need, but I'm not exactly sure what it does.

A: 

hg rollback will revert the one last commit, deleting any history that would otherwise been made.

So, if you start with this:

*merge
| \
|  \
|   *b
*a  |
|  / 
*c

hg rollback will give:

*a  *b
|  / 
*c

note that you can only do hg rollback only once.

You should make a complete backup of the repository before doing rollback. To do this, simply hg clone the whole repository.

Lie Ryan
I think you mean rollback, not backout. :)
Niall C.
I just updated this post so no it should be OK :)
Michal Sznajder
+2  A: 

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. By default it saves a backup in case you change your mind again.


UPDATE (per @Rudi's comment: sorry I missed the "already pushed" part)

Since the merge is already pushed out, NEVER EVER do what I suggested earlier. Hate emails from fellow developers would have been the best outcome.

Do this instead:

hg up -r<*merge>
hg revert -r<*a> -a
hg ci -m "undo unintended merge"

Or you could be more kosher:

hg up -r<*merge>
hg backout -r<*merge> --parent<*a>
Geoffrey Zheng
Since the merge is already pushed, the merge-commit cant be rolled back (push and pull are also transactions). Also this operation wold be needed to be done in every working copy which pulled this commit from the stable repository.
Rudi
What's the difference between using backout and revert? I ended up using backout, but I'd like to know my options (even though I hope it never happens again!)
Neil
`backout` commits a new changeset on top of your existing changesets to effectively reverse the changes. `revert` changes your working copy and does not commit.
Geoffrey Zheng
+1  A: 

I think it to late to make hg rollback since you have already pushed your changes.

You might try with MQ extensions but this also works locally. hg strip will only modify your local repo. You could of course try to modify your server repo directly on server but if somebody pulled it it is too late.

Another option is described in chapter 9 of hgbook in section Backing out a merge. It involves hg backout command but it might be an overkill for you...

I suggest to hg update -C to *a revision, merge with tip and ignore all changes from *merge? Your repository will look more or less like this than:

*second merge
 |  \ 
 |   \ 
 |    \     
 |     \ 
*merge | 
| \    |
|  \   |
|   *b |
*a  | /
|  / /
*c---

Commands for this are

  • $ hg --config ui.merge=internal:local merge #keep my files
  • $ hg --config ui.merge=internal:other merge #keep their files

More details could be found here

Michal Sznajder