fix(task): avoid gix panic when cloning a remote task by commit SHA#9473
fix(task): avoid gix panic when cloning a remote task by commit SHA#9473
Conversation
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]>
There was a problem hiding this comment.
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.
| .main_worktree(gix::progress::Discard, &gix::interrupt::IS_INTERRUPTED)?; | ||
|
|
||
| if let Some(sha) = sha_branch { | ||
| self.update_ref(sha, false)?; |
There was a problem hiding this comment.
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.
| cmd.execute()?; | ||
|
|
||
| if let Some(sha) = sha_branch { | ||
| self.update_ref(sha, false)?; |
There was a problem hiding this comment.
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 SummaryThis PR fixes a hard panic in Confidence Score: 5/5The fix is narrowly scoped and correct — no P0 or P1 issues found. The No files require special attention. Important Files Changed
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
Reviews (2): Last reviewed commit: "fix(task): skip redundant fetch when che..." | Re-trigger Greptile |
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]>
Hyperfine Performance
|
| 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% |
### 🐛 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>
Summary
we map by name only and have no object-id in refspecfromgix-0.81.0/src/clone/fetch/util.rs:217when a remote git task references a commit SHA (e.g.git::https://…/repo.git//path?ref=<sha>).gix-refspecparses any 40- or 64-char hex string as anObjectIdrefspec, butgix::clone::fetch::util::find_custom_refnameonly handles name-based matches andexpect()s onitem_index.Git::clonewas passing the?ref=value straight toprepare_clone.with_ref_name(), so SHAs blew up.Git::clonenow detects SHA-shaped refs, skipswith_ref_name()/git clone -b(neither path accepts bare SHAs anyway), and after the clone fetches + checks out the SHA via the existing CLI-backedupdate_ref. The CLI path also drops--depth 1when the caller passed a SHA, since shallow clones may not contain that object.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.tomlto bump the SHA), with no graceful workaround —MISE_GIX=falsewould have shifted onto the CLI path, wheregit clone -b <sha>also fails.Test plan
git::tests::clone_by_sha_does_not_paniccreates a local repo, callsGit::clonewith a 40-char SHA branch, and asserts the worktree ends up at the requested SHA. Reproduces the original panic without the fix.git::tests::sha_detectioncovers thelooks_like_shaheuristic boundaries (full SHA-1, full SHA-256, branch names, short SHAs, non-hex, empty).cargo test --bin mise -- git::tests::— all greenmise 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
refis a full commit SHA (40/64 hex), avoiding thegixpanic caused by passing SHAs to name-only ref handling.Git::clonenow detects SHA-shaped refs, skipswith_ref_name/git clone -bin that case, performs a full clone (no--depth 1), and then force-checks out the SHA via a newcheckout()helper. Addslooks_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.