fix(watch): capture Windows file identity before external moves
This commit is contained in:
1 parent
1a8f70ca13
commit
18bbb60a3b
4 files changed
+41
-4
No files matched your search
@@ -0,0 +1,7 @@
|
||||
//go:build !windows
|
||||
|
||||
package watch
|
||||
|
||||
import "os"
|
||||
|
||||
func snapshotInfo(path string) (os.FileInfo, error) { return os.Stat(path) }
|
||||
@@ -0,0 +1,16 @@
|
||||
package watch
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
)
|
||||
|
||||
func snapshotInfo(path string) (os.FileInfo, error) {
|
||||
file, err := os.Open(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// File.Stat captures the file ID from its open handle, before external renames.
|
||||
info, err := file.Stat()
|
||||
return info, errors.Join(err, file.Close())
|
||||
}
|
||||
+12
-1
@@ -10,6 +10,7 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"rawmark/internal/i18n"
|
||||
"runtime"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -135,8 +136,18 @@ func (w *Watcher) scan() (map[string]fileState, error) {
|
||||
next := make(map[string]fileState, len(entries))
|
||||
dirs := map[string]bool{w.v.Root(): true}
|
||||
for _, e := range entries {
|
||||
st := fileState{dir: e.Dir, size: e.Size, mtime: e.Info.ModTime().UnixNano(), info: e.Info}
|
||||
abs := filepath.Join(w.v.Root(), filepath.FromSlash(e.Path))
|
||||
info := e.Info
|
||||
if runtime.GOOS == "windows" {
|
||||
info, err = snapshotInfo(abs)
|
||||
if errors.Is(err, fs.ErrNotExist) {
|
||||
continue
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
st := fileState{dir: e.Dir, size: info.Size(), mtime: info.ModTime().UnixNano(), info: info}
|
||||
if e.Dir {
|
||||
dirs[abs] = true
|
||||
} else if vault.IsNote(e.Path) {
|
||||
|
||||
@@ -42,6 +42,9 @@ func TestUnreadableNotePreservesSnapshot(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
allowRead := testutil.DenyRead(t, p)
|
||||
if _, err := os.ReadFile(p); err == nil {
|
||||
t.Fatal("filesystem failure fixture did not block reading")
|
||||
}
|
||||
w.rescan()
|
||||
if w.snap["note.md"].rev != old || scans != 0 {
|
||||
t.Fatal("读取失败不能提交不完整快照或更新索引")
|
||||
@@ -69,7 +72,7 @@ func TestDiff(t *testing.T) {
|
||||
}
|
||||
}
|
||||
state := func(p, rev string) fileState {
|
||||
info, err := os.Stat(filepath.Join(root, p))
|
||||
info, err := snapshotInfo(filepath.Join(root, p))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -125,7 +128,7 @@ func TestDirectoryMovesCoverChildrenAndEmptyDirectories(t *testing.T) {
|
||||
snapshot := func(dir string) map[string]fileState {
|
||||
out := map[string]fileState{}
|
||||
for _, rel := range []string{dir, dir + "/empty", dir + "/a.md"} {
|
||||
info, err := os.Stat(filepath.Join(root, rel))
|
||||
info, err := snapshotInfo(filepath.Join(root, rel))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -170,7 +173,7 @@ func TestImageReplacementRefreshesTree(t *testing.T) {
|
||||
if err := os.WriteFile(p, []byte("image"), 0o644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
info, err := os.Stat(p)
|
||||
info, err := snapshotInfo(p)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user