git log

TL;DR: git log is Git’s primary tool for auditing project history. It traverses the Directed Acyclic Graph (DAG) of commits by starting at the current checkout point (HEAD), reading its parent hashes, and recursively walking backward in time to the initial root commit. It is a powerful search and analysis tool rather than a simple text-based list.


What Does git log Actually Do?

To understand how git log generates your history, you must look at how Git organizes commits. Git does not store your commits in a flat list or array. Instead, it structures them as a Directed Acyclic Graph (DAG).

In this graph:

  • Nodes: These are your commit objects.
  • Edges: These are pointers from a child commit to its parent commit(s). They are directed (they point backward in time) and acyclic (history cannot loop back on itself).
graph RL
    C3[Commit 3<br>HEAD / main] -->|parent| C2[Commit 2]
    C2 -->|parent| C1[Commit 1<br>Root Commit]

When you invoke git log, Git does not scan your entire disk. Instead, it performs a graph traversal starting from a given entry point—by default, the commit pointed to by HEAD (your current location, link git-checkout).

The Traversal Algorithm

  1. Git reads the file .git/HEAD to determine your active branch (e.g., ref: refs/heads/main).
  2. It resolves the active branch reference to its 40-character commit hash by reading .git/refs/heads/main (e.g., 8a4c10...).
  3. It loads the commit object 8a4c10... from the object database (.git/objects/) and extracts the metadata: Author, Committer, Timestamp, Message, and the parent hash.
  4. Git outputs this commit’s details to your console.
  5. Git pushes the parent hash (e.g., 55a420...) to a traversal queue.
  6. The loop repeats: Git pops the next commit hash from the queue, reads it, displays it, and queues its parents.
  7. The traversal halts when a commit has no parent field, which signifies the very first commit of the repository (the root commit).

In the case of a merge commit, the commit object lists multiple parent lines. Git handles this by adding all parent hashes to the queue. Depending on the sorting settings (e.g., chronological, topological, or date-ordered), Git will interleave the commits from the merged branches to reconstruct a unified timeline.


What Happens Inside .git/ (Hands-on exploration)

Let’s build a repository containing multiple commits and branches, then trace exactly how Git parses its files to generate the git log output.

Step 1: Set Up a Multi-Commit Repository

Let’s initialize a repository and make two quick commits on the main branch.

$ mkdir git-log-demo && cd git-log-demo
$ git init
Initialized empty Git repository in /git-log-demo/.git/

$ echo "v1" > file.txt
$ git add file.txt
$ git commit -m "First commit"
[main (root-commit) 5be36ab] First commit

$ echo "v2" > file.txt
$ git add file.txt
$ git commit -m "Second commit"
[main ae39f81] Second commit

Step 2: Create a Branch and a Merge Commit

Now, let’s create a branch named feature (link git-branch), make a commit, switch back, and merge it.

$ git checkout -b feature
Switched to a new branch 'feature'

$ echo "feature-work" > feature.txt
$ git add feature.txt
$ git commit -m "Feature commit"
[feature c4f8101] Feature commit

$ git checkout main
Switched to branch 'main'

$ git merge feature -m "Merge feature branch"
Merge made by the 'ort' strategy.
 feature.txt | 1 +
 1 file changed, 1 insertion(+)
 create mode 100644 feature.txt

Step 3: Resolving refs and cata-logging the commits

Let’s see where HEAD points right now:

$ cat .git/HEAD
ref: refs/heads/main

Now let’s see which commit hash main points to:

$ cat .git/refs/heads/main
6df8d0354178de3d038fa61dfb63e8a157580b33

This is our merge commit. Let’s look at this commit object using the plumbing command git cat-file:

$ git cat-file -p 6df8d0354178de3d038fa61dfb63e8a157580b33
tree e7c3909ea1dc725f187e1451f2ad490e5b0213ef
parent ae39f81ca99d45e5b619d854cf78d1f2a36b1d2e
parent c4f810129a8a72cf5a1db5b2f270a2936274b1e2
author Purna Pattela <purna@example.com> 1772960500 +0000
committer Purna Pattela <purna@example.com> 1772960500 +0000

Merge feature branch

Notice that because this is a merge commit, it has two parents:

  • parent ae39f81... (the last commit on main before the merge).
  • parent c4f8101... (the commit from the feature branch).

If we run git log, Git will output the merge commit (6df8d03), then read its parents. It traverses down the parent ae39f81 and c4f8101 in chronological order. Let’s inspect ae39f81:

$ git cat-file -p ae39f81ca99d45e5b619d854cf78d1f2a36b1d2e
tree b4f93dc52c0dfa6c7f8a9a2a6dfba1d2a36b567d
parent 5be36abc0e9df7f2a7db2e9b0cf2a3d2427a1d1e
author Purna Pattela <purna@example.com> 1772960400 +0000
committer Purna Pattela <purna@example.com> 1772960400 +0000

Second commit

This commit points to the initial parent 5be36ab.... Let’s inspect 5be36ab:

$ git cat-file -p 5be36abc0e9df7f2a7db2e9b0cf2a3d2427a1d1e
tree f3b3a3c9e6db5832a875a6c11d2b270a2936274b
author Purna Pattela <purna@example.com> 1772960300 +0000
committer Purna Pattela <purna@example.com> 1772960300 +0000

First commit

Since 5be36ab... has no parent line, Git knows this is the root of our tree and stops the traversal. The resulting log output lists all four commits in reverse chronological order.


Detailed Command Options & Advanced Usage

