Skip to content

Read Git dependency origin and revision without spawning git - #16042

Closed
onnimonni wants to merge 1 commit into
elixir-lang:mainfrom
onnimonni:git-fast-path
Closed

onnimonni wants to merge 1 commit into
elixir-lang:mainfrom
onnimonni:git-fast-path

Conversation

@onnimonni

Copy link
Copy Markdown
Contributor

Closes #16040.

Mix.SCM.Git.lock_status/1 spawned git twice per Git dependency on every Mix command (~19ms per dependency on macOS). Now the revision is read from a detached .git/HEAD, and the origin from a plain url under [remote "origin"] in .git/config. That is the state Mix leaves its checkouts in.

Each value falls back to git on its own for anything else, so the answer never differs from git's. Fallback cases: symbolic HEAD, includes, config.worktree, GIT_CONFIG* environment variables, quotes, escapes, comments, line continuations, several urls.

Checked against git 2.54 with a differential test covering ~80 config, HEAD and environment variants. The unusual configs are also part of the new tests, which compare lock_status/1 against git config remote.origin.url.

Phoenix app with git dependencies on phoenix and phoenix_live_view: lock_status/1 goes from 19ms to 85µs per dependency.

Mix checks the lock of every Git dependency on every command, which spawned
git twice per dependency (~19ms on macOS). The revision is now read from a
detached .git/HEAD and the origin from a plain url under [remote "origin"] in
.git/config, which is the state Mix leaves checkouts in. Each falls back to git
independently for anything else (symbolic HEAD, includes, worktree config,
GIT_CONFIG* environment variables, quotes, escapes, comments, line
continuations, several urls).

Closes elixir-lang#16040

@josevalim josevalim left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you, some comments inline! I am afraid we should probably only read the HEAD instead.

defp read_rev(git_dir) do
with {:ok, head} <- File.read(Path.join(git_dir, "HEAD")),
rev = String.trim_trailing(head),
true <- rev =~ ~r/\A([0-9a-f]{40}|[0-9a-f]{64})\z/ do

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need this validation? Can it ever be a non-valid ref?

end
end

defp read_origin(git_dir) do

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hrm, there is too much going on in here and I feel we are going to have more pitfalls. If we can't easily get the URL, then I am afraid we will need to use the git CLI indeed.

@josevalim josevalim closed this in 5472035 Nov 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

Mix spawns git twice per git dependency on every command to check the lock

2 participants