1

Colleagues, do I correctly understand that there is no automatic and general answer to seemingly simple question "What a Git merge of a branch has brought to master"? For it to answer - we need a reliable way to find a commit where master and branch diverged for the 1st time.

Also by "automatic" I mean "operating just Git CLI + without human being interpreting a graph of commits".

I'm interested in finding a combined change brought by whole Blue branch + commmit9 (where possible conflict resolution happened + some other changes).

My study - see diagram (left/right pictures are the same commit history, just depicted differently).

We have Blue branch and Black is master branch.

In my understanding generally this task is impossible due to:

  1. git branch is a sliding pointer to some current revision, it can also be renamed. branches have no real identity in git.

  2. git does not keep info that Blue branch was created after 1 before 2 (maybe in local ref log only?)

  3. git merge-base gives us just last merge #6, not #1

  4. generally we can't get "oldest" diversion #1 because of prehistory - there might be other older diversions before, and no reliable start of branch can be found (see item 2)

  5. we can't also automatically "reconstruct" Blue branch: there is no automatic way to understand that Blue branch was 4>5>6 and not C>D>6 path

Question: Am I missing something? How do you solve this task in your practice? By visual interpretation of commit graph?

git history

Max
  • 1,741
  • 3
  • 23
  • 40

2 Answers2

2

Your title asks "What has a Git merge of a branch brought to master?". If you're talking about the content changes, the answer is

git diff commit9 commit9^   # `commit9` is any name for the merge you labeled `9`

or if you're asking what commits were part of the ancestry that led to the merged tip commit9^2's content, that's

git rev-list commit9^1..commit9^2    # you probably want the friendly rev-list, `git log`

every commit reachable from commit9's second parent that isn't also reachable from commit9's first parent.


If instead you want to programmatically find just the commit you labeled 1 and not all the commits and content changes, nobody's ever found it worth adding yet another option to memorize but it's easy enough to just write:

git rev-list --first-parent --parents commit9^1 commit9^2 \
| awk 'seen[$2]++ { print $2; exit }'

which isn't necessary to answer anything about what the merge brought.

jthill
  • 55,082
  • 5
  • 77
  • 137
  • jthill, thanks for your answer. can we concentrate on finding the #1 commit. what if in prehistory of #1 (before Blue branch was created) there where other branches, merges etc? in my current understanding, i can't grasp how would it specifically pick #1? (as for "git rev-list + every commit reachable" part - this looks very cool, i need to study it) – Max Jul 08 '23 at 04:18
  • jthill, also is it possible to combine git diff with rev-list, so that when i give SHA of commit9 + commit8 (possibly) i get all the changes brought by merging Blue into master (including deletions, additions, resolved merge conflicts, etc) - i.e "what was brought by the merge"? – Max Jul 08 '23 at 04:26
  • 1
    Finding the `1` commit operates by chasing only the first parents of each merged tip until it sees the same first parent in a commit on each branch. Commit ancestry cannot have loops, not as a matter of authority but "because math": it simply cannot be done. So the same commit cannot appear as a first parent twice in any first-parent chain, so seeing it a second time means you've found where the two first-parent chains diverged. – jthill Jul 08 '23 at 04:26
  • Look at the options on `git log`. Git is built to cater to how people use it after they understand how it works, a bit of a bootstraps thing but it's worth the effort. Add `-m -p` to `git log` to see all the diffs, or just `-p` to see only the non-merge diffs. – jthill Jul 08 '23 at 04:29
  • Be sure you understand what @matt's answer says. The way I say that is "Git is an extensible dag of immutable snapshots". The labels hardly matter at all. The commits matter, the snapshots and their ancestry. Diffs are generated on the fly for specific purposes. You're diffing two snapshots, be sure the differences between them are the ones you want to see. – jthill Jul 08 '23 at 04:34
  • generally i was talking about seeing all the combined changes (possibly in a form of diff) that occurred in Blue branch + merge conflict resolution (potentially happened at commit9) presented for me when i give to git cli SHAs commit9+commit8 - literally what happened to master when Blue was merged into it at commit9 – Max Jul 08 '23 at 04:38
  • That's the first diff I showed in the answer. – jthill Jul 08 '23 at 04:49
  • jthill. thanks. i'll check it on monday on fancy history i have. by now (i'm not at computer now) that `git diff commit9 commit9^` looks as if it will give me just the small last change brought in concrete merge conflict resolution, changes happened at commit9, and not the whole combined change brought by whole Blue branch path... but i may be wrong. i'm interested in whole change brought by whole Blue branch + last commit9. – Max Jul 08 '23 at 04:54
1

Am I missing something?

No. Git doesn't store events, changes, user actions etc. It stores commits and that's all. A commit is a complete snapshot of all your tracked files, plus a backwards pointer to one or more parents. "History" is just a process of walking from some commit backwards through the chain one parent at a time.

matt
  • 515,959
  • 87
  • 875
  • 1,141
  • (Note however the section about history simplification in the `git log` docs. There's more than one way to describe these backwards paths.) – matt Jul 08 '23 at 03:34