Recover from "hg update" with uncommitted changes - version-control

I run into the following issue all the time with Mercurial, and it's very annoying:
I'm at some revision A.
I have local changes, which I meant to commit or amend on top of A, but haven't yet.
I want to go to some revision B, but I forgot that I had local changes!
I do hg update B. Mercurial "helpfully" tries to rebase my local changes to apply on top of B. This typically results in conflicts, and it now asks me to fix the conflicts.
However, I don't want to fix the conflicts! I don't want my local changes to apply on top of B at all. I want them to stay at A, either as a new commit just after A, or amended into A, as the case may be.
Is there a way I can recover from this state? The only way I know how is to
fix the merge conflicts at B
go back to A, getting merge conflicts again
fix the merge conflicts again at A
commit my changes at A and go back to B
This is a lot of work, and it's pointless. I shouldn't have to rebase my local changes to apply on top of B, only to rebase them again to apply on top of A.
If there's no better way to recover from this mistake, is there a way to get hg to refuse to do an update when you have local changes? I never want to do that - if I wanted that I'd just commit the local changes and rebase them on top of B.

Getting the dirty files back after updating somewhere and back is a bit tricky. The "trick" is to make sure that your working copy has the dirty files after the first update.
So after you do
hg update $SOMEWHERE
and discover the mess because Mercurial begins opening merge tools, calmly close the merge tools and then run
hg resolve --unmark --all
hg resolve --all --tool internal:local
All files that were merged because you had changes in them will now look like they did in your dirty working copy. This includes files that were merged cleanly and files you were prompted to merge. Updating back is now possible:
hg update $BACK
hg resolve --unmark --all
hg resolve --all --tool internal:local
You should now be back to where you started. If you modified the files after the first update, then it is the modified version you see after the second resolve. This is why you will want to resolve the files after the first update.

hg update -c will abort the update if you have any uncommitted changes.

If there's no better way to recover from this mistake, is there a way
to get hg to refuse to do an update when you have local changes?
Add this to your ~/.hgrc:
[commands]
update.check = noconflict
This will still allow hg update with uncommitted changes as long as there is not conflict. You can also call hg help config.update.check for other possible options:
"commands.update.check"
Determines what level of checking 'hg update' will perform before
moving to a destination revision. Valid values are "abort", "none",
"linear", and "noconflict". "abort" always fails if the working
directory has uncommitted changes. "none" performs no checking, and
may result in a merge with uncommitted changes. "linear" allows any
update as long as it follows a straight line in the revision history,
and may trigger a merge with uncommitted changes. "noconflict" will
allow any update which would not trigger a merge with uncommitted
changes, if any are present. (default: "linear")

Maybe the answer is to avoid updating if the working directory is dirty.
Modifying the ~/.hgrc in the following way should help:
[hooks]
preupdate = test -z "$(hg status)"

Related

How to abort an Hg merge without losing working copy changes?

