Generating merge logs with mercurial - mercurial

We're using Mercurial for source control and would like to introduce automatic merging and changelog for our QA process.
For the purpose of this discussion, let's just assume that we use a simple 3-branch flow like the below:
\ = forward merge
| = back merge of fix
Default --O--A--*--B--*---
\ ^ \
\ | \
QA -----M--X--C--*--N---
^ \
| \
Master --------H--------*---
So the question is:
Is it possible to construct a revset query that, at merge N, preferably before but otherwise after actually performing it, will result in a log of all commits on Default branch, which aren't yet in QA branch (On the above drawing that would be A and B).
Alternatively formulation:
Is it possible to construct a revset query that will return all the changesets, which would get merged if we merged Default into QA.
I'm only interested in commits on Default branch, not individual commits in feature branches already merged into Default (Feature branches not included on the drawing). I am also not interested in commits on the QA branch.
The repository is pretty well maintained, but a couple of years old and contains about 13000 nodes.
I've been experimenting with ancestor and ancestors, but either I get X as the latest common ancestor, or some very very old node. I understand the logic of getting X, but I'm really after O.
The most luck I've had is with variations of this:
hg log --rev "((heads(branch('Default')) and !closed()) % heads(branch('QA'))) and branch('Default') and p2(!branch('QA'))"
hg log --rev "((heads(branch('Default')) and !closed()) % heads(branch('QA'))) and branch('Default') and !children(branch('QA'))"
Update
Using "hg merge --preview --rev XYZ", as suggested by Tom, generates output like this:
changeset: 13070:7e59fc16aa4e
branch: Default
parent: 13068:5b9409ad504f
parent: 13069:849bd43d2023
user: *******
date: Mon Dec 18 18:40:46 2017 +0200
summary: Merged Feature branch A
changeset: 13071:07470ff919ff
branch: Feature branch B
parent: 13061:540eda2c959b
parent: 13068:5b9409ad504f
user: *****
date: Mon Dec 18 18:49:42 2017 +0200
summary: Merge with Default
changeset: 13072:a53260ffabca
branch: Feature branch B
user: *****
date: Mon Dec 18 18:58:05 2017 +0200
summary: Some text
changeset: 13073:37c895f2abf0
branch: Default
parent: 13070:7e59fc16aa4e
parent: 13072:a53260ffabca
user: *******
date: Mon Dec 18 18:58:05 2017 +0200
summary: Merged Feature branch B
Not too bad, but not super fun either. At least it requires more logic than simple grep/sls to parse.
Thanks in advance,
John

I found a solution that mostly does what I want:
hg log --rev "branch('Default') & !ancestors(branch('QA')) & ancestors(heads(branch('Default')) & !closed()) & !children(branch('QA'))"
The only thing now missing is to exclude multiple head merges, since they're just noise in a changelog.
Update:
So I picked this project up again and found some great answers elsewhere on SO.
The following produces output similar to hg merge --preview, but can be templated:
hg log -r "ancestors('$sourceBranch') - ancestors(.)" --template $template
This filters out all commits not on source branch:
hg log -r "(ancestors('$sourceBranch') - ancestors(.)) and (branch('$sourceBranch'))" --template $template

Related

Undoing accidental hg amend on top of Mercurial queue

