babalae/better-genshin-impact · error · Exception

仓库HEAD未指向任何提交

Error message

仓库HEAD未指向任何提交

What it means

Thrown by GetRepoSubdirectoryTree when repo.Head?.Tip is null — the current branch (HEAD) resolves to no commit. For a freshly cloned/fetched repo this means HEAD is unborn (no commits on the current branch) or detached with no target. LibGit2Sharp's Head.Tip is null on empty repositories.

Source

Thrown at BetterGenshinImpact/Core/Script/ScriptRepoUpdater.cs:1366

    /// <summary>
    /// 检查指定路径是否为 Git 仓库(非文件式仓库)
    /// </summary>
    /// <param name="repoPath">仓库路径</param>
    /// <returns>如果是 Git 仓库返回 true,否则返回 false</returns>
    private bool IsGitRepository(string repoPath)
    {
        return Repository.IsValid(repoPath) && !Directory.Exists(Path.Combine(repoPath, "repo"));
    }

    /// <summary>
    /// 获取仓库中 repo/ 子目录的树对象
    /// </summary>
    private Tree GetRepoSubdirectoryTree(Repository repo)
    {
        var commit = repo.Head?.Tip;
        if (commit == null)
        {
            throw new Exception("仓库HEAD未指向任何提交");
        }

        // 脚本内容都在 repo/ 子目录下
        var repoEntry = commit.Tree["repo"];
        if (repoEntry == null || repoEntry.TargetType != TreeEntryTargetType.Tree)
        {
            throw new Exception("仓库结构错误:未找到 repo/ 子目录");
        }

        return (Tree)repoEntry.Target;
    }

    /// <summary>
    /// 从中央仓库读取文件内容
    /// </summary>
    /// <param name="relPath">相对于仓库根目录的路径</param>
    /// <returns>文件内容,如果文件不存在则返回null</returns>
    public string? ReadFileFromCenterRepo(string relPath)

View on GitHub (pinned to a7cb36712d)

Solutions

  1. Before calling GetRepoSubdirectoryTree, validate repo.Head.IsTip and repo.Head.Tip != null, and if null, re-clone or report a data-integrity error rather than proceeding.
  2. Verify the clone completed successfully (check CloneRepository return / exception) before any tree access.
  3. If HEAD is unborn, attempt to checkout a known branch: Commands.Checkout(repo, repo.Branches["release"]) before reading the tree.
  4. Run `git -C <repoPath> status` to inspect HEAD state; delete the repo directory and re-clone if HEAD is irrecoverable.

Example fix

// before
var commit = repo.Head?.Tip;
if (commit == null) throw new Exception("仓库HEAD未指向任何提交");

// after (diagnostic + recovery)
var head = repo.Head;
if (head == null || head.Tip == null)
{
    _logger.LogError("仓库HEAD无效: head={Head}, branches={Branches}",
        head?.FriendlyName ?? "null",
        string.Join(", ", repo.Branches.Select(b => b.FriendlyName)));
    throw new InvalidOperationException(
        $"仓库HEAD未指向任何提交(HEAD={head?.FriendlyName ?? "null"}),请删除仓库目录后重新克隆");
}
Defensive patterns

Strategy: validation

Validate before calling

// Precondition helper to reuse across all Head.Tip sites
static Commit EnsureHeadTip(Repository repo)
{
    var tip = repo.Head?.Tip;
    if (tip == null)
        throw new InvalidOperationException(
            $"仓库HEAD无提交 (branches: {string.Join(",", repo.Branches.Select(b=>b.FriendlyName))})");
    return tip;
}

Try / catch

catch (InvalidOperationException ex) when (ex.Message.Contains("HEAD"))
{
    // The repo is unusable; delete and re-clone
    DirectoryHelper.DeleteReadOnlyDirectory(repoPath);
    CloneRepository(repoUrl, repoPath, "release", null);
}

Prevention

When it happens

Trigger: Calling GetRepoSubdirectoryTree(repo) on a Repository whose Head points to an unborn branch (just initialized, no commits) or whose HEAD ref is detached and points at a missing object. This is a precondition check before accessing commit.Tree["repo"].

Common situations: Repository was cloned empty (remote had no commits on the checked-out branch); a partial/failed clone left HEAD pointing nowhere; the .git/HEAD file is corrupt; HEAD points to a branch that was never created locally after a fetch failure.

Related errors


AI-assisted analysis of babalae/better-genshin-impact@a7cb36712d (2026-08-13). Data as JSON: /api/errors/f801e57714b99a27. Report an issue: GitHub.