Skip to content

Reduce Git configuration discovery overhead on Windows - #2964

Draft
Sebastian Thiel (Byron) wants to merge 2 commits into
mainfrom
error-conversion-review
Draft

Reduce Git configuration discovery overhead on Windows#2964
Sebastian Thiel (Byron) wants to merge 2 commits into
mainfrom
error-conversion-review

Conversation

@Byron

@Byron Sebastian Thiel (Byron) commented Sep 2, 2026

Copy link
Copy Markdown
Member

Tasks

This section is for Byron only. Models continuing this PR must not add, remove, check, uncheck, rename, or reorder checkboxes here.

  • refackiew

Everything below this line was generated by Codex.

Created by Codex on behalf of Byron. Byron will review before this is ready to merge.

Summary

  • Query Git once for both installation and system configuration paths on Windows, retaining a fallback for Git versions older than 2.26 and distinguishing multiple system-scoped files.
  • Allow callers to preset both paths through gix::open::Options, which avoids launching Git entirely when both are supplied.
  • Preserve GIT_CONFIG_NOSYSTEM semantics for preset paths.

Reported issue

Current Behavior

After upgrading from 1.17.1 -> 1.26.0 (yes, I hadn't upgraded in a long time), there was a noticeable slowdown in the git prompt speed for small repos. In my prompt, I'm only including git_branch/git_commit/git_state/git_status.

Before, zipping around smaller repo's felt basically instant. Now it takes ~150ms for the prompt to render which is slow enough for my eyes to catch and be grating.

Expected Behavior

For the prompt in smaller repo's, it should be no slower than manually running git status.

Additional context/Screenshots

I manually added instant.elapsed() calls around the code and found that of the ~150ms, most was not actually spent performing git status. Rather, ~100ms was taken for this singular line: https://github.com/starship/starship/blob/cc763c5557a235530ff00c8917169bb77aac1e24/src/context/mod.rs#L369

If I shrink my prompt just to git_branch, this ~100ms now takes up basically all the time.

If I do a targeted revert of starship/starship@daf8d93#diff-d6346fd7d17270b1282142aeeda9c4bc2b7d8fd0f37b24a1c871a9257f0ed0aa
Specifically, only these 4 lines:
Image
Then, that ~100ms turns to single digit milliseconds!

Possible Solution

I'm not familiar enough with the code to know why it's necessary for correctness to have that change. Given the context of the PR that introduced it, maybe those fields could be set back to false if use_git_executable is true and git_metrics is not enabled?

Sebastian Thiel (@Byron) would love to get your insight :)

Environment

  • Starship version: 1.26.0
  • nu version: 0.114.1
  • Operating system: Windows 10.0.19045
  • Terminal emulator:
  • Git Commit Hash: fca92d8
  • Branch/Tag: main
  • Rust Version: rustc 1.96.0 (ac68faa20 2026-05-25)
  • Rust channel: stable-x86_64-pc-windows-msvc release
  • Build Time: 2026-06-28 17:05:55 +00:00

Relevant Shell Configuration

$env.config.show_banner = false
$env.config.buffer_editor = "micro"

alias lg = lazygit

mkdir ($nu.data-dir | path join "vendor/autoload")
source ~/.zoxide.nu

Starship Configuration

# Default format ($all) with all languages disabled and only a minimal set of tools left.
format = """
$username\
$hostname\
$directory\
$git_branch\
$git_commit\
$git_state\
$git_status\
$line_break\
$character"""

[directory]
use_os_path_sep = false
truncate_to_repo = false
truncation_length = 0

[git_branch]
symbol = ''

[git_status]
# Default format with $all_status expanded, $stashed removed, and some rearranging.
format = '([($conflicted )(\[$staged$deleted$renamed$modified$untracked\])(\[$ahead_behind\])]($style))'
conflicted = 'CONFLICT'

[character]
success_symbol = '[>](bold green)'
error_symbol = '[>](bold red)'

Thanks for chiming in David, and thanks so much for your help with all of this Brandon Dong (@brandondong)!

Where is that time being lost? Reading files is not slow on my machine, for example, the time starship spends reading my config toml is <1ms. Maybe when I find some time, I can try looking into that but I was curious if Sebastian Thiel (@Byron) would just know offhand.

