mirror of
https://github.com/octoleo/syncthing.git
synced 2024-11-08 22:31:04 +00:00
Don't ignore ignored items forever (fixes #816)
This commit is contained in:
parent
2090530bbb
commit
7ac84c0660
@ -353,3 +353,69 @@ flamingo
|
|||||||
result = pats.Match("filename")
|
result = pats.Match("filename")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestCacheReload(t *testing.T) {
|
||||||
|
fd, err := ioutil.TempFile("", "")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
defer fd.Close()
|
||||||
|
defer os.Remove(fd.Name())
|
||||||
|
|
||||||
|
// Ignore file matches f1 and f2
|
||||||
|
|
||||||
|
_, err = fd.WriteString("f1\nf2\n")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
pats, err := Load(fd.Name(), true)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify that both are ignored
|
||||||
|
|
||||||
|
if !pats.Match("f1") {
|
||||||
|
t.Error("Unexpected non-match for f1")
|
||||||
|
}
|
||||||
|
if !pats.Match("f2") {
|
||||||
|
t.Error("Unexpected non-match for f2")
|
||||||
|
}
|
||||||
|
if pats.Match("f3") {
|
||||||
|
t.Error("Unexpected match for f3")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rewrite file to match f1 and f3
|
||||||
|
|
||||||
|
err = fd.Truncate(0)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
_, err = fd.Seek(0, os.SEEK_SET)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
_, err = fd.WriteString("f1\nf3\n")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
pats, err = Load(fd.Name(), true)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify that the new patterns are in effect
|
||||||
|
|
||||||
|
if !pats.Match("f1") {
|
||||||
|
t.Error("Unexpected non-match for f1")
|
||||||
|
}
|
||||||
|
if pats.Match("f2") {
|
||||||
|
t.Error("Unexpected match for f2")
|
||||||
|
}
|
||||||
|
if !pats.Match("f3") {
|
||||||
|
t.Error("Unexpected non-match for f3")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -143,26 +143,27 @@ func (w *Walker) walkAndHashFiles(fchan chan protocol.FileInfo) filepath.WalkFun
|
|||||||
|
|
||||||
// Index wise symlinks are always files, regardless of what the target
|
// Index wise symlinks are always files, regardless of what the target
|
||||||
// is, because symlinks carry their target path as their content.
|
// is, because symlinks carry their target path as their content.
|
||||||
if info.Mode()&os.ModeSymlink != 0 {
|
if info.Mode()&os.ModeSymlink == os.ModeSymlink {
|
||||||
var rval error
|
var rval error
|
||||||
// If the target is a directory, do NOT descend down there.
|
// If the target is a directory, do NOT descend down there. This
|
||||||
// This will cause files to get tracked, and removing the symlink
|
// will cause files to get tracked, and removing the symlink will
|
||||||
// will as a result remove files in their real location.
|
// as a result remove files in their real location. But do not
|
||||||
// But do not SkipDir if the target is not a directory, as it will
|
// SkipDir if the target is not a directory, as it will stop
|
||||||
// stop scanning the current directory.
|
// scanning the current directory.
|
||||||
if info.IsDir() {
|
if info.IsDir() {
|
||||||
rval = filepath.SkipDir
|
rval = filepath.SkipDir
|
||||||
}
|
}
|
||||||
|
|
||||||
// We always rehash symlinks as they have no modtime or
|
// If we don't support symlinks, skip.
|
||||||
// permissions.
|
if !symlinks.Supported {
|
||||||
// We check if they point to the old target by checking that
|
|
||||||
// their existing blocks match with the blocks in the index.
|
|
||||||
// If we don't have a filer or don't support symlinks, skip.
|
|
||||||
if w.CurrentFiler == nil || !symlinks.Supported {
|
|
||||||
return rval
|
return rval
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We always rehash symlinks as they have no modtime or
|
||||||
|
// permissions. We check if they point to the old target by
|
||||||
|
// checking that their existing blocks match with the blocks in
|
||||||
|
// the index.
|
||||||
|
|
||||||
target, flags, err := symlinks.Read(p)
|
target, flags, err := symlinks.Read(p)
|
||||||
flags = flags & protocol.SymlinkTypeMask
|
flags = flags & protocol.SymlinkTypeMask
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -180,9 +181,17 @@ func (w *Walker) walkAndHashFiles(fchan chan protocol.FileInfo) filepath.WalkFun
|
|||||||
return rval
|
return rval
|
||||||
}
|
}
|
||||||
|
|
||||||
cf := w.CurrentFiler.CurrentFile(rn)
|
if w.CurrentFiler != nil {
|
||||||
if !cf.IsDeleted() && cf.IsSymlink() && SymlinkTypeEqual(flags, cf.Flags) && BlocksEqual(cf.Blocks, blocks) {
|
// A symlink is "unchanged", if
|
||||||
return rval
|
// - it wasn't deleted (because it isn't now)
|
||||||
|
// - it was a symlink
|
||||||
|
// - it wasn't invalid
|
||||||
|
// - the symlink type (file/dir) was the same
|
||||||
|
// - the block list (i.e. hash of target) was the same
|
||||||
|
cf := w.CurrentFiler.CurrentFile(rn)
|
||||||
|
if !cf.IsDeleted() && cf.IsSymlink() && !cf.IsInvalid() && SymlinkTypeEqual(flags, cf.Flags) && BlocksEqual(cf.Blocks, blocks) {
|
||||||
|
return rval
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
f := protocol.FileInfo{
|
f := protocol.FileInfo{
|
||||||
@ -204,9 +213,15 @@ func (w *Walker) walkAndHashFiles(fchan chan protocol.FileInfo) filepath.WalkFun
|
|||||||
|
|
||||||
if info.Mode().IsDir() {
|
if info.Mode().IsDir() {
|
||||||
if w.CurrentFiler != nil {
|
if w.CurrentFiler != nil {
|
||||||
|
// A directory is "unchanged", if it
|
||||||
|
// - has the same permissions as previously, unless we are ignoring permissions
|
||||||
|
// - was not marked deleted (since it apparently exists now)
|
||||||
|
// - was a directory previously (not a file or something else)
|
||||||
|
// - was not a symlink (since it's a directory now)
|
||||||
|
// - was not invalid (since it looks valid now)
|
||||||
cf := w.CurrentFiler.CurrentFile(rn)
|
cf := w.CurrentFiler.CurrentFile(rn)
|
||||||
permUnchanged := w.IgnorePerms || !cf.HasPermissionBits() || PermsEqual(cf.Flags, uint32(info.Mode()))
|
permUnchanged := w.IgnorePerms || !cf.HasPermissionBits() || PermsEqual(cf.Flags, uint32(info.Mode()))
|
||||||
if !cf.IsDeleted() && cf.IsDirectory() && permUnchanged && !cf.IsSymlink() {
|
if permUnchanged && !cf.IsDeleted() && cf.IsDirectory() && !cf.IsSymlink() && !cf.IsInvalid() {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -232,9 +247,16 @@ func (w *Walker) walkAndHashFiles(fchan chan protocol.FileInfo) filepath.WalkFun
|
|||||||
|
|
||||||
if info.Mode().IsRegular() {
|
if info.Mode().IsRegular() {
|
||||||
if w.CurrentFiler != nil {
|
if w.CurrentFiler != nil {
|
||||||
|
// A file is "unchanged", if it
|
||||||
|
// - has the same permissions as previously, unless we are ignoring permissions
|
||||||
|
// - was not marked deleted (since it apparently exists now)
|
||||||
|
// - had the same modification time as it has now
|
||||||
|
// - was not a directory previously (since it's a file now)
|
||||||
|
// - was not a symlink (since it's a file now)
|
||||||
|
// - was not invalid (since it looks valid now)
|
||||||
cf := w.CurrentFiler.CurrentFile(rn)
|
cf := w.CurrentFiler.CurrentFile(rn)
|
||||||
permUnchanged := w.IgnorePerms || !cf.HasPermissionBits() || PermsEqual(cf.Flags, uint32(info.Mode()))
|
permUnchanged := w.IgnorePerms || !cf.HasPermissionBits() || PermsEqual(cf.Flags, uint32(info.Mode()))
|
||||||
if !cf.IsDeleted() && cf.Modified == info.ModTime().Unix() && permUnchanged {
|
if permUnchanged && !cf.IsDeleted() && cf.Modified == info.ModTime().Unix() && !cf.IsDirectory() && !cf.IsSymlink() && !cf.IsInvalid() {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
146
test/ignore_test.go
Normal file
146
test/ignore_test.go
Normal file
@ -0,0 +1,146 @@
|
|||||||
|
// Copyright (C) 2014 The Syncthing Authors.
|
||||||
|
//
|
||||||
|
// This program is free software: you can redistribute it and/or modify it
|
||||||
|
// under the terms of the GNU General Public License as published by the Free
|
||||||
|
// Software Foundation, either version 3 of the License, or (at your option)
|
||||||
|
// any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
// more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License along
|
||||||
|
// with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
// +build integration
|
||||||
|
|
||||||
|
package integration
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/syncthing/syncthing/internal/symlinks"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestIgnores(t *testing.T) {
|
||||||
|
// Clean and start a syncthing instance
|
||||||
|
|
||||||
|
log.Println("Cleaning...")
|
||||||
|
err := removeAll("s1", "h1/index")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
p := syncthingProcess{ // id1
|
||||||
|
log: "1.out",
|
||||||
|
argv: []string{"-home", "h1"},
|
||||||
|
port: 8081,
|
||||||
|
apiKey: apiKey,
|
||||||
|
}
|
||||||
|
err = p.start()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer p.stop()
|
||||||
|
|
||||||
|
// Create eight empty files and directories
|
||||||
|
|
||||||
|
files := []string{"f1", "f2", "f3", "f4", "f11", "f12", "f13", "f14"}
|
||||||
|
dirs := []string{"d1", "d2", "d3", "d4", "d11", "d12", "d13", "d14"}
|
||||||
|
all := append(files, dirs...)
|
||||||
|
|
||||||
|
for _, file := range files {
|
||||||
|
fd, err := os.Create(filepath.Join("s1", file))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
fd.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, dir := range dirs {
|
||||||
|
err := os.Mkdir(filepath.Join("s1", dir), 0755)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var syms []string
|
||||||
|
if symlinksSupported() {
|
||||||
|
syms = []string{"s1", "s2", "s3", "s4", "s11", "s12", "s13", "s14"}
|
||||||
|
for _, sym := range syms {
|
||||||
|
p := filepath.Join("s1", sym)
|
||||||
|
symlinks.Create(p, p, 0)
|
||||||
|
}
|
||||||
|
all = append(all, syms...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rescan and verify that we see them all
|
||||||
|
|
||||||
|
p.post("/rest/scan?folder=default", nil)
|
||||||
|
m, err := p.model("default")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
expected := len(all) // nothing is ignored
|
||||||
|
if m.LocalFiles != expected {
|
||||||
|
t.Fatalf("Incorrect number of files after initial scan, %d != %d", m.LocalFiles, expected)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add some of them to an ignore file
|
||||||
|
|
||||||
|
fd, err := os.Create("s1/.stignore")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
_, err = fd.WriteString("f1*\nf2\nd1*\nd2\ns1*\ns2") // [fds][34] only non-ignored items
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
err = fd.Close()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rescan and verify that we see them
|
||||||
|
|
||||||
|
p.post("/rest/scan?folder=default", nil)
|
||||||
|
m, err = p.model("default")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
expected = len(all) * 2 / 8 // two out of eight items of each type should remain
|
||||||
|
if m.LocalFiles != expected {
|
||||||
|
t.Fatalf("Incorrect number of files after first ignore, %d != %d", m.LocalFiles, expected)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Change the pattern to include some of the files and dirs previously ignored
|
||||||
|
|
||||||
|
fd, err = os.Create("s1/.stignore")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
_, err = fd.WriteString("f2\nd2\ns2\n")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
err = fd.Close()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rescan and verify that we see them
|
||||||
|
|
||||||
|
p.post("/rest/scan?folder=default", nil)
|
||||||
|
m, err = p.model("default")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
expected = len(all) * 7 / 8 // seven out of eight items of each type should remain
|
||||||
|
if m.LocalFiles != expected {
|
||||||
|
t.Fatalf("Incorrect number of files after second ignore, %d != %d", m.LocalFiles, expected)
|
||||||
|
}
|
||||||
|
}
|
@ -173,6 +173,38 @@ func (p *syncthingProcess) peerCompletion() (map[string]int, error) {
|
|||||||
return comp, err
|
return comp, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type model struct {
|
||||||
|
GlobalBytes int
|
||||||
|
GlobalDeleted int
|
||||||
|
GlobalFiles int
|
||||||
|
InSyncBytes int
|
||||||
|
InSyncFiles int
|
||||||
|
Invalid string
|
||||||
|
LocalBytes int
|
||||||
|
LocalDeleted int
|
||||||
|
LocalFiles int
|
||||||
|
NeedBytes int
|
||||||
|
NeedFiles int
|
||||||
|
State string
|
||||||
|
StateChanged time.Time
|
||||||
|
Version int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *syncthingProcess) model(folder string) (model, error) {
|
||||||
|
resp, err := p.get("/rest/model?folder=" + folder)
|
||||||
|
if err != nil {
|
||||||
|
return model{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var res model
|
||||||
|
err = json.NewDecoder(resp.Body).Decode(&res)
|
||||||
|
if err != nil {
|
||||||
|
return model{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
|
||||||
type event struct {
|
type event struct {
|
||||||
ID int
|
ID int
|
||||||
Time time.Time
|
Time time.Time
|
||||||
|
Loading…
Reference in New Issue
Block a user