1

If there is a topic branch is there a way to show the unique changes that were made on that branch?

The topic branch could have already been merged in to master.

The use-case is if someone wants to see the unique changes on that topic branch 6 months after it has already been merged-in.

Jonathan.Brink
  • 23,757
  • 20
  • 73
  • 115
  • You didn't state whether the merge was a fast-forward merge or generated a merge commit, so my answer below covers both cases, whereas your answer is only correct for the fast-forward merge case, as I have repeatedly explained. – Adam Spiers Apr 01 '15 at 19:35
  • 1
    If you decide not to record the merge, then there's no record of the merge. For recorded merges it's just git diff of the merged tip against the merge base. Where the branch label points to now or even whether it exists or not is irrelevant - the merge commit like every commit is permanent and immutable. – jthill Apr 03 '15 at 17:58
  • @jthill You're right, although if fast-forward merge is not possible then the decision not to record the merge is unavailable: either you merge with a merge commit, or you can't merge at all. – Adam Spiers Apr 04 '15 at 19:08

3 Answers3

2

If the topic branch was merged as a fast-forward merge then there is no way to do this, because there's no way to tell where the topic branch was started.

However in many cases, the merge will not be a fast-forward merge because commits were made on the trunk whilst the topic branch was under development. In this case, there will be a merge commit, and this allows us to achieve what you asked for:

If the commit which merged the topic branch is denoted by the commit-ish $topic_merge, then you can just do:

git log $topic_merge --not $topic_merge^

This excludes all commits from the trunk prior to the merge (i.e. the first parent of the merge commit), and all its ancestors.

Note that this can also be written as

git log $topic_merge ^$topic_merge^

or

git log $topic_merge^..$topic_merge

although when using zsh you may have to avoid filename generation expansion in the first case:

git log $topic_merge \^$topic_merge^

This even works for octopus merges, since it will show all changes from any branch brought in by the merge which were not already in the trunk.

The only downside is that it can include any other commits which were merged into the topic branch. If you don't want to include these, then again you need to rely on the presence of merge commits in order to distinguish topic branch commits from commits merged into the topic. This would need to be done recursively and the results substracted from the results given above.

Community
  • 1
  • 1
