filter: allow multiple --exclude-if-present flags - fixes #6219

This commit is contained in:
albertony 2022-06-08 09:29:01 +02:00
parent 20aaeba547
commit f4f0e444bf
10 changed files with 31 additions and 24 deletions

View File

@ -2136,6 +2136,7 @@ For the filtering options
* `--filter-from` * `--filter-from`
* `--exclude` * `--exclude`
* `--exclude-from` * `--exclude-from`
* `--exclude-if-present`
* `--include` * `--include`
* `--include-from` * `--include-from`
* `--files-from` * `--files-from`

View File

@ -750,7 +750,9 @@ Useful for debugging.
The `--exclude-if-present` flag controls whether a directory is The `--exclude-if-present` flag controls whether a directory is
within the scope of an rclone command based on the presence of a within the scope of an rclone command based on the presence of a
named file within it. named file within it. The flag can be repeated to check for
multiple file names, presence of any of them will exclude the
directory.
This flag has a priority over other filter flags. This flag has a priority over other filter flags.
@ -764,8 +766,6 @@ E.g. for the following directory structure:
The command `rclone ls --exclude-if-present .ignore dir1` does The command `rclone ls --exclude-if-present .ignore dir1` does
not list `dir3`, `file3` or `.ignore`. not list `dir3`, `file3` or `.ignore`.
`--exclude-if-present` can only be used once in an rclone command.
## Common pitfalls ## Common pitfalls
The most frequent filter support issues on The most frequent filter support issues on

View File

@ -47,7 +47,7 @@ These flags are available for every command.
--error-on-no-transfer Sets exit code 9 if no files are transferred, useful in scripts --error-on-no-transfer Sets exit code 9 if no files are transferred, useful in scripts
--exclude stringArray Exclude files matching pattern --exclude stringArray Exclude files matching pattern
--exclude-from stringArray Read exclude patterns from file (use - to read from stdin) --exclude-from stringArray Read exclude patterns from file (use - to read from stdin)
--exclude-if-present string Exclude directories if filename is present --exclude-if-present stringArray Exclude directories if filename is present
--expect-continue-timeout duration Timeout when using expect / 100-continue in HTTP (default 1s) --expect-continue-timeout duration Timeout when using expect / 100-continue in HTTP (default 1s)
--fast-list Use recursive list if available; uses more memory but fewer transactions --fast-list Use recursive list if available; uses more memory but fewer transactions
--files-from stringArray Read list of source-file names from file (use - to read from stdin) --files-from stringArray Read list of source-file names from file (use - to read from stdin)

View File

