Excluding with --exclude-if-present excludes whole source path

Hi all,

I’ve already created a GH issue while waiting for forum access so I’ll cross post here. TBH I’m not sure if this problem is misunderstanding of restic and it’s expected behavior or an actual problem.

Output of restic version

> restic version
restic 0.18.1 compiled with go1.25.6 X:nodwarf5 on linux/amd64

What backend/service did you use to store the repository?

Network share

Problem description / Steps to reproduce

I have encountered problem where using --exclude-if-present=.git excludes whole source path if .git folder is present in home directory which is parent. I experimented a bit and it’s actually a general problem. If parent of source path has file/dir specified in --exclude-if-present=<file> it will skip whole source.

Example of testdir:

/home/marin/testdir
├── abc.txt
├── file11.txt
├── file1.txt
└── test2
    ├── file2.txt
    ├── .git
    ├── test3
    │   └── file3.txt
    └── test4
        └── file4.txt

I also have directory /home/marin/.git present in home folder.

Expected behavior

2026/03/28 19:01:28 DEBUG starting command: /usr/bin/restic backup --exclude-caches --exclude-if-present=.git  --dry-run --verbose=2 /home/marin/testdir
open repository
no parent snapshot found, will read all files
load index files
start scan on [/home/marin/testdir]
start backup on [/home/marin/testdir]
scan finished in 0.216s: 4 files, 0 B
new       /home/marin/testdir/file1.txt, saved in 0.000s (0 B added)
new       /home/marin/testdir/file11.txt, saved in 0.000s (0 B added)
new       /home/marin/testdir/test2/.git, saved in 0.000s (0 B added)
new       /home/marin/testdir/test2/, saved in 0.001s (0 B added, 0 B stored, 259 B metadata)
new       /home/marin/testdir/abc.txt, saved in 0.003s (0 B added)
new       /home/marin/testdir/, saved in 0.003s (0 B added, 0 B stored, 417 B metadata)
new       /home/marin/, saved in 0.004s (0 B added, 0 B stored, 297 B metadata)
new       /home/, saved in 0.005s (0 B added, 0 B stored, 294 B metadata)

Actual behavior

2026/03/28 19:02:53 DEBUG starting command: /usr/bin/restic backup --exclude-caches --exclude-if-present=.git  --dry-run --verbose=2 /home/marin/testdir
open repository
no parent snapshot found, will read all files
load index files
start scan on [/home/marin/testdir]
start backup on [/home/marin/testdir]
scan finished in 0.233s: 0 files, 0 B
new       /home/marin/, saved in 0.001s (0 B added, 0 B stored, 0 B metadata)
new       /home/, saved in 0.002s (0 B added, 0 B stored, 293 B metadata)

Do you have any idea what may have caused this?

Presence of /home/marin/.git. Removing this folder generates expected file list.

Per documentation:
--exclude-if-present foo Specified one or more times to exclude a folder’s content if it contains a file called foo (optionally having a given header, no wildcards for the file name supported)

I interpret from docs that restic will exclude only folder’s content in which a file called foo is present. It should not exclude whole source path if file is present in one of the parent folders.

The docs are a bit ambiguous on this point. I can see them being read either way. In particular, the way the docs are written can be interpreted that because /home/marin contains .git that its excluded from the backup, which is presumably the logic being applied by the software.

However, this is probably a case where the behavior is both intended and unhelpful – I cannot imagine a situation where you would explicitly list a path on the command line and want that to be excluded because something exists in an ancestor directory.

I agree. What’s the best way to suggest change regarding this?

Can we move this post to feature/idea category?

The behavior is by design but you’ve identified a legitimate usability problem. The --exclude-if-present flag checks ancestor directories during traversal, not just the directories being processed within the source path. So if /home/marin has a .git folder and your source path includes /home/marin/, restic encounters .git while descending into the source and excludes the whole thing.

The practical fix is to switch from --exclude-if-present to a regular --exclude with a glob pattern. If your actual goal is to skip .git directories inside projects without excluding the parent, this works cleanly:

--exclude="**/.git"

This excludes any directory literally named .git anywhere in the tree but leaves every parent directory intact. The parent won’t be excluded just because it contains a .git subdirectory.

If you do specifically want the --exclude-if-present behavior for certain subdirectories (for example, using a .nobackup sentinel file to mark directories that should be skipped), you can still combine both: use --exclude-if-present=.nobackup for opt-out directories and --exclude=“**/.git” for the repository exclusions. That way you get the opt-out behavior without the parent exclusion problem.