From 83eb075e3a955f2b2d0faea694779583549ac051 Mon Sep 17 00:00:00 2001 From: Alexander Neumann Date: Mon, 4 Sep 2017 21:13:20 +0200 Subject: [PATCH] Resolve name collisions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit At the moment when two items to be saved have the same directory name, restic only saves the first one to the repo. Let's say we have a structure like this: dir1 └── subdir └── file dir2 └── subdir └── file When restic is run on `dir1/subdir` and `dir2/subdir`, it will only save the first `subdir`: $ restic backup dir1/subdir dir2/subdir [...] $ restic ls -l latest drwxr-xr-x 1000 100 0 2017-08-27 20:56:39 /subdir -rw-r--r-- 1000 100 17 2017-08-27 20:56:39 /subdir/file That's obviously a bad thing, caused by an early decision to strip the full path to the files/dirs to save and only leave the last directory. This commit partly resolves this by handling colliding names and resolving the conflicts. Restic will now append a counter to the file (`-123`) until the conflict is resolved. So in the example above, we'll end up with the following structure: $ restic ls -l latest drwxr-xr-x 1000 100 0 2017-08-27 20:56:39 /subdir -rw-r--r-- 1000 100 17 2017-08-27 20:56:39 /subdir/file drwxr-xr-x 1000 100 0 2017-08-27 20:56:46 /subdir-1 -rw-r--r-- 1000 100 17 2017-08-27 20:56:46 /subdir-1/file This partly addresses #549 and closes #1179. At first I thought that the obvious correction would be to archive the full path. But it turns out that collisions may still occur: Suppose you have a file named `foo` in the current directory, and the parent directory also contains a file `foo`. Archiving these with restic also causes a collision, since restic strips the `../` from the first file: $ restic backup ../foo foo This also happens with `tar`, which does not handle the collision and will happily archive two files called `foo`. So, the best way forward is to handle name collisions and archive the whole path. The latter will be tackled in a separate PR. --- internal/archiver/archiver.go | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/internal/archiver/archiver.go b/internal/archiver/archiver.go index dd7bea06f..3117ec509 100644 --- a/internal/archiver/archiver.go +++ b/internal/archiver/archiver.go @@ -383,7 +383,22 @@ func (arch *Archiver) dirWorker(ctx context.Context, wg *sync.WaitGroup, p *rest panic("invalid null subtree restic.ID") } } - tree.Insert(node) + + // insert node into tree, resolve name collisions + name := node.Name + i := 0 + for { + i++ + err := tree.Insert(node) + if err == nil { + break + } + + newName := fmt.Sprintf("%v-%d", name, i) + fmt.Fprintf(os.Stderr, "%v: name collision for %q, renaming to %q\n", filepath.Dir(node.Path), node.Name, newName) + node.Name = newName + } + } node := &restic.Node{}