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.

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.

Appendix: 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)