unseen: make clone disk-size guard tolerate git's mid-walk churn

disk_bytes() summed the clone tree with rglob + stat() as a size cap
before every git command. On a blob:none clone, `git show` triggers an
on-demand blob fetch that writes and deletes transient
.git/objects/pack/*.rev files, so a path rglob had just listed could be
gone by the stat() call -> FileNotFoundError aborted the whole draft as
an instrument_failure. The race can hit any candidate, so it was a
non-deterministic draft-killer (observed extracting a real npm repo).

Skip a file that raises OSError mid-walk; the size cap is unaffected.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
This commit is contained in:
Marius Ciepluch
2026-10-08 11:16:13 +02:00
co-authored by Claude Opus 4.8
parent 0a637e8989
commit f4c7143525
2 changed files with 32 additions and 1 deletions
+10 -1
View File
@@ -57,7 +57,16 @@ class Git:
return done.stdout.decode("utf-8", errors="replace")
def disk_bytes(self) -> int:
return sum(p.stat().st_size for p in self.path.rglob("*") if p.is_file() and not p.is_symlink())
# git churns .git/objects/pack (incl. transient *.rev) during on-demand blob
# fetches, so a path rglob just listed can vanish before stat(): skip it.
total = 0
for p in self.path.rglob("*"):
try:
if p.is_file() and not p.is_symlink():
total += p.stat().st_size
except OSError:
continue
return total
def parents(self, sha: str) -> list[str]:
return self.run("rev-list", "--parents", "-n", "1", sha).split()[1:]
+22
View File
@@ -312,3 +312,25 @@ def test_unbalanced_cells_remain_proportional_and_seed_changes_draw():
draw, _ = e.draw_ordinary(rows, set(), set(), seed=1)
assert len([r for r in draw if r["size_bucket"] == 0]) == 9
assert draw != e.draw_ordinary(rows, set(), set(), seed=2)[0]
def test_disk_bytes_skips_files_that_vanish_mid_walk(tmp_path):
# git churns .git/objects/pack (transient *.rev) during on-demand blob fetches,
# so a path rglob just listed can be gone by the time disk_bytes stat()s it.
git = e.Git(tmp_path, runner=subprocess.run)
(tmp_path / "a").write_bytes(b"x" * 10)
(tmp_path / "b").write_bytes(b"y" * 20)
real = [p for p in tmp_path.rglob("*")]
class Vanished:
def is_file(self):
return True
def is_symlink(self):
return False
def stat(self):
raise FileNotFoundError(2, "No such file or directory")
git.path = type("P", (), {"rglob": lambda self, pattern: iter([*real, Vanished()])})()
assert git.disk_bytes() == 30