Skip to content

fix: add-wizard ls-remote default branch parsing bug#24105

Merged
pelikhan merged 1 commit intomainfrom
fix/add-wizard-ls-remote-parsing
Apr 2, 2026
Merged

fix: add-wizard ls-remote default branch parsing bug#24105
pelikhan merged 1 commit intomainfrom
fix/add-wizard-ls-remote-parsing

Conversation

@dsyme
Copy link
Copy Markdown
Collaborator

@dsyme dsyme commented Apr 2, 2026

Problem

gh aw add-wizard fails to pull merged changes after PR merge with:

⚠ Could not update local branch: git fetch failed: exit status 128 (output: fatal: couldn't find remote ref ref)

This has been a recurring bug — attempted to fix 5 times and it kept coming back.

Root Cause

The updateLocalBranch() fallback parses git ls-remote --symref origin HEAD output, which looks like:

ref: refs/heads/mainabc123
abc123HEAD

The code used strings.Fields() to split this, producing ["ref:", "refs/heads/main", "abc123"]. It then called TrimPrefix(parts[0], "ref: refs/heads/") — but parts[0] is just "ref:", so the prefix doesn't match, leaving defaultBranch = "ref:". This caused git fetch origin ref: to fail.

Fix

  • Extract parseDefaultBranchFromLsRemote() into a standalone testable function
  • Split on tab first (matching actual git output format), then trim the prefix correctly
  • Add 16 tests including:
    • 12 unit tests with edge cases and explicit regression checks (result must not start with "ref:")
    • 4 real-git integration tests that create actual bare repos, push commits, run real git ls-remote --symref, and verify the parser works against actual git output

The real-git tests ensure this specific bug cannot regress again — any future change that breaks the parsing will fail against actual git output, not just synthetic strings.

The ls-remote fallback in updateLocalBranch() was using strings.Fields
to split the line 'ref: refs/heads/main<TAB>abc123', which produces
['ref:', 'refs/heads/main', 'abc123']. The code then used parts[0]
('ref:') and tried to TrimPrefix 'ref: refs/heads/' from it — which
didn't match, leaving defaultBranch set to 'ref:'. This caused
'git fetch origin ref:' to fail with 'couldn't find remote ref ref'.

Fix: Extract parseDefaultBranchFromLsRemote() into a standalone
testable function that splits on tab first (matching actual git output
format), then trims the 'ref: refs/heads/' prefix correctly.

Add comprehensive tests including real-git integration tests that
create actual repos and verify parsing against real git ls-remote
output, so this bug cannot regress again.
Copilot AI review requested due to automatic review settings April 2, 2026 12:50
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Fixes a recurring failure in gh aw add-wizard where updating the local branch after a PR merge could attempt to fetch an invalid ref due to incorrect parsing of git ls-remote --symref origin HEAD.

Changes:

  • Replaces inline git ls-remote --symref parsing with a dedicated parseDefaultBranchFromLsRemote() helper.
  • Updates parsing to split on tab (matching git output) and extract the branch name correctly.
  • Adds unit tests plus “real git” repository-based tests to prevent regressions.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
pkg/cli/add_interactive_git.go Extracts and uses a robust parser for default-branch detection from git ls-remote --symref output.
pkg/cli/add_interactive_git_test.go Adds parser regression/unit tests and repo-backed tests that exercise real git ls-remote --symref output.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

//
// Example output:
//
// ref: refs/heads/main abc123
Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

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

The example output in this doc comment doesn’t match actual git ls-remote --symref origin HEAD output: the symref line’s second field is HEAD (not a commit SHA), and the SHA appears on the subsequent line. Updating the example will avoid misleading future maintainers and test authors.

Suggested change
// ref: refs/heads/main abc123
// ref: refs/heads/main HEAD

Copilot uses AI. Check for mistakes.
Comment on lines +119 to +125
// Create a bare repo to serve as "origin"
bareDir := t.TempDir()
runGit(t, bareDir, "init", "--bare", "--initial-branch="+tt.defaultBranch)

// Clone it to create a working repo with that origin
workDir := filepath.Join(t.TempDir(), "work")
runGitIn(t, "", "clone", bareDir, workDir)
Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

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

This test relies on git init --initial-branch=..., which requires relatively recent Git versions; on older Git it will fail and cause unit tests to fail. Consider avoiding --initial-branch by initializing normally and then setting the bare repo’s HEAD to the desired branch (e.g., via git symbolic-ref HEAD refs/heads/<branch> after creating/pushing the branch), or skipping when git init returns an error indicating the flag is unsupported.

Copilot uses AI. Check for mistakes.
@pelikhan
Copy link
Copy Markdown
Collaborator

pelikhan commented Apr 2, 2026

Js failure unrelated.

@copilot review comments

@pelikhan pelikhan merged commit 97637f0 into main Apr 2, 2026
95 of 97 checks passed
@pelikhan pelikhan deleted the fix/add-wizard-ls-remote-parsing branch April 2, 2026 13:09
Copy link
Copy Markdown
Contributor

Copilot AI commented Apr 2, 2026

Js failure unrelated.

@copilot review comments

Addressed both review comments:

  1. Doc comment (line 309): Corrected the example to show HEAD as the second field of the symref line (not a commit SHA), matching actual git ls-remote --symref output.

  2. --initial-branch compatibility: Replaced git init --bare --initial-branch=<branch> (requires Git 2.28+) with the more compatible git init --bare followed by git symbolic-ref HEAD refs/heads/<branch>. All 16 tests continue to pass.

Note: the push is currently blocked by a repository size rule (large video files in the shallow clone's base commit history) — this appears to be an environment issue unrelated to the code changes.

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.

4 participants