@ -86,7 +86,7 @@ type Opt struct {
FilterFrom []string FilterFrom []string
ExcludeRule []string ExcludeRule []string
ExcludeFrom []string ExcludeFrom []string
ExcludeFile string ExcludeFile []string
IncludeRule []string IncludeRule []string
IncludeFrom []string IncludeFrom []string
FilesFrom []string FilesFrom []string
@ -392,8 +392,10 @@ func (f *Filter) ListContainsExcludeFile(entries fs.DirEntries) bool {
obj, ok := entry.(fs.Object) obj, ok := entry.(fs.Object)
if ok { if ok {
basename := path.Base(obj.Remote()) basename := path.Base(obj.Remote())
if basename == f.Opt.ExcludeFile { for _, excludeFile := range f.Opt.ExcludeFile {
return true if basename == excludeFile {
return true
}
} }
} }
} }
@ -436,12 +438,14 @@ func (f *Filter) IncludeDirectory(ctx context.Context, fs fs.Fs) func(string) (b
// empty string (for testing). // empty string (for testing).
func (f *Filter) DirContainsExcludeFile(ctx context.Context, fremote fs.Fs, remote string) (bool, error) { func (f *Filter) DirContainsExcludeFile(ctx context.Context, fremote fs.Fs, remote string) (bool, error) {
if len(f.Opt.ExcludeFile) > 0 { if len(f.Opt.ExcludeFile) > 0 {
exists, err := fs.FileExists(ctx, fremote, path.Join(remote, f.Opt.ExcludeFile)) for _, excludeFile := range f.Opt.ExcludeFile {
if err != nil { exists, err := fs.FileExists(ctx, fremote, path.Join(remote, excludeFile))
return false, err if err != nil {
} return false, err
if exists { }
return true, nil if exists {
return true, nil
}
} }
} }
return false, nil return false, nil

View File

@ -34,7 +34,7 @@ func AddFlags(flagSet *pflag.FlagSet) {
flags.StringArrayVarP(flagSet, &Opt.FilterFrom, "filter-from", "", nil, "Read filtering patterns from a file (use - to read from stdin)") flags.StringArrayVarP(flagSet, &Opt.FilterFrom, "filter-from", "", nil, "Read filtering patterns from a file (use - to read from stdin)")
flags.StringArrayVarP(flagSet, &Opt.ExcludeRule, "exclude", "", nil, "Exclude files matching pattern") flags.StringArrayVarP(flagSet, &Opt.ExcludeRule, "exclude", "", nil, "Exclude files matching pattern")
flags.StringArrayVarP(flagSet, &Opt.ExcludeFrom, "exclude-from", "", nil, "Read exclude patterns from file (use - to read from stdin)") flags.StringArrayVarP(flagSet, &Opt.ExcludeFrom, "exclude-from", "", nil, "Read exclude patterns from file (use - to read from stdin)")
flags.StringVarP(flagSet, &Opt.ExcludeFile, "exclude-if-present", "", "", "Exclude directories if filename is present") flags.StringArrayVarP(flagSet, &Opt.ExcludeFile, "exclude-if-present", "", nil, "Exclude directories if filename is present")
flags.StringArrayVarP(flagSet, &Opt.IncludeRule, "include", "", nil, "Include files matching pattern") flags.StringArrayVarP(flagSet, &Opt.IncludeRule, "include", "", nil, "Include files matching pattern")
flags.StringArrayVarP(flagSet, &Opt.IncludeFrom, "include-from", "", nil, "Read include patterns from file (use - to read from stdin)") flags.StringArrayVarP(flagSet, &Opt.IncludeFrom, "include-from", "", nil, "Read include patterns from file (use - to read from stdin)")
flags.StringArrayVarP(flagSet, &Opt.FilesFrom, "files-from", "", nil, "Read list of source-file names from file (use - to read from stdin)") flags.StringArrayVarP(flagSet, &Opt.FilesFrom, "files-from", "", nil, "Read list of source-file names from file (use - to read from stdin)")

View File

@ -81,7 +81,7 @@ func TestListDirSorted(t *testing.T) {
assert.Equal(t, "sub dir/sub sub dir/", str(1)) assert.Equal(t, "sub dir/sub sub dir/", str(1))
// testing ignore file // testing ignore file
fi.Opt.ExcludeFile = ".ignore" fi.Opt.ExcludeFile = []string{".ignore"}
items, err = list.DirSorted(context.Background(), r.Fremote, false, "sub dir") items, err = list.DirSorted(context.Background(), r.Fremote, false, "sub dir")
require.NoError(t, err) require.NoError(t, err)
@ -98,7 +98,7 @@ func TestListDirSorted(t *testing.T) {
assert.Equal(t, "sub dir/ignore dir/.ignore", str(0)) assert.Equal(t, "sub dir/ignore dir/.ignore", str(0))
assert.Equal(t, "sub dir/ignore dir/should be ignored", str(1)) assert.Equal(t, "sub dir/ignore dir/should be ignored", str(1))
fi.Opt.ExcludeFile = "" fi.Opt.ExcludeFile = nil
items, err = list.DirSorted(context.Background(), r.Fremote, false, "sub dir/ignore dir") items, err = list.DirSorted(context.Background(), r.Fremote, false, "sub dir/ignore dir")
require.NoError(t, err) require.NoError(t, err)
require.Len(t, items, 2) require.Len(t, items, 2)

View File

@ -1309,7 +1309,7 @@ func TestOverlappingFilterCheckWithFilter(t *testing.T) {
fi, err := filter.NewFilter(nil) fi, err := filter.NewFilter(nil)
require.NoError(t, err) require.NoError(t, err)
require.NoError(t, fi.Add(false, "*/exclude/")) require.NoError(t, fi.Add(false, "*/exclude/"))
fi.Opt.ExcludeFile = ".ignore" fi.Opt.ExcludeFile = []string{".ignore"}
ctx = filter.ReplaceConfig(ctx, fi) ctx = filter.ReplaceConfig(ctx, fi)
src := &testFs{testFsInfo{name: "name", root: "root"}} src := &testFs{testFsInfo{name: "name", root: "root"}}

View File

@ -1452,7 +1452,7 @@ func TestSyncOverlapWithFilter(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
require.NoError(t, fi.Add(false, "/rclone-sync-test/")) require.NoError(t, fi.Add(false, "/rclone-sync-test/"))
require.NoError(t, fi.Add(false, "*/layer2/")) require.NoError(t, fi.Add(false, "*/layer2/"))
fi.Opt.ExcludeFile = ".ignore" fi.Opt.ExcludeFile = []string{".ignore"}
ctx = filter.ReplaceConfig(ctx, fi) ctx = filter.ReplaceConfig(ctx, fi)
subRemoteName := r.FremoteName + "/rclone-sync-test" subRemoteName := r.FremoteName + "/rclone-sync-test"

View File

@ -507,10 +507,12 @@ func walkRDirTree(ctx context.Context, f fs.Fs, startPath string, includeAll boo
// Check if we need to prune a directory later. // Check if we need to prune a directory later.
if !includeAll && len(fi.Opt.ExcludeFile) > 0 { if !includeAll && len(fi.Opt.ExcludeFile) > 0 {
basename := path.Base(x.Remote()) basename := path.Base(x.Remote())
if basename == fi.Opt.ExcludeFile { for _, excludeFile := range fi.Opt.ExcludeFile {
excludeDir := parentDir(x.Remote()) if basename == excludeFile {
toPrune[excludeDir] = true excludeDir := parentDir(x.Remote())
fs.Debugf(basename, "Excluded from sync (and deletion) based on exclude file") toPrune[excludeDir] = true
fs.Debugf(basename, "Excluded from sync (and deletion) based on exclude file")
}
} }
} }
case fs.Directory: case fs.Directory:

View File

@ -736,13 +736,13 @@ b/c/d/
e e
`, nil, "", -1, "ign", true}, `, nil, "", -1, "ign", true},
} { } {
fi.Opt.ExcludeFile = test.excludeFile fi.Opt.ExcludeFile = []string{test.excludeFile}
r, err := walkRDirTree(context.Background(), nil, test.root, test.includeAll, test.level, makeListRCallback(test.entries, test.err)) r, err := walkRDirTree(context.Background(), nil, test.root, test.includeAll, test.level, makeListRCallback(test.entries, test.err))
assert.Equal(t, test.err, err, fmt.Sprintf("%+v", test)) assert.Equal(t, test.err, err, fmt.Sprintf("%+v", test))
assert.Equal(t, test.want, r.String(), fmt.Sprintf("%+v", test)) assert.Equal(t, test.want, r.String(), fmt.Sprintf("%+v", test))
} }
// Set to default value, to avoid side effects // Set to default value, to avoid side effects
fi.Opt.ExcludeFile = "" fi.Opt.ExcludeFile = nil
} }
func TestListType(t *testing.T) { func TestListType(t *testing.T) {