diff --git a/crates/radicle-cli/src/commands/lfs/init.rs b/crates/radicle-cli/src/commands/lfs/init.rs index 76798810..a703290d 100644 --- a/crates/radicle-cli/src/commands/lfs/init.rs +++ b/crates/radicle-cli/src/commands/lfs/init.rs @@ -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 diff --git a/crates/radicle-cli/src/commands/lfs/store.rs b/crates/radicle-cli/src/commands/lfs/store.rs index b10ebc10..73670536 100644 --- a/crates/radicle-cli/src/commands/lfs/store.rs +++ b/crates/radicle-cli/src/commands/lfs/store.rs @@ -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() {