pcottle/learnGitBranching · error · GitError

git-error-switch-detach

Error message

git-error-switch-detach

What it means

Unlike checkout, `git switch` refuses to land on a detached HEAD. If the target resolves to anything other than a branch (a commit SHA or a tag), the simulation throws and points the user at --detach, matching real git behavior.

Source

Thrown at src/js/git/commands.js:1201

      if (detachOption) {
        // "-d" / "--detach" explicitly asks to check out a commit-ish and leave
        // HEAD detached there. Like "-t" above, a ref sitting right after the
        // flag gets attached to it by the greedy option parser, so fold it back
        // into the general args before we read it.
        let args = detachOption.concat(generalArgs);
        command.validateArgBounds(args, 1, 1, '-d');
        engine.checkout(engine.crappyUnescape(args[0]));
        return;
      }

      command.validateArgBounds(generalArgs, 1, 1);

      // Unlike "git checkout", "git switch" will not silently leave you on a
      // detached HEAD. If the target isn't a branch (i.e. it's a commit or a
      // tag), refuse and point the user at "--detach", matching real git.
      var target = engine.crappyUnescape(generalArgs[0]);
      if (engine.getType(target) !== 'branch') {
        throw new GitError({
          msg: intl.str('git-error-switch-detach', { ref: generalArgs[0] })
        });
      }

      engine.checkout(target);
    }
  }
};

var instantCommands = [
  // "git help {command}" is handled over in the sandbox commands, so only
  // grab the bare forms here
  [/^git +help *$|^git *$/, function() {
    var lines = [
      intl.str('git-version'),
      '<br/>',
      intl.str('git-usage'),
      escapeString(intl.str('git-usage-command')),

View on GitHub (pinned to 5b09d0ff96)

Solutions

  1. Use `git switch --detach <ref>` to intentionally detach
  2. Switch to a branch name instead of a SHA/tag
  3. Use `git checkout <ref>` if you want the old auto-detach behavior

Example fix

// before
git switch c3
// after
git switch --detach c3
Defensive patterns

Strategy: type-guard

Validate before calling

var target = engine.crappyUnescape(args[0]);
if (engine.getType(target) === 'branch') { engine.switch(target); } else { /* use --detach */ }

Type guard

function isBranchRef(engine, name){ return engine.getType(engine.crappyUnescape(name)) === 'branch'; }

Prevention

When it happens

Trigger: `git switch <commit-sha>` or `git switch <tag>` where engine.getType(target) !== 'branch'.

Common situations: Muscle memory from checkout applied to switch; trying to inspect an old commit via switch.

Related errors


AI-assisted analysis of pcottle/learnGitBranching@5b09d0ff96 (2026-08-27). Data as JSON: /api/errors/ad1606f1d2193716. Report an issue: GitHub.