What's the true `--pattern` equivalent of `--exclude`
- Dominant language
- Python
- Stars
- 13.7k
- Forks
- 875
- Avg merge
- 12h 37m
- Merged PRs (30d)
- 199
Description
I'm currently working on refactoring the `borg help patterns` docs. I was wondering: What's the true `--pattern` equivalent of `--exclude`?
Testing with Borg 1.4.4.
According to the current docs (reinforced multiple times), the `--exclude` option matches `-` exclude patterns (`fm:` style, but let's ignore that for now). However, this doesn't seem to be true: In practice it looks more like `--exclude` matches `!` no-recurse exclude patterns.
The main difference between `-` and `!` is that if a directory is excluded with `!`, it isn't traversed by Borg, i.e., the directory's contents are effectively also excluded. This means that if I use a pattern style that doesn't implicitly also match a directory's contents, i.e. `pf:` or `re:`, Borg should exclude the matched directory, but not its contents. This is exactly what I'm seeing with `--pattern` and `-`, but not what I see with `--exclude`. With `--exclude` directory contents are always excluded, too.
Take the following test setup:
```console
$ borg create repo::archive data
$ borg list repo::archive
drwxr-xr-x daniel daniel 0 Sun, 2026-05-17 23:30:25 data
drwxr-xr-x daniel daniel 0 Sun, 2026-05-17 23:30:07 data/foo
drwxr-xr-x daniel daniel 0 Sun, 2026-05-17 23:29:48 data/foo/bar
drwxr-xr-x daniel daniel 0 Sun, 2026-05-17 23:29:53 data/foo/bar/baz
-rw-r--r-- daniel daniel 3 Sun, 2026-05-17 23:29:53 data/foo/bar/baz/42
-rw-r--r-- daniel daniel 4 Sun, 2026-05-17 23:30:00 data/foo/bla
drwxr-xr-x daniel daniel 0 Sun, 2026-05-17 23:30:12 data/foo/Hello
-rw-r--r-- daniel daniel 6 Sun, 2026-05-17 23:30:12 data/foo/Hello/World
-rw-r--r-- daniel daniel 6 Sun, 2026-05-17 23:30:25 data/blubb
```
Let's try a `pf:` exclusion pattern with `--pattern`:
```
$ borg create --pattern='- pf:data/foo/bar' repo::archive1 data
$ borg list repo::archive1
drwxr-xr-x daniel daniel 0 Sun, 2026-05-17 23:30:25 data
drwxr-xr-x daniel daniel 0 Sun, 2026-05-17 23:30:07 data/foo
drwxr-xr-x daniel daniel 0 Sun, 2026-05-17 23:29:53 data/foo/bar/baz
-rw-r--r-- daniel daniel 3 Sun, 2026-05-17 23:29:53 data/foo/bar/baz/42
-rw-r--r-- daniel daniel 4 Sun, 2026-05-17 23:30:00 data/foo/bla
drwxr-xr-x daniel daniel 0 Sun, 2026-05-17 23:30:12 data/foo/Hello
-rw-r--r-- daniel daniel 6 Sun, 2026-05-17 23:30:12 data/foo/Hello/World
-rw-r--r-- daniel daniel 6 Sun, 2026-05-17 23:30:25 data/blubb
```
As expected, `data/foo/bar` is excluded, but the directory's contents (`data/foo/bar/baz` and `data/foo/bar/baz/42`) are not.
Let's try the supposedly same `pf:` exclusion pattern with `--exclude` instead:
```
$ borg create --exclude='pf:data/foo/bar' repo::archive2 data
$ borg list repo::archive2
drwxr-xr-x daniel daniel 0 Sun, 2026-05-17 23:30:25 data
drwxr-xr-x daniel daniel 0 Sun, 2026-05-17 23:30:07 data/foo
-rw-r--r-- daniel daniel 4 Sun, 2026-05-17 23:30:00 data/foo/bla
drwxr-xr-x daniel daniel 0 Sun, 2026-05-17 23:30:12 data/foo/Hello
-rw-r--r-- daniel daniel 6 Sun, 2026-05-17 23:30:12 data/foo/Hello/World
-rw-r--r-- daniel daniel 6 Sun, 2026-05-17 23:30:25 data/blubb
```
The directory's contents are also excluded!
This better matches the behaviour of no-recurse exclusion patterns with `--pattern`:
```
$ borg create --pattern='! pf:data/foo/bar' repo::archive3 data
$ borg list repo::archive3
drwxr-xr-x daniel daniel 0 Sun, 2026-05-17 23:30:25 data
drwxr-xr-x daniel daniel 0 Sun, 2026-05-17 23:30:07 data/foo
-rw-r--r-- daniel daniel 4 Sun, 2026-05-17 23:30:00 data/foo/bla
drwxr-xr-x daniel daniel 0 Sun, 2026-05-17 23:30:12 data/foo/Hello
-rw-r--r-- daniel daniel 6 Sun, 2026-05-17 23:30:12 data/foo/Hello/World
-rw-r--r-- daniel daniel 6 Sun, 2026-05-17 23:30:25 data/blubb
```
This has many implications, like that there's no difference between `pp:` and `pf:` patterns with `--exclude`. It also doesn't matter that with `fm:` and `sh:` patterns Borg will effectively append a `*` when the pattern ends with a `/`. The challenges with `re:` patterns are also wildly different between `--exclude` and `--pattern` then.
What am I missing? Is `--exclude` truly expected to behave like no-recurse exclusions, meaning that the docs are wrong? Or is this a bug and `--exclude` should actually behave like plain `-` exclusions?
Contributor guide
Assessment
This issue has not been assessed yet.