borg-diff(1)
๐ borg-diff โ the "what even changed between these two archives" command ๐
okay bestie, sit down, hydrate, and let me tell you about borg-diff. this is the Borg backup tool's little diff detective, and it's about to make comparing two archives your new favorite hobby. no cap. ๐ต๏ธโ๏ธโจ
๐ธ NAME ๐ธ
borg-diff โ finds differences between two archives. literally its whole personality. ๐
๐ฆ SYNOPSIS ๐ฆ
borg [common options] diff [options] ARCHIVE1 ARCHIVE2 [PATH...]
๐ DESCRIPTION ๐
bestie. this command finds the differences โ file contents AND metadata โ between ARCHIVE1 and ARCHIVE2. that's it, that's the mission. ๐
and if you need more help on include/exclude patterns (iykyk, patterns can be a whole thing), go look at the output of the borg_patterns command. it's giving tutorial. ๐
โจ OPTIONS โจ
for the common options that every Borg command has, go peek at borg-common(1). not me skipping that, it's just literally elsewhere. ๐ซถ
๐ซ arguments ๐ซ
ARCHIVE1โ theARCHIVE1name. yeah, it's the first archive. she's first for a reason. ๐ARCHIVE2โ theARCHIVE2name. the sequel. the "after" picture. ๐PATHโ paths of items inside the archives that you wanna compare; patterns are supported. so you can be picky, and honestly? slay. ๐
๐ options ๐
--numeric-idsโ only consider numeric user and group identifiers. no name-matching, just digits, periodt. ๐ข--same-chunker-paramsโ override the check of chunker parameters. when you wanna tell borg "yes babe I know, trust me". ๐--format FORMATโ specify the format for differences between archives. default is"{change} {path}{NL}". very customizable, very main character. โจ--json-linesโ format output as JSON Lines. one JSON object per line, machine-readable, iconic. ๐ค-s,--statsโ print a summary of the differences at the end. the recap. the little wrap-up montage. ๐--sort-byโ sort output by comma-separated fields (e.g.,'>size_added,path'). your order, your rules. ๐ฅ--content-onlyโ only compare differences in content (exclude metadata differences). metadata girlies, look away. ๐ญ
๐ง Include/Exclude options ๐ง
-e PATTERN,--exclude PATTERNโ exclude paths matchingPATTERN. bye bestie, not you. ๐--exclude-from EXCLUDEFILEโ read exclude patterns fromEXCLUDEFILE, one per line. bulk exclusion, so efficient. ๐--pattern PATTERNโ include/exclude paths matchingPATTERN. it does both, she's versatile. ๐--patterns-from PATTERNFILEโ read include/exclude patterns fromPATTERNFILE, one per line. the whole list, babes. ๐
๐ EXAMPLES ๐
okay okay okay, watch this. ๐
# By default, borg diff reports metadata changes (mode, mtime, ctime, ...) besides
# the content changes, so the lines can get quite long:
$ borg diff archive1 archive2
[mtime: Fri, 2026-08-28 12:26:23.433047048 +0200 -> Fri, 2026-08-28 12:26:24.204040020 +0200] [ctime: Fri, 2026-08-28 12:26:23.433047048 +0200 -> Fri, 2026-08-28 12:26:24.204040020 +0200] .
modified: +17 B -5 B [-rw-r--r-- -> -rwxr-xr-x] [mtime: Fri, 2026-08-28 12:26:23.431245244 +0200 -> Fri, 2026-08-28 12:26:24.199251801 +0200] [ctime: Fri, 2026-08-28 12:26:23.431245244 +0200 -> Fri, 2026-08-28 12:26:24.200688313 +0200] file1
modified: +135 B -252 B [mtime: Fri, 2026-08-28 12:26:23.432895166 +0200 -> Fri, 2026-08-28 12:26:24.202236091 +0200] [ctime: Fri, 2026-08-28 12:26:23.432895166 +0200 -> Fri, 2026-08-28 12:26:24.202236091 +0200] file2
added: 0 B file4
removed: 0 B file3
# --content-only hides the metadata changes and only reports content differences:
$ borg diff --content-only archive1 archive2
modified: +17 B -5 B file1
modified: +135 B -252 B file2
added: 0 B file4
removed: 0 B file3
# Use --json-lines to get one JSON object per changed path:
$ borg diff --json-lines --content-only archive1 archive2
{"changes": [{"added": 17, "removed": 5, "type": "modified"}], "path": "file1"}
{"changes": [{"added": 135, "removed": 252, "type": "modified"}], "path": "file2"}
{"changes": [{"added": 0, "removed": 0, "type": "added"}], "path": "file4"}
{"changes": [{"added": 0, "removed": 0, "type": "removed"}], "path": "file3"}
# Use --sort-by with a comma-separated list; sorts apply stably from last to first.
# Here: primary by net size change descending, tie-breaker by path ascending
$ borg diff --content-only --sort-by=">size_diff,path" archive1 archive2
modified: +17 B -5 B file1
removed: 0 B file3
added: 0 B file4
modified: +135 B -252 B file2
see how the default one just keeps going and going with the metadata? that's her being thorough, not messy. thorough. ๐ฅ
๐ NOTES ๐
๐ The FORMAT specifier syntax ๐
the --format option uses Python's format string syntax: https://docs.python.org/3.11/library/string.html#formatstrings
examples, because we love a demo:
$ borg diff --format '{content:30} {path}{NL}' ArchiveFoo ArchiveBar
modified: +4.1 kB -1.0 kB file-diff
...
# {VAR:<NUMBER} - pad to NUMBER columns left-aligned.
# {VAR:>NUMBER} - pad to NUMBER columns right-aligned.
$ borg diff --format '{content:>30} {path}{NL}' ArchiveFoo ArchiveBar
modified: +4.1 kB -1.0 kB file-diff
...
the following keys are always available (always, bestie, no matter what):
NEWLINE: OS dependent line separatorNL: alias ofNEWLINENUL: NUL character for creatingprint0/xargs -0like outputSPACE: space characterTAB: tab characterCR: carriage return characterLF: line feed character
keys available only when showing differences between archives:
path: archived file pathchange: all available changescontent: file content changemode: file mode changetype: file type changeowner: file owner (user/group) changegroup: file group changeuser: file user changelink: file link changedirectory: file directory changeblkdev: file block device changechrdev: file character device changefifo: file fifo changemtime: file modification time changectime: file change time changeisomtime: file modification time change (ISO 8601)isoctime: file creation time change (ISO 8601)
๐ What is compared ๐
for each matching item in both archives, Borg reports:
- content changes: total added/removed bytes within files. if chunker parameters are comparable, Borg compares chunk IDs quickly โ it aligns the two chunk lists of a file like a text diff aligns lines, and the byte counts are the total sizes of the chunks that are not part of that alignment. inserted, removed, moved and duplicated content is therefore accounted for โ a chunk that only moved within the file shows up as removed and added again. files with very long or very repetitive chunk lists are not aligned (that would be too slow); for these, only the number of occurrences of each chunk ID is compared, so moved content does not show up in their byte counts. if chunker parameters are not comparable, Borg compares the content. in that case, borg can only tell that a file was modified, not by how much: no byte counts are given for it, and the text output shows
modified: (can't get size)instead. ๐ญ - metadata changes: user, group, mode, and other metadata shown inline as
"[old -> new]", like"[-rw-r--r-- -> -rwxr-xr-x]"for a mode change. use--content-onlyto suppress metadata changes. ๐ - added/removed items: printed as
"added: SIZE path"or"removed: SIZE path". ๐
๐ Output formats ๐
the default (text) output shows one line per changed path, e.g.:
modified: +23 B -5 B [-rwxr-xr-x -> -rw-r--r--] path/to/file
added: 4 B path/to/added-file
removed: 5 B path/to/removed-file
JSON Lines output (--json-lines) prints one JSON object per changed path, with a list of change objects. each change object has a "type" plus type-specific data: content changes ("added", "removed", "modified") carry "added"/"removed" byte counts โ EXCEPT for a "modified" that was determined by comparing the content, which carries no counts at all; metadata changes ("changed mode", "changed owner", "mtime", ...) carry the old and new values as "item1" and "item2". example:
{"changes": [{"added": 23, "removed": 5, "type": "modified"}], "path": "path/to/file"}
{"changes": [{"type": "modified"}], "path": "path/to/other-file"}
{"changes": [{"item1": "-rw-r--r--", "item2": "-rwxr-xr-x", "type": "changed mode"}], "path": "some/file"}
{"changes": [{"added": 4, "removed": 0, "type": "added"}], "path": "path/to/added-file"}
{"changes": [{"added": 0, "removed": 5, "type": "removed"}], "path": "path/to/removed-file"}
๐ Statistics ๐
with --stats, borg prints a summary of the differences after the per-path output:
Added items: 23
Removed items: 2
Changed items: 315
Added size: 53.70 MB
Removed size: 51.10 MB
"Added"/"Removed" items only exist in ARCHIVE2/ARCHIVE1. "changed" items exist in both archives but differ. "Added size"/"Removed size" sum up the file content (in bytes) added/removed by all of these items, i.e. the per-path byte counts that --sort-by size_added / size_removed sort by. items whose content borg could only compare byte by byte (see "Performance considerations" below) contribute no byte counts; if there are any, an additional "Items with unknown size changes" line reports how many. ๐งฎ
together with --json-lines, the summary is emitted as a final JSON line of the shape {"stats": {...}} instead, so it is easy to tell apart from the per-path lines (wrapped here for readability, borg prints it as a single line):
{"stats": {"added_items": 23, "changed_items": 315, "removed_items": 2,
"size_added": 53700000, "size_removed": 51100000, "unknown_size_items": 0}}
๐ฅ Sorting ๐ฅ
use --sort-by FIELDS where FIELDS is a comma-separated list of fields. sorts are applied stably from last to first in the given list. prepend ">" for descending, "<" (or no prefix) for ascending, for example --sort-by=">size_added,path". supported fields include:
path: the item pathsize_added: total bytes added for the item contentsize_removed: total bytes removed for the item contentsize_diff:size_added-size_removed(net content change)size: size of the item as stored inARCHIVE2(0 for removed items)user,group,uid,gid,ctime,mtime: taken from the item state inARCHIVE2when presentctime_diff,mtime_diff: timestamp difference (ARCHIVE2-ARCHIVE1)
โก Performance considerations โก
diff automatically detects whether the archives were created with the same chunker parameters. if so, only chunk IDs are compared, which is very fast. speed demon behavior, we love to see it. ๐๏ธ
๐ SEE ALSO ๐
๐ Author ๐
The Borg Collective. legends, honestly. ๐ซถ
okay that's the whole page bestie, you're officially a borg-diff expert, go forth and diff responsibly ๐โจ this was borg backup tool, borg-diff(1), from 2026-10-07 โ mwah, love u, bye! ๐๐ฆ
๐ฆ where this page comes from
- ubuntu stonking: borgbackup2 2.0.0b25-4 ยท BSD-3-Clause
- ๐ girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms