Jenkins 中 Git 操作 not a tree 分析

2023年 1月 4日 92.7k 0

1. 问题描述

配置 Webhook 自动触发执行 Jenkins 流水线时,报错:

1
2
3
hudson.plugins.git.GitException: Command "git checkout -f 23b446ea" returned status code 128:
stdout: 
stderr: fatal: reference is not a tree: 23b446ea

2. Git 如何管理版本

Git 是一个内容寻址文件系统。Git 维护着一棵 sha tree ,通过 sha 值可以回溯到任何一个历史节点。先看看提交记录:执行命令:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
git log

commit e5562ec75fedf0bd66fe752e686db108cf20cf9b (HEAD -> fix_create_pipeline, origin/fix_create_pipeline)
Date:   Thu May 14 15:31:50 2020 +0800

    fix create pipeline error

commit 1bd660c370aca1113ae0b4fa2e814e9ea337e4d7
Date:   Thu May 14 13:22:30 2020 +0800

    fix proxy bug (#2070)

commit 1464ca197d161b2ed693c7e9a053fcc97e0fdbaa (origin/master, origin/HEAD, fix_apiserver_devops)
Merge: 1d48ca34 250dd4b0
Date:   Wed May 13 17:24:31 2020 +0800

    Merge pull request #2057 from 

    fix bug of pvc api

可以发现,每次提交 git 都会生成一个 commitId ,也就是一个 sha 。commitId 后面紧跟的括号,说明了 commit 内容所属分支发生的变化。那么这些 (origin/master, origin/HEAD, fix_apiserver_devops) 又是什么含义呢?在 .git/refs/ 中,Git 存储着指向数据(分支)的提交对象的指针。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
tree .git/refs/

.git/refs/
├── heads
│   ├── feature_add_pipeline_api
│   ├── fix_apiserver_devops
│   ├── fix_create_devops
│   ├── fix_create_pipeline
│   ├── master
│   └── release-2.1
├── remotes
│   ├── origin
│   │   ├── HEAD
│   │   ├── feature_add_pipeline_api
│   │   ├── fix_create_devops
│   │   ├── fix_create_pipeline
│   │   ├── fix_devops
│   │   └── master
│   └── upstream
│       ├── master
│       └── release-2.1
└── tags
1
2
3
cat .git/refs/heads/feature_add_pipeline_api

f0749ac442ef683a2de6ed3fdc8e3e3b44b71076

分支、标签在 Git 看来,就是指向 sha tree 某一个 sha 的指针。

3. 原因分析

Jenkins 或相关 Git 插件出错的概率较小,当然也可以检查一下 Git 的版本。控制变量法,首先在 Jenkins 外部执行发生错误的命令。

1
git checkout -f 23b446ea

在了解 Git 的工作原理之后,大概就能分析出原因:人使用的是 branchName、tagName ,而 Git 使用的是 sha 。reference not a tree 意味着,触发和执行两个阶段 sha tree 发生了变化。触发时,branchName 对应的 sha,在执行阶段不存在。下面是结合其他人遇到的情景进行的一些可能分析:

  • 分支名和标签名相同

分支和标签的差别在于,分支可以继续提交,而标签不能继续更改。两者同名时,会有一些怪异的行为,比如 branchName 消失等。

  • rebase 导致

rebase 通常用于合并 upstream 的代码,也会用于将多个线性 commit 合并为一个。比如,下面的命令,将前 3 个版本的提交合并为 1 个:

1
git rebase -i HEAD~3

这样会导致 commit 丢失,而丢失的 commit 可能正好触发了 Jenkins 的某个动作。

  • PR 合并后,分支被删除

基于 特征开发,主干集成,分支发布 的思想,在持续集成的过程中,最佳实践的方式是当代码合并之后,随即删掉分支,Github 上打通了这个流程。特征分支触发 Jenkins 流水线之后,分支被删除,当执行到拉取代码时,也会报错。

  • Subumodule

子仓库的 commit 记录发生了变化,在执行 git update 之类的操作时,也可能报错。

  • 非最新代码

使用了缓存的代码,在旧代码上找不到最新的 commit 。由于在 Jenkins 实践的过程中,使用方式和场景存在差异,除非是必现的错误,排查问题需要围绕问题的关键,控制变量一步一步筛查。

4. 参考

  • https://github.com/kubernetes/kubernetes/issues/27462

相关文章

KubeSphere 部署向量数据库 Milvus 实战指南
探索 Kubernetes 持久化存储之 Longhorn 初窥门径
征服 Docker 镜像访问限制!KubeSphere v3.4.1 成功部署全攻略
那些年在 Terraform 上吃到的糖和踩过的坑
无需 Kubernetes 测试 Kubernetes 网络实现
Kubernetes v1.31 中的移除和主要变更

发布评论