borgbackup / borgbackup/borg

What's the true `--pattern` equivalent of `--exclude`

Open
#9,654 2 comments 0 reactions 0 assignees View on GitHub
patterns question
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

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.