Skip to content
academia.sh

Lesson 08 / 15

Examining Diffs

Three separate comparisons between the working directory, the staging area, and commits; reading the unified diff format, and diffing at the object level.

Contents

The log says which commit touched which file, but it does not show the change’s content. What is more, changes not yet committed never appear in the log at all: an edit in the working directory is outside history until it is committed.

This lesson defines three separate forms of taking a diff and shows how to read diff output. The three-region model earns its practical value here: the question of which two regions are being compared determines which option to write.

Three Comparisons

Command Compared Question it answers
git diff Working directory ↔ staging area What have I not staged yet?
git diff --staged Staging area ↔ last commit What will go into the next commit?
git diff HEAD Working directory ↔ last commit What has changed in total since the last commit?

The third row is the union of the first two. --staged’s synonym is --cached; the former will be used throughout the course.

Working Directory and Staging Area

The search script stays silent when no match is found and exits with status zero. The script is changed to this:

#!/bin/sh
# terimler.txt içinde arama yapar.

if [ $# -ne 1 ]; then
    echo "kullanım: ara.sh ARANAN" >&2
    exit 1
fi

if ! grep -i -- "$1" terimler.txt; then
    echo "eşleşme yok: $1" >&2
    exit 2
fi
git diff
diff --git a/ara.sh b/ara.sh
index b87baa8..582ddde 100755
--- a/ara.sh
+++ b/ara.sh
@@ -6,4 +6,7 @@ if [ $# -ne 1 ]; then
     exit 1
 fi
 
-grep -i -- "$1" terimler.txt
+if ! grep -i -- "$1" terimler.txt; then
+    echo "eşleşme yok: $1" >&2
+    exit 2
+fi

The Anatomy of Diff Output

The output is in unified diff format and has four parts.

The header line. diff --git a/ara.sh b/ara.sh — the two versions being compared are named with the a/ and b/ prefixes. a is the old side, b the new.

The object line. index b87baa8..582ddde 100755 — the old and new blob IDs, together with the file mode. This line records which two objects in the object database the diff was computed between. If the mode is the same on both sides, a single value is written at the end of the line; if the mode has changed, two separate lines are added.

The file lines. --- marks the old version, +++ the new. For a newly created file, the old side is /dev/null.

The hunk header. Every region where a change occurs is called a hunk, and every hunk opens with a header. @@ -6,4 +6,7 @@ — it carries four numbers: 4 lines starting at line 6 of the old file have been replaced by 7 lines starting at line 6 of the new file. The text if [ $# -ne 1 ]; then at the end of the header is the nearest enclosing line that gives the change’s context; it is not part of the diff itself.

Lines inside a hunk begin with one of three marks: a space (an unchanged context line), - (removed), + (added). Context lines default to three; this is changed with the -U option. A modified line appears in diff format as a delete-add pair — there is no “modify” operation in the format.

Staging Area and Last Commit

git add ara.sh
git diff

The command prints nothing: the working directory and the staging area now match. The diff has moved:

git diff --staged
diff --git a/ara.sh b/ara.sh
index b87baa8..582ddde 100755
--- a/ara.sh
+++ b/ara.sh
@@ -6,4 +6,7 @@ if [ $# -ne 1 ]; then
     exit 1
 fi
 
-grep -i -- "$1" terimler.txt
+if ! grep -i -- "$1" terimler.txt; then
+    echo "eşleşme yok: $1" >&2
+    exit 2
+fi

git add did not move the content; it changed which blob the staging area points at. Because the diff itself is computed between the same two objects, the output is letter-for-letter identical.

Two Regions at Once

A line is added to the term list but not staged:

git status --short
M  ara.sh
 M terimler.txt

The meaning of the two columns becomes clear here: ara.sh is marked in the first column (staged), terimler.txt in the second (only in the working directory).

git diff
diff --git a/terimler.txt b/terimler.txt
index 11660ec..17af3b8 100644
--- a/terimler.txt
+++ b/terimler.txt
@@ -4,3 +4,4 @@ kuyruk | queue
 ağaç | tree
 karma tablosu | hash table
 bağlı liste | linked list
+küme | set

git diff shows only the terimler.txt change, git diff --staged only the ara.sh change. To see both at once, the comparison is made against HEAD:

git diff HEAD --stat
 ara.sh       | 5 ++++-
 terimler.txt | 1 +
 2 files changed, 5 insertions(+), 1 deletion(-)

--stat gives a per-file summary instead of line-by-line output. The bar made of plus and minus marks shows the ratio of added to removed lines at scale; for large diffs, the total count is fitted to a fixed width.

The Diff Between Two Commits

When two refs are given, the diff describes the transition from the first to the second:

git diff 13d31bf 60e13bd -- terimler.txt
diff --git a/terimler.txt b/terimler.txt
index 9b9ef70..11660ec 100644
--- a/terimler.txt
+++ b/terimler.txt
@@ -1,3 +1,6 @@
 yığıt | stack
 kuyruk | queue
 çizge | graph
+ağaç | tree
+karma tablosu | hash table
+bağlı liste | linked list

Order matters: git diff B A shows the same diff in the reverse direction. The path separator is used here too, to limit the output to a single file.

Diffing at the Object Level

Comparison is not limited to refs; the ref:path notation names a specific object at that ref:

git diff 13d31bf:README.md 3132c78:README.md
diff --git a/README.md b/README.md
index 49daf90..c0a6e3d 100644
--- a/README.md
+++ b/README.md
@@ -6,4 +6,4 @@ Bilgisayar bilimi terimlerinin Türkçe karşılıklarını tutan küçük bir l
 
 `terimler.txt` dosyasında her satır şu biçimdedir:
 
-    turkce | english
+    türkçe | english

This notation makes the comparison possible even if the file name changed between the two versions.

Word-Level Diff

Line-based diff shows a small change in a long line as the whole line being deleted and re-added. --word-diff improves readability in this case:

git diff --word-diff HEAD -- terimler.txt
diff --git a/terimler.txt b/terimler.txt
index 11660ec..17af3b8 100644
--- a/terimler.txt
+++ b/terimler.txt
@@ -4,3 +4,4 @@ kuyruk | queue
ağaç | tree
karma tablosu | hash table
bağlı liste | linked list
küme | set

The added portion is enclosed in , the removed portion in [- -]. The presentation format has changed; the diff itself and the content to be recorded are the same.

Recording the Changes

The two changes are split into separate commits:

git commit -m "Eşleşme bulunamadığında hata bildir"
git commit -a -m "Küme terimini ekle"
[main 2c883a7] Eşleşme bulunamadığında hata bildir
 1 file changed, 4 insertions(+), 1 deletion(-)
[main ece664b] Küme terimini ekle
 1 file changed, 1 insertion(+)

The first command recorded the staged ara.sh change; the second picked up the remaining tracked change with the -a option.

Summary

  • git diff compares the working directory with the staging area, --staged compares the staging area with the last commit, and the form called with HEAD compares both at once.
  • Unified diff output is made up of header, object, file, and hunk lines; the hunk header gives the starting line and line count on the old and new sides.
  • There is no “modify” operation in the format; a changed line appears as one deletion and one addition.
  • Giving two refs diffs between commits; the ref:path notation diffs between objects.
  • --stat gives a per-file summary, --word-diff gives a word-level display.

Next Step

The repository so far holds only files written by hand. But files also get produced while working: the script’s output, backups left by the editor, build results. Letting these into history is both noise and risk. The next lesson takes up the ignore rules that determine which files are kept out of the repository, and why those rules do not apply to tracked files.

To keep your progress and take notes, Log in

My notes

Log in to take notes.

Start typing to search.

↑↓ Esc navigate · open · close