如何使用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
相关产品推荐
相关产品推荐

