You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用Go结合GitHub API在单次提交中删除多个文件

问题分析与修复方案

你的代码存在两个关键问题,导致提交后GitHub上无变更显示:

1. 变量名不匹配

函数参数定义为files []string,但循环判断要删除的文件时,误用了未定义的filesToDelete变量。这会导致没有任何文件被排除,新树与原树完全一致,因此提交看不到变化。

2. 创建树时未指定基础树SHA

调用CreateTree时第三个参数传了空字符串,GitHub API无法识别这是基于原仓库树的修改。正确做法是传入原树的SHA,让API基于现有树进行增量更新(包括删除操作)。

删除文件的标准做法有两种,均需依赖基础树:

  • 方法一:从原树条目里排除要删除的文件
  • 方法二:为要删除的文件创建SHA为空字符串的TreeEntry,并指定正确的Path和Mode(文件对应"100644",目录对应"040000")

修正后的代码

func DeleteFiles(ctx context.Context, files []string) error {
    // 获取分支最新引用
    ref, _, err := g.Client.Git.GetRef(ctx, Owner, Repo, "refs/heads/"+Branch)
    if err != nil {
        log.Fatalf("获取分支引用失败: %v", err)
    }

    // 获取当前提交对应的树(递归获取所有条目)
    tree, _, err := g.Client.Git.GetTree(ctx, Owner, Repo, *ref.Object.SHA, true)
    if err != nil {
        log.Fatalf("获取提交树失败: %v", err)
    }

    // 方法一:排除要删除的文件,构建新树条目
    entries := []github.TreeEntry{}
    fileSet := make(map[string]struct{})
    for _, f := range files {
        fileSet[f] = struct{}{}
    }
    for _, entry := range tree.Entries {
        if _, exists := fileSet[*entry.Path]; !exists {
            entries = append(entries, entry)
        }
    }

    // 方法二(可选):通过设置SHA为空来标记删除,无需遍历所有条目
    // for _, f := range files {
    //     entries = append(entries, github.TreeEntry{
    //         Path: github.String(f),
    //         Mode: github.String("100644"), // 文件对应的mode,目录是"040000"
    //         SHA:  github.String(""),
    //     })
    // }

    // 创建新树:传入原树SHA作为基础
    newTree, _, err := g.Client.Git.CreateTree(ctx, Owner, Repo, *tree.SHA, entries)
    if err != nil {
        log.Fatalf("创建新树失败: %v", err)
    }

    // 创建新提交
    parent := []github.Commit{{SHA: ref.Object.SHA}}
    newCommit := &github.Commit{
        Message: github.String("Remove multiple files"),
        Tree:    newTree,
        Parents: parent,
    }
    commit, _, err := g.Client.Git.CreateCommit(ctx, Owner, Repo, newCommit)
    if err != nil {
        log.Fatalf("创建提交失败: %v", err)
    }

    // 更新分支引用到新提交
    ref.Object.SHA = commit.SHA
    _, _, err = g.Client.Git.UpdateRef(ctx, Owner, Repo, ref, false)
    if err != nil {
        log.Fatalf("更新分支引用失败: %v ", err)
    }

    return nil
}

额外优化说明

  • 使用map[string]struct{}存储要删除的文件,将判断时间复杂度从O(n²)降到O(n),文件数量较多时效率更高。
  • 方法二无需遍历所有树条目,直接添加删除标记,代码更简洁,适合删除少量文件的场景。

内容的提问来源于stack exchange,提问作者slvk

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 01:40:15