Skip to content

Git clone fails with "remote HEAD refers to nonexistent ref" when default branch is not master #28943

@pokho

Description

@pokho

Git clone fails with "remote HEAD refers to nonexistent ref" when default branch is not master

Description

When cloning a Keybase git repository where the default branch is main (or any branch other than master), the clone succeeds but no files are checked out. Git reports:

warning: remote HEAD refers to nonexistent ref, unable to checkout

This is a common issue for repositories created on GitHub after October 2020, when GitHub changed the default branch name from master to main.

Steps to Reproduce

  1. Create a git repository locally with main as the default branch:

    mkdir test-repo && cd test-repo
    git init -b main
    echo "test" > README.md
    git add . && git commit -m "initial"
  2. Add Keybase as a remote and push:

    git remote add origin keybase://private/username/test-repo
    git push -u origin main
  3. Clone the repo to a new location:

    cd /tmp
    git clone keybase://private/username/test-repo test-clone

Expected Behavior

The repository should clone successfully with all files checked out on the main branch.

Actual Behavior

Cloning into 'test-clone'...
Initializing Keybase... done.
Syncing with Keybase... done.
Counting: done.
Cryptographic cloning: done.
warning: remote HEAD refers to nonexistent ref, unable to checkout

The .git directory is created but the working directory is empty.

Workaround

Manually specifying the branch works:

git clone --branch main keybase://private/username/test-repo

Root Cause Analysis

After examining the codebase, the issue stems from hardcoded references to master as the default branch:

  1. go/kbfs/libgit/browser.go:75:

    const masterBranch = "refs/heads/master"
  2. go/kbfs/kbfsgit/runner.go - The list command returns a HEAD symref pointing to refs/heads/master, but when the actual branch is main, this reference doesn't exist.

  3. The go-git.v4 library used (gopkg.in/src-d/go-git.v4) initializes repositories with HEAD pointing to refs/heads/master by default, with no option to configure this.

Suggested Fix

  1. Detect the actual default branch from the repository instead of hardcoding master:

    // Instead of:
    const masterBranch = "refs/heads/master"
    
    // Query the actual HEAD target:
    func getDefaultBranch(repo *gogit.Repository) plumbing.ReferenceName {
        ref, err := repo.Storer.Reference(plumbing.HEAD)
        if err != nil {
            return plumbing.ReferenceName("refs/heads/main")
        }
        return ref.Target()
    }
  2. Update the HEAD symref in the list command to point to the actual branch that exists, not a hardcoded master.

  3. Consider upgrading to go-git/v5 which supports InitOptions.DefaultBranch for better default branch configuration.

Environment

  • Keybase client version: (latest)
  • Git version: 2.x
  • OS: Linux

Related Code

  • go/kbfs/libgit/browser.go - Default branch constant
  • go/kbfs/kbfsgit/runner.go - Git remote helper protocol handling
  • go/kbfs/libgit/repo.go - Repository initialization

Impact

This affects any user who:

  • Creates repos on GitHub (which defaults to main since Oct 2020)
  • Uses modern git with init.defaultBranch set to main
  • Pushes existing repos with main as the default branch to Keybase

Given GitHub's dominance and the industry shift to main, this will affect an increasing number of users over time.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions