Accidentally rebased only one of my change sets - mercurial

I had 3 change sets that I wanted to rebase on top of the latest revisions. Unfortunately, I selected only one of them to be rebased and so this did a merge. Is there any way I can either undo the rebase or change it so the other two change sets get rebased as well?

Assuming you haven't pushed it to another repo for others to grab, then you can put those changesets anywhere on the graph you want. You can move changesets with hg rebase and prune changsets and their descendents with hg strip.
Both strip and rebase save "undo" information as bundle files in your .hg/strip-backup/
Note that neither strip nor rebase are enabled by default with mercurial. You need to enable them in the .hgrc file.

If it's the very last thing you did you can do a 'hg rollback' which is a one-level undo. If, however, you've done anything since that alters the repository state (push, pull, commit, etc.) then rollback won't help you.
If it's any consolation, merging is generally preferable to rebasing and a mercurial history with a lot of merges shows someone who is using mercurial to its fullest. :)

Related

Is it possible to undo an accidental "hg unshelve"?

Now my there's an extra few hundred lines of code in my project, scattered over half a dozen classes, and I'm not looking forward to tracking them down one by one.
No, but this is an avoidable problem
Instead of doing hg shelve, do hg commit --secret. This will create a "real" commit in the repository, which can be rebased or stripped (hg strip) when you no longer need it. It can also be folded into another changeset using hg histedit or hg fold (the latter requires the Evolve extension, which is experimental at the time of writing, while the former is interactive and may be hard to script).
The --secret flag ensures the changeset will not be accidentally pushed until you manually run hg phase -d REV on the changeset.
Yes, with a few steps. The help for unshelve specifies "If a shelved change is applied successfully, the bundle that contains the shelved changes is moved to a backup location (.hg/shelve-backup)." So hg update --clean will return you to the pre-unshelve state, unless you added changes in the mean-time, in which case you need to figure out how to recover the world prior to the unwanted patches. Then you can re-apply the patches stored in the backup individually if you wish to essentially re-do the unshelve under whatever conditions you prefer. In any event, the back-up contains the data you thought was missing.

How to undo metadata changes of a commit in mercurial?

I recently grafted a changeset in a development branch into the default branch. After that, my changes went in default as desired.
But unexpectedly, there were a lot of metadata level changeset changes went in with that checkin. To elaborate,
Several files that were present only in my development branch and not in default branch were marked as removed in default.
Several extra lines in certain files that were present only in my development branch and not in the default branch were marked as removed in default.
Mercurial has marked certain files and lines that are not actually present in default as removed files and lines in the default branch.
Due to this, when I try to merge again with default, mercurial marks those files and lines are removed from default and it tries to remove the same from my branch.
I tried backout to revert my problematic commit alone. Unfortunately, backout only changes the source code lines modified during checkin, but doesn't remove the metadata marking of removing the unavailable files and lines.
Is there anyway to undo the commit metadata level changes that happened undesirably? Any help much appreciated.
Thanks.
If you have a bad commit and just want to forget it ever happened, and that commit has no descendants, you can just do hg strip to remove it from history entirely (requires strip extension).
If the commit does have descendants, you can use hg histedit (requires histedit extension) to drop the changesets you don't want to keep.
Both these solutions involve editing history, which is risky if you've distributed the bad commit publicly. You will have to coordinate with everyone who may have pulled the bad commit or any of its descendants, which isn't always possible (e.g. in an open source environment). You will also need to redo these changes server-side, since Mercurial's push --force is not like Git's push --force.
If you don't want to edit history, you can graft the descendants of the bad commit onto the last known good ancestor (e.g. hg up good_ancestor; hg graft 'bad_commit:: - bad_commit'), then do hg commit --close-branch at the bad head(s). Be sure to leave an informative commit message on the close-branch commit, so that other developers will know where to rebase anything that descends from the bad commit.

Mercurial: How can I create an unplanned branch

