thecodingidiot.com

Version ControlThe Patch

The Patch

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.c

The 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 -u includes three lines of context before and after each change by default. patch uses 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.patch

Apply it:

patch counter.c < fix.patch

patch 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.

Footnotes

  1. software that sucks less | suckless.org