lfs: fix pre-commit hook silently skipping non-ASCII filenames
`git diff --cached --name-only` C-quotes filenames with non-ASCII bytes by default (e.g. a literal "\305\202" for a Polish "ł"), which doesn't match any real path on disk -- the hook's `[ -f "$file" ]` check silently skipped those files instead of erroring, so they were committed as valid LFS pointers but never pinned to IPFS. Use `-z` (NUL-separated, unquoted) and convert to newlines for the read loop. Also add an idempotency fast-path to `rad lfs store`/`store_object`: if a note already exists for an object (e.g. written moments earlier by `rad lfs precommit`), return its CID directly instead of re-encrypting. This avoids redundant work, and matters more than that for private repos specifically: `git push`'s custom-transfer-agent subprocess chain has no TTY, so without this fast path every push would fail outright on the passphrase prompt even for objects already stored interactively at commit time.
This commit is contained in:
parent
c070aac6fe
commit
fa4fdf5470
|
|
@ -82,7 +82,13 @@ const HOOK_BODY: &str = r#"rad_lfs_precommit() {
|
|||
|
||||
staged=$(mktemp) || return 1
|
||||
batch=$(mktemp) || { rm -f "$staged"; return 1; }
|
||||
git diff --cached --name-only --diff-filter=ACM > "$staged"
|
||||
# `-z` avoids git's default C-style quoting of non-ASCII filenames (e.g.
|
||||
# a literal "\305\202" for a Polish "ł") in `--name-only`'s normal
|
||||
# output, which would otherwise not match any real file on disk and
|
||||
# silently drop that file from LFS pinning. NUL-separated records are
|
||||
# converted to newline-separated here for a plain `read` loop, which is
|
||||
# safe since filenames practically never contain literal newlines.
|
||||
git diff --cached --name-only --diff-filter=ACM -z | tr '\0' '\n' > "$staged"
|
||||
|
||||
while IFS= read -r file; do
|
||||
[ -n "$file" ] || continue
|
||||
|
|
|
|||
|
|
@ -53,6 +53,20 @@ pub fn store_object(
|
|||
.blob(pointer_text.as_bytes())
|
||||
.context("failed to write LFS pointer blob")?;
|
||||
|
||||
// Fast path: a note already recorded for this object (typically written
|
||||
// moments ago by `rad lfs precommit`, or fetched from a peer) means the
|
||||
// content is already pinned in IPFS -- nothing left to do. This matters
|
||||
// beyond just avoiding duplicate work: `git push`'s custom-transfer-agent
|
||||
// subprocess chain has no TTY (its stdio is consumed by the transfer
|
||||
// protocol, not a terminal), so without this fast path, a private repo's
|
||||
// `git push` would call back into `store_object` per file and fail
|
||||
// outright on the passphrase prompt every time, even though the actual
|
||||
// encryption work was already done (and the passphrase already
|
||||
// supplied) once, interactively, at commit time.
|
||||
if let Some(cid) = lfs_crypto::find_cids(repo, blob_oid)?.into_iter().next() {
|
||||
return Ok(cid);
|
||||
}
|
||||
|
||||
ipfs::check_daemon()?;
|
||||
|
||||
let envelope = if doc.visibility().is_public() {
|
||||
|
|
|
|||
Loading…
Reference in New Issue