Deepen PracticeOrdered learning track

Blame, Log, Pickaxe, and Code Archaeology

Learn Git In Action - Part 080

Code archaeology dengan git blame, git log, pickaxe -S/-G, line history, rename detection, first-parent, dan forensic workflow untuk memahami mengapa kode berubah.

12 min read2289 words
PrevNext
Lesson 80126 lesson track69–103 Deepen Practice
#git#version-control#blame#git-log+5 more

Part 080 — Blame, Log, Pickaxe, and Code Archaeology

Part 079 membahas git bisect sebagai causal search.

bisect menjawab:

Commit mana yang pertama mengubah property dari good menjadi bad?

Part ini menjawab pertanyaan berbeda:

Mengapa kode ini ada? Kapan berubah? Siapa mengubahnya? Apa konteks keputusan saat itu? Apakah perubahan ini bagian dari refactor, bug fix, migration, hotfix, security patch, atau accidental drift?

Inilah code archaeology.

Tools utama:

git blame
git log
git show
git diff
git grep
git log -S
git log -G
git log -L

Tujuan bukan mencari orang untuk disalahkan.

Tujuan adalah membangun timeline teknis yang bisa dipercaya.


1. Core Mental Model

Git menyimpan snapshot dan commit graph.

Git tidak menyimpan “file identity” sebagai entitas permanen independen dari path.

Git tidak menyimpan “function history” sebagai konsep first-class.

Saat Anda bertanya:

git blame src/PolicyEngine.java

Git menghitung, dari history, commit mana yang terakhir mengubah setiap line.

Saat Anda bertanya:

git log -S 'hasSupervisorOverride'

Git mencari commit yang mengubah jumlah kemunculan string tersebut dalam diff.

Saat Anda bertanya:

git log -G 'hasSupervisorOverride\('

Git mencari commit whose patch lines match regex.

Ini bukan database semantik.

Ini toolkit untuk merekonstruksi sejarah dari snapshots dan diffs.


2. Code Archaeology Questions

Pertanyaan yang bagus:

  • Kapan rule ini pertama kali ditambahkan?
  • Apakah perubahan ini bug fix atau feature?
  • Apakah line ini pernah dipindahkan dari file lain?
  • Apakah behavior ini berubah saat migration tertentu?
  • Commit mana yang menghapus guard clause?
  • Apakah perubahan ini masuk via PR tertentu?
  • Apakah perubahan ini sudah ada di release tag tertentu?
  • Apakah line ini berasal dari mechanical refactor?
  • Apakah culprit sebenarnya di config, lockfile, generated code, atau dependency?
  • Apakah comment lama masih menjelaskan invariant saat ini?

Pertanyaan yang buruk:

  • Siapa yang salah?
  • Kenapa orang itu menulis ini?
  • Kenapa Git tidak otomatis menjelaskan semua intent?

git blame memberi pointer.

Anda tetap harus membaca konteks.


3. git blame: Last Modifier per Line

Basic:

git blame src/main/java/com/acme/PolicyEngine.java

Output roughly:

