chore: remove ignore file caching entirely (#10813)

Effectively nobody uses it, it cost more than it was worth, the code
doesn't carry its weight. Remove it and let any similar mechanism in the
future be an internal implementation detail.

Closes #10812

Signed-off-by: Jakob Borg <jakob@kastelo.net>
This commit is contained in:
Jakob Borg
2026-07-25 09:43:22 +02:00
committed by GitHub
parent 2f81bf5d34
commit b15ff42d52
20 changed files with 52 additions and 452 deletions
-1
View File
@@ -116,7 +116,6 @@
<autoUpgradeIntervalH>12</autoUpgradeIntervalH> <autoUpgradeIntervalH>12</autoUpgradeIntervalH>
<upgradeToPreReleases>false</upgradeToPreReleases> <upgradeToPreReleases>false</upgradeToPreReleases>
<keepTemporariesH>24</keepTemporariesH> <keepTemporariesH>24</keepTemporariesH>
<cacheIgnoredFiles>false</cacheIgnoredFiles>
<progressUpdateIntervalS>5</progressUpdateIntervalS> <progressUpdateIntervalS>5</progressUpdateIntervalS>
<limitBandwidthInLan>false</limitBandwidthInLan> <limitBandwidthInLan>false</limitBandwidthInLan>
<minHomeDiskFree unit="%">1</minHomeDiskFree> <minHomeDiskFree unit="%">1</minHomeDiskFree>
-2
View File
@@ -72,7 +72,6 @@ func TestDefaultValues(t *testing.T) {
NATTimeoutS: 10, NATTimeoutS: 10,
AutoUpgradeIntervalH: 12, AutoUpgradeIntervalH: 12,
KeepTemporariesH: 24, KeepTemporariesH: 24,
CacheIgnoredFiles: false,
ProgressUpdateIntervalS: 5, ProgressUpdateIntervalS: 5,
LimitBandwidthInLan: false, LimitBandwidthInLan: false,
MinHomeDiskFree: Size{1, "%"}, MinHomeDiskFree: Size{1, "%"},
@@ -278,7 +277,6 @@ func TestOverriddenValues(t *testing.T) {
NATTimeoutS: 15, NATTimeoutS: 15,
AutoUpgradeIntervalH: 24, AutoUpgradeIntervalH: 24,
KeepTemporariesH: 48, KeepTemporariesH: 48,
CacheIgnoredFiles: true,
ProgressUpdateIntervalS: 10, ProgressUpdateIntervalS: 10,
LimitBandwidthInLan: true, LimitBandwidthInLan: true,
MinHomeDiskFree: Size{5.2, "%"}, MinHomeDiskFree: Size{5.2, "%"},
-4
View File
@@ -320,10 +320,6 @@ func migrateToConfigV15(cfg *Configuration) {
} }
func migrateToConfigV14(cfg *Configuration) { func migrateToConfigV14(cfg *Configuration) {
// Not using the ignore cache is the new default. Disable it on existing
// configurations.
cfg.Options.CacheIgnoredFiles = false
// Migrate UPnP -> NAT options // Migrate UPnP -> NAT options
cfg.Options.NATEnabled = cfg.Options.DeprecatedUPnPEnabled cfg.Options.NATEnabled = cfg.Options.DeprecatedUPnPEnabled
cfg.Options.DeprecatedUPnPEnabled = false cfg.Options.DeprecatedUPnPEnabled = false
-1
View File
@@ -45,7 +45,6 @@ type OptionsConfiguration struct {
AutoUpgradeIntervalH int `json:"autoUpgradeIntervalH" xml:"autoUpgradeIntervalH" default:"12"` AutoUpgradeIntervalH int `json:"autoUpgradeIntervalH" xml:"autoUpgradeIntervalH" default:"12"`
UpgradeToPreReleases bool `json:"upgradeToPreReleases" xml:"upgradeToPreReleases"` UpgradeToPreReleases bool `json:"upgradeToPreReleases" xml:"upgradeToPreReleases"`
KeepTemporariesH int `json:"keepTemporariesH" xml:"keepTemporariesH" default:"24"` KeepTemporariesH int `json:"keepTemporariesH" xml:"keepTemporariesH" default:"24"`
CacheIgnoredFiles bool `json:"cacheIgnoredFiles" xml:"cacheIgnoredFiles" default:"false"`
ProgressUpdateIntervalS int `json:"progressUpdateIntervalS" xml:"progressUpdateIntervalS" default:"5"` ProgressUpdateIntervalS int `json:"progressUpdateIntervalS" xml:"progressUpdateIntervalS" default:"5"`
LimitBandwidthInLan bool `json:"limitBandwidthInLan" xml:"limitBandwidthInLan" default:"false"` LimitBandwidthInLan bool `json:"limitBandwidthInLan" xml:"limitBandwidthInLan" default:"false"`
MinHomeDiskFree Size `json:"minHomeDiskFree" xml:"minHomeDiskFree" default:"1 %"` MinHomeDiskFree Size `json:"minHomeDiskFree" xml:"minHomeDiskFree" default:"1 %"`
-1
View File
@@ -42,7 +42,6 @@
<restartOnWakeup>true</restartOnWakeup> <restartOnWakeup>true</restartOnWakeup>
<autoUpgradeIntervalH>0</autoUpgradeIntervalH> <autoUpgradeIntervalH>0</autoUpgradeIntervalH>
<keepTemporariesH>24</keepTemporariesH> <keepTemporariesH>24</keepTemporariesH>
<cacheIgnoredFiles>true</cacheIgnoredFiles>
<progressUpdateIntervalS>5</progressUpdateIntervalS> <progressUpdateIntervalS>5</progressUpdateIntervalS>
<symlinksEnabled>true</symlinksEnabled> <symlinksEnabled>true</symlinksEnabled>
<limitBandwidthInLan>false</limitBandwidthInLan> <limitBandwidthInLan>false</limitBandwidthInLan>
-1
View File
@@ -22,7 +22,6 @@
<restartOnWakeup>false</restartOnWakeup> <restartOnWakeup>false</restartOnWakeup>
<autoUpgradeIntervalH>24</autoUpgradeIntervalH> <autoUpgradeIntervalH>24</autoUpgradeIntervalH>
<keepTemporariesH>48</keepTemporariesH> <keepTemporariesH>48</keepTemporariesH>
<cacheIgnoredFiles>true</cacheIgnoredFiles>
<progressUpdateIntervalS>10</progressUpdateIntervalS> <progressUpdateIntervalS>10</progressUpdateIntervalS>
<symlinksEnabled>false</symlinksEnabled> <symlinksEnabled>false</symlinksEnabled>
<limitBandwidthInLan>true</limitBandwidthInLan> <limitBandwidthInLan>true</limitBandwidthInLan>
-66
View File
@@ -1,66 +0,0 @@
// Copyright (C) 2014 The Syncthing Authors.
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
// You can obtain one at https://mozilla.org/MPL/2.0/.
package ignore
import (
"time"
"github.com/syncthing/syncthing/lib/ignore/ignoreresult"
)
type nower interface {
Now() time.Time
}
var clock = nower(defaultClock{})
type cache struct {
entries map[string]cacheEntry
}
type cacheEntry struct {
result ignoreresult.R
access int64 // Unix nanosecond count. Sufficient until the year 2262.
}
func newCache() *cache {
return &cache{
entries: make(map[string]cacheEntry),
}
}
func (c *cache) clean(d time.Duration) {
for k, v := range c.entries {
if clock.Now().Sub(time.Unix(0, v.access)) > d {
delete(c.entries, k)
}
}
}
func (c *cache) get(key string) (ignoreresult.R, bool) {
entry, ok := c.entries[key]
if ok {
entry.access = clock.Now().UnixNano()
c.entries[key] = entry
}
return entry.result, ok
}
func (c *cache) set(key string, result ignoreresult.R) {
c.entries[key] = cacheEntry{result, time.Now().UnixNano()}
}
func (c *cache) len() int {
l := len(c.entries)
return l
}
type defaultClock struct{}
func (defaultClock) Now() time.Time {
return time.Now()
}
-94
View File
@@ -1,94 +0,0 @@
// Copyright (C) 2014 The Syncthing Authors.
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
// You can obtain one at https://mozilla.org/MPL/2.0/.
package ignore
import (
"testing"
"time"
"github.com/syncthing/syncthing/lib/ignore/ignoreresult"
)
func TestCache(t *testing.T) {
fc := new(fakeClock)
oldClock := clock
clock = fc
defer func() {
clock = oldClock
}()
c := newCache()
res, ok := c.get("nonexistent")
if res.IsIgnored() || res.IsDeletable() || ok {
t.Errorf("res %v, ok %v for nonexistent item", res, ok)
}
// Set and check some items
c.set("true", ignoreresult.IgnoredDeletable)
c.set("false", 0)
res, ok = c.get("true")
if !res.IsIgnored() || !res.IsDeletable() || !ok {
t.Errorf("res %v, ok %v for true item", res, ok)
}
res, ok = c.get("false")
if res.IsIgnored() || res.IsDeletable() || !ok {
t.Errorf("res %v, ok %v for false item", res, ok)
}
// Don't clean anything
c.clean(time.Second)
// Same values should exist
res, ok = c.get("true")
if !res.IsIgnored() || !res.IsDeletable() || !ok {
t.Errorf("res %v, ok %v for true item", res, ok)
}
res, ok = c.get("false")
if res.IsIgnored() || res.IsDeletable() || !ok {
t.Errorf("res %v, ok %v for false item", res, ok)
}
// Sleep and access, to get some data for clean
*fc += 500 // milliseconds
c.get("true")
*fc += 100 // milliseconds
// "false" was accessed ~600 ms ago, "true" was accessed ~100 ms ago.
// This should clean out "false" but not "true"
c.clean(300 * time.Millisecond)
// Same values should exist
_, ok = c.get("true")
if !ok {
t.Error("item should still exist")
}
_, ok = c.get("false")
if ok {
t.Errorf("item should have been cleaned")
}
}
type fakeClock int64 // milliseconds
func (f *fakeClock) Now() time.Time {
t := time.Unix(int64(*f)/1000, (int64(*f)%1000)*int64(time.Millisecond))
*f++
return t
}
+2 -53
View File
@@ -124,10 +124,7 @@ type Matcher struct {
fs fs.Filesystem fs fs.Filesystem
lines []string // exact lines read from .stignore lines []string // exact lines read from .stignore
patterns []Pattern // patterns including those from included files patterns []Pattern // patterns including those from included files
withCache bool
matches *cache
curHash string curHash string
stop chan struct{}
changeDetector ChangeDetector changeDetector ChangeDetector
mut sync.Mutex mut sync.Mutex
} }
@@ -135,13 +132,6 @@ type Matcher struct {
// An Option can be passed to New() // An Option can be passed to New()
type Option func(*Matcher) type Option func(*Matcher)
// WithCache enables or disables lookup caching. The default is disabled.
func WithCache(v bool) Option {
return func(m *Matcher) {
m.withCache = v
}
}
// WithChangeDetector sets a custom ChangeDetector. The default is to simply // WithChangeDetector sets a custom ChangeDetector. The default is to simply
// use the on disk modtime for comparison. // use the on disk modtime for comparison.
func WithChangeDetector(cd ChangeDetector) Option { func WithChangeDetector(cd ChangeDetector) Option {
@@ -152,8 +142,7 @@ func WithChangeDetector(cd ChangeDetector) Option {
func New(fs fs.Filesystem, opts ...Option) *Matcher { func New(fs fs.Filesystem, opts ...Option) *Matcher {
m := &Matcher{ m := &Matcher{
fs: fs, fs: fs,
stop: make(chan struct{}),
} }
for _, opt := range opts { for _, opt := range opts {
opt(m) opt(m)
@@ -161,9 +150,6 @@ func New(fs fs.Filesystem, opts ...Option) *Matcher {
if m.changeDetector == nil { if m.changeDetector == nil {
m.changeDetector = newModtimeChecker() m.changeDetector = newModtimeChecker()
} }
if m.withCache {
go m.clean(2 * time.Hour)
}
return m return m
} }
@@ -219,9 +205,6 @@ func (m *Matcher) parseLocked(r io.Reader, file string) error {
m.curHash = newHash m.curHash = newHash
m.patterns = patterns m.patterns = patterns
if m.withCache {
m.matches = newCache()
}
return err return err
} }
@@ -232,7 +215,7 @@ func (m *Matcher) parseLocked(r io.Reader, file string) error {
// NFC everywhere else). This is always the case in real usage in syncthing, as // NFC everywhere else). This is always the case in real usage in syncthing, as
// we ensure native unicode normalisation on all entry points (scanning and from // we ensure native unicode normalisation on all entry points (scanning and from
// protocol) - so no need to normalize when calling this, except e.g. in tests. // protocol) - so no need to normalize when calling this, except e.g. in tests.
func (m *Matcher) Match(file string) (result ignoreresult.R) { func (m *Matcher) Match(file string) ignoreresult.R {
switch { switch {
case fs.IsTemporary(file): case fs.IsTemporary(file):
return ignoreresult.IgnoreAndSkip return ignoreresult.IgnoreAndSkip
@@ -254,19 +237,6 @@ func (m *Matcher) Match(file string) (result ignoreresult.R) {
// Change backslashes to slashes (on Windows only) // Change backslashes to slashes (on Windows only)
file = filepath.ToSlash(file) file = filepath.ToSlash(file)
if m.matches != nil {
// Check the cache for a known result.
res, ok := m.matches.get(file)
if ok {
return res
}
// Update the cache with the result at return time
defer func() {
m.matches.set(file, result)
}()
}
// Check all the patterns for a match. Track whether the patterns so far // Check all the patterns for a match. Track whether the patterns so far
// allow skipping matched directories or not. As soon as we hit an // allow skipping matched directories or not. As soon as we hit an
// exclude pattern (with some exceptions), we can't skip directories // exclude pattern (with some exceptions), we can't skip directories
@@ -327,27 +297,6 @@ func (m *Matcher) Hash() string {
return m.curHash return m.curHash
} }
func (m *Matcher) Stop() {
close(m.stop)
}
func (m *Matcher) clean(d time.Duration) {
t := time.NewTimer(d / 2)
for {
select {
case <-m.stop:
return
case <-t.C:
m.mut.Lock()
if m.matches != nil {
m.matches.clean(d)
}
t.Reset(d / 2)
m.mut.Unlock()
}
}
}
func hashPatterns(patterns []Pattern) string { func hashPatterns(patterns []Pattern) string {
h := sha256.New() h := sha256.New()
for _, pat := range patterns { for _, pat := range patterns {
+33 -206
View File
@@ -52,7 +52,7 @@ func newTestFS() fs.Filesystem {
func TestIgnore(t *testing.T) { func TestIgnore(t *testing.T) {
testFs := newTestFS() testFs := newTestFS()
pats := New(testFs, WithCache(true)) pats := New(testFs)
err := pats.Load(".stignore") err := pats.Load(".stignore")
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
@@ -104,7 +104,7 @@ func TestExcludes(t *testing.T) {
i*2 i*2
!ign2 !ign2
` `
pats := New(testFs, WithCache(true)) pats := New(testFs)
err := pats.Parse(bytes.NewBufferString(stignore), ".stignore") err := pats.Parse(bytes.NewBufferString(stignore), ".stignore")
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
@@ -151,7 +151,7 @@ func TestFlagOrder(t *testing.T) {
(?i)(?d)(?d)!ign9 (?i)(?d)(?d)!ign9
(?d)(?d)!ign10 (?d)(?d)!ign10
` `
pats := New(testFs, WithCache(true)) pats := New(testFs)
err := pats.Parse(bytes.NewBufferString(stignore), ".stignore") err := pats.Parse(bytes.NewBufferString(stignore), ".stignore")
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
@@ -188,7 +188,7 @@ func TestDeletables(t *testing.T) {
ign7 ign7
(?i)ign8 (?i)ign8
` `
pats := New(testFs, WithCache(true)) pats := New(testFs)
err := pats.Parse(bytes.NewBufferString(stignore), ".stignore") err := pats.Parse(bytes.NewBufferString(stignore), ".stignore")
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
@@ -229,7 +229,7 @@ func TestBadPatterns(t *testing.T) {
} }
for _, pat := range badPatterns { for _, pat := range badPatterns {
err := New(testFs, WithCache(true)).Parse(bytes.NewBufferString(pat), ".stignore") err := New(testFs).Parse(bytes.NewBufferString(pat), ".stignore")
if err == nil { if err == nil {
t.Errorf("No error for pattern %q", pat) t.Errorf("No error for pattern %q", pat)
} }
@@ -247,7 +247,7 @@ func TestBadPatterns(t *testing.T) {
func TestCaseSensitivity(t *testing.T) { func TestCaseSensitivity(t *testing.T) {
testFs := newTestFS() testFs := newTestFS()
ign := New(testFs, WithCache(true)) ign := New(testFs)
err := ign.Parse(bytes.NewBufferString("test"), ".stignore") err := ign.Parse(bytes.NewBufferString("test"), ".stignore")
if err != nil { if err != nil {
t.Error(err) t.Error(err)
@@ -275,126 +275,6 @@ func TestCaseSensitivity(t *testing.T) {
} }
} }
func TestCaching(t *testing.T) {
fs := fs.NewFilesystem(fs.FilesystemTypeFake, rand.String(32)+"?content=true")
fd1, err := osutil.TempFile(fs, "", "")
if err != nil {
t.Fatal(err)
}
fd2, err := osutil.TempFile(fs, "", "")
if err != nil {
t.Fatal(err)
}
defer fd1.Close()
defer fd2.Close()
defer fs.Remove(fd1.Name())
defer fs.Remove(fd2.Name())
_, err = fd1.Write([]byte("/x/\n#include " + filepath.Base(fd2.Name()) + "\n"))
if err != nil {
t.Fatal(err)
}
fd2.Write([]byte("/y/\n"))
pats := New(fs, WithCache(true))
err = pats.Load(fd1.Name())
if err != nil {
t.Fatal(err)
}
if pats.matches.len() != 0 {
t.Fatal("Expected empty cache")
}
// Cache some outcomes
for _, letter := range []string{"a", "b", "x", "y"} {
pats.Match(letter)
}
if pats.matches.len() != 4 {
t.Fatal("Expected 4 cached results")
}
// Reload file, expect old outcomes to be preserved
err = pats.Load(fd1.Name())
if err != nil {
t.Fatal(err)
}
if pats.matches.len() != 4 {
t.Fatal("Expected 4 cached results")
}
// Modify the include file, expect empty cache. Ensure the timestamp on
// the file changes.
fd2.Write([]byte("/z/\n"))
fd2.Sync()
fakeTime := time.Now().Add(5 * time.Second)
fs.Chtimes(fd2.Name(), fakeTime, fakeTime)
err = pats.Load(fd1.Name())
if err != nil {
t.Fatal(err)
}
if pats.matches.len() != 0 {
t.Fatal("Expected 0 cached results")
}
// Cache some outcomes again
for _, letter := range []string{"b", "x", "y"} {
pats.Match(letter)
}
// Verify that outcomes preserved on next load
err = pats.Load(fd1.Name())
if err != nil {
t.Fatal(err)
}
if pats.matches.len() != 3 {
t.Fatal("Expected 3 cached results")
}
// Modify the root file, expect cache to be invalidated
fd1.Write([]byte("/a/\n"))
fd1.Sync()
fakeTime = time.Now().Add(5 * time.Second)
fs.Chtimes(fd1.Name(), fakeTime, fakeTime)
err = pats.Load(fd1.Name())
if err != nil {
t.Fatal(err)
}
if pats.matches.len() != 0 {
t.Fatal("Expected cache invalidation")
}
// Cache some outcomes again
for _, letter := range []string{"b", "x", "y"} {
pats.Match(letter)
}
// Verify that outcomes provided on next load
err = pats.Load(fd1.Name())
if err != nil {
t.Fatal(err)
}
if pats.matches.len() != 3 {
t.Fatal("Expected 3 cached results")
}
}
func TestCommentsAndBlankLines(t *testing.T) { func TestCommentsAndBlankLines(t *testing.T) {
testFs := newTestFS() testFs := newTestFS()
@@ -409,7 +289,7 @@ func TestCommentsAndBlankLines(t *testing.T) {
` `
pats := New(testFs, WithCache(true)) pats := New(testFs)
err := pats.Parse(bytes.NewBufferString(stignore), ".stignore") err := pats.Parse(bytes.NewBufferString(stignore), ".stignore")
if err != nil { if err != nil {
t.Error(err) t.Error(err)
@@ -451,59 +331,6 @@ flamingo
} }
} }
func BenchmarkMatchCached(b *testing.B) {
stignore := `
.frog
.frog*
.frogfox
.whale
.whale/*
.dolphin
.dolphin/*
~ferret~.*
.ferret.*
flamingo.*
flamingo
*.crow
*.crow
`
// Caches per file, hence write the patterns to a file.
fs := fs.NewFilesystem(fs.FilesystemTypeFake, rand.String(32)+"?content=true")
fd, err := osutil.TempFile(fs, "", "")
if err != nil {
b.Fatal(err)
}
_, err = fd.Write([]byte(stignore))
defer fd.Close()
defer fs.Remove(fd.Name())
if err != nil {
b.Fatal(err)
}
// Load the patterns
pats := New(fs, WithCache(true))
err = pats.Load(fd.Name())
if err != nil {
b.Fatal(err)
}
// Cache the outcome for "filename"
pats.Match("filename")
// This load should now load the cached outcomes as the set of patterns
// has not changed.
err = pats.Load(fd.Name())
if err != nil {
b.Fatal(err)
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
result = pats.Match("filename")
}
}
func TestCacheReload(t *testing.T) { func TestCacheReload(t *testing.T) {
fs := fs.NewFilesystem(fs.FilesystemTypeFake, rand.String(32)+"?content=true") fs := fs.NewFilesystem(fs.FilesystemTypeFake, rand.String(32)+"?content=true")
@@ -522,7 +349,7 @@ func TestCacheReload(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
pats := New(fs, WithCache(true)) pats := New(fs)
err = pats.Load(fd.Name()) err = pats.Load(fd.Name())
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
@@ -579,7 +406,7 @@ func TestCacheReload(t *testing.T) {
func TestHash(t *testing.T) { func TestHash(t *testing.T) {
testFs := newTestFS() testFs := newTestFS()
p1 := New(testFs, WithCache(true)) p1 := New(testFs)
err := p1.Load(".stignore") err := p1.Load(".stignore")
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
@@ -595,7 +422,7 @@ func TestHash(t *testing.T) {
/ffile /ffile
lost+found lost+found
` `
p2 := New(testFs, WithCache(true)) p2 := New(testFs)
err = p2.Parse(bytes.NewBufferString(stignore), ".stignore") err = p2.Parse(bytes.NewBufferString(stignore), ".stignore")
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
@@ -610,7 +437,7 @@ func TestHash(t *testing.T) {
/ffile /ffile
lost+found lost+found
` `
p3 := New(testFs, WithCache(true)) p3 := New(testFs)
err = p3.Parse(bytes.NewBufferString(stignore), ".stignore") err = p3.Parse(bytes.NewBufferString(stignore), ".stignore")
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
@@ -636,7 +463,7 @@ func TestHash(t *testing.T) {
func TestHashOfEmpty(t *testing.T) { func TestHashOfEmpty(t *testing.T) {
testFs := newTestFS() testFs := newTestFS()
p1 := New(testFs, WithCache(true)) p1 := New(testFs)
err := p1.Load(".stignore") err := p1.Load(".stignore")
if err != nil { if err != nil {
@@ -678,7 +505,7 @@ func TestWindowsPatterns(t *testing.T) {
a/b a/b
c\d c\d
` `
pats := New(testFs, WithCache(true)) pats := New(testFs)
err := pats.Parse(bytes.NewBufferString(stignore), ".stignore") err := pats.Parse(bytes.NewBufferString(stignore), ".stignore")
if err != nil { if err != nil {
@@ -706,7 +533,7 @@ func TestAutomaticCaseInsensitivity(t *testing.T) {
A/B A/B
c/d c/d
` `
pats := New(testFs, WithCache(true)) pats := New(testFs)
err := pats.Parse(bytes.NewBufferString(stignore), ".stignore") err := pats.Parse(bytes.NewBufferString(stignore), ".stignore")
if err != nil { if err != nil {
@@ -728,7 +555,7 @@ func TestCommas(t *testing.T) {
foo,bar.txt foo,bar.txt
{baz,quux}.txt {baz,quux}.txt
` `
pats := New(testFs, WithCache(true)) pats := New(testFs)
err := pats.Parse(bytes.NewBufferString(stignore), ".stignore") err := pats.Parse(bytes.NewBufferString(stignore), ".stignore")
if err != nil { if err != nil {
@@ -762,7 +589,7 @@ func TestIssue3164(t *testing.T) {
(?d)(?i)/foo (?d)(?i)/foo
(?d)(?i)**/bar (?d)(?i)**/bar
` `
pats := New(testFs, WithCache(true)) pats := New(testFs)
err := pats.Parse(bytes.NewBufferString(stignore), ".stignore") err := pats.Parse(bytes.NewBufferString(stignore), ".stignore")
if err != nil { if err != nil {
@@ -801,7 +628,7 @@ func TestIssue3174(t *testing.T) {
stignore := ` stignore := `
*ä* *ä*
` `
pats := New(testFs, WithCache(true)) pats := New(testFs)
err := pats.Parse(bytes.NewBufferString(stignore), ".stignore") err := pats.Parse(bytes.NewBufferString(stignore), ".stignore")
if err != nil { if err != nil {
@@ -822,7 +649,7 @@ func TestIssue3639(t *testing.T) {
stignore := ` stignore := `
foo/ foo/
` `
pats := New(testFs, WithCache(true)) pats := New(testFs)
err := pats.Parse(bytes.NewBufferString(stignore), ".stignore") err := pats.Parse(bytes.NewBufferString(stignore), ".stignore")
if err != nil { if err != nil {
@@ -858,7 +685,7 @@ func TestIssue3674(t *testing.T) {
{"as/dc", true}, {"as/dc", true},
} }
pats := New(testFs, WithCache(true)) pats := New(testFs)
err := pats.Parse(bytes.NewBufferString(stignore), ".stignore") err := pats.Parse(bytes.NewBufferString(stignore), ".stignore")
if err != nil { if err != nil {
@@ -893,7 +720,7 @@ func TestGobwasGlobIssue18(t *testing.T) {
{"bbaa", false}, {"bbaa", false},
} }
pats := New(testFs, WithCache(true)) pats := New(testFs)
err := pats.Parse(bytes.NewBufferString(stignore), ".stignore") err := pats.Parse(bytes.NewBufferString(stignore), ".stignore")
if err != nil { if err != nil {
@@ -925,7 +752,7 @@ func TestRoot(t *testing.T) {
{"b", true}, {"b", true},
} }
pats := New(testFs, WithCache(true)) pats := New(testFs)
err := pats.Parse(bytes.NewBufferString(stignore), ".stignore") err := pats.Parse(bytes.NewBufferString(stignore), ".stignore")
if err != nil { if err != nil {
@@ -951,7 +778,7 @@ func TestLines(t *testing.T) {
!/a !/a
` `
pats := New(testFs, WithCache(true)) pats := New(testFs)
err := pats.Parse(bytes.NewBufferString(stignore), ".stignore") err := pats.Parse(bytes.NewBufferString(stignore), ".stignore")
if err != nil { if err != nil {
@@ -992,7 +819,7 @@ func TestDuplicateLines(t *testing.T) {
/* /*
` `
pats := New(testFs, WithCache(true)) pats := New(testFs)
err := pats.Parse(bytes.NewBufferString(stignore), ".stignore") err := pats.Parse(bytes.NewBufferString(stignore), ".stignore")
if err != nil { if err != nil {
@@ -1025,7 +852,7 @@ func TestIssue4680(t *testing.T) {
{"#snapshot/foo", true}, {"#snapshot/foo", true},
} }
pats := New(testFs, WithCache(true)) pats := New(testFs)
err := pats.Parse(bytes.NewBufferString(stignore), ".stignore") err := pats.Parse(bytes.NewBufferString(stignore), ".stignore")
if err != nil { if err != nil {
@@ -1045,7 +872,7 @@ func TestIssue4689(t *testing.T) {
stignore := `// orig` stignore := `// orig`
pats := New(testFs, WithCache(true)) pats := New(testFs)
err := pats.Parse(bytes.NewBufferString(stignore), ".stignore") err := pats.Parse(bytes.NewBufferString(stignore), ".stignore")
if err != nil { if err != nil {
@@ -1076,7 +903,7 @@ func TestIssue4901(t *testing.T) {
puppy puppy
` `
pats := New(testFs, WithCache(true)) pats := New(testFs)
fd, err := pats.fs.Create(".stignore") fd, err := pats.fs.Create(".stignore")
if err != nil { if err != nil {
@@ -1119,7 +946,7 @@ func TestIssue4901(t *testing.T) {
func TestIssue5009(t *testing.T) { func TestIssue5009(t *testing.T) {
testFs := newTestFS() testFs := newTestFS()
pats := New(testFs, WithCache(true)) pats := New(testFs)
stignore := ` stignore := `
ign1 ign1
@@ -1152,7 +979,7 @@ func TestIssue5009(t *testing.T) {
func TestSpecialChars(t *testing.T) { func TestSpecialChars(t *testing.T) {
testFs := newTestFS() testFs := newTestFS()
pats := New(testFs, WithCache(true)) pats := New(testFs)
stignore := `(?i)/#recycle stignore := `(?i)/#recycle
(?i)/#nosync (?i)/#nosync
@@ -1179,7 +1006,7 @@ func TestSpecialChars(t *testing.T) {
func TestIntlWildcards(t *testing.T) { func TestIntlWildcards(t *testing.T) {
testFs := newTestFS() testFs := newTestFS()
pats := New(testFs, WithCache(true)) pats := New(testFs)
stignore := `1000春 stignore := `1000春
200?春 200?春
@@ -1208,7 +1035,7 @@ func TestPartialIncludeLine(t *testing.T) {
// Loading a partial #include line (no file mentioned) should error but not crash. // Loading a partial #include line (no file mentioned) should error but not crash.
pats := New(testFs, WithCache(true)) pats := New(testFs)
cases := []string{ cases := []string{
"#include", "#include",
@@ -1267,7 +1094,7 @@ func TestSkipIgnoredDirs(t *testing.T) {
} }
} }
pats := New(testFs, WithCache(true)) pats := New(testFs)
stignore := ` stignore := `
/foo/ign* /foo/ign*
@@ -1711,7 +1538,7 @@ func testEscape(t *testing.T, tests []escapeTest, noErrors bool) {
for name, content := range testEscapeFiles { for name, content := range testEscapeFiles {
fs.WriteFile(testFS, name, []byte(content), 0o666) fs.WriteFile(testFS, name, []byte(content), 0o666)
} }
pats := New(testFS, WithCache(true)) pats := New(testFS)
err := pats.Parse(bytes.NewBufferString(test.pattern), ".stignore") err := pats.Parse(bytes.NewBufferString(test.pattern), ".stignore")
if noErrors { if noErrors {
@@ -1753,7 +1580,7 @@ func TestIgnoreThroughSymlink(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
pats := New(testFS, WithCache(true)) pats := New(testFS)
if err := pats.Load(".stignore"); err != nil { if err := pats.Load(".stignore"); err != nil {
t.Fatal(err) t.Fatal(err)
} }
+10 -10
View File
@@ -300,7 +300,7 @@ func (m *model) initFolders(cfg config.Configuration) error {
folderCfg.CreateRoot() folderCfg.CreateRoot()
continue continue
} }
err := m.newFolder(folderCfg, cfg.Options.CacheIgnoredFiles) err := m.newFolder(folderCfg)
if err != nil { if err != nil {
return err return err
} }
@@ -335,8 +335,8 @@ func (m *model) fatal(err error) {
} }
// Need to hold lock on m.mut when calling this. // Need to hold lock on m.mut when calling this.
func (m *model) addAndStartFolderLocked(cfg config.FolderConfiguration, cacheIgnoredFiles bool) { func (m *model) addAndStartFolderLocked(cfg config.FolderConfiguration) {
ignores := ignore.New(cfg.Filesystem(), ignore.WithCache(cacheIgnoredFiles)) ignores := ignore.New(cfg.Filesystem())
if cfg.Type != config.FolderTypeReceiveEncrypted { if cfg.Type != config.FolderTypeReceiveEncrypted {
if err := ignores.Load(".stignore"); err != nil && !fs.IsNotExist(err) { if err := ignores.Load(".stignore"); err != nil && !fs.IsNotExist(err) {
slog.Error("Failed to load ignores", slogutil.Error(err)) slog.Error("Failed to load ignores", slogutil.Error(err))
@@ -509,7 +509,7 @@ func (m *model) cleanupFolderLocked(cfg config.FolderConfiguration) {
delete(m.folderEncryptionFailures, cfg.ID) delete(m.folderEncryptionFailures, cfg.ID)
} }
func (m *model) restartFolder(from, to config.FolderConfiguration, cacheIgnoredFiles bool) error { func (m *model) restartFolder(from, to config.FolderConfiguration) error {
if to.ID == "" { if to.ID == "" {
panic("bug: cannot restart empty folder ID") panic("bug: cannot restart empty folder ID")
} }
@@ -539,7 +539,7 @@ func (m *model) restartFolder(from, to config.FolderConfiguration, cacheIgnoredF
m.cleanupFolderLocked(from) m.cleanupFolderLocked(from)
if !to.Paused { if !to.Paused {
m.addAndStartFolderLocked(to, cacheIgnoredFiles) m.addAndStartFolderLocked(to)
} }
runner, _ := m.folderRunners.Get(to.ID) runner, _ := m.folderRunners.Get(to.ID)
@@ -560,11 +560,11 @@ func (m *model) restartFolder(from, to config.FolderConfiguration, cacheIgnoredF
return nil return nil
} }
func (m *model) newFolder(cfg config.FolderConfiguration, cacheIgnoredFiles bool) error { func (m *model) newFolder(cfg config.FolderConfiguration) error {
m.mut.Lock() m.mut.Lock()
defer m.mut.Unlock() defer m.mut.Unlock()
m.addAndStartFolderLocked(cfg, cacheIgnoredFiles) m.addAndStartFolderLocked(cfg)
// Cluster configs might be received and processed before reaching this // Cluster configs might be received and processed before reaching this
// point, i.e. before the folder is started. If that's the case, start // point, i.e. before the folder is started. If that's the case, start
@@ -2972,7 +2972,7 @@ func (m *model) CommitConfiguration(from, to config.Configuration) bool {
slog.Info("Paused folder", cfg.LogAttr()) slog.Info("Paused folder", cfg.LogAttr())
} else { } else {
slog.Info("Adding folder", cfg.LogAttr()) slog.Info("Adding folder", cfg.LogAttr())
if err := m.newFolder(cfg, to.Options.CacheIgnoredFiles); err != nil { if err := m.newFolder(cfg); err != nil {
m.fatal(err) m.fatal(err)
return true return true
} }
@@ -2998,8 +2998,8 @@ func (m *model) CommitConfiguration(from, to config.Configuration) bool {
// This folder exists on both sides. Settings might have changed. // This folder exists on both sides. Settings might have changed.
// Check if anything differs that requires a restart. // Check if anything differs that requires a restart.
if !reflect.DeepEqual(fromCfg.RequiresRestartOnly(), toCfg.RequiresRestartOnly()) || from.Options.CacheIgnoredFiles != to.Options.CacheIgnoredFiles { if !reflect.DeepEqual(fromCfg.RequiresRestartOnly(), toCfg.RequiresRestartOnly()) {
if err := m.restartFolder(fromCfg, toCfg, to.Options.CacheIgnoredFiles); err != nil { if err := m.restartFolder(fromCfg, toCfg); err != nil {
m.fatal(err) m.fatal(err)
return true return true
} }
+3 -3
View File
@@ -1603,7 +1603,7 @@ func TestIgnores(t *testing.T) {
ID: "fresh", Path: "XXX", ID: "fresh", Path: "XXX",
FilesystemType: config.FilesystemTypeFake, FilesystemType: config.FilesystemTypeFake,
} }
ignores := ignore.New(fcfg.Filesystem(), ignore.WithCache(m.cfg.Options().CacheIgnoredFiles)) ignores := ignore.New(fcfg.Filesystem())
m.mut.Lock() m.mut.Lock()
m.folderCfgs[fcfg.ID] = fcfg m.folderCfgs[fcfg.ID] = fcfg
m.folderIgnores[fcfg.ID] = ignores m.folderIgnores[fcfg.ID] = ignores
@@ -1618,7 +1618,7 @@ func TestIgnores(t *testing.T) {
pausedDefaultFolderConfig := defaultFolderConfig pausedDefaultFolderConfig := defaultFolderConfig
pausedDefaultFolderConfig.Paused = true pausedDefaultFolderConfig.Paused = true
m.restartFolder(defaultFolderConfig, pausedDefaultFolderConfig, false) m.restartFolder(defaultFolderConfig, pausedDefaultFolderConfig)
// Here folder initialization is not an issue as a paused folder isn't // Here folder initialization is not an issue as a paused folder isn't
// added to the model and thus there is no initial scan happening. // added to the model and thus there is no initial scan happening.
@@ -2216,7 +2216,7 @@ func TestIndexesForUnknownDevicesDropped(t *testing.T) {
t.Error("expected two devices") t.Error("expected two devices")
} }
m.newFolder(defaultFolderConfig, false) m.newFolder(defaultFolderConfig)
defer cleanupModel(m) defer cleanupModel(m)
if devs, err := m.sdb.ListDevicesForFolder("default"); err != nil || len(devs) != 1 { if devs, err := m.sdb.ListDevicesForFolder("default"); err != nil || len(devs) != 1 {
+1 -1
View File
@@ -250,7 +250,7 @@ func (*alwaysChanged) Changed() bool {
func folderIgnoresAlwaysReload(t testing.TB, m *testModel, fcfg config.FolderConfiguration) { func folderIgnoresAlwaysReload(t testing.TB, m *testModel, fcfg config.FolderConfiguration) {
t.Helper() t.Helper()
m.removeFolder(fcfg) m.removeFolder(fcfg)
ignores := ignore.New(fcfg.Filesystem(), ignore.WithCache(true), ignore.WithChangeDetector(newAlwaysChanged())) ignores := ignore.New(fcfg.Filesystem(), ignore.WithChangeDetector(newAlwaysChanged()))
m.mut.Lock() m.mut.Lock()
m.addAndStartFolderLockedWithIgnores(fcfg, ignores) m.addAndStartFolderLockedWithIgnores(fcfg, ignores)
m.mut.Unlock() m.mut.Unlock()
+3 -3
View File
@@ -749,7 +749,7 @@ func TestRecurseInclude(t *testing.T) {
* *
` `
testFs := newTestFs() testFs := newTestFs()
ignores := ignore.New(testFs, ignore.WithCache(true)) ignores := ignore.New(testFs)
if err := ignores.Parse(bytes.NewBufferString(stignore), ".stignore"); err != nil { if err := ignores.Parse(bytes.NewBufferString(stignore), ".stignore"); err != nil {
t.Fatal(err) t.Fatal(err)
} }
@@ -859,7 +859,7 @@ func TestSkipIgnoredDirs(t *testing.T) {
w := &walker{} w := &walker{}
pats := ignore.New(fss, ignore.WithCache(true)) pats := ignore.New(fss)
stignore := ` stignore := `
/foo/ign* /foo/ign*
@@ -892,7 +892,7 @@ func TestIncludedSubdir(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
pats := ignore.New(fss, ignore.WithCache(true)) pats := ignore.New(fss)
stignore := ` stignore := `
!/foo/bar !/foo/bar
-1
View File
@@ -91,7 +91,6 @@ type Report struct {
NATType string `json:"natType,omitempty" metric:"nat_detection,gaugeVec:type" since:"3"` NATType string `json:"natType,omitempty" metric:"nat_detection,gaugeVec:type" since:"3"`
AlwaysLocalNets bool `json:"alwaysLocalNets,omitempty" metric:"feature_count{feature=AlwaysLocalNets},gauge" since:"3"` AlwaysLocalNets bool `json:"alwaysLocalNets,omitempty" metric:"feature_count{feature=AlwaysLocalNets},gauge" since:"3"`
CacheIgnoredFiles bool `json:"cacheIgnoredFiles,omitempty" metric:"feature_count{feature=CacheIgnoredFiles},gauge" since:"3"`
OverwriteRemoteDeviceNames bool `json:"overwriteRemoteDeviceNames,omitempty" metric:"feature_count{feature=OverwriteRemoteDeviceNames},gauge" since:"3"` OverwriteRemoteDeviceNames bool `json:"overwriteRemoteDeviceNames,omitempty" metric:"feature_count{feature=OverwriteRemoteDeviceNames},gauge" since:"3"`
ProgressEmitterEnabled bool `json:"progressEmitterEnabled,omitempty" metric:"feature_count{feature=ProgressEmitterEnabled},gauge" since:"3"` ProgressEmitterEnabled bool `json:"progressEmitterEnabled,omitempty" metric:"feature_count{feature=ProgressEmitterEnabled},gauge" since:"3"`
CustomDefaultFolderPath bool `json:"customDefaultFolderPath,omitempty" metric:"feature_count{feature=CustomDefaultFolderPath},gauge" since:"3"` CustomDefaultFolderPath bool `json:"customDefaultFolderPath,omitempty" metric:"feature_count{feature=CustomDefaultFolderPath},gauge" since:"3"`
-1
View File
@@ -217,7 +217,6 @@ func (s *Service) reportData(ctx context.Context, urVersion int, preview bool) (
report.Uptime = s.UptimeS() report.Uptime = s.UptimeS()
report.NATType = s.connectionsService.NATType() report.NATType = s.connectionsService.NATType()
report.AlwaysLocalNets = len(opts.AlwaysLocalNets) > 0 report.AlwaysLocalNets = len(opts.AlwaysLocalNets) > 0
report.CacheIgnoredFiles = opts.CacheIgnoredFiles
report.OverwriteRemoteDeviceNames = opts.OverwriteRemoteDevNames report.OverwriteRemoteDeviceNames = opts.OverwriteRemoteDevNames
report.ProgressEmitterEnabled = opts.ProgressUpdateIntervalS > -1 report.ProgressEmitterEnabled = opts.ProgressUpdateIntervalS > -1
report.CustomDefaultFolderPath = defaultFolder.Path != "~" report.CustomDefaultFolderPath = defaultFolder.Path != "~"
-1
View File
@@ -101,7 +101,6 @@
<autoUpgradeIntervalH>12</autoUpgradeIntervalH> <autoUpgradeIntervalH>12</autoUpgradeIntervalH>
<upgradeToPreReleases>false</upgradeToPreReleases> <upgradeToPreReleases>false</upgradeToPreReleases>
<keepTemporariesH>24</keepTemporariesH> <keepTemporariesH>24</keepTemporariesH>
<cacheIgnoredFiles>false</cacheIgnoredFiles>
<progressUpdateIntervalS>5</progressUpdateIntervalS> <progressUpdateIntervalS>5</progressUpdateIntervalS>
<limitBandwidthInLan>false</limitBandwidthInLan> <limitBandwidthInLan>false</limitBandwidthInLan>
<minHomeDiskFree unit="%">1</minHomeDiskFree> <minHomeDiskFree unit="%">1</minHomeDiskFree>
-1
View File
@@ -99,7 +99,6 @@
<autoUpgradeIntervalH>12</autoUpgradeIntervalH> <autoUpgradeIntervalH>12</autoUpgradeIntervalH>
<upgradeToPreReleases>false</upgradeToPreReleases> <upgradeToPreReleases>false</upgradeToPreReleases>
<keepTemporariesH>24</keepTemporariesH> <keepTemporariesH>24</keepTemporariesH>
<cacheIgnoredFiles>false</cacheIgnoredFiles>
<progressUpdateIntervalS>5</progressUpdateIntervalS> <progressUpdateIntervalS>5</progressUpdateIntervalS>
<limitBandwidthInLan>true</limitBandwidthInLan> <limitBandwidthInLan>true</limitBandwidthInLan>
<minHomeDiskFree unit="%">1</minHomeDiskFree> <minHomeDiskFree unit="%">1</minHomeDiskFree>
-1
View File
@@ -117,7 +117,6 @@
<autoUpgradeIntervalH>12</autoUpgradeIntervalH> <autoUpgradeIntervalH>12</autoUpgradeIntervalH>
<upgradeToPreReleases>false</upgradeToPreReleases> <upgradeToPreReleases>false</upgradeToPreReleases>
<keepTemporariesH>24</keepTemporariesH> <keepTemporariesH>24</keepTemporariesH>
<cacheIgnoredFiles>false</cacheIgnoredFiles>
<progressUpdateIntervalS>5</progressUpdateIntervalS> <progressUpdateIntervalS>5</progressUpdateIntervalS>
<limitBandwidthInLan>false</limitBandwidthInLan> <limitBandwidthInLan>false</limitBandwidthInLan>
<minHomeDiskFree unit="%">1</minHomeDiskFree> <minHomeDiskFree unit="%">1</minHomeDiskFree>
-1
View File
@@ -69,7 +69,6 @@
<autoUpgradeIntervalH>12</autoUpgradeIntervalH> <autoUpgradeIntervalH>12</autoUpgradeIntervalH>
<upgradeToPreReleases>false</upgradeToPreReleases> <upgradeToPreReleases>false</upgradeToPreReleases>
<keepTemporariesH>24</keepTemporariesH> <keepTemporariesH>24</keepTemporariesH>
<cacheIgnoredFiles>false</cacheIgnoredFiles>
<progressUpdateIntervalS>5</progressUpdateIntervalS> <progressUpdateIntervalS>5</progressUpdateIntervalS>
<limitBandwidthInLan>false</limitBandwidthInLan> <limitBandwidthInLan>false</limitBandwidthInLan>
<minHomeDiskFree unit="%">1</minHomeDiskFree> <minHomeDiskFree unit="%">1</minHomeDiskFree>