ocaml-git: implement Repository.is_dirty + 4 adversarial pull tests
Replace the stub Repository.is_dirty (which always returned false) with
a real walk of HEAD's tree against the working directory: any tracked
file whose blob hash differs from the WT contents, or that's missing
from the WT, makes the repo dirty. This is a one-pass walk over the
HEAD tree using Blob.digest, no index file needed.
monopam pull now also checks the mono itself (not just the per-package
checkouts under src/) and refuses to merge when there are uncommitted
edits that would be silently overwritten by the working-tree write.
Four new adversarial cram tests pin previously-undefined behaviour:
- pull_dirty.t: alice edits a subtree file but doesn't commit; pull
must exit 2 with "Dirty packages: lib" and the WIP edit must be
preserved.
- pull_no_trailing_newline.t: conflict in a file with no trailing
newline. Naive line splitters drop the last character; this pins
that diff3 still produces correct conflict markers.
- pull_both_same_edit.t: alice and bob make the EXACT same edit
independently. The diff3 algorithm must recognise "both changed
identically" and merge cleanly with no markers — the canary for
the rule that prevents content false positives.
- pull_deleted_checkout.t: a user deletes src/lib manually (cache
free / debugging / corruption recovery); pull must re-clone the
checkout from the upstream URL and continue the merge.