hg: Commit some changes to another branch - mercurial

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

Related

Disassociate a file on a feature branch from parent in mercurial?

Is there a way to disassociate a file on a feature branch from its parent branch, in that changes to the file on the parent branch will no longer merge into the feature branch?
ie: app/index.html [develop] changes should no longer merge into app/index.html [feature/redesign] when merging develop into feature/redesign ?
You should ask yourself why you want to do this, it sounds backwards. Anyway, the following should work: perform the merge as usual, don't care about what happens to app/index.html (that is, in case of conflict just accept something, don't think about it). Then, before doing the merge commit, revert that file to its previous revision on redesign branch. To make an example:
(on branch redesign)
hg merge develop
hg revert -r redesign app/index.html
Now hg status will still show app/index.html as modified, but if you look at its contents, they are the same as before the merge. Do the merge commit as usual:
hg commit -m 'Merge develop into redesign'
Note: you must do this each time you do a merge, there is no "dissociate" capability.
Just delete and re-create it on the branch.

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, commit changes in working copy to another branch

I forgot to switch my working copy back to the correct branch and made some changes.
How can I commit the current changes (but not all other changes in the current branch) to my original branch?
If I understand correctly, you have uncommitted changes that need to be on top of another branch.
My prefered way is to use the shelf functionality to save all your changes, update to the correct branch, and then un-shelf your changes back on the proper branch.
To do it on the command line, save the result of hg diff in a patch file, then update to the other branch and hg import <patch>.
Finally, just commit your changeset, as you wanted.
Shelve extension allow you to store WIP, get clean working dir, update to needed branch, release shelve
MQ exension (somehow overkill) allow to do the same thing, as shelve
At last, you can commit into wrong branch and rebase this changeset to the correct parent, using Rebase extension

How do you Rebase an immutable changeset with 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.

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)