Mercurial - diff multiple changesets at same time? - mercurial

To diff we use:
hg diff -c <xyz>: Show diffs for a given changeset
hg diff -r <xyz>: Show all diffs since a given changeset
But let's say you have changesets 4-5-6-7-8 where changesets 4, 6, 8 were related to a particular area of the system and in one diff you wanted to see the changes made from JUST these three changesets, how would you do this? If file A was modified in changeset 4 and 8, the diff would show the difference between changeset 3 and 8.

If changesets 4,5,6,7,8 are linear in history I don't think that even with revsets you can do that using just -r. However, if the changes in 5 and 7 really are from a different part of the system you can likely get the output you want by adding a -X or a -I. Something like this:
hg diff -r 3::8 -X part/you/do/not/want/**
or
hg diff -r 3::8 -I part/you/do/want/**
If, alternately, you're a little more exact about parenting a changeset as early as possible in history you'd have a topology like this:
[3]---[4]---[6]---[8]---[9]
\ /
------[5]---[7]------
and then you'd get what you want using:
hg diff -r 3::8
(note the double colon which tells the range to follow topology not just numeric range)

Related

Mercurial: when did this changeset take effect in the stable-branch?

Scenario: a colleague told me that a bug-fix I made was not in the current build. I said: "but I pushed it 2 weeks ago!"
How can I check that my change (commited into a bug-fix-branch) was merged into the stable-branch and also pushed to the server-repository?
So this really has 3 parts:
1) was it commited? when? See revision number with:
hg blame FILENAME
see the log with date and so on with:
hg log REVISION
2) was it merged?
hg glog -r "(merge() and branch(2.5) and children(branch('BRANCHNAME')))" \
--template "{branches} [{rev}] ({date|shortdate}): {desc|firstline}\n"
Could this be shorter?
3) was it pushed?
It can be shorter and cleaner (under some conditions)
Task 1
If you can recall any part of any string of you patch-work in any file - hg help grep. F.e (pattern is regexp, thus - brackets are escaped for processing "as is"):
>hg grep "repo\[None\].parents()" test_hooks.py
tests/test_hooks.py:1417: state = repo[None].parents()
Second field in output (easy parsable for auto-processing) is changeset in which this change was made in this file
Task 2
Just fix for rather dirty (and bad in common logic) revset:
with merge() and branch(DST) and children(branch('SRC')) you'll got all mergesets for merging SRC -> DST, even irrelevant to known revset of bugfix. Output for the above repository and your revset (note also slightly changed command - hg glog is deprecated)
>hg log -g -r "(merge() and branch(default) and children(branch('stable')))" --template "{branches} [{rev}] ({date|shortdate}): {desc|firstline}\n"
[839] (2011-10-19): Merge with stable.
[841] (2011-10-21): Merge with stable.
[843] (2011-11-01): Merge with stable
[982] (2012-11-11): Merge with stable.
[1021] (2013-06-23): Merge with stable.
[1108] (2014-02-11): Merge with stable.
[1126] (2014-02-12): Merge with stable.
[1171] (2014-04-04): Merge with stable.
[1180] (2014-05-02): Merge with stable.
[1228] (2014-08-01): Merge with stable.
[1231] (2014-08-12): Merge with stable.
[1257] (2014-11-05): Merge stable back into default.
[1266] (2014-11-15): Merge with stable.
[1280] (2014-12-10): Merge with stable.
[1307] (2015-01-30): Merge with stable.
[1326] (2015-05-08): Merge stable back into default.
[1335] (2015-05-29): Merge with stable.
[1338] (2015-07-08): Merge with stable.
[1341] (2015-07-09): Merge with stable.
[1346] (2015-08-11): Merge with stable.
[1349] (2015-09-26): Merge with stable.
[1358] (2015-09-30): Merge with stable.
[1362] (2015-10-21): Merge with stable.
[1367] (2015-12-31): Merge with stable.
[1372] (2016-01-27): Merge with stable.
[1378] (2016-03-04): Merge with stable.
[1410] (2016-05-11): Merge with stable.
[1418] (2016-05-23): Merge with stable.
[1489] (2016-06-26): Merge with stable.
I started with plain descendants(1417) (greedy revset, yes)
>hg log -r "descendants(1417)" -T "{rev}\n"
1417
1418
1419
1420
1421
...
because I'm interested in only in merges into default (in my case) possible shorter output will be hg log -r "descendants(1417) and branch(default)" (but it can be rather long too - I really eliminated two revsets) and only first revset from list needed
>hg log -r "first(descendants(1417) and branch(default))" -T "{rev}\n"
1418
Or in a little bit different way (recalling about mergesets entities, can also be first()-ed)
>hg log -r "merge() and descendants(1417) and branch(default)" -T "{rev}\n"
1418
1489
Notes:
Solution for p.3 - perform all tests on CENTRAL-REPO clone (which you have already, yes)
"Bad logic" remark - branch(2.5) and children(branch('BRANCHNAME') by design extract only mergesets in 2.5, merge() doesn't apply any additional effective filter
Addenda:
After some (still unfinished) headache I got the above command as alias with two parameters: problematic changeset and target-branch. It's something like
[alias]
ismerged = log -r "first(descendants($1) and branch('$2'))" --template "{ifeq(branch, '$2', 'Merged in {rev}','Not merged')}\n"
in repo's .hgrc
Output for merged changeset
>hg ismerged 62 default
Merged in 63
But I can't get else-part to work for not merged changesets and still have dumb empty output in this case
Future (more decorative, than functional) improvements may include:
transferring revset definition from alias into `[revsetalias]
getting good else-part of template

mercurial - see changes on the branch ignoring all the merge commits

I have a branch that was developed for a long period of time. During the development default branch was merged into that branch several times. I would like now to review all the changes done on that branch ignoring merges, to decide whether it is safe to merge it to default.
I tried
hg diff -r "branch('myBranch') - merge()"
but it still shows changes introduced by merges. Also tried following this How to show the diff specific to a named branch in mercurial but
hg diff -r "branch('myBranch') - branch('default')"
still bring changes introduced by merges.
You have to read about revsets syntax
Your case
hg log -r "branch('myBranch') and ! merge()"
The problem with your commands is that when you perform a hg diff and pass it several changesets, you actually perform a diff between those changesets, hence you will see the merge result.
If you want to see just the changes made by the changesets then you could use export:
$ hg export -r "branch('mybranch') and not merge()"
// lists the changes made by each changeset
For easier reviewing, you can output these to files with names based on the revision/changeset id:
$ hg export -r "branch('mybranch') and not merge()" -o "%H.patch"
... creates a file for each non-merge changeset in mybranch, and outputs it to a file with the name "40-digit changeset id.patch". If you'd prefer the revision number (only useful for your local repository as revision id's are local), use "%R.patch".
The following uses the log command but with the --patch parameter it can show the modified lines as well:
hg log --branch my-branch --no-merges --patch
Short form:
hg log -Mpb my-branch
That is a very good question, which I am trying to find good answer for a long time, and yet not found a good one. OK, one thing which 100% works is this:
hg status # make sure that you don't have local changes
hg up <target_branch>
hg merge <your branch>
hg diff > merge.diff
hg up -C # this one cleans the merge
I use this workflow all the time, but it does not satisfy me fully because it requires to switch branches (when I actually might not want to do actual merge right at this exact moment, I am just checking whats there)
I've been searching for ages for a good solution here, but so far there are none found. Tried those:
hg diff -r "branch('.') and ! merge()" # this page
hg diff -r "default:branch('.') and not merge()"
hg diff -r "parents(branch(.)):branch('.') and not merge()"
This problem also discussed in:
Mercurial: how can I see only the changes introduced by a merge?
which has good answer as: "so if you can define it unambiguously you might convince one of us Mercurial contributors that read SO to implement it."
Try:
hg diff -r"ancestor(default,my_branch)" -rmy_branch
If you haven't done any merges to the branch, then "ancestor" will pick up the original branch point. If you've done merges, then "ancestor" will pick up the latest merge point.
For example, on the graph below you'll get the diff between 520 and 519:
# 521 (default)
|
| o 520 (my_branch)
|/|
o | 519
| |
o | 518
| |
o | 517
| |
| o 516
| |
| o 515
| |
| o 514
|/
o 513
On the simpler graph below you'll get a diff between 516 and 513:
# 517 (default)
|
| o 516 (my_branch)
| |
| o 515
| |
| o 514
|/
o 513
Per Matt Mackall what you probably want is:
hg diff -r mainbranchrev -r mywork
He writes:
You may be under the impression that "a changeset is a delta" or
similar, and that this means that Mercurial can magically chain together
a bunch of non-contiguous deltas to construct a bigger delta that's just
your changes.
In fact, Mercurial is much simpler than that. A changeset is actually a
snapshot: a complete set of complete files representing the state of the
project at the time of commit. And a diff is simply the difference
between these two snapshots. So when you do:
hg stat --rev '3408::3575 and user(mdiamond) and not merge()'
..status simply uses the first and last changesets of the resulting set
to do its calculation.
When you do incremental merges on your development branch, you're
inextricably tangling your work with mainline development, and Mercurial
is not nearly smart enough to untangle it in the way you're asking. Your
best bet is to compare the head of your work with the head of the main
branch:
hg diff -r mainbranchrev -r mywork
..which will show all your work plus whatever merge fixups you had to
do.

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 to show list of unapplied changesets in Mercurial

After pushing changesets to a repository called 'A' how can I see the list of changesets waiting to be applied when I am in 'A'?
Expanding on that,
In repo B I push changesets to repo B
I change to repo B
How can I list the changesets pushed in step 1?
Not sure what you mean by "unapplied" changesets, however here's a couple thoughts.
You can easily see what changesets will be pushed to a repository by doing hg outgoing prior to doing the hg push. This will list all of the changesets that will be pushed using default options.
Similarly you can use hg incoming in the destination repository to show what changesets would be pulled from another repo.
As for "unapplied" changesets, if I assume you mean changesets that are newer than the working directory, you could use hg log -r .:tip, which should (I've not had a chance to test it) show all newer revisions, but not actually all recently-pushed ones.
Edit: I've updated the revision set in the -r option to something that should work. Have a look at revsets on the Mercurial manpage for more possibilities.
$ hg summary
parent: 0:9f47fcf4811f
.
branch: default
commit: (clean)
update: 2 new changesets (update) <<<<<
The update bit tells you what (I think) you want.
I had written a different answer, but I ended up with a better way of doing what is needed here (an even better and definitive –for me– solution is at the end of this post, in the [EDIT] section).
Use hg log.
Specifically, issue an hg sum command first. This will give me:
parent: 189:77e9fd7e4554
<some commit message>
branch: default
commit: (clean)
update: 2 new changesets (update)
To see what those 2 new changesets are made of, I use
hg log -r tip -r 2 -v
Obviously, 2 is to be replaced with the number of changesets that hg sum reports.
This works because tip will refer to the most recent (or "unapplied") changeset. By limiting the output to the 2 latest changes (-l 2), the information is shown only for those changesets that I'm interested in. With -v, the list of files affected by the changeset is also shown.
To make things simpler, I have defined a user command in my .bashrc file:
alias hglog="hg log -r tip -l $1"
This allows me to type hg sum (to get the number of pending/unapplied changesets) and then to type hglog x where x is the number of changesets revealed by hg sum.
There is probably a more complete way of doing this, for instance using custom templates, but I guess it's pushing things too far in terms of sophistication.
[EDIT] (Third iteration)
I have reached the most satisfying answer to this question by expanding on the alias idea so that I no longer have to type hg sum. My .bashrc file now contains this:
show_pending_changesets() {
nb=$(hg sum | grep "update:" | sed 's/update: \([0-9]*\) .*/\1/');
if [ `expr $nb + 1 2> /dev/null` ] ; then
hg log -r tip -v -l $nb
else
echo "Nothing new to report"
fi ;
}
...
alias hgwhatsnew=show_pending_changesets
Explanation: I'm using sed to extract the number of changesets from the last line (which is the one that starts with update:) of the output of hg sum. That number is then fed to hg log. All I have to do then is to type hgw and tab-complete it. HTH

