Artifact fd0db43f915880bd73991ea0380d1a4d774c16adb06276fa5ebf439a05adadb1:
- Executable file
scripts/archive-compare.py
— part of check-in
[9033c13f21]
at
2026-09-17 17:04:25
on branch trunk
— Remove issue-tracker numbers from source and documents
The tracker is disposable and does not live with the source, so a number is a dangling pointer. Every zit#NN and #NN reference in code comments, test names, help text, smoke labels, identifiers, release notes and errata is replaced by enough context to stand alone, or by a short plain statement of the problem. No behaviour changed. (user: dmestas, size: 3144) [annotate] [blame] [check-ins using]
#!/usr/bin/env python3 """Compare two unpacked-archive descriptions. Each input is one member per line, tab separated: RELATIVE-PATH <TAB> KIND <TAB> SHA256 where KIND is `file`, `exec`, or `symlink`, and SHA256 is over the file's contents or, for a symlink, over its target path. The three categories are reported separately on purpose. "Same bytes, wrong bit" is a different defect from "wrong bytes": the first ships a release whose ./configure will not run, the second ships a corrupt file, and a comparator that only said "these differ" would leave the reader to work out which. Exits 0 when the two describe the same tree, 1 otherwise. """ import sys def load(path): members = {} with open(path) as handle: for lineno, line in enumerate(handle, 1): line = line.rstrip("\n") if not line: continue parts = line.split("\t") if len(parts) != 3: raise SystemExit(f"{path}:{lineno}: expected 3 tab-separated fields") rel, kind, digest = parts members[rel] = (kind, digest) return members def main(argv): if len(argv) != 3: raise SystemExit("usage: archive-compare.py FOSSIL.members ZIT.members") fossil, zit = load(argv[1]), load(argv[2]) bad = False only_fossil = sorted(set(fossil) - set(zit)) only_zit = sorted(set(zit) - set(fossil)) if only_fossil or only_zit: bad = True print(" member sets DIFFER") for path in only_fossil: print(f" only in fossil: {path}") for path in only_zit: print(f" only in zit: {path}") else: print(f" member sets identical ({len(fossil)})") shared = sorted(set(fossil) & set(zit)) mode_bad = [p for p in shared if fossil[p][0] != zit[p][0]] if mode_bad: bad = True print(" modes DIFFER (an executable that unpacks 0644 is the archetypal release bug)") for path in mode_bad: print(f" {path}: fossil={fossil[path][0]} zit={zit[path][0]}") else: print(" modes identical") hash_bad = [p for p in shared if fossil[p][1] != zit[p][1]] if hash_bad: bad = True print(" contents DIFFER") for path in hash_bad: print(f" {path}") else: print(" contents identical") # Say what was actually compared. "modes identical" over a corpus of plain # files proves nothing about modes, and reads exactly like a corpus that # exercised them -- silence is not success. Reported rather than enforced, # because a caller may legitimately point this at a corpus it did not # choose; the caller that builds its own fixture should check this line. kinds = sorted({fossil[p][0] for p in shared}) print(" kinds compared: %s" % (", ".join(kinds) if kinds else "none")) for expected in ("exec", "symlink"): if expected not in kinds: print(f" note: no {expected} member in this corpus, so that mode is untested") return 1 if bad else 0 if __name__ == "__main__": sys.exit(main(sys.argv))