Rolling back changes in mercurial best pracitice - mercurial

I am trying to undo a committed change that I have not yet pushed.
I use use TortisHG. I could not work out how to get the strip extension enabled.
There is a solution in there I suspect might be best practice, but I can't get my head around the merge part. And I don't think it solves the problem of resolving a locally committed, but un pushed change-set.
I have found rollback in Tortis does not cut the mustard.
Finally - is this easier with GIT?

Just update the workspace to the point before the unwanted committed change.
Then just graft subsequent bits after that change that you require working forwards in time from that location
You can then tidy up using strip

Almost exactly the same question was asked a couple of days ago - Discarding more than one commit in TortoiseHg. It works when the unwanted changesets are on the head of a branch. To save you the trouble of looking, I've reproduced my answer below. If you've committed desirable changesets on top of the unwanted ones, there is a slightly more complex answer using rebase, but it's not clear that that is a problem here:
In a recent mercurial (since v2.8 I think), you can make changesets obsolete which hides them from most views, yet they are still there if you find you ever want them. This is the recommended way to do what you want.
Lets say the most recent changeset is 132 and the latest good change is 130. Simply make changesets 131 & 132 obsolete like this:
hg prune -r 131::132
Couldn't be simpler (just make sure the current parent isn't one of the unwanted changesets). [And I don't see how git could make that any simpler.]
If you find you want to see the contents of the obsolete changesets, just add --hidden to the normal command line, e.g.:
hg log -r 120::132 --hidden
NB. Tortoise has a feature to view obsolete changesets, but I don't believe it yet has the ability to make them obsolete.

One option is to amend the old changeset. This is particularly useful is the changeset you want to undo is the tip but includes some changes you would like to revert and some that you would like to keep. Just revert the files you want undone and run hg commit --amend. This applies any working directory changes to the previous commit. The effect this will have is cancelling out the changes you wanted to get rid of.

The 'Keep "My" or "Their" files when doing a merge' you have mentioned from the 'best practices', is more about discarding changes from one of the branches. I believe that all you need is the "strip" extension".
You can also make a trick with MQ by importing temporarily your changesets to a patch queue and cherry-picking back only the changess that you need:
Right click on your lowest changeset that you want discard, "Modify history" -> "Import to MQ"
Open "Patch Queue" dialog and press "Unapply all patches"
Re-arrange patches in the order that you need with drag-n-drop
Apply only the patches that you need
Finalize them with right click on a changeset, "Modify history" -> "Finish patch"
Regarding enabling the "strip" extension. If you have enabled MQ extension the "strip" is activated automatically even though it's been showing as "unchecked" and "disabled". Disabling MQ (and restarting the TortoiseHG), will allow you to enable/disable "strip" explicitly.
PS: checked on Win32 machine, TortoiseHG 3.2.3
PPS: sorry for "GUIsh" way of solving the problem :-)
Update:
I tried to reproduce the command line version of described above with an older version of Mercurial.
Firstly we create a repo and fill it with changesets:
C:\temp>mkdir testhg
C:\temp>cd testhg
C:\temp\testhg>hg init
C:\temp\testhg>hg --version
Mercurial Distributed SCM (version 2.2.1)
(see http://mercurial.selenic.com for more information)
Copyright (C) 2005-2012 Matt Mackall and others
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE
C:\temp\testhg>echo test1 > file.txt
C:\temp\testhg>hg addremove
adding file.txt
C:\temp\testhg>hg ci -m "test 1"
C:\temp\testhg>echo test2 >> file.txt
C:\temp\testhg>hg ci -m "test 2"
C:\temp\testhg>echo test3 >> file.txt
C:\temp\testhg>hg ci -m "test 3"
These are our changesets:
C:\temp\testhg>hg log
2[tip] 201b257237ac 2015-01-13 17:59 +0200 Username
test 3
1 2054f00740b6 2015-01-13 17:58 +0200 Username
test 2
0 160251440fb0 2015-01-13 17:58 +0200 Username
test 1
Move two last changes into a patch queue:
C:\temp\testhg>hg qimport -r 1 -r 2
C:\temp\testhg>hg qseries
1.diff
2.diff
Remove a change in the middle (the patch file itself is still kept in patched directory though):
C:\temp\testhg>hg qremove -k 1.diff
Push back a change that we want to keep in the history and finalize it:
C:\temp\testhg>hg qpush
applying 2.diff
patching file file.txt
Hunk #1 succeeded at 2 with fuzz 2 (offset -1 lines).
now at: 2.diff
C:\temp\testhg>hg log
1[2.diff,qbase,qtip,tip] 9d0771cf5145 2015-01-13 17:59 +0200 Username
test 3
0[qparent] 160251440fb0 2015-01-13 17:58 +0200 Username
test 1
C:\temp\testhg>type file.txt
test1
test3
C:\temp\testhg>hg qfinish 1
C:\temp\testhg>hg log
1[tip] 9d0771cf5145 2015-01-13 17:59 +0200 Username
test 3
0 160251440fb0 2015-01-13 17:58 +0200 Username
test 1

Related

Mercurial hg pull hg up hg merge heads blah blah the whole process not very clear

We are using Kiln as our mercurial server and I there are three developers working on the same repo and i get this issue often and I am not quite sure what is happening.
Every once in a while I will do an hg pull.
Then when I run hg up I get the following message
abort: crosses branches (use 'hg merge' to merge or use 'hg update -C'
to discard changes )
ok so I run hg merge and I get
abort: outstanding uncommitted changes (use 'hg status' to list
changes)
ok so I run hg status and I get something like this
! Safemail 3.0\CSM3.0\AddinSetup\Release\AddinSetup.tmp
! Safemail 3.0\CSM3.0\Outlook2007Addin\Outlook2007Addin_TemporaryKey.pfx
no idea what to do with that.
If I run hg commit again it says no changes.
Finally if i run hg heads I get
changeset: 51:daea74a29d5c
tag: tip
parent: 49:b88e6e522672
user: Rahul Chandran
date: Mon Jan 23 13:30:54 2012 -0800
summary: added login code
changeset: 50:cb6f6e1eec5e
parent: 46:d83431c322ad
user: dthompson#Jabberwock.lan
date: Mon Jan 16 22:10:11 2012 -0600
summary: Adjusted Email-PDF formatting
Clearly I do not come from a DSCS background and I am probably missing some basic understanding of what is going on . Any help appreciated.
What I do know is that the code that my co worker checked in via a push has like no files in common with my changes. I would not expect like a merge conflict or some such in a non distributed source control system . essentially I am having some trouble understanding what is happening here exactly
Check out the help for hg status. It will tell you that ! means:
! = missing (deleted by non-hg command, but still tracked)
So you deleted some files that are tracked in your repository. If you didn't mean to track them in the first place (they look like good .hgignore candidates) you can hg forget FILENAMES; hg commit them. If, instead, you do mean to track them you can hg revert FILENAMES them (to get them back into your working dir from your repository.
Once hg status agrees you have no uncommitted changes you'll be able to hg merge.
Consider taking a little time to read the first few chapters of the (free) Mercurial book. These basic concepts are easy when presented well and in order, but horribly frustrating when you try to pick them up piecemeal from google searches and Stack Overflow questions. It's worth the 30 minutes to save you hours.

Find what mercurial commit changed the executable bit on a file

I know how to use hg blame to find what exact commit changed a line in a file, but I can't find a similar way to find when the executable bit on a file was changed.
First note that as changes in the exec bit don't affect the file contents, like deletions, they will not necessarily be shown by 'hg log filename'. (Unix nerds can compare the ctime/mtime rules for files and directories with respect to rm/chmod to understand this distinction.) So you will need to use something like:
$ hg log --deleted file
to show all changesets that touch a file, include exec changes, deletions, and duplicates. This is not enabled by default for various reasons including that it can be an order of magnitude slower.
Finding exec bits while perusing the log will also mean looking at git-style patches as standard patch(1)-compatible patches don't know about exec bits. So the total command might look something like:
$ hg log --removed -pg contrib/simplemerge | grep "^new mode" -B 10
+ import os
sys.exit(main(sys.argv))
changeset: 4363:2e3c54fb79a3
user: Alexis S. L. Carvalho <alexis#cecm.usp.br>
date: Mon Apr 16 20:17:39 2007 -0300
summary: actually port simplemerge to hg
diff --git a/contrib/simplemerge b/contrib/simplemerge
old mode 100644
new mode 100755
That reads: "search all git patches on simplemerge for lines starting with 'new mode' and show the previous 10 lines".
Another alternative is to use bisect. This can be used for finding basically any sort of change you can test for. For instance, if you're looking for where the X bit is set:
$ hg bisect -g 1000 # some past revision without the X bit
$ hg bisect -b tip # some recent revision with the X bit
Testing changeset 8114:ad3ba2de2cba (14179 changesets remaining, ~13 tests)
993 files updated, 0 files merged, 716 files removed, 0 files unresolved
$ hg bisect -c "[ ! -x contrib/simplemerge ]" # shell expression returns 0 (good) if no x bit
Changeset 8114:ad3ba2de2cba: bad
Changeset 4566:087b3ae4f08a: bad
Changeset 2797:a3c6e7888abf: good
Changeset 3678:7e622c9a9707: good
Changeset 4121:d250076824e3: good
Changeset 4345:ec64f263e49a: good
Changeset 4454:28778dc77a4b: bad
Changeset 4403:15289406f89c: bad
Changeset 4371:d7ad1e42a368: bad
Changeset 4355:10edaed7f909: good
Changeset 4366:390d110a57b8: bad
Changeset 4363:2e3c54fb79a3: bad
Changeset 4361:99c853a1408c: good
Changeset 4362:465b9ea02868: good
The first bad revision is:
changeset: 4363:2e3c54fb79a3
user: Alexis S. L. Carvalho <alexis#cecm.usp.br>
date: Mon Apr 16 20:17:39 2007 -0300
summary: actually port simplemerge to hg
Here we've automated the test with a standard Bourne shell expression to check a file's exec bit and Mercurial then runs through checking out revisions and testing them for us.
There may not be a simple build-in method a la hg blame (or maybe there is, and I just don't know it!), but you should be able to use a brute-force approach to hunt down the changeset by looking at the diffs.
First, you need to enable git-style diffs, because hg's normal diff output does not show changes in file modes. To do this, add to your hgrc the following:
[diff]
git = True
Then, you can look at all the diffs for the file you're interested in and find the commit that changed the mode, by running:
hg log -p file_of_interest
If you're on a *nix system, it helps to pipe to less or grep to easily search through the output. The mode changes should be displayed just below the beginning of the patch line that starts with:
diff --git a/file_of_interest b/file_of_interest
old mode ....
new mode ....
So, for instance, you might be looking for a mode change from 644 (no exec) to 755 (exec bit set), in which case you'd see something like:
old mode 100644
new mode 100755
Once you find the diff you're after that contains the mode change you're after, you can search backwards to get the commit hash.
Not quite as simple as hg blame, but it should work to some degree. Hope that helps.

Mercurial branched revision history, two remote heads when pushing

I committed a bunch of changesets locally (23-28) and then I realized that I wanted to go back to revision 25, so I ran "hg up -r 25" to go back. Then I started working from there and committed a few changesets. Now I'm ready to push my changes to the server, but when I try to do that it complains with "abort: push creates new remote heads on branch 'default'!". I thought someone else may have committed to the repository so I did a hg pull, but did not get any changes.
Here is the revision tree that I am working with
23
|
24
|
25
/ \
26 29
| |
27 30
| |
28 31
|
32
|
33
|
34
|
35
Is it possible to remove revisions 26,27, and 28? How can I fix things so that I can run "hg push" without errors? Should I have used a different command to go back to revision 25?
Let me just throw in a voice of dissent on Lasse's answer. Mercurial is a system about building an immutable history. Think of a scientist working in his or her lab book on numbered pages in pen. Everything is important, even the stuff you wish you didn't write and don't want at this second. The strip command isn't enabled by default for a reason -- it violates the immutability that is a mercurial goal.
The more "Mercurial" way to address this would be to merge 26 into 35, selecting entirely the options from 35, so you're back down to one head. Then your push will still have only one head, but all of history is preserved.
Alternately you had the option to 'hg push -r 35' which would have given you no warning or error nor required -f because you'd be leaving the repo repo with only one head.
There's nothing wrong with strip, but it's not part of the traditional toolset or mindset in Mercurial.
Well, there's two ways I know of.
Note: Please make a backup, or a clone to experiment on, I do not take responsibility for any lost work
First, if you got MQ extension enabled, you can strip those off. I only know how to do that using TortoiseHg, but I'll find the right commands after posting here.
To do this, you would execute this command:
hg strip 26
Secondly, you can create a new clone locally of only some of the changesets, again I only know how to do that using TortoiseHg.
To do this, you would execute this command:
hg clone SOURCEDIRECTORY CLONEDIRECTORY -r35
Then, from your clone, after verifying that it contains the changesets you want, you push against the target repository.
As for answering what you should've done instead, you could've deleted your original clone and re-cloned from the server to get a clean clone only containing up to changeset 25, but of course you could use the strip command to get rid of the excess changesets then as well.
There's nothing inherently wrong with having multiple heads - Mercurial is just checking it's what you want. In this case, you might want to keep the abandoned development path for future reference. If so, just force the push (with hg -f push) and create the abandoned branch. If you just want to forget everything about it, #Lasse's answer is just fine.

Abandoning changes without deleting from history

There is a commit that just didn't work, so I want to abandon it without deleting it from history.
I have updated from an earlier revision and committed, thus creating a new head.
I don't have branches, I don't want branches, I just want to simply go on with the new head exactly as it is, nothing fancy, no merge, no worries, just go on forgetting the previous one.
I can't seem to find how to do that, and I'm starting to believe it can't be done. All I find is stuff about branches, or stuff about merging.
Update your repository to the head with the revision that you want to forget about, then use hg commit --close-branch to mark that (anonymous) branch as closed. Then update to the head of the branch that you do want, and continue working.
You can still see the closed branch if you use the -c option to hg heads, but it won't show up by default and hg merge will know not try to merge with the closed head.
You will need to use hg push --force the first time you push this closed head to another repository since you are actually create additional heads in the remote repository when you push. So tell Mercurial that this is okay with --force. People who pull the closed head wont be bothered by any warnings.
I know you don't want to work with branches at this stage, but that's exactly what you've done. When you went back to an earlier version and committed something that worked you created a branch - an unnamed branch, but a branch all the same.
There's no problem with just carrying on just as you are and not worrying about having multiple heads, but if you want to tidy things up so you don't accidentally pick the wrong head one time then you can kill off the old branch.
There's a good section in the Mercurial documentation that takes you through a number of options around Pruning Dead Branches.
I think the best option for you is to mark the old branch as "closed". If your old head is revision "123" then:
hg update -r 123
hg commit --close-branch -m 'Closing old branch'
hg update -C default
First of all, type:
hg heads
Imagine, you have three heads listed:
changeset: 223:d1c3deae6297
user: Your name <your#email.com>
date: Mon Jun 09 02:24:23 2014 +0200
summary: commit description #3
changeset: 123:91c5402959z3
user: Your name <your#email.com>
date: Sat Dec 23 16:05:38 2013 +0200
summary: commit description #2
changeset: 59:81b9804156a8
user: Your name <your#email.com>
date: Sat Sep 14 13:14:40 2013 +0200
summary: commit description #1
Let's say, you want to keep the last head active (223) and close the rest.
You would then do as follows:
Close head #59
hg up -r 59
hg ci --close-branch -m "clean up heads; approach abandoned"
Close head #123
hg up -r 123
hg ci --close-branch -m "clean up heads; approach abandoned"
Commit the changes
hg push
Don't forget to switch to the right head at the end
hg up -r 223
And you're done.
You want to use hg backout. This removes the changes made by the changeset from any child changeset.
Check this out for a good explanation.
Mercurial Backout
An alternative to closing or stripping the unwanted branch would be to merge it in a way that totally discards its effects, but leaves it in history. This approach will allow those unwanted changes to propagate in a push - so only use this if that is the intended effect.
Let's say the changeset history looks like this:
1-2-3-4-5-6
\
7-8-*
and it is 5 and 6 which are no longer wanted.
You can do this:
hg up 8
hg merge -r 6 -t :local
hg commit ...
which will create this:
1-2-3-4-5-6
\ \
7-8-9-*
The update to 8 ensures you are working at the desired head in history, which you want to keep.
The -t :local instructs hg to use the merge "tool" called local which tells it to ignore changes from the other branch, i.e., the one NOT represented by the current working folder state. More info.
Thus the unwanted changes in 5 and 6 are preserved in history but do not affect anything more recent.
Both Niall's and Nick's answers are straight on. Because I find myself creating lots of dangling heads, I ended up writing an alias to close heads more easily. By adding this to your .hgrc:
[alias]
behead = !REV=$($HG id -i); $HG update $# -q && $HG ci --close-branch -m "Closing dead head" && $HG update $REV -q
(if you already have an [alias] section, you can append to it instead)
You can now close a head in one single-command (and without having to update to a different changeset manually) like this:
$ hg behead 123
Note: the alias takes advantage of the fact that Mercurial aliases can be shell commands. This means that this will probably only work on UNIX, not on Windows.
This is a use case for the Evolve extension. It's currently not bundled with Mercurial, so it is technically a third party extension. But it's being used quite heavily by a bunch of people, including Mercurial developers, is being very actively developed, and isn't going anywhere.
With the Evolve extension, you simply do
hg prune -r revname
and get on with your life. The cset will still be there, but obsoleted. It won't be visible unless you pass the --hidden option to Mercurial commands, and by default won't be pushed to remote repositories. Though I think you can force it if you really want to.
If the cset you are pruning has ancestors you want to keep, then you'll have to run hg evolve to rebase those changesets. hg evolve will do so automatically. Otherwise, you don't have to do anything.
You may clone your corrupted repo to a new one without cloning that unwanted head. Then remove old repository, move newly created clone to the original place and continue working with it. This will take some time, but you'll get a perfectly clean repository without a sign of that unwanted revision.
hg clone --rev myGoodResition myDirtyRepo myCleanRepo
I have run into this issue many times when I want to behead a head that was created in error. I always want to see it disappear off the face of the Earth.
On your local copy, get the latest and then:
Find the beginning of a head you want to strip (where a new neck starts to branch off), get the revision number
Strip it.
Source: TipsAndTricks.
Source: PruningDeadBranches#Using_strip.
hg --config extensions.hgext.mq= strip -n <rev>
Make a trivial file update (add a whitespace to a file), commit and push.
Your repo should now have the head stripped. The last step is important as stripping doesn't create any changes you can push to your central repository. Without the last step you only have stripped the head locally.

Is there any way to delete local commits in Mercurial?

So I keep making a silly mistake in Mercurial. Often times, I'll start work without doing an "hg pull" and an "hg update." When I try to push my changes, I get an error.
Is there any way to delete my local commits so I can avoid creating multiple heads, branches, etc? I just want to delete my local commits, merge my changes with the tip, and then re-commit. Sounds simple, right? I can't seem to find any way to easily delete local commits so I can cleanly merge with the tip.
Again, I'm only trying to delete local commits made with "hg ci". I don't want to modify files, revert, etc.
Enable the "strip" extension and type the following:
hg strip #changeset# --keep
Where #changeset# is the hash for the changeset you want to remove. This will remove the said changeset including changesets that descend from it and will leave your working directory untouched. If you wish to also revert your committed code changes remove the --keep option.
For more information, check the Strip Extension.
If you get "unkown command 'strip'" you may need to enable it. To do so find the .hgrc or Mercurial.ini file and add the following to it:
[extensions]
strip =
Note that (as Juozas mentioned in his comment) having multiple heads is normal workflow in Mercurial. You should not use the strip command to battle that. Instead, you should merge your head with the incoming head, resolve any conflicts, test, and then push.
The strip command is useful when you really want to get rid of changesets that pollute the branch. In fact, if you're in this question's situation and you want to completely remove all "draft" change sets permanently, check out the top answer, which basically suggests doing:
hg strip 'roots(outgoing())'
If you are using Hg Tortoise just activate the extension "strip" in:
File/Settings/Extensions/
Select strip
Then select the bottom revision from where you want to start striping, by doing right click on it, and selecting:
Modify history
Strip
Just like this:
In this example it will erase from the 19th revision to the last one commited(22).
Modern answer (only relevant after Mercurial 2.1):
Use Phases and mark the revision(s) that you don't want to share as secret (private). That way when you push they won't get sent.
In TortoiseHG you can right click on a commit to change its phase.
Also: You can also use the extension "rebase" to move your local commits to the head of the shared repository after you pull.
As everyone else is pointing out you should probably just pull and then merge the heads, but if you really want to get rid of your commits without any of the EditingHistory tools then you can just hg clone -r your repo to get all but those changes.
This doesn't delete them from the original repository, but it creates a new clone that doesn't have them. Then you can delete the repo you modified (if you'd like).
I came across this problem too. I made 2 commit and wanted to rollback and delete both commits.
$ hg rollback
But hg rollback just rolls back to the last commit, not the 2 commits. At that time I did not realize this and I changed the code.
When I found hg rollback had just rolled back one commit, I found I could use hg strip #changeset#. So, I used hg log -l 10 to find the latest 10 commits and get the right changeset I wanted to strip.
$ hg log -l 10
changeset: 2499:81a7a8f7a5cd
branch: component_engine
tag: tip
user: myname<myname#email.com>
date: Fri Aug 14 12:22:02 2015 +0800
summary: get runs from sandbox
changeset: 2498:9e3e1de76127
branch: component_engine
user: other_user_name<name#email.com>
date: Mon Aug 03 09:50:18 2015 +0800
summary: Set current destination to a copy incoming exchange
......
$ hg strip 2499
abort: local changes found
What does abort: local changes found mean? It means that hg found changes to the code that haven't been committed yet. So, to solve this, you should hg diff to save the code you have changed and hg revert and hg strip #changeset#. Just like this:
$ hg diff > /PATH/TO/SAVE/YOUR/DIFF/FILE/my.diff
$ hg revert file_you_have_changed
$ hg strip #changeset#
After you have done the above, you can patch the diff file and your code can be added back to your project.
$ patch -p1 < /PATH/TO/SAVE/YOUR/DIFF/FILE/my.diff
You can get around this even more easily with the Rebase extension, just use hg pull --rebase and your commits are automatically re-comitted to the pulled revision, avoiding the branching issue.
hg strip is what you are looking for. It's analogous of git reset if you familiar with git.
Use console:
You need to know the revision number. hg log -l 10. This command shows the last 10 commits. Find commit you are looking for. You need 4 digit number from changeset line changeset: 5888:ba6205914681
Then hg strip -r 5888 --keep. This removes the record of the commit but keeps all files modified and then you could recommit them.
(if you want to delete files to just remove --keep hg strip -r 5888
If you are familiar with git you'll be happy to use histedit that works like git rebase -i.
[Hg Tortoise 4.6.1]
If it's recent action, you can use "Rollback/Undo" action (Ctrl+U).
In addition to Samaursa's excelent answer, you can use the evolve extension's prune as a safe and recoverable version of strip that will allow you to go back in case you do anything wrong.
I have these alias on my .hgrc:
# Prunes all draft changesets on the current repository
reset-tree = prune -r "outgoing() and not obsolete()"
# *STRIPS* all draft changesets on current repository. This deletes history.
force-reset-tree = strip 'roots(outgoing())'
Note that prune also has --keep, just like strip, to keep the working directory intact allowing you to recommit the files.