Does merge direction matter in Mercurial?

Take a simple example: I'm working on the default branch, have some changesets committed locally, and I pulled a few more from the master repository. I've been working for a few days in my isolated local repository, so there's quite a few changes to merge before I can push my results back into master.
default ---o-o-o-o-o-o-o-o-o-o-o (pulled stuff)
\
o----o------------o (my stuff)
I can do two things now.
Option #1:
hg pull
hg merge
Result #1:
default ---o-o-o-o-o-o-o-o-o-o-o
\ \
o----o------------o-O
Option #2:
hg pull
hg update
hg merge
Result #2:
default ---o-o-o-o-o-o-o-o-o-o-o-O
\ /
o----o------------o
These two results look isomorphic to me, but in practice it seems that option #2 results in way smaller changesets (because it only applies my few changes to the mainline instead of applying all the mainline changes to my few).
My question is: does this matter? Should I care about the direction of my merges? Am I saving space if I do this? (Doing hg log --patch --rev tip after the merge suggests so.)
They're (effectively) identical. You see a difference in the hg log --patch --rev X output size because log shows the diff of the result and (arbitrarily) its 'left' parent (officially p1), but that's not how it's stored (Mercurial has a binary diff storage format that isn't patch/diff based) and it's now how it's computed (p1, p2, and most-recent-common-ancestor are all used).
The only real difference is, if you're using named branches, the branch name will be that of the left parent.
There is also a difference if you are using Bookmarks. When doing a merge, the branch that you are is the branch that is receiving the changes, so the new changeset will be part of that branch. Supose you have this situation:
default ---o-o-o-o-o-o-o-o-o-o-o -- Head: Rev 200
\
o----o------------o -- Head: Rev 195, Bookmark: my-stuff
If you merge Rev 200 into Rev 195, the bookmark my-stuff will move on to Rev 201, as you are generating a new changeset in the same branch that has the bookmark.
On the other hand, if you merge 195 into 200, you are generating a changeset in the branch that don't have the bookmark. The my-stuff bookmark will remain in Rev 195.