Before you touch git, you need to see what git is doing underneath. The mechanism is older than git — a pair of Unix tools that have been part of every C workflow since the 1970s.
Create two versions of a small C program. The line numbers are shown
here because diff reports changes by line number, and you will need
to follow along.
The original counts from zero:
#include <stdio.h>
int main(void)
{
int i;
for (i = 0; i < 10; i++)
printf("%d\n", i);
return (0);
}The fixed version counts from one:
#include <stdio.h>
int main(void)
{
int i;
for (i = 1; i <= 10; i++)
printf("%d\n", i);
return (0);
}Run diff -u to see exactly what changed:
diff -u counter.c counter_fixed.cThe output is a unified diff:
--- counter.c 2026-04-19 10:00:00 +0000
+++ counter_fixed.c 2026-04-19 10:01:00 +0000
@@ -4,7 +4,7 @@
{
int i;
- for (i = 0; i < 10; i++)
+ for (i = 1; i <= 10; i++)
printf("%d\n", i);
return (0);
}Reading the format:
---and+++name the old and new files.@@ -4,7 +4,7 @@is the hunk header: starting at line 4 in the old file, the hunk covers 7 lines; starting at line 4 in the new file, it also covers 7 lines. Line 4 in the numbered source above is the opening brace{.- Lines prefixed with
-were removed. Lines prefixed with+were added. Lines with neither are context —diff -uincludes three lines of context before and after each change by default.patchuses them to locate the right position even when other edits have shifted line numbers elsewhere in the file.
This is a complete, unambiguous description of the change.
Save it as a patch file:
diff -u counter.c counter_fixed.c > fix.patchApply it:
patch counter.c < fix.patchpatch reads the hunk header, locates the context lines in
counter.c, and applies the changes. Open counter.c — it now
counts from one. diff described the change; patch applied it.
How real projects use this
The suckless[1] team has built minimal Unix tools — dwm, st,
dmenu — since the mid-2000s. Their philosophy is that software should be
simple enough to read and modify by hand. None of their programs
have configuration files or plugin systems. You edit the source and
recompile. All optional features are distributed as .patch files
on their website — the same format you just produced.
This is not an old approach that git replaced. Plenty of C projects
still distribute changes as patch files because a .patch file is
self-contained and reviewable: you can read exactly what it will do
before applying it. Open any .patch file from suckless.org and you
will see ---, +++, @@, context lines, -, +. The format is
the same. The only difference from fix.patch is scale.
The connection to git
A git commit records a change along with information like who made it
and when. git format-patch turns a commit into a .patch file.
git apply applies a patch. When you run git log -p and see + and
- lines next to your history, you are looking at the same format.
Git automates what you have just done by hand. It keeps a history of changes in commits, and that history can be reorganized later when needed. In the c-tier chapters ahead, the commit history will be the article: every technique gets its own commit so the reader can follow the construction step by step.