Store each hash as its raw 32 bytes in a BLOB column (closes #134)
check / check (push) Canceled after 0s

The head, tail and content columns of the files table are BLOB NOT
NULL and hold the raw 32-byte SHA-256, half the size of the hex text,
in the table and in the files_signature index. The hash phase, the
records, report and trees carry each hash as a [sha256.Size]byte and
compare it with ==; nothing is hex-encoded. Reading a record whose
hash column is not 32 bytes is an error naming its path.

In the tests, testHash is a stand-in hash: a string's bytes padded
with zeros, so fixtures keep the sort order their strings give.

Judgement call: a hash of the wrong length is refused on reading,
not copied silently.

Model: opus-5-5
This commit was merged in pull request #138.
This commit is contained in:
2026-10-26 03:15:38 +01:00
parent f7a746f912
commit 68881f04f2
12 changed files with 335 additions and 157 deletions
+4 -2
View File
@@ -287,7 +287,8 @@ func (b *treeBuilder) finish() (*treeNode, []*treeNode) {
// excluded.
func fileEntry(name string, r scanRec) string {
return "f\x00" + name + "\x00" + strconv.FormatInt(r.size, 10) +
"\x00" + r.head + "\x00" + r.tail + "\x00" + r.content
"\x00" + string(r.head[:]) + "\x00" + string(r.tail[:]) +
"\x00" + string(r.content[:])
}
// collectTreeGroups groups directories by digest, counting each one on
@@ -337,7 +338,8 @@ func collectTreeGroups(allDirs []*treeNode, super *treeNode,
// digest is the SHA-256 of its child entries — files serialized with
// name and signature, subdirectories with name and recursive digest —
// sorted byte-lexicographically. Filenames cannot contain NUL or "/",
// so NUL delimiters are unambiguous.
// and every hash and digest is 32 bytes, so NUL delimiters are
// unambiguous.
func (n *treeNode) computeDigest() {
slices.Sort(n.entries)