From 0dfdf028852215bf2ac13981fda43cbfca6e63bb Mon Sep 17 00:00:00 2001
From: Alexander Neumann <alexander@bumpern.de>
Date: Sun, 10 Sep 2017 14:34:28 +0200
Subject: [PATCH] Rework pattern excludes

---
 cmd/restic/cmd_backup.go | 26 ++++++++++----------------
 cmd/restic/exclude.go    | 22 +++++++++++++++++++++-
 2 files changed, 31 insertions(+), 17 deletions(-)

diff --git a/cmd/restic/cmd_backup.go b/cmd/restic/cmd_backup.go
index 413dd8862..3ae109085 100644
--- a/cmd/restic/cmd_backup.go
+++ b/cmd/restic/cmd_backup.go
@@ -15,7 +15,6 @@ import (
 	"github.com/restic/restic/internal/archiver"
 	"github.com/restic/restic/internal/debug"
 	"github.com/restic/restic/internal/errors"
-	"github.com/restic/restic/internal/filter"
 	"github.com/restic/restic/internal/fs"
 	"github.com/restic/restic/internal/restic"
 )
@@ -416,39 +415,34 @@ func runBackup(opts BackupOptions, gopts GlobalOptions, args []string) error {
 
 	Verbosef("scan %v\n", target)
 
+	// rejectFuncs collect functions that can reject items from the backup
+	var rejectFuncs []RejectFunc
+
 	// add patterns from file
 	if len(opts.ExcludeFiles) > 0 {
 		opts.Excludes = append(opts.Excludes, readExcludePatternsFromFiles(opts.ExcludeFiles)...)
 	}
 
+	if len(opts.Excludes) > 0 {
+		rejectFuncs = append(rejectFuncs, rejectByPattern(opts.Excludes))
+	}
+
 	if opts.ExcludeCaches {
 		opts.ExcludeIfPresent = append(opts.ExcludeIfPresent, "CACHEDIR.TAG:Signature: 8a477f597d28d172789f06886806bc55")
 	}
 
-	var excludesByFile []RejectFunc
 	for _, spec := range opts.ExcludeIfPresent {
 		f, err := rejectIfPresent(spec)
 		if err != nil {
 			return err
 		}
 
-		excludesByFile = append(excludesByFile, f)
+		rejectFuncs = append(rejectFuncs, f)
 	}
 
 	selectFilter := func(item string, fi os.FileInfo) bool {
-		matched, _, err := filter.List(opts.Excludes, item)
-		if err != nil {
-			Warnf("error for exclude pattern: %v", err)
-		}
-
-		if matched {
-			debug.Log("path %q excluded by a filter", item)
-			return false
-		}
-
-		for _, excludeByFile := range excludesByFile {
-			if excludeByFile(item, fi) {
-				debug.Log("path %q excluded by tagfile", item)
+		for _, reject := range rejectFuncs {
+			if reject(item, fi) {
 				return false
 			}
 		}
diff --git a/cmd/restic/exclude.go b/cmd/restic/exclude.go
index 34d3971f1..294a8e3eb 100644
--- a/cmd/restic/exclude.go
+++ b/cmd/restic/exclude.go
@@ -9,6 +9,8 @@ import (
 	"strings"
 
 	"github.com/restic/restic/internal/debug"
+	"github.com/restic/restic/internal/errors"
+	"github.com/restic/restic/internal/filter"
 	"github.com/restic/restic/internal/fs"
 )
 
@@ -17,6 +19,24 @@ import (
 // should be excluded (rejected) from the backup.
 type RejectFunc func(filename string, fi os.FileInfo) bool
 
+// rejectByPattern returns a RejectFunc which rejects files that match
+// one of the patterns.
+func rejectByPattern(patterns []string) RejectFunc {
+	return func(item string, fi os.FileInfo) bool {
+		matched, _, err := filter.List(patterns, item)
+		if err != nil {
+			Warnf("error for exclude pattern: %v", err)
+		}
+
+		if matched {
+			debug.Log("path %q excluded by a filter", item)
+			return true
+		}
+
+		return false
+	}
+}
+
 // rejectIfPresent returns a RejectFunc which itself returns whether a path
 // should be excluded. The RejectFunc considers a file to be excluded when
 // it resides in a directory with an exclusion file, that is specified by
@@ -24,7 +44,7 @@ type RejectFunc func(filename string, fi os.FileInfo) bool
 // non-nil if the filename component of excludeFileSpec is empty.
 func rejectIfPresent(excludeFileSpec string) (RejectFunc, error) {
 	if excludeFileSpec == "" {
-		return func(string, os.FileInfo) bool { return false }, nil
+		return nil, errors.New("name for exclusion tagfile is empty")
 	}
 	colon := strings.Index(excludeFileSpec, ":")
 	if colon == 0 {