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 diffcompares the working directory with the staging area,--stagedcompares the staging area with the last commit, and the form called withHEADcompares 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:pathnotation diffs between objects. --statgives a per-file summary,--word-diffgives 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.