pcottle/learnGitBranching · error · GitError

git-error-relative-ref

Error message

git-error-relative-ref

What it means

Relative ref resolution failed: a suffix like ~2 or ^1 walked off the graph — the requested parent/ancestor does not exist for the given commit. Matches git's 'unknown revision or path not in the working tree' for over-long ancestry chains.

Source

Thrown at src/js/git/index.js:1778

  while (matches = regex.exec(relative)) {
    var next = commit;
    var num = matches[2] ? parseInt(matches[2], 10) : 1;

    if (matches[1] == '^') {
      next = commit.getParent(num-1);
    } else {
      while (next && num--) {
        next = next.getParent(0);
      }
    }

    if (!next) {
      var msg = intl.str('git-error-relative-ref', {
        commit: commit.id,
        match: matches[0]
      });
      throw new GitError({
        msg: msg
      });
    }

    commit = next;
  }

  return commit;
};

GitEngine.prototype.doesRefExist = function(ref) {
  return !!this.refs[ref]
};

GitEngine.prototype.resolveStringRef = function(ref) {
  ref = this.crappyUnescape(ref);

  if (this.refs[ref]) {

View on GitHub (pinned to 5b09d0ff96)

Solutions

  1. Count actual ancestors and shorten the chain (use git log / the visualization)
  2. Use ^ only up to the commit's parent count (merges have 2)
  3. Resolve the intermediate ref first to verify it exists

Example fix

// before
git checkout main~7 // only 3 commits
// after
git checkout main~3
Defensive patterns

Strategy: validation

Validate before calling

// verify each hop exists before resolving
var c = engine.getCommitFromRef(baseName);
var steps = relative.match(/[~^]\d*/g) || [];
steps.forEach(function(s){
  c = s[0] === '~' ? engine.getCommitFromRef(c.id+'~'+(s.slice(1)||1))
                    : c.get('parents')[parseInt(s.slice(1)||1,10)-1];
  if (!c) throw new Error('chain too long');
});

Type guard

function refChainResolves(engine, ref) {
  try { engine.resolveID(ref); return true; } catch (e) { return false; }
}

Try / catch

try { engine.resolveID(ref); } catch (e) { if (e instanceof GitError) showUsageHint(); else throw e; }

Prevention

When it happens

Trigger: Calling the ref resolver with something like 'HEAD~5' or 'main^2' where following the ~/^ chain yields a falsy next commit (too few ancestors, or ^N on a commit with fewer parents).

Common situations: Counting commits wrong in a level solution (main~9 when only 3 exist); using ^2 on a non-merge commit; walking past the root commit.

Related errors


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