Mercurial: Picking commits to be included in a release - mercurial

I have a local mercurial repository (for now) within which I have already made several commits, each commit is a self contained bug fix. Is it possible to pick which of the bug fixes (commits) I want to be included when it is time to build a release version of my application.
To elaborate, assuming A, B, C, D, and E are commits I have already done to my repository and each of them relates to a bug fix like so:
A <- B <- C <- D <- E <- working dir
I need to be able to for example pick which of the bug fixes will go into the release version (this depends on the time allocated for deployment as well as testing outcomes). So for example I might get a report saying the release should only contain bug fixes A, C and D.
Is it possible to construct a release version containing only the A, C and D commits (Keeping in mind that each commit is self contained and does not depend on the other commits to actually be there)?
Probably having a branch for each bug fix and then merging into a release branch is the easiest way to accomplish this (or is it not?), but the current situation at hand is as described above with no branches.

This isn't the normal work mode of Mercurial (or git). A repository can only contain a changeset if it also contains all of that changeset's ancestors. So you can't get D into a repo without also having A, B, and C in there.
So here's:
What you Should have Done
Control the parentage of your changesets. Don't make C the parent of D just because you happen to have fixed D after C. Before you fix a bug hg update to the previous release.
Imagine A was a release and B, C, and D, were all bug fixes. If you do a loop like this:
foreach bug you have:
hg update A
... fix bug ...
hg commit
hg merge # merges with the "other" head
then you'll end up with a graph like this:
---[A]----[B2]--[C2]--[D2]----
| / / /
+-[B] / /
| / /
+-----[C] /
| /
+---------[D]
and now if you want to create a release with only, say, B and D in it you can do:
hg update B
hg merge D
and that creates a new head that has A + B + D but no C.
Tl;Dr: make a change's parent be as early in history as you can, not whatever happens to be tip at the time.
What you can do Now
That's the ideal, but fortunately it's no big thing. You can never bring exactly D across without bringing C (because C's hash is part of the calculation of D's hash), but you can bring the work that's in D into a new head easily enough. Here are some ways, any of which will work:
hg export / hg import
hg transplant
hg graft (new in 2.0)
hg rebase (only possible if you haven't yet pushed)
Any of those will let you bring that patch/delta that's in D over -- it will have a different hash ID and when some day you merge D in for real (using merge) you'll have duplicate work in two different changesets, but merge will figure it all out.

If this was my tree and it hasn't been pushed anywhere, I'd (assuming an empty patch queue and MQ enabled):
hg qimport -g -r B: # import revisions B and later into mq as "git" style patches
hg qpop -a # unapply them all
hg qpush --move C # Apply changes in C (--move rearranges the order)
hg qpush --move D # Apply changes in D
hg qfin -a # Convert C & D back to changesets
hg push <release server> # Push them out to the release branch
Then you can hg qpush -a; hg qfin -a to get B & E back into changesets.
Final Result:
---A---C---D---B---E
Advantages:
Nobody needs know you didn't do things in this order to start with (evil grin)
You could modify any of the change-sets whilst doing this
Alternatively, with graft in 2.0:
hg update -r A # Goto rev A (no need to do anything special for A)
hg graft C # Graft C on to a new anonymous branch
hg graft D # Graft D
This will give you
---A---B---C---D---E
\
--C'--D' <-You are here
An hg push -r D' should just push the new, cherry-picked, head.
You can then hg merge to get one head again with B and E included.
Advantages:
Non destructive, so true history is kept, and no chance of loss if you muck up
hg tags the new changesets with the hash of the original version, so totally trackable
Probably a little simpler.

While it's somehow strange way and release-policy, you can do it in different form. You have to manipulate with two main objects: changesets and branches
Version 1
You use two branches (default + f.e "release 1.0"). Default branch is mainline of your work - all changesets commited to this branch. At release-time, you branch first needed-for-release changeset into (new) branch, transplant or graft rest of needed in release changesets from default to this branch, head of release 1.0 will be prepared for release this way.
Next release will differ only in new branch name
Version 2
One branch used, MQ extension added. Variations:
all changesets are MQ-pathes and only needed for release are applied to repo
changesets are changesets, only unwanted for release converted to mq-pathes, later qfinish'ed and returned to repo

Related

How to copy working directory to a new commit

I have the following commit structure:
a->b->c
Now c is an experiment, and it diverges significantly from b (to the point where any changes to b cause conflicts in c). I'd like to keep c, but want to stop maintaing the chain. Because there are so many conflicts between b and c, simple rebasing would be a bunch of work.
So I want to make
a->b
\->c'
where c' is simply a copy of the workspace in c. In git I would use git reset --soft a and just make a new commit for my c', is there something equivalent I can do in mercurial?
Sounds like you want to use revert --all
pseudo commands (letters a,c mean revids of commits ):
update -r a
revert --all -r c
commit -m "new c"
strip -r c
a guide to using revert all using thg GUI can be found here
So I'm going to post what I ended up doing, but hope I get a better answer.
Make a "do nothing" commit on a (in my case, I added a line). We need the fake because rebasing onto a from b will stop saying nothing to rebase.
a->b->c
\->f
hg rebase -s b -d f --keep clones the whole branch:
a->b->c
\->f->b'->c'
hg histedit, fold c' and drop f.
a->b->c
\->c''
hg prune c
a->b
\->c''

List all merged changesets since last merge?

Using Mercurial, how can I list all the changesets applied by merging a branch, since the last merge from that branch?
Revsets are your friend. Or your nemesis, depending on how complex they get :)
The following command will show all associated changesets between the last two merges:
$ hg log -r "first(last(merge(),2)):last(merge()) & ancestors(last(merge()))"
That complex little expression (which I'll look at making simpler later) does the following:
x:y gives you all changesets between x and y inclusive
merge() is a revset that contains all merges.
last(...,n) gives the last n changesets of a set, with n defaulting to 1
first(...) gives you the first changeset of a set
ancestors(last(merge())) is a set containing all ancestors of the last merge
Combining all of those, the expression above becomes (ready?): Give me all changesets between the first of the last two merges, and the last merge, inclusive, which happen to be contributing ancestors of the last merge.
The ancestors(...) bit filters out any changesets that are not related.
You can limit this to be the changes on a specific branch by adding & branch(branchname). For example, if you are merging onto a release branch from default, you could do:
$ hg log -r "first(last(merge(),2)):last(merge()) & ancestors(last(merge())) & branch(default)"
This wouldn't include the actual merges themselves, as they would appear on the release branch.
Hopefully this makes sense - I'll have a look this afternoon to see if I can get a simpler way, but that's the first that springs to mind. In the meantime, if you use this, you can make it easier by creating a revset alias in your user hgrc file:
[revsetalias]
contrib = first(last(merge(),2)):last(merge()) & ancestors(last(merge()))
So you can then use:
$ hg log -r "contrib"
$ hg log -r "contrib & branch(default)"
For more information have a look at hg help revsets.
I'm not sure icabod's solution is quite right. Let me see if I can explain.
Let's take this change graph.
o----A1----A2----M1--------A3---M2
\ / /
---B1----B2--- /
\ /
----C1--------C2----C3
B is a branch taken from o, and C is a branch taken from B1. If we're at M2 and run icabod's command then:
last(merge()) is M2
first(last(merge(),2)) is M1
So the expression becomes:
hg log -r "M1:M2 & ancestors(M2)"
M1:M2 is the changes made than have revision numbers between M1 and M2, which in this case is A3, C2 & C3, which completely ignores C1.
What I think is you're looking for the set of ancestors of M2, that weren't ancestors of M1. i.e.
hg log -r "ancestors(M2) & not ancestors(M1)"
or
hg log -r "ancestors(last(merge())) and not ancestors(first(last(merge(), 2)))"
I think this should be C1, C2, C3 & A3. It also has the advantage that it doesn't care how, or when, change-sets were added to the repo.
The only problem with this is if the second to last merge isn't an ancestor of the latest merge. I'll leave that as an exercise for the reader ;-)
Of course, all of this can be avoided by doing hg merge --preview (or -P) prior to doing a merge, and it lists all the change-sets that will be included when you do a merge.