2f4a8c91 (Ayu 2026-04-12 14:20:11 +0700 102) if (actor.hasRole(SUPERVISOR)) {
9c10deaa (Bima 2026-05-03 09:18:40 +0700 103)     return Decision.allow();

Meaning:

line 102 was last changed by commit 2f4a8c91
line 103 was last changed by commit 9c10deaa

Not meaning:

that author invented the rule
that author owns the bug
that commit is causal
that line was not moved from elsewhere

Blame is starting point, not verdict.


4. Blame a Specific Range

Avoid blaming entire large file.

Use line ranges:

git blame -L 90,130 -- src/main/java/com/acme/PolicyEngine.java

Function-like range:

git blame -L :evaluateEscalation -- src/main/java/com/acme/PolicyEngine.java

Depending on language and hunk header detection, function matching can vary.

If function range is unreliable, use explicit lines.

With commit details:

git blame -L 90,130 --show-email --date=iso -- src/main/java/com/acme/PolicyEngine.java

With commit summary:

git blame -L 90,130 --line-porcelain -- src/main/java/com/acme/PolicyEngine.java \
  | sed -n '/^summary /p' \
  | sort \
  | uniq -c

5. Ignore Mechanical Formatting Commits

Mechanical formatting destroys blame signal.

Example:

commit A: adds authorization rule
commit B: formats entire file

Naive blame points to B.

Use ignore revs.

Create .git-blame-ignore-revs:

# 2026-03-12: apply spotless formatting to Java files
abc1234def5678...

# 2026-05-01: normalize import ordering
fedcba987654...

Run:

git blame --ignore-revs-file .git-blame-ignore-revs \
  -L 90,130 -- src/main/java/com/acme/PolicyEngine.java

Configure default:

git config blame.ignoreRevsFile .git-blame-ignore-revs

Team rule:

If you run mechanical formatting, put the commit hash in .git-blame-ignore-revs and keep formatting commit separate from behavior changes.

This is why atomic commits matter.


6. Follow Renames and Moved Lines

Git can follow whole-file renames in blame.

For moved/copied lines, use -M and -C.

git blame -M -C -C -L 90,130 -- src/main/java/com/acme/PolicyEngine.java

Mental model:

  • -M tries to detect moved/copied lines within a file;
  • -C tries harder across files;
  • repeated -C increases cross-file copy/move detection scope.

Do not blindly trust one run.

Compare:

git blame -L 90,130 -- file.java
git blame -M -C -C -L 90,130 -- file.java

If results differ, inspect both histories.

Rename/copy detection is heuristic.

It is useful, not absolute.


7. Blame Tells Last Touch, Pickaxe Finds Add/Delete History

git blame cannot tell you about lines that were deleted or replaced.

If a guard clause disappeared, blame current file may show nothing.

Use pickaxe.

Example removed line:

if (!actor.hasPermission(CASE_ESCALATE)) throw new ForbiddenException();

Search exact string history:

git log --all -S 'CASE_ESCALATE' -- src/main/java

Show patches:

git log --all -p -S 'CASE_ESCALATE' -- src/main/java

Search regex in patch:

git log --all -p -G 'hasPermission\(.*CASE_ESCALATE' -- src/main/java

Key difference:

ToolFinds
git blamelast commit that changed current lines
git log -Scommits where occurrence count of string changed
git log -Gcommits whose patch contains lines matching regex
git log -Levolution of a line/function range

8. -S vs -G Pickaxe

This distinction matters.

-S<string>

Find commits that changed number of occurrences of exact string.

git log -S 'isSupervisorOverrideAllowed' --all -- src/

Good for:

  • symbol introduced/removed;
  • constant added/deleted;
  • function call appears/disappears;
  • config key introduced;
  • feature flag added/removed.

Bad for:

  • expression changed but same symbol count remains;
  • rename without count change in same diff;
  • whitespace/comment-only pattern;
  • regex search.

-G<regex>

Find commits whose patch text matches regex.

git log -G 'isSupervisorOverrideAllowed\s*=\s*true' --all -- config/

Good for:

  • pattern mutation;
  • condition changed;
  • config value changed;
  • regex over patch lines.

Potentially noisier than -S.

Use both when investigating.


9. Archaeology Workflow: Why Is This Line Here?

Start from line:

git blame -M -C -C -L 118,118 -- src/main/java/com/acme/PolicyEngine.java

Get commit:

COMMIT=2f4a8c91

Inspect full commit:

git show --stat --summary "$COMMIT"
git show --find-renames --find-copies "$COMMIT"
git show --format=fuller --no-patch "$COMMIT"

Inspect surrounding history:

git log --oneline --decorate --graph "$COMMIT"~5.."$COMMIT"~5

Better:

git log --oneline --decorate --graph --ancestry-path "$COMMIT"..HEAD -- src/main/java/com/acme/PolicyEngine.java

Find PR/merge context:

git log --merges --oneline --ancestry-path "$COMMIT"..HEAD

Check releases:

git tag --contains "$COMMIT"
git branch --contains "$COMMIT"

Search linked issue/commit trailer:

git show -s --format='%B' "$COMMIT"

Conclusion should include:

  • line origin;
  • surrounding change;
  • reason from commit message/PR if available;
  • release exposure;
  • whether current behavior still matches original invariant.

10. Archaeology Workflow: Who Removed This Guard?

You remember this line used to exist:

requireSupervisorApproval(caseId, actor);

Search:

git log --all -p -S 'requireSupervisorApproval' -- src/

If symbol renamed:

git log --all -p -G 'Supervisor.*Approval|Approval.*Supervisor' -- src/

If file moved:

git log --all --follow -- src/main/java/com/acme/OldPolicyEngine.java

But --follow is single-path oriented and has limitations.

For broader history:

git log --all --name-status --find-renames -- src/main/java/com/acme

Once commit found:

git show --stat --patch <commit>
git show --format=fuller --no-patch <commit>

Then ask:

  • Was guard intentionally removed?
  • Was it replaced by central middleware?
  • Was it removed due to migration?
  • Was it hidden behind feature flag?
  • Did tests change in same commit?
  • Did docs/policy change?
  • Did approval come from CODEOWNERS/security reviewer?

The actual answer may be:

The guard was not removed; it moved into AuthorizationInterceptor in commit X, then that interceptor was bypassed in commit Y.

That is why archaeology chains tools.


11. git log as Query Engine

git log is not only “show commits”.

It is a history query engine.

By path

git log --oneline -- src/main/java/com/acme/authz

By author

git log --author='@company.com' --since='6 months ago'

By message

git log --grep='CVE\|security\|authorization' --all --extended-regexp

By content change

git log -S 'ForbiddenException' --all -- src/

By patch regex

git log -G 'return\s+Decision\.allow\(' --all -- src/

By first-parent release history

git log --first-parent --oneline v2.4.0..main

By ancestry path

git log --ancestry-path <old>..<new>

By merge commits only

git log --merges --first-parent --oneline v2.4.0..main

By non-merge commits only

git log --no-merges --oneline v2.4.0..main

By file status

git log --diff-filter=D --summary -- src/
git log --diff-filter=R --summary -- src/

You should think of git log as a programmable lens over the commit DAG.


12. git show: Commit-Level Inspection

Once you have a commit, inspect it from multiple angles.

git show <commit>

Better for archaeology:

git show --stat --summary <commit>
git show --name-status <commit>
git show --find-renames --find-copies <commit>
git show --word-diff <commit>
git show --color-moved <commit>
git show --format=fuller <commit>

For merge commit:

git show --cc <merge>
git show -m <merge>
git show -m --first-parent <merge>

For commit metadata only:

git show -s --format=fuller <commit>
git show -s --format='%H%n%an <%ae>%n%ad%n%s%n%n%b' <commit>

Read commit message first.

Then read diff.

Then read parent context.


13. Use git log -L for Function/Line Evolution

git log -L traces evolution of a line range or function.

Example:

git log -L :evaluateEscalation:src/main/java/com/acme/PolicyEngine.java

Or explicit lines:

git log -L 90,130:src/main/java/com/acme/PolicyEngine.java

Good for:

  • seeing how a function evolved;
  • reviewing semantic drift;
  • finding when a condition changed;
  • understanding incremental policy relaxation.

Limitations:

  • function detection depends on diff hunk header/language configuration;
  • large refactors can confuse continuity;
  • rename/move may require additional options or manual search;
  • output can be noisy.

Use log -L when you care about evolution of a code region, not just one line’s last touch.


14. Combining blame and show

A practical one-liner:

COMMIT=$(git blame -L 118,118 --porcelain -- src/main/java/com/acme/PolicyEngine.java \
  | sed -n '1s/ .*//p')

git show --stat --patch "$COMMIT"

But do not automate away thinking.

If blame points to formatting commit, use ignore revs.

If blame points to move commit, use -M -C and pickaxe.

If blame points to merge commit, inspect parents.

If blame points to generated file, inspect generator input.


15. First-Parent History for Release Archaeology

When asking “what reached main/release?”, use first-parent.

git log --first-parent --oneline v2.4.0..main

Why?

In a merge-commit workflow, first-parent follows the mainline integration path.

First-parent answers:

Which integration events changed main?

Without first-parent, you see every internal feature commit mixed into main history.

For release notes, incident timeline, and merge queue debugging, first-parent is often the right lens.

For finding internal culprit inside PR, non-first-parent history may be needed.


16. Two-Dot and Three-Dot in Archaeology

Recall Part 046.

For commits reachable from B but not A:

git log A..B

For commits reachable from either side but not both:

git log A...B

For diff from merge-base to B:

git diff A...B

For direct tree diff A vs B:

git diff A..B
# equivalent to git diff A B for diff semantics

In archaeology, wrong range creates wrong story.

Examples:

# What changed on branch since it forked from main?
git diff main...feature

# What commits are on feature but not main?
git log main..feature

# What commits differ between both branches?
git log --left-right main...feature

# What went into release after v2.4.0 along mainline?
git log --first-parent v2.4.0..release/2.5

Be explicit about whether you are asking about commits or tree diff.


17. Finding When a File Was Deleted

git log --diff-filter=D --summary -- path/to/file

If path unknown:

git log --all --diff-filter=D --summary -- '*PolicyEngine*'

Show deletion patch:

git show <delete-commit> -- path/to/file

Recover file from parent:

git show <delete-commit>^:path/to/file > /tmp/file.before-delete

Or restore into working tree:

git restore --source=<delete-commit>^ -- path/to/file

Then inspect why:

git show --format=fuller --stat <delete-commit>

Deletion may be:

  • intentional migration;
  • rename;
  • generated file cleanup;
  • accidental delete;
  • repo split;
  • vendor code removal;
  • security takedown.

Do not infer from deletion alone.


18. Finding Renames

git log --follow -- path/to/current-file

But --follow works on a single path and has limitations.

For rename-heavy investigation:

git log --name-status --find-renames --all -- path/to/area

For specific commit:

git show --name-status --find-renames <commit>

Tune similarity if needed:

git log --find-renames=40% --name-status -- path/to/file

Lower threshold may detect more renames but increase false positives.

Higher threshold may miss heavy rewrite renames.

Best practice:

Commit pure rename separately from content changes.

That preserves archaeology signal.


19. Finding When Config Changed

Config drift often causes production incidents.

Search for key:

git log --all -p -S 'authorization.supervisorOverride' -- config/ deploy/ helm/

Search for value pattern:

git log --all -p -G 'supervisorOverride:\s*true' -- config/ deploy/ helm/

Find releases containing change:

COMMIT=<config-change>
git tag --contains "$COMMIT"
git branch --contains "$COMMIT"

Compare environment folders:

git diff v2.4.0..v2.5.0 -- deploy/prod deploy/staging

Watch for environment-branch anti-pattern:

prod branch has direct config edits not present in main

Then archaeology must include both Git history and deployment history.


20. Finding Dependency-Introduced Behavior

Sometimes no source code changed.

Dependency did.

Search lockfiles/manifests:

git log --all -p -- package-lock.json pnpm-lock.yaml yarn.lock pom.xml build.gradle gradle.lockfile go.mod go.sum

Search dependency name:

git log --all -p -S 'spring-security' -- build.gradle gradle.lockfile pom.xml

Search CVE/security update message:

git log --all --grep='CVE\|security\|dependency\|bump' --extended-regexp

If behavior changed in transitive dependency, culprit may be a lockfile update.

Treat lockfile diff as code review surface.


21. Finding Introduced Secrets or Sensitive Text

For incident response:

git log --all -p -S 'AKIA' -- .
git log --all -p -G 'secret|password|private_key' -- .

But do not paste real secrets into commands stored in shell history.

Safer:

PATTERN_FILE=/tmp/secret-regex.txt
printf '%s\n' 'AKIA[0-9A-Z]{16}' > "$PATTERN_FILE"

git log --all -p -G "$(cat "$PATTERN_FILE")" -- .

For large-scale scanning, use dedicated secret scanning tools.

Git archaeology helps identify:

  • first introduction commit;
  • branches/tags containing secret;
  • removal commit;
  • whether rewrite is needed;
  • who needs to rotate credentials.

Remember:

Removing a secret from latest commit does not remove it from history.


22. Archaeology for Regulated Systems

For enforcement/case management systems, code archaeology must answer evidentiary questions.

Example:

When did rule “case escalation requires supervisor role” change, who approved it, and which release contained it?

Workflow:

# 1. Search code symbol.
git log --all -p -S 'requiresSupervisorRole' -- src/

# 2. Search policy text/config.
git log --all -p -G 'supervisor.*role|role.*supervisor' -- src/ config/ docs/

# 3. Inspect candidate commits.
git show --format=fuller --stat --patch <commit>

# 4. Find merge/integration event.
git log --merges --first-parent --ancestry-path <commit>..main

# 5. Find release exposure.
git tag --contains <commit>

# 6. Find associated review metadata.
git show -s --format='%B' <commit>

Evidence record should include:

  • commit hash;
  • parent hash;
  • author/committer;
  • timestamp;
  • diff summary;
  • full patch or hash of patch;
  • PR/review reference;
  • release tag;
  • runtime artifact hash if available;
  • whether change was policy, implementation, config, or test.

Do not rely only on current code.

Regulatory defensibility depends on traceable change history.


23. Reading Commit Message as Evidence

Good commit messages make archaeology easy.

Bad:

fix stuff

Useful:

Require supervisor approval for cross-team case escalation

Previously, an investigator could escalate a closed case from another team
when they owned an earlier task. This violated escalation rule ER-42.

This changes PolicyEngine to require SUPERVISOR role for cross-team escalation
when case state is CLOSED or UNDER_REVIEW.

Decision record: ADR-0142
Risk: authorization boundary
Tests: AuthorizationBoundaryTest

Archaeology commands:

git show -s --format='%B' <commit>
git log --grep='ER-42\|ADR-0142\|AuthorizationBoundary' --all

Commit messages are part of the engineering record.

They are not decoration.


24. Timeline Reconstruction

Often you need a timeline, not just one commit.

Example:

git log --date=iso --pretty=format:'%ad %h %an %s' \
  --first-parent v2.4.0..main \
  -- src/main/java/com/acme/authz config/security.yml

Include patches for candidates:

git log --date=iso --stat --patch \
  --grep='authz\|authorization\|permission' \
  --extended-regexp \
  v2.4.0..main

For release boundary:

git log --first-parent --oneline v2.4.0..v2.5.0

For a specific symbol:

git log --date=iso --pretty=format:'%ad %h %s' -S 'Permission.CASE_ESCALATE' --all -- src/ config/

Timeline output should separate:

  • observed incident time;
  • commit author time;
  • commit committer time;
  • merge time;
  • release tag time;
  • deployment time.

Git alone does not know deployment time unless your deployment metadata is committed or tagged.


Current code search:

git grep -n 'CASE_ESCALATE'

Search specific revision:

git grep -n 'CASE_ESCALATE' v2.4.0 -- src/

Search all branches is trickier.

Use git log -S or git grep over refs selectively:

for ref in $(git for-each-ref --format='%(refname)' refs/heads refs/remotes); do
  git grep -n 'CASE_ESCALATE' "$ref" -- src/ || true
done

For large repos, be careful: this can be expensive.

Use targeted refs or pathspecs.


26. Archaeology Anti-Patterns

Anti-pattern 1: Blame and stop

git blame file.java
# conclude author caused bug

Wrong.

Blame is a pointer, not proof.

Anti-pattern 2: Ignore merge context

A line may be correct in branch but wrong after merge.

Inspect integration event.

Anti-pattern 3: Trust current filename

File may have moved, split, or merged.

Use rename detection and pickaxe.

Anti-pattern 4: Ignore config and dependencies

Behavior may change without source code diff.

Search config, lockfiles, generated code, deployment manifests.

Anti-pattern 5: Use wrong range

git log A..B and git diff A...B answer different questions.

Anti-pattern 6: Treat timestamps as absolute truth

Author date and committer date differ.

Rebase/cherry-pick can preserve author date but change committer date.

Use both.

Anti-pattern 7: Forget release exposure

A bad commit that never reached release may not be production culprit.

Use:

git tag --contains <commit>
git branch --contains <commit>

27. Practical Query Recipes

Find when string was introduced or removed

git log --all -p -S 'SomeSymbol' -- path/

Find when regex pattern appeared in patch

git log --all -p -G 'Some.*Regex' -- path/

Find current line origin

git blame -M -C -C -L 120,140 -- path/file.java

Trace function evolution

git log -L :functionName:path/file.java

Ignore formatting commits

git blame --ignore-revs-file .git-blame-ignore-revs -L 120,140 -- path/file.java

Find deletion of a file

git log --diff-filter=D --summary -- path/file.java

Find renames

git log --name-status --find-renames -- path/file.java

Inspect merge commit

git show --cc <merge>
git show -m <merge>

Find release exposure

git tag --contains <commit>
git branch --contains <commit>

Find commits touching path between releases

git log --oneline v2.4.0..v2.5.0 -- path/

Find mainline integrations between releases

git log --first-parent --oneline v2.4.0..v2.5.0

28. Mini Lab: Track a Removed Guard

mkdir /tmp/git-archaeology-lab
cd /tmp/git-archaeology-lab
git init

mkdir -p src
cat > src/policy.js <<'EOF'
function canEscalate(user, kase) {
  if (!user.roles.includes('SUPERVISOR')) {
    return false;
  }
  return kase.state === 'OPEN';
}
EOF

git add src/policy.js
git commit -m 'Require supervisor role for escalation'

git tag v1-good

cat > src/policy.js <<'EOF'
function canEscalate(user, kase) {
  // state validation moved to workflow layer
  if (!user.roles.includes('SUPERVISOR')) {
    return false;
  }
  return true;
}
EOF

git add src/policy.js
git commit -m 'Move case state validation out of policy engine'

cat > src/policy.js <<'EOF'
function canEscalate(user, kase) {
  // temporary relaxation for legacy queue migration
  return true;
}
EOF

git add src/policy.js
git commit -m 'Relax escalation during legacy queue migration'

Find current line origin:

git blame src/policy.js

Find when role guard disappeared:

git log -p -S "roles.includes('SUPERVISOR')" -- src/policy.js

Find state check removal:

git log -p -S "kase.state === 'OPEN'" -- src/policy.js

Trace function:

git log -L :canEscalate:src/policy.js

Exercise:

  • Which commit removed state validation?
  • Which commit removed supervisor guard?
  • Which commit is more dangerous?
  • What regression test should be added?
  • Which commit message gives better domain context?

29. Building a Forensic Note

A useful archaeology output is not a pile of command output.

It is a concise, evidence-backed note.

Template:

## Finding

The supervisor-role guard for case escalation was removed in commit `<hash>`.

## Evidence

- `git log -S 'roles.includes('SUPERVISOR')' -- src/policy.js` identifies `<hash>`.
- `git show <hash>` shows the guard removed without replacement in the same file.
- `git tag --contains <hash>` shows the change is included in `v2.5.0` and later.
- No corresponding policy migration appears in `config/` or `docs/` within the same commit.

## Interpretation

The commit message says the relaxation was temporary for legacy queue migration.
No follow-up commit reintroduced the guard.
The current behavior violates escalation rule ER-42.

## Recommended Remediation

- Add regression test for cross-team closed-case escalation.
- Reintroduce authorization guard in PolicyEngine or central interceptor.
- Audit releases `v2.5.0` through `v2.5.3`.

This is the difference between browsing history and doing engineering forensics.


30. Code Archaeology Decision Tree


31. How This Connects to Bisect

bisect finds boundary.

Archaeology explains boundary.

Typical incident flow:

# 1. Find culprit boundary.
git bisect start HEAD v2.4.0 --
git bisect run ./scripts/repro.sh
CULPRIT=$(git rev-parse refs/bisect/bad)
git bisect reset

# 2. Explain culprit.
git show --stat --patch "$CULPRIT"
git show -s --format='%B' "$CULPRIT"

# 3. Find exposure.
git tag --contains "$CULPRIT"
git branch --contains "$CULPRIT"

# 4. Search related symbols/config.
git log --all -p -S 'RelatedSymbol' -- src/ config/

Causal boundary plus historical explanation gives a remediation plan.


32. Team Practices That Improve Archaeology

  • Keep behavior changes separate from formatting/refactor commits.
  • Use meaningful commit messages.
  • Reference issue/ADR/security ticket in commit body or trailers.
  • Put mechanical commit hashes in .git-blame-ignore-revs.
  • Commit pure renames separately from content changes.
  • Avoid mixing lockfile bump, feature, and formatting in one commit.
  • Preserve release tags.
  • Embed commit SHA in runtime artifact.
  • Use first-parent merge commits if you need integration-level release traceability.
  • Keep CODEOWNERS/review metadata tied to sensitive paths.

Good history is not aesthetic.

Good history reduces incident cost.


33. Commands Worth Memorizing

# Current line origin.
git blame -L 10,30 -- path/file

# Better blame across movement/copy.
git blame -M -C -C -L 10,30 -- path/file

# Ignore formatting commits.
git blame --ignore-revs-file .git-blame-ignore-revs -L 10,30 -- path/file

# Exact string add/remove history.
git log --all -p -S 'SomeSymbol' -- path/

# Regex patch history.
git log --all -p -G 'Some.*Regex' -- path/

# Function evolution.
git log -L :functionName:path/file

# Path history.
git log --oneline -- path/

# Rename-aware path history.
git log --name-status --find-renames -- path/

# Single-file follow.
git log --follow -- path/file

# File deletion.
git log --diff-filter=D --summary -- path/file

# First-parent release history.
git log --first-parent --oneline v1.0.0..v1.1.0

# Exposure.
git tag --contains <commit>
git branch --contains <commit>

# Commit detail.
git show --stat --patch --find-renames <commit>

34. References


35. Summary

Code archaeology is disciplined history investigation.

Use:

git blame     -> who last touched current lines
git log -S    -> where exact string count changed
git log -G    -> where patch matched regex
git log -L    -> how a line/function evolved
git show      -> what commit actually did
git tag --contains / branch --contains -> where change is exposed

The goal is not blame.

The goal is explanation.

A top-tier engineer can look at a system, identify a suspicious behavior, reconstruct the relevant historical chain, distinguish mechanical change from semantic change, and produce an evidence-backed remediation plan.

Next part: debugging regressions with history by combining bisect, blame, diff, range-diff, release tags, and runtime metadata into one investigation workflow.

Lesson Recap

You just completed lesson 80 in deepen practice. Use the series map if you want to review the broader track, or continue directly into the next lesson while the context is still warm.

Continue The Track

Keep the momentum while the lesson is still fresh. Move backward for review or continue forward into the next concept.