Adam Spiers
  • 17,397
  • 5
  • 46
  • 65
  • Is it on the user to create the $topic_merge commit-ish? Or does git automatically store that somewhere? – Jonathan.Brink Mar 31 '15 at 17:53
  • If commits were made on the main branch during development of the topic branch, then the merge will automatically result in a merge commit, otherwise git will do a fast-forward merge by default, unless the `--no-ff` option to `git merge` is used. I'll update the answer to clarify this. – Adam Spiers Mar 31 '15 at 23:56
  • useful information, but I don't think I can do what I originally set out to do...be able to identify the unique commits of a topic branch that has already been merged into master (determining the unique commits after-the-fact) – Jonathan.Brink Apr 01 '15 at 01:26
  • @Jonathan.Brink Why can't you? Are you saying that your merge of the topic was a fast-forward merge? That's the only thing which would prevent it from being possible. – Adam Spiers Apr 01 '15 at 08:35
  • So you have downvoted this answer without even explaining why you believe it doesn't answer the question? – Adam Spiers Apr 01 '15 at 19:38
  • This answer was downvoted because it places a constraint on the question that wasn't originally present. – Jonathan.Brink Apr 01 '15 at 23:40
  • And what constraint is that? – Adam Spiers Apr 02 '15 at 08:31
  • How the merge was performed. In the original question there no mention of which merge type was used. – Jonathan.Brink Apr 02 '15 at 12:23
  • 1
    Please re-read my answer more carefully. As I have already repeatedly explained, it covers BOTH fast-forward merges (in the first sentence) AND non-fast-forward merges (in the rest of the answer). In contrast your answer only covers fast-forward merges, so the claim "it places a constraint on the question that wasn't originally present" applies to your answer, not mine. – Adam Spiers Apr 02 '15 at 16:01
  • @Jonathan.Brink I created an example. Could you specify what commits you would want to know in this example for the branch 'feature': https://gist.github.com/Ikke/d8dece57e6e0202dedc3 – Ikke Apr 03 '15 at 16:05
  • @Ikke...I don't have time at the moment to go through the gist, but a quick look seemed promising. Would you consider turning that into an answer? – Jonathan.Brink Apr 03 '15 at 17:10
  • @Jonathan.Brink The command in the gist is exactly the approach I already gave in this answer. So you downvoted this answer but you think a gist with the same answer looks promising? – Adam Spiers Apr 03 '15 at 20:22
  • But what if I have a branch name, but not the hash of the merge commit? – Marius Gedminas Aug 18 '16 at 13:34
  • @MariusGedminas A branch name *is* a [commit-ish](http://stackoverflow.com/questions/23303549/what-are-commit-ish-and-tree-ish-in-git), so this answer does not require use of a hash. – Adam Spiers Aug 23 '16 at 09:06
  • Yes, but the branch name doesn't point to the merge commit -- it points to one of its parents. – Marius Gedminas Aug 24 '16 at 05:19
  • If it points to one of its parents then that topic branch hasn't yet been merged, so a simple `master..topic` gives you the commits only on the topic branch. But I don't think that's what the original question was asking. – Adam Spiers Aug 24 '16 at 11:21
0

I ran across the various log options for working with cherry-pick{able,ed} commits, I'm pretty sure some variation on

git log --decorate --oneline --cherry   master...topic

will serve here (note the three dots).

For reference, git log and git revisions (ways to specify revisions and revision sets) docs.

To find the latest recorded merge of topic to master whether or not the topic ref has since been deleted,

git log --oneline -1 --first-parent --merges --grep="^Merge branch 'topic'" master

and to remember it for later use do e.g.

merge=$(git log --pretty=%H -1 --first-parent --merges --grep="^Merge branch 'topic'")

so you can then

git log --decorate --oneline --cherry   $merge^...$merge^2

Other options:

In the ordinary case topic is still active and you only really care what change hunks merge is working with:

git diff master...topic

To find the latest recorded merge of topic to master if the topic ref has since been deleted, i.e. if you're dealing with the archaeological case here:

git log --oneline -1 --first-parent --merges --grep="^Merge branch 'topic'" master

and you can set a variable for the merge and merged-tip commit ids with

merge=$(git log --pretty=%H -1 --first-parent --merges \
              --grep="^Merge branch 'topic'"   master)
topic=$(git rev-parse -q --verify $merge^2)

edit: someday I promise I'll stop giving keyboard-to-editbox commands. The above works now.

If you decided not to record the merge then of course there's no record of the merge.

Recorded merges can clutter up the commit graph and if there are many of them they can make it hard to follow; also, commit-message subjects are intended to be enough to identify the scope of the work done in them. So git ordinarily prefers linear histories when possible. To avoid depending on the commit subjects, to ensure administratively-significant boundaries are recorded in the ancestry chain (and for largeish projects this is often a really really good idea), do a merge and specify --no-ff.

To get either the current topic tip or failing that its most recently merged tip:

topic=$( git rev-parse -q --verify refs/heads/topic \
         || git rev-parse -q --verify $( git log --pretty=%H \
                            -1 --first-parent --merges  \
                            --grep="^Merge branch 'topic'"   master)^2 )

The first commit off master in $topic's branch:

first=`git rev-list master..$topic --reverse --first-parent | sed q`

And that commit's first parent is the branch point from master:

branchbase=`git rev-parse $first^`

To show all the changes on $topic since the histories first diverged:

git diff $branchbase..$topic
jthill
  • 55,082
  • 5
  • 77
  • 137
  • 1) Is there an advantage to jumping through these `rev-parse` / `log` / `rev-list` hoops, vs. the approach in my answer? 2) Why not use `git merge-base` to determine the branch point? 3) How do you deal with topic branches which contain merge commits, e.g. as shown in [this gist](https://gist.github.com/Ikke/d8dece57e6e0202dedc3)? – Adam Spiers Apr 08 '15 at 09:00
  • @AdamSpiers We both gave partial answers, and it seems to me there's little-to-no overlap at all between them. The rev-parse and rev-list and log commands find the topic tip "even if it's already been merged to master", a task yours wholly leaves to OP, referring to the result as `$topic_merge --not $topic_merge^`. The assignment to `first` finds the branch base, not the latest merge base, also a task your answer doesn't even mention. That accounts for all the "hoops". – jthill Apr 08 '15 at 15:04
  • Thanks for the explanation. Yes, I was assuming that the OP could already locate the topic branch in the history, and that it was beyond the scope of the question; after all one could equally use `gitk` or an IDE to grep/browse the history and locate it, and I don't think an answer should be too opinionated about that. But I can see how your commands could be useful to some. However I still don't understand the need to calculate the branch base. Maybe you are worried about topic branches which contain merges from the trunk? But my answer handles these fine. – Adam Spiers Apr 08 '15 at 15:47
  • @AdamSpiers for the merges, our answers both ignore all but the latest -- yours, by (a) ignoring their existence and also all `topic` commits prior to its tip (and also, if `topic` is still active, is partially merged, and has unmerged commits, all commits since the previous merge), but including all merges and commits merged from other branches with no way to distinguish which commit came from which branch -- mine, by including the effects of _all_ changes on the topic branch, whether incorporated by merge or ordinary work. – jthill Apr 08 '15 at 15:54
  • Whichever, OP's going to have to bang on what we've said and probably ask more questions about whatever he can't figure out. – jthill Apr 08 '15 at 15:54
  • Didn't mean to ignore your comment, had that loaded and was in a rl conversation. – jthill Apr 08 '15 at 15:55
  • @AdamSpiers ("prior to its _base_" in mine, oops, sorry). Sure, OP could locate the tip and base by eyeball, that's easy. But if he can do that what's he asking? How to do it to me means how to script it. – jthill Apr 08 '15 at 16:04
  • >5min -- re the base/tip correction, gahh, "it" was oriignally meant to refer to the merge before '$topic_merge` and tip is right for that reference, "it" (heh) was so unclear I misread it myself. – jthill Apr 08 '15 at 16:12
  • OK I think I have finally figured out what you are talking about. You seem to be worrying not only about merges into the topic branch from other branches (which is a valid concern, but which my answer handles fine), but also about repeated merges from the topic branch into the trunk, and I fundamentally disagree with that. If that is happening then IMHO the topic branch ceased being a real topic branch the moment someone abused it by committing to it after it was merged into the trunk, because they are then reusing the branch for multiple topics. Why would they want to do that? – Adam Spiers Apr 08 '15 at 22:42
-1

I believe the answer here is that this is not possible.

The reason is git doesn't keep track of where the branch was originally created. A branch is simply a pointer that points to the tip of the branch.

Thus, if merge-commits have been made on the topic branch since it was originally created, it is not possible to definitively say which commit started the branch.

Jonathan.Brink
  • 23,757
  • 20
  • 73
  • 115
  • Not true - you don't need `git` to keep track of where the branch was created. – Adam Spiers Mar 31 '15 at 12:41
  • 2
    To clarify, it's possible as long as there is a merge commit. In the cases where it was a fast-forward merge with no merge commit then you are right, which is a good argument in some scenarios for using the `--no-ff` option to `git merge`. – Adam Spiers Mar 31 '15 at 23:57
  • I'm sorry but this answer is wrong, so you shouldn't have accepted it. Your original question didn't say anything about whether the merge was a fast-forward merge or not, so as I explained, in some cases it *is* possible. – Adam Spiers Apr 01 '15 at 08:38
  • My question was stated very generally. The answer to my question is that it is not possible. It helps that in certain situations (depending on how the topic branch was integrated) this can be accomplished, but that's not the use-case described in the original question. – Jonathan.Brink Apr 01 '15 at 14:11
  • Sorry, but I really think you still haven't understood me. You just contradicted yourself: firstly you stated that it is not possible, and then you said "in certain situations this can be accomplished", i.e. that it IS possible. I have clearly shown that it IS possible in some cases, so why are you still claiming that it is impossible? I agree with your assertion that your question was stated very generally - it was stated generally enough to include the non-fast-forward merge case. So I have no idea why you then say "but that's not the use-case described in the original question". – Adam Spiers Apr 01 '15 at 19:31
  • 1
    It is possible in some cases, but the original question isn't clear enough to explain the scenario under which it was attempted so this answer is technically incorrect. – DaveStephens Apr 03 '15 at 13:19
  • @AdamSpiers That it's possible in *some* situations, doesn't mean it's usefull in general. You have to know that the situation applies to be able to use it. If you know that the situation applies, you already know what commits there are in the branch. – Ikke Apr 03 '15 at 13:58
  • 1
    @Ikke Huh? I never claimed I had a general programmatic solution, and the OP didn't ask for one. None of that prevents my answer from being a) correct and b) useful, so I have no idea what your point is. And I also struggle to see any point to your second sentence; the original question asked for a way to *show* the changes, not a way to "know" them (whatever you meant by that). – Adam Spiers Apr 03 '15 at 16:14
  • @AdamSpiers The original question _was_ asking for a general programmatic solution – Jonathan.Brink Apr 03 '15 at 17:02
  • @Jonathan.Brink No it wasn't. Maybe that's what you intended, but it's not what you wrote. – Adam Spiers Apr 03 '15 at 20:20