'Forgetting' a dead-end branch

I've got a mercurial repository. It was on rev A. I made some changes, committed (to rev B), and pushed. However, later, I realised I didn't want to make those changes. I updated back to rev A, and made some alternative changes, to rev C.
C
| -
| B
|/
A
However, now I can't push rev C, because it complains that it would create a new remote head (which it would). How do I make the remote mercurial simply forget about rev B and all the changes therein, so I can push rev C and carry on from there?
Editing History is hard. Once you push a changeset to a public repository, it can no longer be easily pruned from the history.
The most direct solution to your problem is:
hg update <tip of branch you want to forget>
hg commit --close-branch -m "close unwanted branch"
hg update <tip of branch you want to keep>
Push all your changes. As you noted, you will need to use --force since multiple heads on the branch now exist.
If you really need to prune the branch, then read EditingHistory again. If it still seems feasible, you can use one of the methods described in PruningDeadBranches.
Personally I'd close the branch and force the push (as Tim Henigan describes), as it leaves the DAG in a state which is truthful. There is another option though. Doing a dummy merge. This is a merge, but one where you ignore the incoming changes.
hg update C
hg -y merge --tool=internal:fail B
hg revert --all --rev .
hg resolve -a -m
hg ci
The end result is
M
|\
C |
| |
| B
|/
A
... but M doesn't contain any of B's changes.
Use the backout command to reverse the B change.
hg up B
hg backout B
Now, you're working directory is in the same state it was before B, which I'll call A'.
After you backout, make your real change and commit. This is what your history will look like:
C
|
A'
|
B
|
A
Mercurial branches are permanent and registered in commit objects themselves. There are some (not too easy) methods for closing and/or removing branches, mainly listed here. I have even already used some of them before. Note that those are somewhat desperate solutions for people who did use branches thinking they can be temporary.
However, if you really want temporary branches, use bookmarks.
Other people have some good answers for getting rid of 'B', but just to put it out there you can always do:
hg push --rev C
which will push C but not B and not complain about new heads.

