Count a file as deleted only if the previous snapshot of its name had it (closes #304)
check / check (push) Successful in 11m35s

A backup counted as deleted every file in the local index under the
source path that the walk did not find: files the snapshot excludes
but another snapshot backs up, and files removed long ago, which every
later backup, and the first backup of another name over the same
path, reported as deleted again.

The backup now counts only files that the latest completed snapshot of
the same name lists, the walk did not find, and are gone from disk,
including a file whose directory became a regular file. Excluded files
and files the walk could not reach under --skip-errors are not
counted.

Only the backup's output changes; the snapshots row stores no deleted
figure.

Model: opus-5-5
This commit was merged in pull request #342.
This commit is contained in:
2026-10-14 04:24:30 +02:00
parent d156a02c49
commit 54ad279dd6
8 changed files with 481 additions and 50 deletions
+15 -11
View File
@@ -16,6 +16,9 @@ type ScannerParams struct {
Fs afero.Fs
Exclude []string // Exclude patterns (combined global + snapshot-specific)
SkipErrors bool // Skip file read errors (log loudly but continue)
// PreviousSnapshotID is the most recent completed snapshot of the
// same name, "" for none.
PreviousSnapshotID string
}
// Module exports backup functionality as an fx module.
@@ -44,17 +47,18 @@ func provideScannerFactory(
}
return NewScanner(ScannerConfig{
FS: params.Fs,
ChunkSize: cfg.ChunkSize.Int64(),
Repositories: repos,
Storage: storer,
MaxBlobSize: cfg.BlobSizeLimit.Int64(),
CompressionLevel: cfg.CompressionLevel,
AgeRecipients: cfg.AgeRecipients,
EnableProgress: params.EnableProgress,
UI: params.UI,
Exclude: excludes,
SkipErrors: params.SkipErrors,
FS: params.Fs,
ChunkSize: cfg.ChunkSize.Int64(),
Repositories: repos,
Storage: storer,
MaxBlobSize: cfg.BlobSizeLimit.Int64(),
CompressionLevel: cfg.CompressionLevel,
AgeRecipients: cfg.AgeRecipients,
EnableProgress: params.EnableProgress,
UI: params.UI,
Exclude: excludes,
SkipErrors: params.SkipErrors,
PreviousSnapshotID: params.PreviousSnapshotID,
})
}
}