The following sequence of commands requires that the Mercurial Queues exension be enabled, and also the Evolve extension (for hg amend, alias hg refresh).
This combination of commands messes up the existing Mercurial queue, making the commit corresponding to the pushed patch obsolete, and creating a new commit with exactly the same contents.
This error is in theory quite easy to make: just type hg ref instead of hg qref, but I just made it recently after using Mercurial Queues for years. Regardless, I haven't figured out a clean way to fix this and get back the original state.
Note that a simple hg rollback does work in my example, but I'm not sure it will work in my case, because I've tried other things trying to fix this. In any case, hg rollback isn't something one should rely on.
In summary, how do I undo the hg amend and get back my applied MQ patch?
#!/bin/sh
hg init test
cd test
echo "This is foo" >> foo
hg add
hg ci -m "Add foo"
hg init --mq
echo "Line 2 of foo" >> foo
hg qnew p
hg ci --mq -m "Add patch p"
hg ref
hg log -vG --hidden
# changeset: 2:f7f038d3aab5
| tag: tip
| parent: 0:9d3a95922194
| user: Faheem Mitha <faheem#faheem.info>
| date: Sun Mar 11 16:38:51 2018 +0530
| files: foo
| description:
| [mq]: p
|
|
| x changeset: 1:e467a2433c7f
|/ tag: p
| tag: qbase
| tag: qtip
| user: Faheem Mitha <faheem#faheem.info>
| date: Sun Mar 11 16:38:50 2018 +0530
| obsolete: rewritten using amend as 2:f7f038d3aab5 by Faheem Mitha <faheem#faheem.info> (at 2018-03-11 16:38 +0530)
| obsolete: rewritten by Faheem Mitha <faheem#faheem.info> as f7f038d3aab5 (at 2018-03-11 16:38 +0530)
| files: foo
| description:
| [mq]: p
|
|
o changeset: 0:9d3a95922194
tag: qparent
user: Faheem Mitha <faheem#faheem.info>
date: Sun Mar 11 16:38:50 2018 +0530
files: foo
description:
Add foo
That's not what I get with your test case. I need an additional
echo "Line 3" >> foo
before the hg ref. Also, versioning your mq seems irrelevant here; I think you can remove the hg init --mq and hg ci --mq lines.
hg amend really ought to block that. But if you wanted to manually fix things up, edit .hg/patches/series and replace the obsolete hash with the successor's hash. (Just make sure to use the 20-byte hash, as is given by eg hg log -T '{node}\n').
Warning: when I tried that with your test case and qpopped, something mysteriously un-obsoleted the old head, and it gives a strange warning about the current directory not being a head (despite hg log -r 'head()' listing it.) But at least you have your queue back in a working state.

How to push, without creating new heads, after creating multi-head branch, rebase and several merges

