Repository navigation
Find git-lfs in the user's PATH instead of hardcoding /usr/local/bin - #2835
Open
mluisbrown wants to merge 4 commits into
Open
mluisbrown wants to merge 4 commits into
mluisbrown wants to merge 4 commits into
Conversation
The LFS filter was only registered if git-lfs existed at /usr/local/bin/git-lfs, so with Homebrew on Apple Silicon (/opt/homebrew/bin) or MacPorts (/opt/local/bin) LFS files were silently left unfiltered (git-up#805). The filter is now always registered and git-lfs is resolved lazily, the first time a file with the lfs filter attribute is processed, from the login shell PATH with fallbacks to the common install locations. If git-lfs isn't found the filter passes through, preserving the previous behavior. git-lfs is also run with the login shell PATH so it uses the same git as the command line. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
[Medium impact] The PR appears safe to merge; both previous findings are addressed and no new actionable issue was found.
|
Extract the lookup into GCFindExecutableInPATH() so it can be tested with a controlled PATH and fake executables, independent of whether git-lfs is installed or present at /usr/local/bin. Also ignore directories named like the tool, which -isExecutableFileAtPath: would otherwise accept. The real git-lfs test remains as a smoke test. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Executing "git-lfs clean" or "git-lfs smudge" for each file costs ~200ms as git-lfs runs several git commands on startup, so checking out hundreds of LFS files (e.g. when rebasing onto a commit that changed many snapshot images) took minutes instead of the couple of seconds the git command line needs. Like the git command line, talk to a single "git-lfs filter-process" per working directory using the long-running filter protocol version 2 and terminate it once idle. Fall back to executing git-lfs per file if the process can't be used. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Address review feedback on the long-running filter process: - Capture what git-lfs writes to stderr, draining it continuously so the pipe can't fill up, and include it in the error so e.g. a failed download says why instead of only "git-lfs filter process failed". The per-file fallback now does the same. - git-lfs replies "status=success" then exits when it can't download a file. Report that as an error for the file instead of retrying it by executing git-lfs again, which would repeat the failing download. - Retry with a new process if a cached idle one turns out to have exited, before falling back to executing git-lfs per file. - Count filter process launches and per-file fallbacks so the tests can check that many files go through a single process, and that a failure reports the git-lfs error without falling back. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #805.
GitUp only registered its Git LFS filter if
git-lfsexisted at exactly/usr/local/bin/git-lfs. With Homebrew on Apple Silicon (/opt/homebrew/bin) or MacPorts (/opt/local/bin), the filter was never registered. LFS files were then checked out as pointer files and committed as raw content instead of LFS pointers, with no error.Changes
lfsfilter is now always registered.git-lfsis looked up the first time a file with thefilter=lfsattribute is processed, and the result is cached. The lookup searches the login shellPATH(the same approach hooks already use), then falls back to/opt/homebrew/bin,/usr/local/binand/opt/local/bin. Repositories that don't use LFS never run the lookup.checkcallback returnsGIT_PASSTHROUGHifgit-lfscan't be found, so LFS files are left as pointer files. This preserves the previous behavior when LFS isn't installed.git-lfsnow runs with the login shellPATH, so thegitit invokes is the same one the user gets on the command line.PATHlookup moved into a shared static function so the filter can call it without a repository instance.-getPATHUsingShell:error:now wraps it and behaves the same as before.Speeding up checkouts with many LFS files
Once the filter was active, checkouts that touch many LFS files became very slow. Each file started its own
git-lfs smudgeorgit-lfs clean, and each start-up runs severalgitcommands, about 200 ms in total. Rebasing a branch onto a commit that changed 346 LFS-tracked snapshot images took about 1.5 minutes.git checkouttakes 2.2 s for the same change.git-lfs filter-processfor each working directory, using Git's long-running filter protocol version 2. The process is reused for later files and closed after 10 s idle. That checkout now takes 1.25 s.filter-processcan't be used at all (e.g. git-lfs older than 2.0), GitUp runsgit-lfsfor each file as before.git-lfs filter process failed: … Smudge error: Error downloading … batch request: missing protocol. When git-lfs can't download a file it repliesstatus=successand then exits partway through. That's reported as an error for the file, and the download isn't retried.Testing
testGitLFSCleanFilter. It checks that adding an LFS-tracked file to the index stores an LFS pointer, and it skips itself ifgit-lfsisn't installed. I confirmed that it fails against the old code when nothing exists at/usr/local/bin/git-lfs, and that it passes with this change.testFindExecutableInPATH, which covers thePATHlookup with fake executables in temporary directories, independent of whethergit-lfsis installed.testGitLFSFilterRoundTrip. It sends 20 files, some spanning several protocol packets, through clean and then smudge. It asserts that all of them go through one filter process with no per-file fallbacks.testGitLFSFilterError. It checks that smudging a pointer to a missing object reports the git-lfs error without falling back, and that the next file gets a new process.git-lfsand skip themselves if it isn't installed.testDateFormatting("This Monday" vs "Last Monday") depends on the current date.testClonefails at its SSH clone from GitHub in my environment.The wiki currently says
git-lfsmust be installed at/usr/local/bin/git-lfs, so it should be updated after this is merged.🤖 Generated with Claude Code