Mercurial - basic process to revert a merge and try again - mercurial

We merge using this process:
cd c:\myapp
hg pull ssh://hg/myapp-1_0_1
hg merge
Now sometimes we mess up the merge or sometimes we get an error (ie: "can't merge file xxx - file in use"). Usually I just delete my c:\myapp repo, re-clone from the remote master repo and start over. This works but it's a pain. What is a better way?
I've read about using hg update -C but I'm not clear on the process. So you'd do hg update -C, this would delete the merged files but keep the changesets that were pulled down, and then you'd issue the hg merge command again and try again?
Update Regarding the related question: hg update -C still keeps the changesets in the repo which were pulled down with hg pull - how can you get rid of those?

hg update -C is just perfect. Why would you want to get rid of the pulled changesets? Because the update command updates to the head of the pulled changes? If this is your problem with the pulled changes, run hg update -C <revision-of-your-local-head> and don't care about the pulled changes, probably you'll pull them again at some point anyway. After the update proceed your work or try the merge again.
However, if your really want to get rid of pulled changes, use the strip command.

See this post How to abandon a hg merge?

Related

Can I get back my code changes after hg update removed them?

I am new to Mercurial. I had made some some changes in the code in some of the files and i hadn't committed them yet. but when I did hg update -c <branch-name>, all my code changes were gone. Is there any way I can get my code changes back or am I screwed?
Are you perhaps using a very, very, old version of Mercurial?
The help for hg update shows that -c won't discard uncommitted changes:
-C --clean discard uncommitted changes (no backup)
-c --check update across branches if no uncommitted changes
That's been the case for at least four years. If you try to update to a revision that would discard your local changes Mercurial warns you:
ry4an#four:~/test$ hg checkout 0
abort: uncommitted changes
(commit or update --clean to discard changes)
If you used -c it should have done nothing. If you used -C you should find whomever suggested you do so and yell at them.
Sorry for your loss. :(
Unfortunately no, as explained in the update help (hg help update), --clean don't create backup.
Check if you can get the old version of your files with your text editor with CTRL + Z, it already saved my life.

Mercurial workflow: why do I seem to commit everything twice?

I'm trying to grasp the Mercurial basics so please bear with me. :) My current workflow is as follows:
do some work until I'm ready to commit or need the others' changes
pull
at this point I'd like to merge my work with the latest changesets and commit, however Mercurial insists on me committing before merging
so it goes like "commit, merge, commit" and I'm basically committing everything twice, writing the same notes in both changesets and pushing two changesets at a time
Is it intended to be so? Is it possible to have just one changeset coming from me with every merge? Is it indeed desirable?
I've read a lot of online manuals but still feel I do not have solid understanding of the process. All comments are welcome. Thanks!
EDIT: Turns out I didn't know that update could merge incoming changes with uncommitted edits.
Merging always creates a separate changeset in Mercurial.
Plus, merging is not possible as long as you have uncommitted stuff in your local repository.
So the solution is to commit first, and pull and merge afterwards.
This will always result in two changesets, not one.
(...because merging always creates a separate changeset)
But you don't commit the same stuff twice, and especially you shouldn't write the same commit message twice:
The first commit is what you actually changed ("fixed a bug in the foo bar").
The second commit is just the merge (TortoiseHG actually pre-populates the commit message with "Merge", 99% of the time I just leave it like that).
This workflow will prevent a merge in the history, but you still do a merge as noted below:
Do some work until you are ready to commit or need another's changes.
hg pull
hg update (Note: hg pull -u does this and the previous in one step.
During hg update, your uncommitted changes will be merged with the new tip of your current branch. You will still have to resolve any conflicts.
hg commit when ready.
I still recommend if you have extensive changes to commit first before pulling/merging because it is easier to start over by updating to that changeset if the merge goes badly.
Keeping the hg pull and hg update separate allows you to look at the incoming changesets and predict how the merge will go.
The reason is feels strange is that you delay your commit until you want to integrate with the others.
A big feature of distributed version control is that commits are local. Because they're local you should commit often — commit every time you have a small consistent chunk of work done. Your commits are not inflicted on others immediately so you wont interrupt them by making many small commits.
If you begin making more commits you'll see that your workflow becomes:
$ hg commit -m "Refactoring for Issue123"
$ hg commit -m "Basic functionality for Issue123"
$ hg commit -m "Fixed off-by-one error (Issue123)"
$ hg commit -m "Finished implementing Issue123"
$ hg commit -m "Added more tests for Issue123"
$ hg commit -m "Begin use new function from Issue123"
$ hg pull
$ hg merge
$ hg commit -m "Merge"
Here the ratio of merge commits to "real" commits is much more sensible.
Many people (myself included) like to use the rebase extension to avoid the merge completely. That extension linearizes the commits by faking the history so that it looks like you did your four commits after the changesets you pulled down with hg pull. The only change in workflow is that you hg rebase instead of hg merge above and then skip the final commit.

Discard all and get clean copy of latest revision?

I'm moving a build process to use mercurial and want to get the working directory back to the state of the tip revision. Earlier runs of the build process will have modified some files and added some files that I don't want to commit, so I have local changes and files that aren't added to the repository.
What's the easiest way to discard all that and get a clean working directory that has the latest revision?
Currently I'm doing this:
hg revert --all
<build command here to delete the contents of the working directory, except the .hg folder.>
hg pull
hg update -r MY_BRANCH
but it seems like there should be a simpler way.
I want to do the equivalent of deleting the repo, doing a fresh clone, and an update. But the repo is too big for that to be fast enough.
Those steps should be able to be shortened down to:
hg pull
hg update -r MY_BRANCH -C
The -C flag tells the update command to discard all local changes before updating.
However, this might still leave untracked files in your repository. It sounds like you want to get rid of those as well, so I would use the purge extension for that:
hg pull
hg update -r MY_BRANCH -C
hg purge
In any case, there is no single one command you can ask Mercurial to perform that will do everything you want here, except if you change the process to that "full clone" method that you say you can't do.
hg up -C
This will remove all the changes and update to the latest head in the current branch.
And you can turn on purge extension to be able to remove all unversioned files too.
To delete untracked on *nix without the purge extension you can use
hg pull
hg update -r MY_BRANCH -C
hg status -un|xargs rm
Which is using
update -r --rev REV revision
update -C --clean discard uncommitted changes (no backup)
status -u --unknown show only unknown (not tracked) files
status -n --no-status hide status prefix
hg status will show you all the new files, and then you can just rm them.
Normally I want to get rid of ignored and unversioned files, so:
hg status -iu # to show
hg status -iun0 | xargs -r0 rm # to destroy
And then follow that with:
hg update -C -r xxxxx
which puts all the versioned files in the right state for revision xxxx
To follow the Stack Overflow tradition of telling you that you don't want to do this, I often find that this "Nuclear Option" has destroyed stuff I care about.
The right way to do it is to have a 'make clean' option in your build process, and maybe a 'make reallyclean' and 'make distclean' too.
If you're looking for a method that's easy, then you might want to try this.
I for myself can hardly remember commandlines for all of my tools, so I tend to do it using the UI:
1. First, select "commit"
2. Then, display ignored files. If you have uncommitted changes, hide them.
3. Now, select all of them and click "Delete Unversioned".
Done. It's a procedure that is far easier to remember than commandline stuff.

No changes are pushed when using hg-git

I'm trying to get the hg-git extension working under Windows and after hours of fiddling, I finally seem to have it working. However, nothing shows up in my git repository even though the output of hg push reads:
importing Hg objects into Git
creating and sending data
github::refs/heads/master => GIT:8d946209
[command completed successfully Wed Oct 20 15:26:47 2010]
Try issuing the command hg bookmark -f master
(use -f to force an existing bookmark to move)
Then try pushing again.
This works because Hg-Git pushes your bookmarks up to the Git server as branches and will pull Git branches down and set them up as bookmarks. (from the official README.md)
And it seems that just after I asked this, I made a trivial change. This was picked up and pushed. So it seems that you have to wait until you've made a new commit in order for hg-git to pick it up.
I had chosen to 'Initialize this repository with a README'. This meant I ended up with two heads, which I couldn't hg merge because one had a bookmark.
To get pushing working, I had to:
configure hg-git and github remote as per https://blog.glyphobet.net/essay/2029
pull from github and update
force the merge (checking which id to use with hg heads),
commit the merge
add a trivial change to a file (add a space char to the end),
commit, then
move the bookmark to the tip
push to my configured github remote
This ended up with commands as follows (substituting in <x> sections)
hg pull github
hg update
hg merge <revision-id-of-incoming-git-version>
hg addremove
hg commit -m 'merged with github'
# make some trivial change to a file - eg add a space where it doesn't cause harm
hg add <changed-file>
hg commit -m 'trivial change'
hg bookmark -f master
hg push github
make sure you pick the remote revision for the merge above - if you don't it doesn't work!

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.