The default output of git log is verbose and sequential. You can format, filter, and trace history using flags.

1. Advanced Formatting Options

--oneline

Condenses each commit to a single line containing the abbreviated commit hash and the subject line. This is ideal for scanning commit histories.

$ git log --oneline
6df8d03 Merge feature branch
c4f8101 Feature commit
ae39f81 Second commit
5be36ab First commit

--graph --decorate

Plots an ASCII-art representation of the branch execution paths alongside branch labels (HEAD, branch names, tags).

$ git log --graph --oneline --decorate --all
*   6df8d03 (HEAD -> main) Merge feature branch
|\  
| * c4f8101 (feature) Feature commit
* | ae39f81 Second commit
|/  
* 5be36ab First commit

Custom Formats (--pretty=format:)

You can design your own output templates using placeholders. For example, to print only the hash, author name, date, and commit message:

$ git log --pretty=format:"%h - %an (%ar): %s"
6df8d03 - Purna Pattela (2 minutes ago): Merge feature branch
c4f8101 - Purna Pattela (5 minutes ago): Feature commit

Key placeholders include:

  • %h: Abbreviated commit hash.
  • %an: Author name.
  • %ar: Author date (relative, e.g. “3 days ago”).
  • %ad: Author date (absolute format).
  • %s: Subject line (commit message first line).
  • %d: Ref decorations (e.g. (HEAD -> main)).

2. Forensic History Filtering

Searching by code changes (The Pickaxe -S)

If you want to find out which commit introduced or removed a specific text string (like a function name or API endpoint) from your codebase, use the -S flag:

$ git log -S "payment_processor_v2" --oneline

This is called the Pickaxe. It searches the contents of the files at each commit state, rather than just searching commit message text.

Searching by commit message (--grep)

To search only the commit messages for a keyword:

$ git log --grep="bugfix"

Filtering by path

To see only commits that modified a specific file or folder, append a double-dash followed by the file path:

$ git log --oneline -- src/utils/helper.js

Real-World Scenario: Debugging a Regression

Imagine your team deploys a new version of an application, and users report that the application is crashing when they click the “Submit” button. You know the button worked fine a week ago.

To find the culprit, you can combine history filters:

# Step 1: List all commits from the last week that modified the checkout button component
$ git log --since="1 week ago" --oneline -- src/components/SubmitButton.js
a3d5f9c refactor: rewrite submit click handler
54f8b9e chore: update button styling

# Step 2: Show the actual code modifications in the suspicious commit (a3d5f9c)
$ git log -p -1 a3d5f9c

Using the patch flag -p, you review the exact changes in the handler function, spot the missing event parameter, and write a hotfix.


Common Pitfalls and Mistakes

1. Getting Trapped in the Pager

By default, if the log output is longer than your screen height, Git pipes the output to a pager utility (typically less). Beginners often don’t know how to return to their normal terminal.

  • The Fix: Press q to quit the pager.
  • Tip: If you want Git to print the output directly to the terminal without opening a pager, set the pager config to cat:
$ git --no-pager log -n 5

2. Missing Commits from Other Branches

Running git log only shows commits that are reachable from your current branch’s history. If you are on main and someone else pushed changes to feature-b, you will not see their commits.

  • The Fix: Use the --all flag to show all commits on all branches:
$ git log --oneline --all --graph

3. Confusing git log with git reflog

If you rebase (link git-rebase) or reset commits, those commits are removed from the branch history and will no longer show up in git log.

  • Why: git log only traverses active pointers.
  • The Fix: Use git reflog. The reference log tracks every single change made to the HEAD pointer on your local machine (including checkout switches, amends, resets, and rebases). It is your ultimate safety net for recovering “deleted” commits.

Command Quick Reference

CommandAction
git logShows full commit history (hashes, author, date, message).
git log --onelineShows a compact, single-line summary per commit.
git log --graph --oneline --allDisplays a visual tree of all branches and merges.
git log -pShows the patch (code diff) introduced by each commit.
git log -n <limit>Limits the output to the specified number of commits (e.g. -n 5).
git log --author="Name"Filters history by author name.
git log --grep="pattern"Filters commits by keywords in the message.
git log -S "code"Finds commits adding or removing a specific line/word of code.
git log --no-mergesExcludes merge commits from the history log.

FAQ (People Also Ask)

Why are my commits not showing up in git log?

If you just ran git commit but it doesn’t appear in the log, check the following:

  1. Staging check: Did you run git add before committing? If not, the commit may have been rejected because nothing was staged.
  2. Detached HEAD: If you are in a detached HEAD state, your commits aren’t associated with a branch. If you checkout main, they will seem to disappear from your log. Run git reflog to find the missing hashes.

What is the difference between Author Date and Committer Date?

  • Author Date: The timestamp of when the commit was originally created (e.g., using git commit).
  • Committer Date: The timestamp of when the commit was last written to the repository (e.g., when it was cherry-picked, rebased, or amended).
  • By default, git log displays the Author Date. To see the Committer Date, use: git log --pretty=fuller.

How do I see who changed a specific line in a file?

While git log -S can find the commit that introduced a string, git blame <file> is the fastest way to view line-by-line authorship. It displays the commit hash, author name, and timestamp for every single line of a file.

How can I search the commit history for a specific date range?

You can combine --since (or --after) and --until (or --before) flags:

$ git log --since="2026-06-01" --until="2026-07-01" --oneline

Git also parses relative human dates, such as --since="3 weeks ago" or --until="yesterday".


Test Your Knowledge

Loading quiz…