How do you Rebase an immutable changeset with Mercurial? - mercurial

I'm trying to rebase some changes I pulled in to my local machine. I'm getting the error:
abort: can't rebase immutable changeset 110e73ed65a4
(see hg help phases for details)
And I get the same error even after I change the phase on the changesets that I'm rebaseing (and the phase change seems to be successful). using:
hg phase -f -d REV
I'm wondering if there's a changeset in the history that I'm missing and is still immutable, and if so, if there is a way that I can change all of the changesets in a changeset's history to be mutable with a single command.
Or, is there a way to force rebase, even with the immutable changesets?

Rebasing changes that are public is considered a very bad idea. You shouldn't change any history that's been pushed -- the point of phases is to track what changes haven't been pushed yet (so they're able to be modified), and what changes have been pushed (so they're immutable). From the rebase documentation:
You should not rebase changesets that have already been shared with others. Doing so will force everybody else to perform the same rebase or they will end up with duplicated changesets after pulling in your rebased changesets.
It's better to either merge in your changes or graft them in. Graft (also known as cherry-picking) takes one or more changesets and copies them to your current branch.

Related

Duplicate a branch in Mercurial?

How can I duplicate a branch in Mercurial? I need the new branch to be against head (as the first one is).
The GIT equivalent (if I was in branch-a) would be:
git checkout -b branch-b
A Mercurial branch is a named entity that consists of all the commits contained within the branch. So in order to duplicate some existing branch, you must also duplicate all of its commits to new commits that are in the new branch. We then get into metaphysics arguments about commit identity. It's probably not a good idea to go here at all, but if you do want to go here, use hg graft to copy all the desired commits into the new branch.
A Git branch consists of a name containing a raw commit hash ID. So duplicating a Git branch under a new name is trivial. Note that the set of branches that contain any given commit changes dynamically over time: a branch that was only on feature/tall may now only be on master, even though that commit is still that commit, even via most of these metaphysical arguments. (Only the "no identity over time" argument lets us claim that this is not the same commit.)
Another way to put it is that Mercurial's branches actually mean something, but Git's don't. If you need true branches, you can't use Git in the first place. Don't try to import Git's bizzareness into Mercurial: you'll just make your own life miserable.
Meanwhile, though, Mercurial contains a DAG just like Git. If you use Mercurial bookmarks, those work like Git branches. It's probably wiser, then, to just use bookmarks and be done with it.
If all else fails, see hg graft.
the new branch to be against head
What is this (in usual business-term, not Git-lingua)? While in common (and in details) #torek is totally right, he forgot to write exact command-set, something like
hg up <rev-id>
hg branch <new-branch-name>
hg graft -r "branch(old-branch-name)" --log

In Mercurial what's the difference between hg graft and hg rebase

I know Rebase is a (bundled) extension, while Graft is a core feature (that replaced the Transplant (bundled) extension).
graft is documented as:
copy changes from other branches onto the current branch
This command uses Mercurial's merge logic to copy individual changes from other branches without merging branches in the history graph. This is sometimes known as 'backporting' or 'cherry-picking'.
rebase is documented as:
Rebase allows moving commits around in Mercurial's history (using a series of internal merges). This has many uses:
moving changesets between branches
"linearizing" history
reordering changesets
collapsing multiple changes into one changeset
Both seem to use merging to move or copy changesets between branches.
Graft copies. Rebase moves. But rebase --keep copies.
So often it seems I can accomplish my goal of copying a changeset either way.
Does it matter which one I use? When should I prefer one over the other?
E.g. should graft only be used when copying to a different named branch? Or only when there's just a single changeset?
Edit: Could it be that rebase is a potentially unsafe superset of graft, but can only be used with draft changesets during development for editing local history, while graft is a safe subset of rebase that can be used with public changesets during maintenance for backporting?
hg graft allows "cherry-picking," as you noted in your question. For example, you can run hg graft -D "2085::2093 and not 2091" to copy only some changes from another revision. By comparison, hg rebase (with or without --keep) will grab whatever changeset you specify and all of its decendant changes.
Also, rebase allows you to collapse changesets (with --collapse). As far as I can tell, graft does not.
One more difference I have noticed: hg graft --edit 123 lets you graft revision 123 to the working directory and edit the commit message. I can't find an hg rebase equivalent. I should point out, though, that hg histedit also allows for editing the commit message while rebasing.
There are probably other differences that I am not thinking of. SO community: feel free to point those out in the comments, and I will happily revise this answer to make it more complete.
See the graft documentation and the Rebase Extension documentation for more details.

hg: Commit some changes to another branch

I was working in branch-a when I found an unrelated bug that should be fixed in the default branch. So, I'd like commit some of my changes to default, then merge default into the current branch, and keep working.
As far as I know, Mercurial doesn't allow committing directly into another branch, so I would have to switch to the default branch first. The problem is, I can't simply checkout the default branch, because the other changes would cause conflicts. One workflow I can think of is to shelve, checkout default, unshelve only the files that relate to the fix, commit, checkout branch-a, merge default, and finally, unshelve the rest of the files. Is there an easier way to accomplish this?
Commit only subset of files, related to branch-a changes (use additionally power of Record Extension, if bugfix's and branch-a's changes happens in some file(s) - commit only needed hunks of file) as changeset A
If you haven't MQ Extension:
Commit the rest of changes into branch-a as changeset B (child of A)
Rebase B (with Rebase Extension) changeset into default branch with --keep option in oder to have B in original location also
If you have MQ extension
Create new patch with working-dir changes
Unapply MQ-patch
Update to default
Apply patch
Test, test...
Finish patch (convert to permanent changeset)
Graft this changeset only into branch-a branch

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)

Accidentally rebased only one of my change sets

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. :)