GeezerVisonThe World Wide Web, restored to 800 pixels of glory. |
| Home|About|Gaming cabinet|Archives|How it works|Starter sites |
GeezerVison is showing zetcode.com — Linux diff and patch Commands - Compare Files and Apply Patches Linux diff and patch Commandslast modified October 23, 2026
The
The examples use GNU diffutils 3.8 and GNU patch 2.7.6. Most of them work
with two small fruit lists and with two versions of a small Python project,
Comparing two files
In its simplest form, diff fruits1.txt fruits2.txt
The default, or normal, format consists of commands: $ diff fruits1.txt fruits2.txt 2c2 < banana --- > blueberry 4a5 > fig Exit status
diff fruits1.txt fruits1.txt; echo "exit: $?" diff fruits1.txt fruits2.txt > /dev/null; echo "exit: $?" diff fruits1.txt missing.txt; echo "exit: $?" diff -q fruits1.txt fruits2.txt diff -s fruits1.txt fruits1.txt
The status is 0 when the files are identical, 1 when they differ, and 2 on
trouble, such as a missing file. Note that a difference is not an error, so
in a script with $ diff fruits1.txt fruits1.txt; echo "exit: $?" exit: 0 $ diff fruits1.txt fruits2.txt > /dev/null; echo "exit: $?" exit: 1 $ diff fruits1.txt missing.txt; echo "exit: $?" diff: missing.txt: No such file or directory exit: 2 $ diff -q fruits1.txt fruits2.txt Files fruits1.txt and fruits2.txt differ $ diff -s fruits1.txt fruits1.txt Files fruits1.txt and fruits1.txt are identical Unified format
The diff -u fruits1.txt fruits2.txt
The header names the old file after $ diff -u fruits1.txt fruits2.txt --- fruits1.txt 2026-10-20 09:15:00.000000000 +0200 +++ fruits2.txt 2026-10-22 16:40:00.000000000 +0200 @@ -1,5 +1,6 @@ apple -banana +blueberry cherry date +fig elderberry Unified diff of source codeThe unified format is easy to read for code changes. diff -u v1/src/app.py v2/src/app.py
By default, each change is shown with three lines of context before and
after it. Changes that are close together share their context and form one
hunk, which is why we get a single hunk here. The context lets
$ diff -u v1/src/app.py v2/src/app.py
--- v1/src/app.py 2026-09-01 10:00:00.000000000 +0200
+++ v2/src/app.py 2026-10-20 14:30:00.000000000 +0200
@@ -1,15 +1,15 @@
import sys
-def greet(name):
- print("Hello " + name)
+def greet(name, greeting="Hello"):
+ print(f"{greeting}, {name}!")
def main():
if len(sys.argv) < 2:
- print("usage: app.py NAME")
+ print("usage: app.py NAME [GREETING]", file=sys.stderr)
return 1
- greet(sys.argv[1])
+ greet(*sys.argv[1:3])
return 0
Number of context lines
The diff -U 1 v1/src/app.py v2/src/app.py
With one line of context, the changes are far enough apart to form two
separate hunks. $ diff -U 1 v1/src/app.py v2/src/app.py
--- v1/src/app.py 2026-09-01 10:00:00.000000000 +0200
+++ v2/src/app.py 2026-10-20 14:30:00.000000000 +0200
@@ -3,4 +3,4 @@
-def greet(name):
- print("Hello " + name)
+def greet(name, greeting="Hello"):
+ print(f"{greeting}, {name}!")
@@ -9,5 +9,5 @@
if len(sys.argv) < 2:
- print("usage: app.py NAME")
+ print("usage: app.py NAME [GREETING]", file=sys.stderr)
return 1
- greet(sys.argv[1])
+ greet(*sys.argv[1:3])
return 0
Context format
The older context format, produced with diff -c fruits1.txt fruits2.txt
Changed lines are marked with $ diff -c fruits1.txt fruits2.txt *** fruits1.txt 2026-10-20 09:15:00.000000000 +0200 --- fruits2.txt 2026-10-22 16:40:00.000000000 +0200 *************** *** 1,5 **** apple ! banana cherry date elderberry --- 1,6 ---- apple ! blueberry cherry date + fig elderberry Side-by-side output
The diff -y -W 40 fruits1.txt fruits2.txt diff -y -W 40 --suppress-common-lines fruits1.txt fruits2.txt
The middle column marks changed lines with $ diff -y -W 40 fruits1.txt fruits2.txt apple apple banana | blueberry cherry cherry date date > fig elderberry elderberry $ diff -y -W 40 --suppress-common-lines fruits1.txt fruits2.txt banana | blueberry > fig Ignoring whitespace
Differences in whitespace and line endings often hide the real changes.
The file diff settings.conf settings-win.conf | cat -A diff --strip-trailing-cr settings.conf settings-win.conf diff --strip-trailing-cr -b -B settings.conf settings-win.conf; echo "exit: $?"
$ diff settings.conf settings-win.conf | cat -A 1,2c1,3$ < host = localhost$ < port = 8080$ ---$ > host = localhost^M$ > port = 8080^M$ > ^M$ $ diff --strip-trailing-cr settings.conf settings-win.conf 2c2,3 < port = 8080 --- > port = 8080 > $ diff --strip-trailing-cr -b -B settings.conf settings-win.conf; echo "exit: $?" exit: 0 Comparing directories
With two directories, diff -r v1 v2 diff -rq v1 v2
For each pair of differing files, $ diff -r v1 v2
diff -r v1/config.ini v2/config.ini
3,4c3,4
< version = 1.0
< debug = true
---
> version = 1.1
> debug = false
Only in v2: docs
diff -r v1/src/app.py v2/src/app.py
4,5c4,5
< def greet(name):
< print("Hello " + name)
---
> def greet(name, greeting="Hello"):
> print(f"{greeting}, {name}!")
10c10
< print("usage: app.py NAME")
---
> print("usage: app.py NAME [GREETING]", file=sys.stderr)
12c12
< greet(sys.argv[1])
---
> greet(*sys.argv[1:3])
$ diff -rq v1 v2
Files v1/config.ini and v2/config.ini differ
Only in v2: docs
Files v1/src/app.py and v2/src/app.py differ
New files and exclusions
By default, files that exist only on one side are just reported. The
diff -ruN v1 v2 | grep -E '^(diff|---|\+\+\+)' diff -rq -x '*.md' -x docs v1 v2
The $ diff -ruN v1 v2 | grep -E '^(diff|---|\+\+\+)' diff -ruN v1/config.ini v2/config.ini --- v1/config.ini 2026-09-01 10:00:00.000000000 +0200 +++ v2/config.ini 2026-10-20 14:30:00.000000000 +0200 diff -ruN v1/docs/CHANGES.md v2/docs/CHANGES.md --- v1/docs/CHANGES.md 1970-01-01 01:00:00.000000000 +0100 +++ v2/docs/CHANGES.md 2026-10-20 14:30:00.000000000 +0200 diff -ruN v1/src/app.py v2/src/app.py --- v1/src/app.py 2026-09-01 10:00:00.000000000 +0200 +++ v2/src/app.py 2026-10-20 14:30:00.000000000 +0200 $ diff -rq -x '*.md' -x docs v1 v2 Files v1/config.ini and v2/config.ini differ Files v1/src/app.py and v2/src/app.py differ Comparing command output
diff <(ls v1) <(ls v2) echo "Hello" | diff -i - <(echo "hello") && echo "equal ignoring case"
The first command compares the directory listings. The same technique
compares sorted files, $ diff <(ls v1) <(ls v2) 1a2 > docs $ echo "Hello" | diff -i - <(echo "hello") && echo "equal ignoring case" equal ignoring case Binary files
For binary files, diff /bin/ls /bin/cat cmp fruits1.txt fruits2.txt
$ diff /bin/ls /bin/cat Binary files /bin/ls and /bin/cat differ $ cmp fruits1.txt fruits2.txt fruits1.txt fruits2.txt differ: byte 8, line 2 git diff for any files
Git's diff works outside of repositories with the git diff --no-index v1/config.ini v2/config.ini git diff --no-index --stat v1 v2
The output is a unified diff with a Git header; the $ git diff --no-index v1/config.ini v2/config.ini
diff --git a/v1/config.ini b/v2/config.ini
index 3b76cb1..f653d59 100644
--- a/v1/config.ini
+++ b/v2/config.ini
@@ -1,4 +1,4 @@
[app]
name = greeter
-version = 1.0
-debug = true
+version = 1.1
+debug = false
$ git diff --no-index --stat v1 v2
{v1 => v2}/config.ini | 4 ++--
/dev/null => v2/docs/CHANGES.md | 5 +++++
{v1 => v2}/src/app.py | 8 ++++----
3 files changed, 11 insertions(+), 6 deletions(-)
Applying a patch to a file
A patch is simply saved diff -u fruits1.txt fruits2.txt > fruits.patch cp fruits1.txt list.txt patch list.txt < fruits.patch diff list.txt fruits2.txt && echo "identical"
When a file name is given, $ diff -u fruits1.txt fruits2.txt > fruits.patch $ cp fruits1.txt list.txt $ patch list.txt < fruits.patch patching file list.txt $ diff list.txt fruits2.txt && echo "identical" identical Patching a directory tree
For a whole project, we create the patch with diff -ruN v1 v2 > update.patch head -n 4 update.patch cp -r v1 project cd project patch -p1 --dry-run < ../update.patch patch -p1 < ../update.patch diff -r . ../v2 && echo "project matches v2"
The file names in the patch start with $ diff -ruN v1 v2 > update.patch $ head -n 4 update.patch diff -ruN v1/config.ini v2/config.ini --- v1/config.ini 2026-09-01 10:00:00.000000000 +0200 +++ v2/config.ini 2026-10-20 14:30:00.000000000 +0200 @@ -1,4 +1,4 @@ $ cp -r v1 project $ cd project $ patch -p1 --dry-run < ../update.patch checking file config.ini checking file docs/CHANGES.md checking file src/app.py $ patch -p1 < ../update.patch patching file config.ini patching file docs/CHANGES.md patching file src/app.py $ diff -r . ../v2 && echo "project matches v2" project matches v2 The wrong strip level
The most common problem when applying patches is a wrong patch -p0 -t --dry-run < ../update.patch
With $ patch -p0 -t --dry-run < ../update.patch can't find file to patch at input line 4 Perhaps you used the wrong -p or --strip option? The text leading up to this was: -------------------------- |diff -ruN v1/config.ini v2/config.ini |--- v1/config.ini 2026-09-01 10:00:00.000000000 +0200 |+++ v2/config.ini 2026-10-20 14:30:00.000000000 +0200 -------------------------- No file to patch. Skipping patch. 1 out of 1 hunk ignored checking file v1/docs/CHANGES.md can't find file to patch at input line 23 Perhaps you used the wrong -p or --strip option? The text leading up to this was: -------------------------- |diff -ruN v1/src/app.py v2/src/app.py |--- v1/src/app.py 2026-09-01 10:00:00.000000000 +0200 |+++ v2/src/app.py 2026-10-20 14:30:00.000000000 +0200 -------------------------- No file to patch. Skipping patch. 1 out of 1 hunk ignored Applying a patch twice
patch -p1 -N < ../update.patch; echo "exit: $?" ls src
Without options, $ patch -p1 -N < ../update.patch; echo "exit: $?" patching file config.ini Reversed (or previously applied) patch detected! Skipping patch. 1 out of 1 hunk ignored -- saving rejects to file config.ini.rej The next patch would create the file docs/CHANGES.md, which already exists! Skipping patch. 1 out of 1 hunk ignored patching file src/app.py Reversed (or previously applied) patch detected! Skipping patch. 1 out of 1 hunk ignored -- saving rejects to file src/app.py.rej exit: 1 $ ls src app.py app.py.rej Reversing a patch
The patch -p1 -R < ../update.patch diff -r . ../v1 && echo "back to v1" ls docs The project is back at version 1. The file that the patch created is removed, and so is its now empty directory. Keeping patches makes it easy to revert changes that turned out to be bad. $ patch -p1 -R < ../update.patch patching file config.ini patching file docs/CHANGES.md patching file src/app.py $ diff -r . ../v1 && echo "back to v1" back to v1 $ ls docs ls: cannot access 'docs': No such file or directory Making backups
The patch -p1 -b < ../update.patch ls src config.ini*
The backups can be compared with the patched files or used to restore
them. The $ patch -p1 -b < ../update.patch patching file config.ini patching file docs/CHANGES.md patching file src/app.py $ ls src config.ini* config.ini config.ini.orig src: app.py app.py.orig Offsets
A patch can still apply when the target file has changed in other places.
We add two lines to the top of sed -i '1i #!/usr/bin/env python3\n"""A greeting program."""' src/app.py patch -p1 < ../update.patch
The hunk expected its text at line 1, but $ sed -i '1i #!/usr/bin/env python3\n"""A greeting program."""' src/app.py $ patch -p1 < ../update.patch patching file config.ini patching file docs/CHANGES.md patching file src/app.py Hunk #1 succeeded at 3 (offset 2 lines). Failed hunksWhen the code that a hunk changes has been modified, the hunk cannot be applied. sed -i 's/print("Hello " + name)/print("Hi " + name)/' src/app.py
diff -U 1 ../v1/src/app.py ../v2/src/app.py > ../app.patch
patch src/app.py < ../app.patch; echo "exit: $?"
cat src/app.py.rej
grep -n 'greet\|usage' src/app.py
We change a line of a fresh copy of version 1 that the patch also changes,
and create a patch with two hunks. The second hunk applies, the first one
fails: it is saved in $ sed -i 's/print("Hello " + name)/print("Hi " + name)/' src/app.py
$ diff -U 1 ../v1/src/app.py ../v2/src/app.py > ../app.patch
$ patch src/app.py < ../app.patch; echo "exit: $?"
patching file src/app.py
Hunk #1 FAILED at 3.
1 out of 2 hunks FAILED -- saving rejects to file src/app.py.rej
exit: 1
$ cat src/app.py.rej
--- app.py 2026-09-01 10:00:00.000000000 +0200
+++ app.py 2026-10-20 14:30:00.000000000 +0200
@@ -3,4 +3,4 @@
-def greet(name):
- print("Hello " + name)
+def greet(name, greeting="Hello"):
+ print(f"{greeting}, {name}!")
$ grep -n 'greet\|usage' src/app.py
4:def greet(name):
10: print("usage: app.py NAME [GREETING]", file=sys.stderr)
12: greet(*sys.argv[1:3])
Best practices for diff and patch
SourceGNU diffutils manual, patch(1) manual page
In this article, we have compared files and directories with
AuthorMy name is Jan Bodnar, and I am a passionate programmer with extensive programming experience. I have been writing programming articles since 2007. To date, I have authored over 1,400 articles and 8 e-books. I possess more than ten years of experience in teaching programming. List all Linux tutorials. |
| You are visitor 01402520 — GeezerVison does not store the pages you look up. Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required. |