Skip to content

fix(task): avoid gix panic when cloning a remote task by commit SHA#9473

Merged
jdx merged 3 commits intomainfrom
claude/trusting-clarke-1cfcb1
Apr 29, 2026
Merged

fix(task): avoid gix panic when cloning a remote task by commit SHA#9473
jdx merged 3 commits intomainfrom
claude/trusting-clarke-1cfcb1

Conversation

@jdx
Copy link
Copy Markdown
Owner

@jdx jdx commented Apr 29, 2026

Summary

  • Fixes the panic reported in #9472: we map by name only and have no object-id in refspec from gix-0.81.0/src/clone/fetch/util.rs:217 when a remote git task references a commit SHA (e.g. git::https://…/repo.git//path?ref=<sha>).
  • gix-refspec parses any 40- or 64-char hex string as an ObjectId refspec, but gix::clone::fetch::util::find_custom_refname only handles name-based matches and expect()s on item_index. Git::clone was passing the ?ref= value straight to prepare_clone.with_ref_name(), so SHAs blew up.
  • Git::clone now detects SHA-shaped refs, skips with_ref_name() / git clone -b (neither path accepts bare SHAs anyway), and after the clone fetches + checks out the SHA via the existing CLI-backed update_ref. The CLI path also drops --depth 1 when the caller passed a SHA, since shallow clones may not contain that object.
  • Named branches and tags continue to use the existing fast paths unchanged.

Why

Without the fix, any user who pinned a remote task to a commit SHA hit a hard process panic on cache miss (e.g. after editing mise.toml to bump the SHA), with no graceful workaround — MISE_GIX=false would have shifted onto the CLI path, where git clone -b <sha> also fails.

Test plan

  • New unit test git::tests::clone_by_sha_does_not_panic creates a local repo, calls Git::clone with a 40-char SHA branch, and asserts the worktree ends up at the requested SHA. Reproduces the original panic without the fix.
  • New unit test git::tests::sha_detection covers the looks_like_sha heuristic boundaries (full SHA-1, full SHA-256, branch names, short SHAs, non-hex, empty).
  • cargo test --bin mise -- git::tests:: — all green
  • mise run lint — clean

🤖 Generated with Claude Code


Note

Medium Risk
Changes cloning behavior and shallow-clone logic based on ref parsing, which could affect performance or edge-case ref names, but is localized to the git clone/update path.

Overview
Fixes cloning a repo when the requested ref is a full commit SHA (40/64 hex), avoiding the gix panic caused by passing SHAs to name-only ref handling.

Git::clone now detects SHA-shaped refs, skips with_ref_name/git clone -b in that case, performs a full clone (no --depth 1), and then force-checks out the SHA via a new checkout() helper. Adds looks_like_sha() plus unit tests covering SHA detection and a regression test ensuring cloning by SHA succeeds and lands on the requested commit.

Reviewed by Cursor Bugbot for commit 337eda7. Bugbot is set up for automated code reviews on this repo. Configure here.

Cloning a remote git task with `?ref=<sha>` (e.g.
`git::https://github.com/foo/bar.git//path?ref=<sha>`) panicked in
`gix::prepare_clone().with_ref_name()` with "we map by name only and
have no object-id in refspec". gix-refspec parses any 40- or 64-char
hex string as an ObjectId refspec, but `find_custom_refname` only
handles name-based matches and crashes on object-id sources.

Detect SHA-shaped refs in `Git::clone` and skip `with_ref_name`/`-b`
(neither gix nor `git clone -b` accept bare SHAs anyway). Also drop
`--depth 1` in the CLI path for SHA refs since shallow clones may not
contain the requested object. After the clone completes, fetch and
check out the SHA via the existing CLI-backed `update_ref`.

Fixes #9472.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces logic to handle commit SHAs when cloning repositories, preventing panics in the gix backend and ensuring compatibility with the git CLI. It adds a heuristic to detect SHAs and adjusts the clone process to perform a full clone and a subsequent checkout when a SHA is provided instead of a branch name. However, the current implementation uses update_ref to perform the checkout, which is identified as a bug because it attempts to fetch using an invalid refspec format for SHAs, likely leading to execution failure.

Comment thread src/git.rs Outdated
.main_worktree(gix::progress::Discard, &gix::interrupt::IS_INTERRUPTED)?;