I am also interested in this, maybe there is a regression that disguised itself, as that code definitely changed. However, from my testing on macOS, it got faster.
So how can we learn what's going on here? Could you try to turn git_binary off, all the other flags off as well, and see if there is one of them adding disproportional amounts of time? Maybe they are all the same amount of time as well, which would also be good to know. My guess is of course that this is related to IO which you say is fast, so the other guess is that the binary under test isn't a release build (but that's easily discarded as unlikely). So I am out of guesses, without IO and CPU profiling.

I tested with both debug and release builds, no difference.

So how can we learn what's going on here? Could you try to turn git_binary off, all the other flags off as well, and see if there is one of them adding disproportional amounts of time?

It turns out it's the system flag that is adding all the 50ms time. I don't have gix cloned to be able to add timing logs but just looking at the source code, I think it's

Image Image

Neither environment variable is set on my system. If I set GIT_CONFIG_NOSYSTEM to "true" or GIT_CONFIG_SYSTEM to some dummy path, the 50ms overhead is eliminated. Same if I run from the git bash app but not git bash through Windows Terminal.

Commits

  • 51a9dbd802 — query both configuration paths with one Git process.
  • be1698c200 — expose both preset paths through gix::open::Options and prove they avoid Git execution.
  • 04bef3fb94 — preserve GIT_CONFIG_NOSYSTEM for preset paths.
  • e8d5a09289 — retain a distinct system path when Git reports multiple system-scoped files.

Validation

  • cargo test -p gix-path
  • cargo test -p gix-config
  • cargo check -p gix-path --tests --target aarch64-pc-windows-msvc
  • cargo clippy -p gix-path --all-targets -- -D warnings
  • cargo test -p gix --test open_options
  • cargo test -p gix --lib open::tests::size_of_options
  • cargo test -p gix --doc
  • cargo check -p gix --all-targets
  • cargo check -p gix --no-default-features --features sha1
  • cargo clippy -p gix --test open_options -- -D warnings
  • cargo fmt --all -- --check
  • git diff --check

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 04bef3fb94

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "Codex (@codex) review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "Codex (@codex) address that feedback".

Comment thread gix-path/src/env/git/mod.rs Outdated
…s and add `env::system_config()`

<!-- agent -->
On Windows, enabling both Git-installation and system configuration could launch
Git twice: once to inspect configuration origins and once for `--exec-path`.
This is especially visible where process startup costs about 50 ms.

Request `--show-scope` alongside `--show-origin`, parse both the
highest-precedence file and the first distinct system-scoped file, and cache
the pair. Keeping the paths distinct matters when Git for Windows reports its
installation file before the ProgramData system file. `gix-config` now consumes
the direct system-config path, so both sources share one probe.

Fall back to an origin-only query when Git predates `--show-scope`, which was
introduced in Git 2.26. Keep system-config environment overrides authoritative
by hiding paths obtained through ambient configuration when those overrides
are present.

Assisted-by: GPT 5.6
Co-authored-by: GPT 5.6 <codex@openai.com>
Callers that already know the Git-installation and system configuration files can now provide both paths through `gix::open::Options`. The paths flow through repository opening and standalone configuration loading, while the existing source permissions continue to decide whether each file is read.

Using both presets bypasses automatic path discovery entirely. A dedicated integration test loads distinct values from both files and uses `GIT_TRACE` to verify that no Git process was launched.

Validation: `cargo test -p gix --test open_options`; `cargo test -p gix --lib open::tests::size_of_options`; `cargo test -p gix --doc`; `cargo check -p gix --all-targets`; `cargo check -p gix --no-default-features --features sha1`; `cargo clippy -p gix --test open_options -- -D warnings`; `cargo fmt --all -- --check`; `git diff --check`.

# 04bef3f fix(gix): honor no-system config with preset paths

fix(gix): honor no-system config with preset paths

Preset paths replace path discovery, but must not bypass Git's explicit `GIT_CONFIG_NOSYSTEM` switch. Read the permitted environment value once before selecting Git-installation and system sources, preserving the behavior already implemented by `gix_config::Source::storage_location()`.

Extend the preset-path integration test to prove both files remain unloaded when the switch is enabled and that Git is still never launched.

Validation: `cargo test -p gix --test open_options`; `cargo clippy -p gix --test open_options -- -D warnings`; `cargo check -p gix --all-targets`; `cargo fmt --all -- --check`; `git diff --check`.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants