Author SHA1 Message Date
sneak f373b7d9e4 Carry the README changes from next into the rewrite
check / check (push) Canceled after 0s
`next` changed the old README after this branch was cut, in
#146 (the `totals` table, commit
98193b2) and #147 (`disk_size`,
commit 4ed2e37). This commit states the same facts in the rewritten
README, in the places #140 (comment)
lists, and changes nothing else. Every statement was checked against the
code on `next`.

Model: fable-5-1
2026-11-02 22:02:50 +00:00
sneak d8b58bf281 Rewrite README.md sentence by sentence for a newcomer
Rewrites `README.md` from scratch for
#135. Every statement was checked
against the code on `next` and against what the built binary prints on
small trees under `/var/tmp`. The headings that the code comments cite
are kept.

Where the old text and the repository disagreed, the new text states
what the code does. #140 lists
every such point. Among them: `@latest` installs the newest tagged
release, ten million files in both "Rationale" and "Design", no Windows
excludes, a relative operand is joined to the working directory as `pwd`
prints it, and an operand reached through a symlinked directory below
another operand is dropped (#144).

Dropped from "Constraints": the permission to add `github.com/spf13/viper`
if configuration-file support is ever needed.

Model: fable-5-1
2026-11-02 21:29:48 +00:00
2 changed files with 459 additions and 643 deletions

No files matched your search

+436 -600
View File
File diff suppressed because it is too large. Load diff
+23 -43
View File
@@ -43,13 +43,10 @@ func writeFile(t *testing.T, dir, name string, data []byte) string {
} }
// diskSizeOf returns the bytes the file at path takes on disk, from its // diskSizeOf returns the bytes the file at path takes on disk, from its
// own lstat: st_blocks counts 512-byte units. ZFS counts a write's // own lstat: st_blocks counts 512-byte units. ZFS counts a new file's
// blocks only when it commits the write, seconds later and sometimes in // blocks only seconds after the write, and fsync does not hasten that.
// steps, so this value can change at any moment. fsync does not hasten // A test therefore calls unix.Sync, which waits for it, before the scan
// that. unix.Sync usually does, but it can return before the blocks are // whose disk_size it compares with this.
// counted. A test that needs the value to change therefore waits for
// that, and a test that compares it with a scan's disk_size reads it
// before and after the scan and accepts either.
func diskSizeOf(t *testing.T, path string) int64 { func diskSizeOf(t *testing.T, path string) int64 {
t.Helper() t.Helper()
@@ -1449,26 +1446,22 @@ func TestScanStoresDiskSize(t *testing.T) {
p := sparseFile(t, t.TempDir(), "sparse", length) p := sparseFile(t, t.TempDir(), "sparse", length)
pokeAt(t, p, 0, []byte("x")) pokeAt(t, p, 0, []byte("x"))
size := diskSizeOf(t, p) unix.Sync() // see diskSizeOf
if size == 0 || size >= length {
want := diskSizeOf(t, p)
if want == 0 || want >= length {
t.Fatalf("the file takes %d bytes on disk, want more than none "+ t.Fatalf("the file takes %d bytes on disk, want more than none "+
"and fewer than its length %d", size, length) "and fewer than its length %d", want, length)
} }
for _, operand := range []string{filepath.Dir(p), p} { for _, operand := range []string{filepath.Dir(p), p} {
db := openTestDB(t) db := openTestDB(t)
// ZFS may count the blocks during the scan; see diskSizeOf.
before := diskSizeOf(t, p)
syncTree(t, db, operand) syncTree(t, db, operand)
after := diskSizeOf(t, p)
got := recordByPath(t, dbRecords(t, db), p).diskSize got := recordByPath(t, dbRecords(t, db), p).diskSize
if got != before && got != after { if got != want {
t.Errorf("scan of %s: disk_size = %d, want %d or %d", t.Errorf("scan of %s: disk_size = %d, want %d",
operand, got, before, after) operand, got, want)
} }
} }
} }
@@ -1486,7 +1479,7 @@ func TestSyncScanRewriteUpdatesDiskSize(t *testing.T) {
syncTree(t, db, dir) syncTree(t, db, dir)
recorded := recordByPath(t, dbRecords(t, db), p).diskSize before := recordByPath(t, dbRecords(t, db), p).diskSize
pokeAt(t, p, 512*1024, pattern(7, 64*1024)) pokeAt(t, p, 512*1024, pattern(7, 64*1024))
@@ -1499,31 +1492,22 @@ func TestSyncScanRewriteUpdatesDiskSize(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
// Wait for ZFS to count the write; see diskSizeOf. unix.Sync() // see diskSizeOf
deadline := time.Now().Add(30 * time.Second)
for diskSizeOf(t, p) == recorded {
if time.Now().After(deadline) {
t.Fatalf("the file still takes %d bytes on disk 30 seconds "+
"after the write into its hole", recorded)
}
unix.Sync() want := diskSizeOf(t, p)
if want == before {
t.Fatalf("the file still takes %d bytes on disk after the write "+
"into its hole", want)
} }
// ZFS may still be counting, so the size may change during the rescan.
before := diskSizeOf(t, p)
st := syncTree(t, db, dir) st := syncTree(t, db, dir)
after := diskSizeOf(t, p)
if st != (scanStats{walked: 1, updated: 1}) { if st != (scanStats{walked: 1, updated: 1}) {
t.Fatalf("rescan stats = %+v, want 1 updated", st) t.Fatalf("rescan stats = %+v, want 1 updated", st)
} }
got := recordByPath(t, dbRecords(t, db), p).diskSize got := recordByPath(t, dbRecords(t, db), p).diskSize
if got != before && got != after { if got != want {
t.Errorf("disk_size = %d, want %d or %d", got, before, after) t.Errorf("disk_size = %d, want %d", got, want)
} }
} }
@@ -1547,21 +1531,17 @@ func TestSyncScanUnchangedUpdatesDiskSize(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
// ZFS may count the blocks during the rescan; see diskSizeOf. unix.Sync() // see diskSizeOf
before := diskSizeOf(t, p)
// A file read again would count as updated. // A file read again would count as updated.
st := syncTree(t, db, dir) st := syncTree(t, db, dir)
after := diskSizeOf(t, p)
if st != (scanStats{walked: 1, unchanged: 1}) { if st != (scanStats{walked: 1, unchanged: 1}) {
t.Fatalf("rescan stats = %+v, want 1 unchanged", st) t.Fatalf("rescan stats = %+v, want 1 unchanged", st)
} }
got := recordByPath(t, dbRecords(t, db), p).diskSize got := recordByPath(t, dbRecords(t, db), p).diskSize
if got != before && got != after { if want := diskSizeOf(t, p); got != want {
t.Errorf("disk_size = %d, want %d or %d", got, before, after) t.Errorf("disk_size = %d, want %d", got, want)
} }
} }