if let Some(sha) = sha_branch {
self.update_ref(sha, false)?;
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.

high

Calling update_ref with a commit SHA will likely fail during the fetch step. Inside update_ref (at line 91), the refspec is constructed as {gitref}:{gitref}. If gitref is a 40 or 64-character SHA, git will attempt to run git fetch origin <sha>:<sha>, which is invalid because a SHA is not a valid local ref name.

Since the goal here is to checkout a specific commit, you should consider modifying update_ref to handle SHAs differently (e.g., by skipping the fetch if the object is already present, or using a valid refspec) or performing the checkout manually here.

Comment thread src/git.rs Outdated
cmd.execute()?;

if let Some(sha) = sha_branch {
self.update_ref(sha, false)?;
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.

high

Similar to the gix path, calling update_ref here with a SHA will fail because of the invalid {sha}:{sha} refspec used in the fetch command.

In this CLI path, you have already performed a full clone (due to the logic at line 174), so the commit object is guaranteed to be present locally. You could avoid the failing fetch entirely by performing a direct checkout instead of calling update_ref.

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented Apr 29, 2026

Greptile Summary

This PR fixes a hard panic in Git::clone when a remote task is pinned to a full commit SHA (ref=<sha>). The looks_like_sha heuristic now intercepts 40/64-char hex refs before they reach gix::with_ref_name or git clone -b (neither of which accept bare SHA strings), performs a ref-less clone, and then checks out the requested commit via a new checkout helper. The CLI path also correctly drops --depth 1 for SHA clones, since shallow clones may not contain the target object.

Confidence Score: 5/5

The fix is narrowly scoped and correct — no P0 or P1 issues found.

The checkout helper avoids the previously-flagged invalid <sha>:<sha> refspec entirely by skipping the fetch and going straight to git checkout --force <sha>. Both gix and CLI paths handle SHA refs consistently. looks_like_sha is a safe and well-documented heuristic. The unit and regression tests cover the core scenarios. No new security surface is introduced.

No files require special attention.

Important Files Changed

Filename Overview
src/git.rs Adds looks_like_sha heuristic, a checkout helper, and SHA-aware branching in both gix and CLI clone paths; includes unit tests for the heuristic and a regression test for the panic.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A["Git::clone(url, options)"] --> B{looks_like_sha?}
    B -- "yes (40/64 hex chars)" --> C["sha_branch = Some(sha)\nnamed_branch = None"]
    B -- "no" --> D["sha_branch = None\nnamed_branch = Some(branch)"]
    C --> E{gix/libgit2?}
    D --> E
    E -- "gix path" --> F["gix::prepare_clone(url, dir)"]
    F --> G{named_branch?}
    G -- "yes" --> H["prepare_clone.with_ref_name(branch)"]
    G -- "no (SHA)" --> I["(no ref name set)"]
    H --> J["fetch_then_checkout + main_worktree"]
    I --> J
    J --> K{sha_branch?}
    K -- "yes" --> L["self.checkout(sha)\n(git checkout --force sha)"]
    K -- "no" --> M["return Ok()"]
    L --> M
    E -- "CLI path" --> N["git clone -q -o origin -c core.autocrlf=false"]
    N --> O{sha_branch is None?}
    O -- "yes" --> P["append --depth 1"]
    O -- "no" --> Q["(full clone)"]
    P --> R["append url + dir"]
    Q --> R
    R --> S{named_branch?}
    S -- "yes" --> T["append -b branch --single-branch"]
    S -- "no" --> U["cmd.execute()"]
    T --> U
    U --> V{sha_branch?}
    V -- "yes" --> W["self.checkout(sha)\n(git checkout --force sha)"]
    V -- "no" --> X["return Ok()"]
    W --> X
Loading

Reviews (2): Last reviewed commit: "fix(task): skip redundant fetch when che..." | Re-trigger Greptile

Comment thread src/git.rs Outdated
Comment thread src/git.rs Outdated
Comment thread src/git.rs Outdated
jdx and others added 2 commits April 29, 2026 09:58
After clone, the worktree already has every reachable object, so calling
update_ref (which fetches `<sha>:<sha>` from origin first) is both
redundant and brittle — servers without
`uploadpack.allowReachableSHA1InWant` reject fetching by SHA. Replace
the post-clone update_ref call with a plain detached checkout via a new
private `Git::checkout` helper.

Addresses review feedback on #9473.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
… SHA

Greptile pointed out that the regression test only exercised the
default-branch SHA case and didn't pin the backend, so the gix panic
path could go untested.

Park the target SHA on a feature branch (so a shallow / single-branch
clone would miss it) and run the test against gix=true and gix=false
in sequence via Settings::override_with, restoring the prior values
afterward.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
@github-actions
Copy link
Copy Markdown

Hyperfine Performance

mise x -- echo

Command Mean [ms] Min [ms] Max [ms] Relative
mise-2026.4.25 x -- echo 23.3 ± 0.8 22.0 33.0 1.00
mise x -- echo 24.5 ± 7.1 22.7 181.0 1.05 ± 0.31

mise env

Command Mean [ms] Min [ms] Max [ms] Relative
mise-2026.4.25 env 22.7 ± 0.8 21.7 29.9 1.00
mise env 23.2 ± 0.9 22.0 30.3 1.02 ± 0.05

mise hook-env

Command Mean [ms] Min [ms] Max [ms] Relative
mise-2026.4.25 hook-env 23.3 ± 0.6 22.1 26.4 1.00
mise hook-env 24.1 ± 0.7 22.8 26.7 1.03 ± 0.04

mise ls

Command Mean [ms] Min [ms] Max [ms] Relative
mise-2026.4.25 ls 23.7 ± 0.6 22.6 26.9 1.00
mise ls 24.4 ± 0.6 23.1 26.8 1.03 ± 0.03

xtasks/test/perf

Command mise-2026.4.25 mise Variance
install (cached) 159ms 162ms -1%
ls (cached) 83ms 84ms -1%
bin-paths (cached) 84ms 85ms -1%
task-ls (cached) 825ms 816ms +1%

@jdx jdx merged commit a905b8b into main Apr 29, 2026
31 checks passed
@jdx jdx deleted the claude/trusting-clarke-1cfcb1 branch April 29, 2026 16:56
mise-en-dev added a commit that referenced this pull request Apr 30, 2026
### 🐛 Bug Fixes

- **(copr)** remove stale pinned image digest and rebuild copr image on
Dockerfile changes by @bestagi in
[#9451](#9451)
- **(task)** avoid gix panic when cloning a remote task by commit SHA by
@jdx in [#9473](#9473)

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
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.

1 participant