Git Basics: Trace a Line to Its Last Changing Commit
When one configuration value looks surprising, inspect the history of that line before reading the entire file history. A two-commit fixture demonstrates a narrow blame query.
Prepare a scratch directory
Tested on Linux with Git 2.52.0 and GNU Bash 5.2.37. You need Bash, a text editor, the named runtime, and standard mktemp, rm, and rmdir utilities. The examples use only controlled local data; generated fixture directories are cleaned automatically.
Keep the same terminal open. Stop if setup fails, before saving the source:
start_dir="$PWD"
work_dir="$(mktemp -d)" || exit 1
cd -- "$work_dir" || exit 1Run the example
Save this as demonstrate-blame.sh:
#!/usr/bin/env bash
set -euo pipefail
export LC_ALL=C
fixture_dir="$(mktemp -d)"
trap 'rm -rf -- "$fixture_dir"' EXIT
export GIT_CONFIG_NOSYSTEM=1
export GIT_CONFIG_GLOBAL=/dev/null
export GIT_AUTHOR_NAME='Example Author'
export GIT_AUTHOR_EMAIL='author@example.invalid'
export GIT_COMMITTER_NAME="$GIT_AUTHOR_NAME"
export GIT_COMMITTER_EMAIL="$GIT_AUTHOR_EMAIL"
export GIT_AUTHOR_DATE='2026-10-28T00:00:00+00:00'
export GIT_COMMITTER_DATE="$GIT_AUTHOR_DATE"
git init -q -b main "$fixture_dir/repo"
cd -- "$fixture_dir/repo"
git config commit.gpgsign false
git config core.hooksPath /dev/null
printf 'timeout=10\nretries=2\n' > service.conf
git add service.conf
git commit -qm 'Set initial options'
printf 'timeout=30\nretries=2\n' > service.conf
git commit -qam 'Increase timeout'
printf 'Last change to line 1:\n'
git blame --line-porcelain -L 1,1 HEAD -- service.conf | sed -n 's/^summary //p'
printf 'Last change to line 2:\n'
git blame --line-porcelain -L 2,2 HEAD -- service.conf | sed -n 's/^summary //p'
test "$(git blame --line-porcelain -L 1,1 HEAD -- service.conf | sed -n 's/^summary //p')" = 'Increase timeout'
printf 'Current line:\n'
git show HEAD:service.conf | head -1Run it:
bash demonstrate-blame.shObserved output:
Last change to line 1:
Increase timeout
Last change to line 2:
Set initial options
Current line:
timeout=30Read the result
The line range limits blame to one current line. Porcelain output includes a summary field, which the example extracts without depending on abbreviated hashes or dates. The unchanged second line still points to the initial commit.
Apply it carefully
Blame identifies the last attributed change, not the person responsible for a defect or the reason for a decision. Read that commit and its surrounding discussion. Deleted lines require history searches instead; line numbers refer to the selected revision. Run this script from an ordinary shell with no repository-selection variables such as GIT_DIR or GIT_WORK_TREE set. All commits and cleanup stay inside the newly created temporary fixture.
The official reference describes the behavior and options. These checks cover the fixture above rather than every input or platform.
Clean up
Remove the saved source, return to the starting directory, then remove the empty practice directory:
rm -- demonstrate-blame.sh
cd -- "$start_dir" || exit 1
rmdir -- "$work_dir"
unset start_dir work_dirIf the directory is not empty, inspect its remaining contents rather than deleting unknown files.
Acknowledgements
This post was created with assistance from AI.