Skip to main content

Git

git show#

git show for merge commits provides a smaller diff than what the GitHub UI shows, which may be helpful for code reviews.

An example#

Prepare for a merge conflict#

We are on main branch. Everything is already committed.

README.md
# Git Sandbox

Create a side branch and make some edits.

git checkout -b side
# Create some random files (with Bash expansion)echo "This is added on side branch" > side-{01..20}.txt

Edit README.md as follows:

README.md
# Git Sandbox
Line 1 added on side branch.
Line 2 added on side branch.
Line 3 added on side branch.

Add all files and commit.

git add -Agit commit -m 'update on side branch'

Go back to main branch and make some edits:

git checkout mainecho "This is added on main branch" > main-{01..20}.txt

Edit README.md as follows:

README.md
# Git Sandbox
Line 1 added on main branch.
Line 2 added on main branch.
Line 3 added on main branch.

Add all files and commit:

git add -Agit commit -m 'update on main branch'

Merge main into side#

(Note that the ultimate goal is to merge side into main. In reality we can merge main into side, and resolve all merge conflicts first, so that later we can merge side into main without issues.)

Create a new branch merge-main-into-side from side.

git checkout sidegit checkout -b merge-main-into-sidegit merge main

We will see merge conflicts in README.md.

README.md
# Git Sandbox
<<<<<<< HEADLine 1 added on side branch.
Line 2 added on side branch.
Line 3 added on side branch.=======Line 1 added on main branch.
Line 2 added on main branch.
Line 3 added on main branch.>>>>>>> main

Resolve the merge conflicts as follows:

README.md
# Git Sandbox
Line 1 added on side branch.
Line 2 added on side branch. Edited during merge conflicts resolution.
A new line added during merge conflicts resolution.
Line 3 added on main branch.

Add all files, commit. Push to GitHub:

git add -Agit commit # type :wq to save the default commit messagegit push --set-upstream origin merge-main-into-side

Go to GitHub and create a PR#

Create a PR from merge-main-into-side to side. The GitHub UI shows 21 files (README.md + main-*.txt files) changed in this PR.

GitHub PR screenshot

However, what if I don't care about all other files changed on main (e.g. main-*.txt files), and how do I highlight what happens during the merge conflict resolution? (In reality, the main branch would contain all changes people make between now and when you create your feature branch, so often the PR contains a lot of noise)

git show FTW#

Use git show on merge-main-into-side, and we see:

commit 90657bb9626560b3927994c3c2c62647f66039c7 (HEAD -> merge-main-into-side, origin/merge-main-into-side)Merge: af0cba2 9b7efa0Author: davidfeng88 <david.feng.ge@gmail.com>Date:   Sat Jul 24 15:05:02 2021 -0400
    Merge branch 'main' into merge-main-into-sidediff --cc README.mdindex 59b8de1,8ddc204..e597416--- a/README.md+++ b/README.md@@@ -1,7 -1,7 +1,9 @@@  # Git Sandbox -Line 1 added on main branch. +Line 1 added on side branch.- Line 2 added on side branch. -Line 2 added on main branch.++Line 2 added on side branch. Edited during merge conflicts resolution. +- Line 3 added on side branch.++A new line added during merge conflicts resolution.++ Line 3 added on main branch.

The diff only contains README.md, and it shows exactly what happened during the merge conflict resolution:

  • Lines with + or - in the first column is the diff between the merge commit and the first parent (side, sine we are merging main into side). For example, - Line 3 added on side branch means this line was on side but on this merge commit it is deleted.
  • Lines with + or - in the second column is the diff between the merge commit and the second parent (main). For example, -Line 1 added on main branch means this line was on main but on this merge commit it is deleted.
  • Therefore, lines start with ++ are new compared to both side and main, e.g. ++A new line added during merge conflicts resolution..

What's happening under the hood?#

A commit is a snapshot. git show shows a commit as a diff, between this commit and its parent.

In git, you can refer to the parent commit by appending a caret (^) to a commit hash (e.g. git-hash^ is the parent of git-hash). If a commit has two parents, git-hash^ and git-hash^1 both point to the first parent, and git-hash^2 is the second parent.

Merge commits have two parents. GitHub UI shows the diff between the merge commit and the first parent (side). Since main-*.txt files do not exist on the side branch but they are in the merge commit, they appear in the GitHub UI diff.

git show of the merge commit:

  • compares the commit with its first parent
  • compares the commit with its second parent
  • combines the two diffs and show a combined diff

From the doc:

Note that combined diff lists only files which were modified from all parents.

Since main-*.txt files are only modified when compared to side, but not modified when compared to main, they don't appear in the diff git show produces.

Acknowledgements#

Thanks my teammate Gil Broochian for finding this.

StackOverflow Ref