My use case is this:
I am working on a new feature and I have several commits on that feature.
Since it was a minor feature, I didn't even consider doing the feature in a feature branch.
However. Now my boss comes along and tells me to fix a bug on the same branch that I am working on (default).
To fix that I'd like to create a feature branch for my feature, push all my existing (unpushed) commits into that branch.
So I'd like to create a branch just before my first commit and then somehow move all my commits to that branch.
How can I do this?
There’s two ways to approach this, depending on your preference:
In a new repository.
Make a new clone of your repository, and do the bug fix you need to make there. Then push it to the main repository when you’re done, and continue where you left off in the original repository. Pull and merge to get the new changes as usual.
In the existing repository.
Update to the changeset before your local changes, and just start fixing and committing there. This creates a new anonymous branch. When you’re done, push using push -r ., this will only push the changes that are included in the working copy. After this, merge with your original branch (hg merge) and continue where you left off.
Note that you can bookmark the feature branch with hg bookmark if you do not feel comfortable with leaving your changes unlabeled. Also you can easily find back any heads you left behind using hg heads.
Personally I prefer to work in a new clean clone, as you don’t need to worry about branching and where to leave uncommitted changes. However if your project setup is complicated it may be more convenient to reuse the existing repo.
For this situation you can fix it by rebasing (which may need enabling in your configuration).
On your branch, update to the revision before the change-sets you want to move:
hg up -r<revison>
This assumes contiguous revisions need moving.
Create a new branch:
hg branch "TempWork"
Put a dummy commit onto it in order to get a new revision:
hg commit -m"New Branch"
Then perform the rebase from the first of the change-sets you want to move (it moves descendants automatically) and specify the new branch revision as the destination:
hg rebase -s<base revision> -d<new branch revision>
Then update back onto your main-line branch.
Fourth method: Using mq-patches
You have to have mq extension enabled and initiated for repo
On hotfix moment you
convert feature-commits into set of mq-patches (hg qimport)
Unapply all patches in set (hg qpop -a)
Code hotfix, commit
...
Finish and test hotfix on clean codebase
Apply all patches in set (hg qpush -a), fix possible conflicts
Convert patches back to changeset (hg qfinish)

how to undo a commit pull

I faced today my first mercurial problem. I was in my repo, I modified a file and I did a
hg commit
hg pull
followed by
hg update
hg rollback
to repair what I've done, (but actually I didn't push anything)
The problem is that when I did the pull (that I should do before the commit, the head changed and so hg heads looks like :
- Modif from yesterday
- My modif
- Modif from last week
and now I see that someone also did another modification (via the http interface). What should I do to repair my local repo, (if possible modifying my summary) and push it after the 2 others modifications.
Thanks a lot. Quiet confusing, was easier on my "one-man" repo..
Your local repo doesn't need "repairing". This is a very standard case that you will see often if you use Mercurial a lot.
The issue is multiple heads.
You can either merge your heads, assuming your working directory is your version and there is only the other head:
hg merge
This will result in a merge changeset (same as if you were merging across branches).
Or you can enable the rebase extension to re-base your version onto the tip of the branch (the other head):
hg rebase --source<YourVersionNumber> --dest<TipVersionNumber>
This will not result in a merge changeset, and will simply transplant your changes on top of the changeset you specify as if they were born of that changeset all along (hence rebase or "new"basing).
Multiple heads is a funny sort of inner-branch branching... you can continue checking stuff in against your own head and change between heads using hg update. We block multiple heads per branch on our server, so our push would fail. I'd advise keeping multiple heads local as they are less clear-cut than branches.
I tend to work with Mercurial in one of two ways:
If the work is large in scale I will branch it off and follow Continuous Integration practices (constantly merging the main branch into my own etc). I then re-merge back into main when I am happy with the end result.
If the work is small in scale I will simply work against main branch and "merge" heads every so often. I say "merge" as I usually use rebase. Re-basing works great if the changes are simple and conflicts are unlikely.
My hard and fast rule: if I can't use rebase I put it on a branch born of main.

Mercurial commit only tip

In my setup I have a central Hg repo to which I'm pushing my local changes. Say in my local clone I have a series of local commits and then I want to push the changes to the central repo. How can I push only the final state without including all of the "small" local commits that I made?
I want this because sometimes I dont want to pollute the central repo's history with all of the small local commits that I made.
Why would you want to do that? Committing small changes makes it easy to revert something. If you collect everything in one big commit, reverting a small change might not be as easy.
I agree with bjorn (and I'm upvoting his answer), what you're doing isn't a great idea -- meaningful history is a good thing. If you can't be talked out of it then what you're trying to do isn't just push the last changeset but a new changeset that is a combination of all those changesets. The easiest way to do that is to use the collapse extension, though mq or even export/import can do it. The key there is that in collapsing multiple changesets into one you're rewriting history and you're going to remove your existing changesets and replace them with that new combined changeset. Doing so violates the immutability of history that makes Mercurial so trust worthy.
How to do this without any extensions is explained in the mercurial wiki page ConcatenatingChangesets.
That page also links to a few alternative approaches with hg extensions, like the CollapseExtension.
It is possible to rewrite your history using the mq extension. Suppose the revisions you want to collapse are revs, 5,6,7 with 7 being the tip. You would accomplish this via:
# Import the revs you want to collapse into mq
# mq will create patches for each revision from 5:tip, with the name
# <local rev number>.diff
hg qimport -r5:tip
# Goto the first commit
hg qgoto 5.diff
# Fold in the other commits successively. Aside from shell magic, there is
# no command line way to specify multiple patches at once.
hg qfold 6.diff
hg qfold 7.diff
# Commit the new mq patch as a changeset of its own
hg qfinish 5.diff
Now, your repository contains only a rev 5 with the contents of what was previously revisions 5, 6, and 7.