Last night I started a merge; and forgot to commit it.
Later on I made some changes to the working copy.
Today I no longer want that merge at all (as I'll merge on a newer revision), but I want to keep the local working changes. The changes are independent of any merge resolution.
How can the local changes be kept (and/or reapplied later) while aborting the current merge?
Using shelve on the local changes was not allowed:
$ hg shelve foo/bar
abort: cannot shelve while merging
Using an hg up -C, the normal way to 'abort a merge', would eliminate the local changes.
This is not like How to 'hg merge' without affecting the working directory? because a merge has already been started, just not committed. Answers that involve finishing the commit first, and then picking changes is suitable if such can be shown simply, although the question is focused about 'abort without the commit'.
The easiest solution is probably to record the changes temporarily in a secret commit and then to revert to that commit, e.g.:
hg resolve -m -a # mark all files as resolved
hg commit -s -m "Savepoint." # Temporary commit.
hg update .^ # Back to original revision.
# (Use ".^" in cmd.exe on Windows.)
hg revert -r tip --all # Revert files to saved commit.
hg diff # Verify that you've got all the changes.
hg strip -r tip --keep # And discard the temporary commit.
We're using a secret commit here so that it doesn't accidentally get pushed if you forget to strip it (or if you plan to keep it around afterwards).
If you're using the evolve extension, hg prune -r tip is to be preferred over hg strip -r tip --keep, as it still keeps the temporary commit around (hidden) in case you need to refer to it later.

how to resolve conflict with git after autoformat in Eclipse?

We have two users working on the same class files in two separate git branches.
User A does nothing else but an "autoformat" Ctrl-Sift-F in Eclipse
User B just adds a space in a comment
Now we get a "conflicting change" and can't merge anymore.
Basically we are stuck just because of the autoformat.
How to resolve this situation in Eclipse git or on command line git bash?
You can use git merge -s ours or git merge -s their to decide which version of the file you wanna keep after the merge.
Resolve the conflict as you would with any merge conflict. It sounds like a pretty simple conflict to resolve. Or since the changes were trivial, the person doing the merge (i.e. the person who hasn't pushed their commit) can just reset back to a common commit, allowing them to pull the other person's changes.
git reset --hard THECOMMITID^
Where THECOMMITID is the unwanted formatting change. If it's just the most recent commit then you can use HEAD^.

How to undo a Git rollback

I wanted to rollback to the last commit after making a massive error, but I managed to rollback a little too fair. The commit I wanted to reassert doesn't appear when I enter 'git log' in bash (I suppose because it's no longer in the history). Is there any way I can recover the last commit by date?
I'm also using eGit in eclipse for the same project if that makes things easier. Thanks.
If you are ok with command line, go to you repo, do a git reflog and get the commit which you want to "rollback" to and do a git reset --hard <commit>
You would also be able to do git reset --hard HEAD#{1} and then come back to egit and rollback to the desired commit.
I find that generally it's better to make your changes forward in time rather than backward.
Git's approach is to "revert" the commit. When you revert a commit, you check out into your working directory the inverse of the commit in question. Then you add and commit that, and you've just made a NEW commit, that commits the "undoing" of the commit you're reverting, AND it leaves a record in history that such a thing happened, so if you want to undo your undoing, it's easy to do.

Delete all local changesets and revert to tree

I'm using Mercurial and I've got into a terrible mess locally, with three heads. I can't push, and I just want to delete all my local changes and commits and start again with totally clean code and a clean history.
In other words, I want to end up with (a) exactly the same code locally as exists in the tip of the remote branch and (b) no history of any local commits.
I know hg update -C overwrites any local changes. But how do I delete any local commits?
To be clear, I have no interest in preserving any of the work I've done locally. I just want the simplest way to revert back to a totally clean local checkout.
When the simplest way (a new hg clone) isn't practical, I use hg strip:
% hg outgoing -l 1
% hg strip $rev # replace $rev with the revision number from outgoing
Repeat until hg outgoing stays quiet. Note that hg strip $rev obliterates $rev and all its descendants.
Note that you may have to first enable strip in your Mercurial settings.
PS: an even smarter approach is to use the revset language, and do:
% hg strip 'roots(outgoing())'
You'll want to make a local clone where you preserve only the changesets that are also present in the remote repository. Use TortoiseHg, hg log or similar to figure out which of your revisions is that lastest revision you didn't make (the one before the mess started). Using hg outgoing can help here -- it will list all the changesets you made -- pick a revision number earlier than any of those.
If the target revision is called good and your clone is called foo, then do:
hg clone -r good foo foo-clean
This will be a fast, local operation -- there is no reason to download everything again. The foo-clean clone will only contain changesets up to revision good. You can now replace foo-clean/.hg/hgrc with foo/.hg/hgrc in order to preserve your repository-local settings such as the default push/pull path.
When you are satisfied that foo-clean has everything you need from foo, then simply delete foo and rename foo-clean to foo. Do a hg pull to get any new changesets from the remote repository into your clone and continue like normal.
If nobody has pushed new changesets to the remote repository, then it is very simple to determine which revision you want to use as good above: hg id default will tell you the ID of the tip in the remote repository.
Ok. So just delete all the local stuff, hg init the new local repository and hg pull the latest tip you have. Don't forget to hg update after this.
You may use
hg strip revision
to kill any revision and its subtree in your local repository.
https://www.mercurial-scm.org/wiki/Strip
But don't try to use it for anything that has been already pushed.
Just delete everything you have on your local system and re-clone the remote repo.
hg strip `hg out --template "{rev} {author}\n" | grep YOUR_AUTHOR_NAME | cut -d " " -f 1`
does the trick for me.
It strips all revisions that aren't pushed to the default repository which are created with your author name.
You can also use this style to make it not checking with the default repository but with another Repository
hg strip `hg out OTHER_REPO_ALIAS --template "{rev} {author}\n" | grep YOUR_AUTHOR_NAME | cut -d " " -f 1`
If you are using TortoiseHg, one simple way to get out of a (small) mess is to first update to the latest revision, then select your changesets and initiate "merge with local". When the merge dialogue appears, simply click the little '+' icon to reveal some extra options, one of which is "discard changesets from merge target (other) revision". Doing this will mean your changesets will still be in the repo and get pushed, but will have no effect, because they will be discarded in the merge. If you have a lot of changesets spanning many heads, you might not want to pollute the repo this way, but it's a simple fix and worth considering if the changesets you are discarding contain data that you may later want to reference.

I'm working on a project, and I want to see how it ran in its last revision. How do I do it without losing my changes?

Specifically, I'm using bzr, but tips for any VCS are welcome.
I think there are three options.
Use shelving
bzr shelve --all
bzr unshelve
Create a separate branch with the
latest
Create a patch of you
changes and revert the changes.
Apply patch when you need your
changes back.
Using Git:
git checkout HEAD^ # get the previous version, changing files on disk to match
make # run your project (or whatever command you use)
git checkout master # return to the "master" branch
The above applies if you've already committed whatever current changes you're working on, and want to go back to the previous commit. If you have changes that have not been committed yet, then use the stash:
git stash # save the uncommitted changes away
make # ...
git stash pop # restore your uncommitted changes
You can make and commit other changes in between the stash and the pop; this is Git's solution to the "boss interrupts with an immediate bug fix request" problem.