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.
Related
Typically, in HG my workflow is to exclusively use:
hg pull --rebase
If I wanted to run this in two commands, how would I do it?
hg pull
hg rebase <probably with some options?>
What hg pull --rebase does is to indeed first do a hg pull and then hg rebase with default arguments on top of that (you can look at the code in rebase.py in the Mercurial distribution in function pullrebase()), but only if any new revisions were pulled in. If no rebasing is necessary, hg pull --rebase will update to the new branch tip instead. So, hg pull && hg rebase is approximately correct, but doesn't quite capture some corner cases (no new revisions, no rebase necessary).
By default, hg rebase will use the parent of the working directory as the base revision of the rebase and the most recent revision of the current branch (i.e. usually what you just pulled in) as the destination. In short, it's equivalent to hg rebase -b . -d 'last(branch(.))'.
What does "base revision" mean in this context? It means that Mercurial will go and look for the least common ancestor of the base revision and the destination. Then it will rebase everything up to, but not including that least common ancestor on top of the destination. I.e., specifying the base revision allows you to pick pretty much any revision on the branch [1] that you want to rebase and let Mercurial figure out which revisions belong to that branch.
Note that because the rebase is based on the parent of the current working directory, this means that if your current checkout is not what you have been working on, then hg pull --rebase may surprise you by actually trying to rebase a different branch (it will usually fail, because those revisions are generally part of the public phase, but it's something you need to be aware of if you're working with so-called non-publishing repositories and don't use named branches).
[1] Branch in this context refers to an anonymous or topological branch, not a named branch. See hg help glossary for further details.
If you want to rebase by hand (bad idea in common), you have to
Read hg help rebase before
Understand usable for you options of rebase (at least -s and -d)
Use these options
Let's see at toy-repos:
Repo A
A>hg log -T "{rev} {desc}\n"
1 A2
0 A1
with 2 changesets A1 and A2 was cloned to repos B and C (B for pull --rebase A, C for clean pull A)
and two additional changesets was added to A and B+C after clone in order to test your use-case (diverged history)
A>hg log -T "{rev} {desc}\n"
3 A2++
2 A2+
1 A2
0 A1
B>hg log -T "{rev} {desc}\n"
3 B2
2 B1
1 A2
0 A1
C state is identical to B
B>hg pull --rebase
...
B>hg log -T "{rev} {desc}\n" -G
# 5 B2
|
o 4 B1
|
o 3 A2++
|
o 2 A2+
|
o 1 A2
|
o 0 A1
I.e. result of rebased pull is "linear history with local changes on top of remote changes", compared to just pull from C
C>hg log -T "{rev} {desc}\n" -G
# 5 A2++
|
o 4 A2+
|
| o 3 B2
| |
| o 2 B1
|/
o 1 A2
|
o 0 A1
or, in GUI
and in order to get B from C, you have to rebase 2 (-s 2) to new parent 5 (-d 5), but short hg rebase -b 2 will work also and will have the same effect
Here is what I am trying to do. I have a commit to the default branch that I need off default. We build for production from default and this commit is not ready. So I have used hg backout to undo the changes from the default branch. Now I have created a new branch to store the changes. But when I attempt to transplant or graft the changes onto the branch I get told that I cannot graft in changes from an ancestor. This makes sense, but I need to pull changes from an ancestor, how do I do this.
I have tried
hg diff -c 8c13fc133926 > new_branch.diff
hg import new_branch.diff
but this Fails with no explanation. Any pointers?
EDIT
It seems I'm a little hard to follow, so I will try and clarify.
o Default with only ready changes
|
| o Not ready changes (new commit on a branch)
| |
|/
o Hg backout commit Undoing not ready changes from default
|
o Lots of other commits and merges to default
|
o ...
|
o Not ready changes (original commit)
The graph above shows where I need to get to.
I have done this by using hg diff -c 6877 | patch -p1 where 6877 is the revision of the commit that needs to be moved off of default.
But I think I am doing this wrong.
Make your new branch off of the revision BEFORE you made the "not ready changes" commit. I will call that revision 123:
hg up 123
hg branch NewBranch
hg ci -m "branching for NewBranch"
Now you can do your GRAFT, with no concerns about ancestors.
Tying up loose ends:
It seems that you'll already have another head on NewBranch, the one you said you created. You can merge with that or just close it. You can see the heads with:
hg heads NewBranch
I will call that unwanted head revision 456. And I would close it like this:
hg up 456
hg ci --close-branch -m "It was all just a dream... a terrible, terrible dream."
Which reminds me: you could have used the same technique (closing an unwanted branch head) instead of hg backout! Then you could have done the steps above ... except you'd be able to do a neat-and-tidy REBASE ("leaving no witnesses", like they say in the movies) instead of cherry-picking with GRAFT, and leaving behind the wreckage on the default branch!
I personally avoid grafting as much as possible, and your commit is already in the history. You seem to be using the correct commands, but it is hard to understand your issue, more information is needed.
How about creating your new branch from the commit that you wanted to backout?
o NewBranch: head, creation of the branch
|
|
o | default: head, Backed-out changeset
| |
|/
o default: commit (not ready)
|
|
o default: initial state
|
Another idea, if you don't care about grafting, simply start your new branch from the initial state, and transplant the commit; it won't be considered an ancestor anymore.
o NewBranch: head, grafted commit
|
/|
| o NewBranch: creation
| |
| |
o | | default: Backed-out changeset
| | |
|/ |
o / default: commit (not ready)
| /
|/
o default: initial state
|
I am writing code to do analysis of our commits to Hg, and am comparing my results to TortoiseHg. I am having trouble understanding the behavior of TortoiseHg in the case of a merge.
When I select a merge changeset in TortoiseHg, the list of affected files only shows those files that had conflicts, unless I press the "Show All" button. At least that appears to be the intent, based on what I can glean from the web, and from observation that the files shown in the list have a double headed arrow if I press the Show All button.
I am attempting to emulate that by diffing each file in the changeset against both parents, and only including the file in my analysis if it differs from both parents. However, I am encountering files that TortoiseHg shows in the description of a merge, but that only differ from one parent. I see that in TortoiseHg as well - diffing against parent 1 or 2 shows a change, but the other parent doesn't.
I have also tried diffing with the --git option, to make sure it is not a metadata change I am missing, but that doesn't change the results at all.
To get the information about a changeset I am using:
hg log -v -r <rev> --removed --style xml
I pick up the parents of the merge changeset, and for each file in the merge, do
hg diff -r <parent1> -r <rev> filename
hg diff -r <parent2> -r <rev< filename
And I find that files TortoiseHg shows in its summary of the merge I report as having merged with no conflicts.
Can anyone shed light on the discrepancy?
Update:
I was able to reproduce this with the source code for TortoiseHg itself.
Clone from https://hg01.codeplex.com/tortoisehg
Open the repo in tortoiseHg and select rev 12602 (58eb7c70). This is a merge with parents of 12599 (6c716caa) and 12601 (39c95a81).
TortoiseHg shows the file tortoisehg/hgqt/repowidget.py as the only conflicted file in the merge, yet
hg diff -r 12599 -r 12602 tortoisehg/hgqt/repowidget.py
returns nothing, while
hg diff -r 12601 -r 12602 tortoisehg/hgqt/repowidget.py
shows two lines changing.
I think I've figured out what tortoisehg's logic is here (though I haven't checked the source to be sure).
As you've guessed, tortoise shows files changed on both sides of a merge with a double arrow. However, it does not look simply at the diff of the merge to each of its parents (e.g. p1(58eb7c70)::58eb7c70 and p2(58eb7c70)::58eb7c70). Instead, tortoise finds all changes introduced in the merge, compared the last common ancestor of the two parents.
Let's take the tortoise repo as an example. The graph view of the ancestry of 58eb7c70 is:
Jonathan:tortoisehg $ hg log --graph -r ::58eb7c70 -l 5 --template "{node|short}\n{desc|firstline}\n\n"
o 58eb7c70d501
|\ Merge with stable (noop)
| |
| o 39c95a813105
| | repowidget: show all errors on infobar
| |
| o da7ff15b4b96
| | repowidget: limit infobar error messages to 2 lines of up to 140 chars by default
| |
o | 6c716caa11fd
|\| Merge with stable
| |
| o 48c055ad634f
| | sync: show non-ascii command-line arguments correctly
| |
As you can see, merge 58eb7c70d501 merged two branches of development, with one changeset (p1, 6c716caa11fd) on one side, but two on the other (p2, 39c95a813105, and its parent, da7ff15b4b96). The point where these branches diverged is the last common ancestor of p1 and p2 -- 48c055ad634f.
(The last common ancestor can be found directly with hg log -r "last(ancestor(p1(58eb7c70), p2(58eb7c70)))")
Let's look at the changes that were made on those two branches. We'll compare each parent of the merge with the common ancestor:
Jonathan:tortoisehg $ hg status --rev "48c055ad634f::6c716caa11fd"
M .hgtags
M tortoisehg/hgqt/commit.py
M tortoisehg/hgqt/compress.py
M tortoisehg/hgqt/hgemail.py
M tortoisehg/hgqt/postreview.py
M tortoisehg/hgqt/purge.py
M tortoisehg/hgqt/rename.py
M tortoisehg/hgqt/repowidget.py
M tortoisehg/hgqt/revset.py
M tortoisehg/hgqt/run.py
M tortoisehg/hgqt/settings.py
M tortoisehg/hgqt/status.py
M tortoisehg/hgqt/sync.py
M tortoisehg/hgqt/visdiff.py
M tortoisehg/util/cachethg.py
M tortoisehg/util/hglib.py
Jonathan:tortoisehg $ hg status --rev "48c055ad634f::39c95a813105"
M tortoisehg/hgqt/repowidget.py
These are the changes that were actually merged by 58eb7c70d501 -- everything changed on the two branches since they diverged. As you can see, the only file in common between the lists -- the only file that was changed on both branches -- is tortoisehg/hgqt/repowidget.py, just as you expected. You'll see that this file was changed in da7ff15b4b96, the one changeset that's not a parent of the merge but is still included in the changes merged from the two branches.
tortoisehg/hgqt/repowidget.py was modified in 6c716caa11fd, which would explain why your second call to hg diff gives you results; the first call compares two revisions across which no changes were registered to tortoisehg/hgqt/repowidget.py; this seems sound to me, unless I am missing something else about how hg diff behaves.
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
I'm trying to get in the habit of doing code reviews, but merges have been making the process difficult because I don't know how to ask Mercurial to "show only changes introduced by the merge which were not present in either of its parents."
Or, slightly more formally (thanks to Steve Losh):
Show me every hunk in the merge that wasn't present in either of its parents, and show me every hunk present in either of its parents that isn't also present in 3.
For example, assume I have a repository with two files, a and b. If "a" is changed in revision 1, "b" is changed in revision 2 (which is on a separate branch) and these two changes are merged in revision 3, I'll get a history which looks like this:
# changeset: 3
|\ summary: Merged.
| |
| o changeset: 2
| | summary: Changing b
| |
o | changeset: 1
|/ summary: Changing a
|
o changeset: 0
summary: Adding a and b
But if I ask to see the changes introduced by revision 3, hg di -c 3, Mercurial will show me the same thing as if I asked to see the changes introduced in revision 1, hg di -c 1:
$ hg di -c 3
--- a/a
+++ b/a
## -1,1 +1,1 ##
-a
+Change to a
$ hg di -c 1
--- a/a
+++ b/a
## -1,1 +1,1 ##
-a
+Change to a
But, obviously, this isn't very helpful - instead, I would like to be told that no new changes were introduced by revision 3 (or, if there was a conflict during the merge, I would like to see only the resolution to that conflict). Something like:
$ hg di -c 3
$
So, how can I do this?
ps: I know that I can reduce the number of merges in my repository using rebase… But that's not my problem - my problem is figuring out what was changed with a merge.
The short answer: you can't do this with any stock Mercurial command.
Running hg diff -c 3 will show you the changes between 3 and its first parent -- i.e. the changeset you were at when you ran hg merge.
This makes sense when you think of branches as more than just simple changesets. When you run hg up 1 && hg merge 2 you're telling Mercurial: "Merge changeset 2 into changeset 1".
It's more obvious if you're using named branches. Say changeset 2 in your example was on a named branch called rewrite-ui. When you run hg update 1 && hg merge rewrite-ui you're effectively saying: "Merge all the changes in the rewrite-ui branch into the current branch." When you later run hg diff -c on this changeset it's showing you everything that was introduced to the default branch (or whatever branch 1 happens to be on) by the merge, which makes sense.
From your question, though, it looks like you're looking for a way to say:
Show me every hunk in this changeset that wasn't present in either of its parents, and show me every hunk present in either of its parents that isn't also present in 3.
This isn't a simple thing to calculate (I'm not even sure I got the description right just now). I can definitely see how it would be useful, though, so if you can define it unambiguously you might convince one of us Mercurial contributors that read SO to implement it.
In order to do code reviews you really want to see just the changes in the project that you are reviewing. To that end we use a new branch for each story and use pull requests to spotlight the changes, having merged all changes into the story branch before creating the pull request. We host our code on bitbucket and the review / pull request tools are really very good, offering a side by side diff.
Pull requests with side-by-side diffs