What's the best way to back out multiple changesets in mercurial?

Is the most reliable method to go one-by-one, using the backout command for each of many changesets, or is there a way to create one big reversal changeset to cover a whole bunch of [edit: non-contiguous] changesets.
If one-by-one, does order matter? (Should one go last-to-first?)
Does the best method differ if there are merges among different sub-projects along the way?
Does this tend to go smoothly in your experience? :-)
If you have no merges along the way, you can either back out every individual change (in reverse order), or, if there are many of them, do it with one big inverse patch.
If you have good changesets atop the ones you need to back out, better commit the inverse patch on top of the most recent bad changeset, then rebasing them onto the tip of the branch.
1 -- 2 -- A -- B -- C -- 3 -- 4
\
C'B'A'
$ hg up C
$ hg diff -r C:2 > backout.diff
$ hg import --no-commit backout.diff
$ hg ci -m "Backout A, B, C"
$ hg up 4
$ hg rebase -s C'B'A -d .
There will be problems if you want to back out merge changesets, see this wiki page for more information.
In such a case, if possible, consider re-doing the branch and stripping the old lineage. Otherwise, you might have to abandon the branch altogether, salvaging the good changesets via graft or transplant.
There is --collapse option for rebase.
Helgi's answer can be upgraded into:
1 -- A -- 2 -- B -- 3 -- C -- 4 -- 5
\
C' -- B' -- A'
$ hg update --clean C
$ hg backout --rev C --message "Backed out changeset: C"
$ hg backout --rev B
$ hg commit --message "Backed out changeset: B"
$ hg backout --rev A
$ hg commit --message "Backed out changeset: A"
$ hg rebase --collapse --source C' --dest 5
$ hg commit --message "Backed out C, B, A"
which will result in the following
1 -- A -- 2 -- B -- 3 -- C -- 4 -- 5 -- C'B'A'
However, backing out in separate branch may result in [logical] conflict in the subsequent merge.
1 -- A -- 2 -- B -- 3 -- X -- 4
\ \
B' -- A' -- M
if X depends on A or B, then M will have conflict (at least logical conflict).
What I came up with is inelegant, but got the job done, despite that the changes I needed to back out were interspersed with other work and had some internal branching. Here's what I did. (Comments and improvements are welcome.)
Got a list of all of the changesets (which I then used to generate the commands below):
hg log -r 'keyword(xyz)' --template '{rev}\n'
Generated a patch for each changeset:
hg diff -p -U 8 --reverse -c 15094 > 15094.rev.patch
hg diff -p -U 8 --reverse -c 15095 > 15095.rev.patch
...
Then, applied each reverse patch. Here the order matters, last-to-first:
hg import -m "reversing changeset 15302" 15302.rev.patch
hg import -m "reversing changeset 15292" 15292.rev.patch
...
This process was interrupted several times for merges that didn't go through automatically, where I had to manually apply changes to a file from its .rej file and then manually commit, before picking up the imports where it had left off.
Finally (in another clone... did I mention I did this all in a clone?) I compressed the whole set of reverse changesets into one changeset using hg histedit -o and its fold command.
Now I've got a single changeset that I should be able to reverse and apply if I decide to put the work back in at a later date (Although if I cross that bridge, I might apply the "forward" patches piecemeal again in order to get better blame/annotate information)
This is how you can do it with TortoiseHg.
Of course you can do the same with the command line.
Given this history, where you wan't to get rid of changeset A, B and C:
1 -- 2 -- A -- B -- C -- 3 -- 4
First update to revision 2.
Then rebase the first of any later revisions you wan't to keep - in this case revision 3.
Your history now looks like this:
1 -- 2 -- A -- B -- C
\
3 -- 4
Now update to revison 4.
And finally use "Merge with local" to merge revision C onto revision 4.
At this point it is crucial that you select the option "Discard all changes from merge target (other) revision".
The description may not be the most logical, but it means that you merge the old tip C back to the default branch - but without the changesets A, B and C.
The result is:
1 -- 2 -- A -- B -- C --
\ /
3 -- 4
Commit and you're done.
If you don't want the "backout" changesets in your history, you could also do something else:
Make a clone of your repository, but only up to the last changeset that you don't want to get rid of.
See Mercurial: Fix a borked history for an example how to do this.
If your repository was a local one, that's all you have to do.
But if the bad changesets were already pushed to a central repository, you'd need server access to delete the repository there and replace it by your clone.
Plus, if someone else already pulled from the repo with the bad changesets, they need to delete and re-clone (otherwise the bad changesets are in the central repo again as soon as one of the other people pushes again).
So it depends on the circumstances whether this solution is a good one for you...