I did a series of dumb steps with my local copy of our shared repository, and I'm looking for a way how to fix it. The steps are:
I used bookmarks to have multiple heads of a development branch, that other people use:
-o---o---o-----o--- <- dev branch
\----1----1------ <- another head of the dev branch,
where I committed stuff
I created a new branch, still local, some time later
/---------------x <- new branch
-o---o---o-----o--- <- dev branch
\----1----1------ <- another head of the dev branch,
where I committed stuff
for one head, that contains only my code, I did a rebase on another branch
/-1'--1'-- <- rebase
/---------------x <- new branch
-o---o---o-----o--- <- dev branch
\----1----1------ <- another head of the dev branch,
where I committed stuff
then, I merged the rebase, and then, a couple of commits later, I merged default
----------d-\ <-default
\
/-1'--1'\ \
/---------------x--------x--x-x-x-- <- new branch
-o---o---o-----o---
\----1----1------
Now, I wanted to push my new branch to the server (hg push --new-branch -b newBranch), but I get abort: push creates new remote head, since commits 1' belong to dev branch.
What is the right thing to do? I would like to avoid creating this additional head.
Update:
Per request, this is the output of hg heads:
changeset: 839:f2033d695fcd <- want to push this
branch: newBranch
tag: tip
user: me
date: Wed Oct 31 13:05:51 2012 +0100
changeset: 826:7fde19d7f467
branch: devBranch
user: my-collegue
date: Tue Oct 23 14:59:42 2012 +0200
changeset: 820:23853bbf68df <- the part after rebase that got merged
branch: devBranch
user: me
date: Mon Oct 22 15:36:26 2012 +0200
changeset: 807:899344cfb145 <- obsolete (branch with 1's)
branch: devBranch
parent: 711:454f29c03fb1
user: me
date: Mon Oct 22 15:36:26 2012 +0200
changeset: 712:d5e8a62a7f5f <- default, needs to stay
parent: 648:2bbcc01aa191
user: me
date: Wed Aug 22 16:21:09 2012 +0200
You can push only the one head you're interested in mercurial. For you that would mean doing:
hg push -r f2033d695fcd
If the destination repo has been updated you need to pull, merge, and re-push:
hg pull
hg up -r <remote head>
hg merge -r f2033d695fcd
hg ci
hg push
I solved the problem, without pushing another head to the repo, and without merging 23853bbf68df onto newBranch. This is probably not the cleanest way to do it, but I will leave it as a reference. In a nutshell, I reconstructed the whole branch by taking all commits and re-applying them.
First, I killed the newBranch'es head 899344cfb145, by using strip on the first diverging revision I did:
hg strip -r 646
Then, I produced email patches (couldn't play with mq) for all commits that are in newBranch, since it's inception:
hg export -g -r 797:808 -r 810 -r 815:822 -r 824:830 -o "%n-%m.patch"
797:808 are the patches in newBranch that were in the rebased part of devBranch (1' commits from the original figure).
810 and 815:822 are other patchs in newBranch. 811:814 belong to a different branch, so I had to exclude those.
823 is a merge commit with default, so I skipped this one.
824:830 are all the commits after the merge with default.
Now, I imported these patches on a new head of newBranch:
hg up -r 796
# there are 29 patches, applying till merge
hg import --bypass {01..21}*.patch
hg up tip
hg merge default
hg ci -m 'merging in default'
hg import --bypass {22..28}*.patch
Finally, I just stripped of the original head of newBranch.
hg strip -r 797
This may not work in every situation. During the merge, I had to resolve some conflicts, too, but pretty benign. Hope this helps someone.

How can I list a series of revisions using 'hg log'?

I'm attempting to use the hg log command to show a series of revisions, x through y.
When I do this:
hg log -r 1+5
I get this:
changeset: 1:7320d2a9baa5
user: Tim Post <tpost#whereiwork.com>
date: Fri Sep 30 20:38:29 2011 +0800
summary: Foo foo everywhere is foo
changeset: 5:8d6bea76ce60
user: Tim Post <tpost#whereiwork.com>
date: Fri Sep 30 20:51:42 2011 +0800
summary: Blah blah blah
Which is Mercurial understanding that I want to see revisions one and five instead of one through five.
Oddly enough, this works:
hg log -r 1+2+3+4+5
But, that gets extremely cumbersome, especially when trying to get a summary between revisions that are +500 away from each other.
Is there a way to get logs for revisions x through y instead of x and y without concatenating every revision in the series?
I'm using the output in order to determine how many commitments each developer made in a given series. If I simply can't do that using the hg command, I'm more than open to using the Mercurial API. I resorted to the hg command because I did not see an obvious way of doing it via the API.
By API, I mean just using Python via a hook or extension.
hg log -r1:5.
Mercurial has an entire mini-language devoted to selecting revisions for commands (not just for logs). For more information, see hg help revsets (needs Mercurial 1.6+).

Delete multiple heads

C:\Users\Itu\workspace\Compiler>hg branches
default 13:aa6af82c5458
C:\Users\Itu\workspace\Compiler>hg heads
changeset: 13:aa6af82c5458
tag: tip
user: Itun
date: Sun Sep 18 22:27:06 2011 +0400
summary: Update 1.0.0.2
changeset: 9:d36e5ac454b1
parent: -1:000000000000
user: Itu
date: Sun Sep 18 06:50:25 2011 +0400
summary: New PR
changeset: 8:758cd394d82a
user: Itun
date: Sun Sep 18 06:48:17 2011 +0400
summary: asdasd
changeset: 4:c4c981b4ff43
user: Itun
date: Sun Sep 18 03:31:58 2011 +0400
summary: Update
C:\Users\Itu\workspace\Compiler>hg merge
abort: branch 'default' has 4 heads - please merge with an explicit rev
(run 'hg heads .' to see heads)
How you can see I have the one branch and 4 heads.
When I try to pull in my eclipse mercurial plugin, it returns me message about multiple heads and offers me to rebase or merge. Then I did not understand anything and closed it.
I think I need something to delete this heads. It would be great if somebody could explain me this and show how to do this in the eclipse plugin, but other help is appreciated.
The safest way to remove the extra heads is to make a new clone, grabbing just the head you want with -r:
cd C:\Users\Itu\workspace
mv Compiler Compiler-backup
hg clone -r aa6af82c5458 Compiler-backup Compiler
This will move just aa6af82c5458 and its ancestors to the new clone, leaving the old heads in Compiler-backup. You can always hg pull them over later, or just delete the backup (losing your data).
Mercurial has an entire wiki page devoted to the topic of multiple heads.
I highly suggest reading it before proceeding on your apparent course of action.
The folks at Fog Creek have provided some friendlier material.
You might consider working with them, since it sounds like you might need some support.

Mercurial undo merge that has more commits following it?

Have a scenario where we un-intentionally merged a named branch (ABC) into our default branch.
hg rollback is not an option because there have been a couple commits since.
Is there a way to undo this?
You're going to need the Mq extension. If you don't have it turned on, do so by adding this to your Mercurial.ini or .hgrc file.
[extensions]
hgext.mq=
If you're not familiar with it, the Mq extension let's you manipulate history. The good news is, this will allow us to fix your repo. The bad news is that anyone who has a clone of the messed up repo will have to clone it again, because we'll be changing history.
First, go make another clone of your repo to work in, so we don't mess anything up.
Now, find the revision id of the merge changeset (that merged default and your named branch). Write it down. We'll refer to it as changesetM. Now find the revision id of the next changeset. Write it down. We'll refer to it as changesetN.
Once you have those two revision ids, head over to your command prompt and cd into your repo. Then type out the following, replacing changeset[M|N] with the appropriate revision id.:
$ hg qimport -r changesetN:tip
# This will add all of your changes since the merge to the queue
$ hg qpop -a
# This pops them all out of your history.
$ hg strip changesetM
# This removes the merge changeset.
$ hg update -C default
# Make sure we're on the default branch
$ hg qpush -a
# Take the changesets in the queue and push them back onto your history.
$ hg qfinish -a
# Remove changesets from the queue and finalize them as normal changesets.
Essentially, you are rebasing the new changesets on top of the default branch, removing the merge changeset in the process. Once you're done, you'll need to push the changes to a new repository on the server, and have your colleagues clone fresh copies.
Lastly, if you have any other Mercurial questions, also check out kiln.stackexchange.com.
UPDATE
I forgot to mention: If someone has based changes on something that was only in the other branch, it is possible that hg qpush -a will fail. You'll see a foo.txt.rej and foo.txt.orig file laying around. Unfortunately, you'll have to fix this yourself. To fix it, open the original file, the .orig file, and the .rej file and choose the right changes to merge in, saving them in the original file. Once you've merged it in, use hg qrefresh to update that patch to the new, merged patch. From their, you should be able to run hg qpush -a again and continue. If you run into the same error again on another patch, just follow the same process.
If you haven't publish the repo publicly you can do this
hg clone -r (parent1 of bad merge) -r (parent2 of bad merge) old new
and delete the old repo.
I came across the following scenario today:
# changeset: 1728:5d703e1051d3
|\ parent: 1727:1a5f73b5edb4
| | parent: 1720:65ddd0bde225
| | user: nn
| | date: Wed Feb 27 10:35:00 2013 +0100
| | summary: Merge with SomeBranch
| |
| o changeset: 1727:1a5f73b5edb4
| | user: nn
| | date: Wed Feb 27 10:34:30 2013 +0100
| | summary: lorem ipsum
| |
[some more changesets]
| |
o | changeset: 1720:65ddd0bde225
| | branch: SomeBranch
| | user: nn
| | date: Wed Feb 27 07:44:46 2013 +0100
| | summary: lorem ipsum
Where SomeBranch should not have been merged into default. What we did to solve this, was to use the backout command with the parent option like so:
hg backout --rev=1728 --parent=1727
By this you don't undo the merge itself: Looking at a branch graph (either with graph log or in TortoiseHg) you'll still see SomeBranch going into default at r1728. The result of the merge is however undone, meaning that the changeset containing the backout (r1729 in my case) is identical to r1727.