<!--order=3-->
<h1>Git: Committing and Viewing History</h1>
<p>A commit is a permanent snapshot of the staging area at a point in time. It stores what changed, who changed it, when, and why (the message). The commit log is the history of every snapshot ever taken. Learning to read and navigate that history is as important as knowing how to create commits.</p>

<h2>Commands Covered</h2>
<p><code>git commit</code> · <code>git log</code> · <code>git show</code> · <code>git diff</code> · <code>git describe</code> · <code>git shortlog</code> · <code>git whatchanged</code></p>

<h2>git commit</h2>
<pre>
# Commit staged changes (opens editor for message)
$ git commit

# Commit with an inline message
$ git commit -m "Fix null pointer in login handler"

# Stage all tracked modified files and commit in one step (skips git add)
$ git commit -a -m "Update configuration"
# Note: -a does NOT add new untracked files — only modified tracked files

# Amend the most recent commit (change message or add more files)
$ git commit --amend
$ git commit --amend -m "Better commit message"
# WARNING: amend rewrites history — don't amend commits already pushed to shared branches

# Commit with a longer message (summary + body)
$ git commit
# Write in editor:
# Short summary (50 chars max)
#
# More detailed explanation. Wrap at 72 characters.
# Explain what changed and why, not how (the diff shows how).
#
# Closes #123

# Empty commit (useful for triggering CI)
$ git commit --allow-empty -m "Trigger CI"

# Set a specific author for a commit
$ git commit --author="Name <email@example.com>" -m "message"
</pre>

<h3>Good Commit Messages</h3>
<table>
  <thead><tr><th>Rule</th><th>Example</th></tr></thead>
  <tbody>
    <tr><td>Subject line ≤ 50 chars</td><td><code>Fix race condition in session handler</code></td></tr>
    <tr><td>Use imperative mood</td><td><code>Add</code>, <code>Fix</code>, <code>Remove</code>, <code>Update</code> — not "Added", "Fixed"</td></tr>
    <tr><td>Blank line before body</td><td>Tools treat the first line as a title</td></tr>
    <tr><td>Wrap body at 72 chars</td><td>Terminals and email clients expect this</td></tr>
    <tr><td>Explain why, not how</td><td>The diff explains how; the message explains the reasoning</td></tr>
  </tbody>
</table>

<h2>git log</h2>
<p>Shows the commit history. One of the most-used commands — learn its formatting options.</p>
<pre>
# Default log (most recent first, full info)
$ git log

# One line per commit
$ git log --oneline

# Graph view with branch visualization
$ git log --oneline --graph --decorate --all

# Useful alias (add to ~/.gitconfig):
# [alias]
#   lg = log --oneline --graph --decorate --all

# Log with file change stats
$ git log --stat

# Show the actual diff for each commit
$ git log -p
$ git log --patch

# Limit to last N commits
$ git log -10
$ git log -n 5

# Log since/until a date
$ git log --since="2024-01-01"
$ git log --until="2024-06-01"
$ git log --since="2 weeks ago"

# Filter by author
$ git log --author="Jason"
$ git log --author="@gmail.com"   # partial match works

# Filter by commit message content
$ git log --grep="fix"
$ git log --grep="closes #" -i    # case-insensitive

# Find commits that changed a specific string (the "pickaxe")
$ git log -S "functionName"       # commits that added/removed this string
$ git log -G "regex.*pattern"     # commits where diff matches regex

# Log for a specific file
$ git log -- README.md
$ git log --follow -- src/utils.c  # follow renames

# Show commits reachable from branch A but not branch B
$ git log main..feature
$ git log origin/main..HEAD       # local commits not yet pushed

# Custom format
$ git log --format="%h %an %s"    # short hash, author name, subject
$ git log --format="%ci %s"       # commit date, subject

# Show log for a specific date range and file
$ git log --since="last month" --until="today" -- src/
</pre>

<h2>git show</h2>
<p>Shows a specific Git object — usually a commit's diff and metadata.</p>
<pre>
# Show the most recent commit
$ git show

# Show a specific commit
$ git show abc1234
$ git show HEAD~2        # two commits back from HEAD
$ git show main          # tip of main branch

# Show only the files changed (not the diff content)
$ git show --stat abc1234
$ git show --name-only abc1234
$ git show --name-status abc1234   # with M/A/D status

# Show a specific file as it was at a commit
$ git show abc1234:src/main.c
$ git show HEAD~1:README.md

# Show a tag
$ git show v1.0.0

# Show a tree object (directory listing at a commit)
$ git show HEAD:src/
</pre>

<h2>git diff</h2>
<p>Shows differences between various states. Takes some time to learn all its modes, but it's essential.</p>
<pre>
# Diff working tree vs staging area (unstaged changes)
$ git diff

# Diff staging area vs last commit (staged changes — what will be committed)
$ git diff --staged
$ git diff --cached   # same thing

# Diff working tree vs last commit (staged + unstaged combined)
$ git diff HEAD

# Diff between two commits
$ git diff abc1234 def5678
$ git diff HEAD~3 HEAD

# Diff between two branches
$ git diff main feature-branch

# Diff a specific file only
$ git diff -- src/main.c
$ git diff HEAD -- README.md

# Show only the names of changed files (not the content)
$ git diff --name-only HEAD~5 HEAD
$ git diff --name-status HEAD~5 HEAD    # with M/A/D/R status

# Statistics (files changed, insertions, deletions)
$ git diff --stat HEAD~10 HEAD

# Diff with word-level highlighting instead of line-level
$ git diff --word-diff

# Ignore whitespace changes
$ git diff -w
$ git diff --ignore-all-space
</pre>

<h2>git describe</h2>
<p>Gives a human-readable name to a commit based on the nearest tag. Useful for version strings in build systems.</p>
<pre>
$ git describe
# v1.4-14-g2414721
# Means: tag v1.4, 14 commits since that tag, current commit starts with 2414721

$ git describe --tags           # use any tag, not just annotated ones
$ git describe --always         # always output something even with no tags
$ git describe --abbrev=0       # just the tag name, no extra info

# Typical use in a Makefile or build script:
VERSION := $(shell git describe --tags --always --dirty)
</pre>

<h2>git shortlog</h2>
<p>Summarizes <code>git log</code> output grouped by author. Useful for changelogs and contributor summaries.</p>
<pre>
$ git shortlog
# Outputs commits grouped by author with count

# Show only commit counts per author
$ git shortlog -sn
# Output:
#   143  Jason Smith
#    52  Alice Brown
#     8  Bob Jones

# Summary for a specific range
$ git shortlog -sn v1.0..HEAD

# Include all branches
$ git shortlog -sn --all
</pre>

<h2>git whatchanged</h2>
<p>An older command similar to <code>git log --stat</code> that shows which files changed in each commit. Mostly superseded by <code>git log</code>, but still available.</p>
<pre>
$ git whatchanged
$ git whatchanged -p   # include diffs
$ git whatchanged --since="1 week ago"

# Equivalent modern command:
$ git log --name-status
</pre>

<h2>References</h2>
<ul>
  <li><a href="https://git-scm.com/docs/git-commit" target="_blank" rel="noopener">git-commit documentation ↗</a></li>
  <li><a href="https://git-scm.com/docs/git-log" target="_blank" rel="noopener">git-log documentation ↗</a></li>
  <li><a href="https://git-scm.com/docs/git-diff" target="_blank" rel="noopener">git-diff documentation ↗</a></li>
  <li><a href="https://cbea.ms/git-commit/" target="_blank" rel="noopener">How to Write a Git Commit Message ↗</a></li>
</ul>