How do I cherry-pick a single revision in Mercurial?

In Mercurial/TortoiseHg, given the following example, what is the easiest way to merge revision "G" into repo A without taking D,E and F (Assume that G has no dependency on D,E or F).
Repo A: A - B - C
Repo B (Clone of A) A - B - C - D - E - F - G
Is a patch the best bet?
Tonfa is right. What you're describing isn't 'merging' (or 'pushing' or 'pulling'); it's 'cherry-picking'. A push or a pull moves all the changesets from one repo to another that aren't already in that repo. A 'merge' takes two 'heads' and merges them down to a new changeset that's the combination of both.
If you really need to move G over but can't possibly abide having D,E,F there you should 'hg export' G from repo A, and then 'hg import' it in repo A. The Transplant extension is a wrapper around export/import with some niceties to help avoid moving the same changeset over multiple times.
However, the drawback to using import/export, transplant, and cherry-picking in general is that you can't really move over G without its ancestors, because in Mercurial a changeset's name is its 'hashid' which includes the hashids of its parents. Different parents (G's new parent would be C and not F) means a different hashid, so it's not G anymore -- it's the work of G but a new changeset by name.
Moving over G as something new, let's call it G' (Gee prime), isn't a big deal for some uses, but for others it's a big pita. When soon repo B get's a new changeset, H, and you want to move it over its parent will be changing from G to G', which have different hashes. That means H will move over as H' -- 100 changesets down the line and you'll have different hashids for everything all because you couldn't stand having D,E,F in repo A.
Things will get even more out of whack if/when you want to move stuff from Repo A into Repo B (the opposite direction of your earlier move). If you try to do a simple 'hg push' from A to B you'll get G' (and H' and by subsequent descendants) which will be duplicates of the changesets you already have in Repo B.
What then, are your options?
Don't care. Your data is still there you just end up with the same changesets with different names and more work on future exchanges between the two repos. It's not wrong, it's just a little clumsy maybe, and some folks don't care.
Move all of D,E, and F over to Repo A. You can move all the changesets over if they're harmless and avoid all the hassle. If they're not so harmless you can move them over and then do a 'hg backout' to undo the effects of D,E and F in a new changeset H.
Give G better parentage to begin with. It's mean for me to mention this because it's too late to go this route (without editing history). What you should have done before working on changeset G was to hg update C. If G doesn't rely on or require changesets D,E, and F then it shouldn't be their kid.
If instead you update to C first you'll have a graph like this:
A - B - C - D - E - F
\
G
then, the whole answer to this question would just be hg push -r G ../repoA and G would move over cleanly, keeping its same hashid, and D, E, and F wouldn't go with it.
UPDATE:
As pointed out in the comments. With modern Mercurials the hg graft command is the perfect way to do this.
Refering to the title, which addresses cherry picking in general, I give the example of working in one repo, as internet search engines might bring people here for cherry picking in general. Working in one repository, it would be done with hg graft:
hg update C
hg graft G
The result is:
G'
/
A - B - C - D - E - F - G
Extra warning: The two changesets will be treated as independent, parallel commits on the same files and might make you run into merge conflicts, which is why cherry picking should be avoided in general for branch management. For example, if G is a bug fix applied to a stable version branch bookmarked as 1.0.1, you should rather merge the freeze branch with it, and from time to time merge the master branch with the freeze branch's bugfixes.
Here's another approach:
hg import =(hg diff -c 7b44cc577701f956f12b029ad54d32fdce0a002d services/webpack/package.json)
This creates a diff for the changeset you want to patch in, then saves it to a temporary file and imports it. The filename(s) are optional.
<(...) also seems to work if you're not using zsh (creates a named pipe instead). Or you can manually save the patch to a file:
hg diff -c xxx > mypatchfile