Keep records on other filesystems under an operand with -x (closes #99)
check / check (push) Canceled after 0s
check / check (push) Canceled after 0s
With -x the walk does not enter a directory on another filesystem than its operand, but the update phase still deleted every record below it. A separate scan of a filesystem mounted under the operand therefore lost its records on every -x scan and re-hashed its whole tree. The walk now sends an event naming each directory it declines this way, and the walk phase treats the records below those directories like records outside the operands: they are not deleted, and their sizes join the size census. README "scan mode" and "Rules for the walk" state the rule. Model: opus-5-5
This commit was merged in pull request #105.
This commit is contained in:
@@ -391,15 +391,26 @@ func (s *scanState) loadIndex(ctx context.Context, roots []string) error {
|
||||
// (the content phase may still fill in its content hash). It returns
|
||||
// the new-or-changed files and the unchanged files whose records lack
|
||||
// hashes; both remain candidates until the census decides whether
|
||||
// their sizes are shared.
|
||||
// their sizes are shared. A record below a directory -x did not enter
|
||||
// counts as outside the roots: it is not deleted, and its size joins
|
||||
// the census.
|
||||
func (s *scanState) walkPhase(
|
||||
events <-chan walkEvent,
|
||||
) ([]fileRec, []fileRec) {
|
||||
prog := newProgress("walk", -1)
|
||||
|
||||
var changed, unhashed []fileRec
|
||||
var (
|
||||
changed, unhashed []fileRec
|
||||
otherFSDirs []string
|
||||
)
|
||||
|
||||
for ev := range events {
|
||||
if ev.otherFSDir != "" {
|
||||
otherFSDirs = append(otherFSDirs, ev.otherFSDir)
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
if ev.fail {
|
||||
s.st.skipped++
|
||||
|
||||
@@ -429,6 +440,14 @@ func (s *scanState) walkPhase(
|
||||
|
||||
prog.finish()
|
||||
|
||||
for path, old := range s.existing {
|
||||
if underAnyRoot(path, otherFSDirs) {
|
||||
delete(s.existing, path)
|
||||
|
||||
s.sizes = append(s.sizes, old.size)
|
||||
}
|
||||
}
|
||||
|
||||
return changed, unhashed
|
||||
}
|
||||
|
||||
@@ -866,16 +885,20 @@ type dirJob struct {
|
||||
}
|
||||
|
||||
// walkEvent is one walk result delivered to the walk phase: a regular
|
||||
// file's statted record, or a warning when fail is set.
|
||||
// file's statted record, a warning when fail is set, or, when
|
||||
// otherFSDir is set, a directory -x did not enter because it is on
|
||||
// another filesystem than its operand.
|
||||
type walkEvent struct {
|
||||
rec fileRec
|
||||
warn string
|
||||
fail bool
|
||||
rec fileRec
|
||||
warn string
|
||||
fail bool
|
||||
otherFSDir string
|
||||
}
|
||||
|
||||
// startWalk seeds every root into the shared walk worker pool and
|
||||
// returns the event stream: one record per regular file, one warning
|
||||
// event per per-path error. The channel is closed when the walk
|
||||
// event per per-path error, and one event per directory -x did not
|
||||
// enter. The channel is closed when the walk
|
||||
// completes, and also when ctx is cancelled — every goroutine in the
|
||||
// pool abandons its blocking send in that case, so the consumer sees a
|
||||
// truncated but properly terminated stream instead of a stalled one.
|
||||
@@ -1144,7 +1167,8 @@ func emitFile(ctx context.Context, p string, e fs.DirEntry,
|
||||
// subdirJob applies the descent rules to directory p: never enter
|
||||
// .zfs (ZFS snapshot pseudo-dirs would list every file once per
|
||||
// snapshot), and with -x never enter a directory on a different
|
||||
// filesystem than its operand.
|
||||
// filesystem than its operand, sending an event that names it so the
|
||||
// scan keeps the records below it.
|
||||
func subdirJob(ctx context.Context, p string, e fs.DirEntry,
|
||||
parent dirJob, oneFS bool, events chan<- walkEvent,
|
||||
) (dirJob, bool) {
|
||||
@@ -1168,6 +1192,8 @@ func subdirJob(ctx context.Context, p string, e fs.DirEntry,
|
||||
}
|
||||
|
||||
if dev, ok := deviceOfInfo(info); ok && dev != parent.rootDev {
|
||||
sendEvent(ctx, events, walkEvent{otherFSDir: p})
|
||||
|
||||
return dirJob{}, false
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user