背景 1
在一个分支上的提交顺序如下:-> 代表新的提交
- 在提交 E 中,文件包含了 GitHub 生成的 token
- 提交 F 是一次普通的提交,不包含 token
Bash
A -> ... -> E -> F (敏感信息在 E 中)
附:给提交起名是为了方便说明问题。在 Git 中是用 Hash 值来表示一个提交的。
准备工作
在 GitHub 生成 1 个 token,用于复现 bug。
rebase_token:
Bash
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
解决方案
1、根据控制台错误信息或查看提交日志(git log --oneline),确定包含 token 的第一次提交。
2、启动交互式 rebase,在编辑器中将第一个包含 token 的提交的状态由 pick 修改为 edit。
3、在停止状态下,删除提交中包含的 token。
4、(1 ) 将变更添加到暂存区(git add .);(2) 将这次修改作为新的提交(git commit --amend),并覆盖之前的提交。
5、完成 rebase(git rebase --continue),会再次进入编辑器模式,提示为新提交提供 commit message。
6、提供提交信息后,由于后续的提交都需要基于当前的新提交,可能会有冲突,需要先解决冲突。解决冲突也会发生变基。最后,完成 rebase,push 代码到 GitHub。
bug复现&解决
1、进行两次提交
提交 E:在提交的文档中插入一个 token,用来演示后续的 push 操作被 GitHub 拒绝
![](https://i-blog.csdnimg.cn/direct/1af6063d6ae24b3d89f95bdec3e9dfc2.png)
提交 F:提交 F 相较于提交 E 是增量修改。
![](https://i-blog.csdnimg.cn/direct/ca71b41bb0ae4fc9a77133651f78d828.png)
F 提交完以后,要将本地代码 push 到 GitHub,push 被 GitHub 拒绝:
![](https://i-blog.csdnimg.cn/direct/8a77bebd94d644288c298499b06d0a34.png)
push 被 GitHub 拒绝:
![](https://i-blog.csdnimg.cn/direct/de362f7f33704157a0841b24b75f506c.png)
在控制台查看细节,如下:
Bash
14:30:47.941: [JavaWiki] git -c credential.helper= -c core.quotepath=false -c log.showSignature=false push --progress --porcelain origin refs/heads/master:master
Enumerating objects: 11, done.
Counting objects: 9% (1/11)
Counting objects: 18% (2/11)
Counting objects: 27% (3/11)
Counting objects: 36% (4/11)
Counting objects: 45% (5/11)
Counting objects: 54% (6/11)
Counting objects: 63% (7/11)
Counting objects: 72% (8/11)
Counting objects: 81% (9/11)
Counting objects: 90% (10/11)
Counting objects: 100% (11/11)
Counting objects: 100% (11/11), done.
Delta compression using up to 8 threads
Compressing objects: 12% (1/8)
Compressing objects: 25% (2/8)
Compressing objects: 37% (3/8)
Compressing objects: 50% (4/8)
Compressing objects: 62% (5/8)
Compressing objects: 75% (6/8)
Compressing objects: 87% (7/8)
Compressing objects: 100% (8/8)
Compressing objects: 100% (8/8), done.
Writing objects: 12% (1/8)
Writing objects: 25% (2/8)
Writing objects: 37% (3/8)
Writing objects: 50% (4/8)
Writing objects: 62% (5/8)
Writing objects: 75% (6/8)
Writing objects: 87% (7/8)
Writing objects: 100% (8/8)
Writing objects: 100% (8/8), 866 bytes | 866.00 KiB/s, done.
Total 8 (delta 6), reused 0 (delta 0), pack-reused 0 (from 0)
remote: Resolving deltas: 0% (0/6)
remote: Resolving deltas: 16% (1/6)
remote: Resolving deltas: 33% (2/6)
remote: Resolving deltas: 50% (3/6)
remote: Resolving deltas: 66% (4/6)
remote: Resolving deltas: 83% (5/6)
remote: Resolving deltas: 100% (6/6)
remote: Resolving deltas: 100% (6/6), completed with 3 local objects.
remote: error: GH013: Repository rule violations found for refs/heads/master.
remote:
remote: - GITHUB PUSH PROTECTION
remote: ---------------------------------------------------------------------------------------------------------------------------
remote: Resolve the following violations before pushing again
remote:
remote: - Push cannot contain secrets
remote:
remote:
remote: (?) Learn how to resolve a blocked push
remote: https://docs.github.com/code-security/secret-scanning/working-with-secret-scanning-and-push-protection/working-with-push-protection-from-the-command-line#resolving-a-blocked-push
remote:
remote:
remote: ------ GitHub Personal Access Token ------------------------------------------------------------------
remote: locations:
remote: - commit: 5b8f49aca4ce563a463a6750222692724ae2f0f7
remote: path: 玩转MacBook/PicGo + Typora + GitHub搭建图床.md:112
remote: - commit: a6d81c0aff0efc8b8a8db1b8e6a2d731dd33820c
remote: path: 玩转MacBook/PicGo + Typora + GitHub搭建图床.md:112
remote:
remote: (?) To push, remove secret from commit(s) or follow this URL to allow the secret.
To github.com:Acura-bit/JavaWiki.git
remote: https://github.com/Acura-bit/JavaWiki/security/secret-scanning/unblock-secret/2qph1YYqOWV1XcOtE5UkX42tUh2
! refs/heads/master:refs/heads/master [remote rejected] (push declined due to repository rule violations)
remote:
remote:
Done
remote:
error: failed to push some refs to 'github.com:Acura-bit/JavaWiki.git'
由标红处可以看到,GitHub 拒绝 push 的原因是提交中包含 secret,也就是 token。标绿处提示我们移除 token 后再尝试提交。
思考:我们检查代码,然后把代码中的 token 删掉后再提交、再 push,能通过吗?
- 答案是不可以。因为报错信息明确要求我们要将提交历史中包含的 token 移除,因此需要修改之前的提交。
这时候,rebase 就要登场了。
2、定位第一次包含 token 的提交
控制台的报错信息给出了包含 token 的两次提交:
在提交 E 中包含 token,因为提交 F 是基于提交 E 创建的,所以 F 中也被认为含有 token。但由于 F 是在 E 的基础上做了增量修改,所以在变基编辑时只需要修改提交 E。
![](https://i-blog.csdnimg.cn/direct/56153e3c3826419cab19b6ee8aeb5c91.png)
其实在背景中已经说明了,提交 E 是第一次包含 token 的提交。问题是控制台显示的两个提交那个是第一次提交呢?
查看提交日志:git log --oneline
![](https://i-blog.csdnimg.cn/direct/ffa94cdd79b2437e8dae36fed6387613.png)
由提交日志可知,提交 E 的哈希值为 a6d81c0;提交 F 的哈希值为 5b8f49a。
3、启动交互式 rebase,列出所有包含 token 的提交
执行如下命令,从提交 E 的前一个提交开始 rebase。之所以从前一次开始,是为了包含提交 E。
Bash
git rebase -i a6d81c0~1
执行命令后,IDEA 的终端进入了交互时状态,如下图所示:
![](https://i-blog.csdnimg.cn/direct/5922bf3aab334cdabfd5027210eef117.png)
4、修改有问题的提交
将目标提交 E 从 pick
改为 edit
,然后保存:
- 英文输入模式下,输入 i 进入 insert 模式
- 将第一行的 pick 修改为 edit
- 按 ESC,退出 insert 模式
- 输入 :wq,然后回车
将提交 E 由 pick 状态修改为 edit 状态:
![](https://i-blog.csdnimg.cn/direct/d01ea406998c4879ab1c6daa8ef8c92c.png)
当前,控制台打印如下:
![](https://i-blog.csdnimg.cn/direct/febf3204121740bea0c80eab36b23e51.png)
这样,Git 会暂停在这个提交上,让你对其内容进行修改。如下所示:
![](https://i-blog.csdnimg.cn/direct/9adf76ae69654f8cb8f8a9e438ca97b8.png)
这时,我们记录一下提交 E 和 F:
(1) 提交 E 的现状:
![](https://i-blog.csdnimg.cn/direct/51f2e5f1949b479b9e8805e8a7cd4479.png)
(2) 提交 F 的现状:
![](https://i-blog.csdnimg.cn/direct/480e271bcb86481898692e6239f3cbf1.png)
开始编辑,如下图所示,当前确实暂停在了提交 E 上:
![](https://i-blog.csdnimg.cn/direct/c67aee108c074d6b918ef0007ddcf733.png)
删除代码中的 token:
![](https://i-blog.csdnimg.cn/direct/e8a8c7e2b4b24a2b80107e09e849c948.png)
5、提交修改
(1) 修改完成后,将改动添加到暂存区:
Bash
git add .
将当前修改(删除了 token)提交以覆盖之前的提交:
Bash
git commit --amend
执行上述两步后,控制台打印如下:
![](https://i-blog.csdnimg.cn/direct/7439a0e231e24ac094d3710c167bacc3.png)
6、完成 rebase
输入命令 git rebase --continue
,完成 rebase:
如下所示,终端又进入了编辑器模式:在上面的变基编辑(删除 token)后,执行 git commit --amend 是为了覆盖提交 E,所以会出现如下界面,提示你修改 commit message,当然也可以不用修改。
![](https://i-blog.csdnimg.cn/direct/f2c8774c20f049ea97ec53f0e0dc8ea1.png)
这里我们使用全新的 commit message,进行如下操作:
- 英文输入模式下,输入 i 进入 insert 模式
- 修改提交说明
- 按 ESC,退出 insert 模式
- 输入 :wq,然后回车
![](https://i-blog.csdnimg.cn/direct/98ed3dfbe77e4ca3b54c86f0cad269fe.png)
如上图所示,在编辑器中修改了 commit message。
但是,退出编辑器,提交 E 被全新的提交 G 覆盖,提交 G 的哈希值为 7f41eb9。然而,发生了冲突,
![](https://i-blog.csdnimg.cn/direct/a4bac0dfe54a4a9ab5cae56659dc17bb.png)
冲突原因:在提交 E 中编辑(删除 token)后,使用 git commit --amend 覆盖提交 E,生成了新的提交 G,如上图粉色框。因为,提交 F 是基于原来的提交 E 的,在 E 被 G 覆盖后,需要基于 G。然而,不能直接将 G 的内容应用于 F 如上绿色框,因为缺少内容。如下图所示:
![](https://i-blog.csdnimg.cn/direct/fa387e8c28e94db2a05430f251ab8c9a.png)
解决冲突:
将修改添加到暂存区 git add .
,然后执行 git rebase -- continue,弹出了终端编辑模式:
![](https://i-blog.csdnimg.cn/direct/9760fefbee4c438b80cdb3608d89987a.png)
7、将最新的提交push到GitHub
执行 push 操作时可以看到,原来的提交 E 和 F 都发生了编辑,都被新的提交覆盖了:
![](https://i-blog.csdnimg.cn/direct/a7ee1468b64b4d91b9ed325688488bfa.png)
查看提交状态:
(1) 提交 E 被新的提交 7f41eb9f(G)覆盖:因为提交 E 相较于之前的提交只是新增了几行,所以在变基编辑(删除)后,新的提交没有相较于之前的更改。
![](https://i-blog.csdnimg.cn/direct/2c19aa26226f4ef3ba00d3c08a27bf27.png)
(2) 提交 F 被新的提交 a594d2f0(H)覆盖了
![](https://i-blog.csdnimg.cn/direct/c2fcf1cced1a44668f1fdd8bf70b2efc.png)
思考
在背景 1 中,E 是包含 token 的第一个提交,提交 F 没有改动提交 E 关于 token 的内容,只是另外添加了一些内容。我们在交互式变基时,只修改了提交 E 的内容,而没有修改提交 F 的内容,这是为啥呢??
Bash
A -> ... -> E -> F (敏感信息在 E 中)
解释:
Git 的提交记录是一个链式的结构,每次新的提交都会基于上一提交的快照创建。提交 F 本身只记录了在提交 E 的基础上新增或修改的内容,但并不会重复记录提交 E 的全部内容。
- 如果提交 F 本身没有显式地重新添加该 token,则 token 实际上仍是提交 E 中的遗留问题。
- 当通过
rebase
修改提交 E 时,Git 会重新生成基于修改后提交 E 的新历史,而提交 F 也会被重新基于这次修改后的记录进行构建。
举个例子:
假设有以下提交记录:
原始提交记录:
Plain
A: 初始化项目
B: 不小心加入了 token
C: 修改代码
提交 B 的内容:
Bash
新增 README.md:
- API 文档说明
- token = "abc1234"
提交 C 的内容:
Bash
修改 README.md:
- 调整 API 的描述文案
如果只清理提交 B 中的 token,Git 在重构历史时会自动保证提交 C 基于清理后的提交 B:
清理后记录:
Plain
A: 初始化项目
B': 删除 token(修订的提交 B)
C': 修改代码(基于提交 B' 重构)
提交 B' 的内容:
Bash
新增 README.md:
- API 文档说明
提交 C' 的内容,或者说相对于提交 B' 不同的内容:
Bash
修改 README.md:
- 调整 API 的描述文案
由于 token 已在 B' 中被移除,C' 不会再继承该敏感信息。
背景 2
由上面的思考引出了下面的新问题。
在一个分支上的提交顺序如下:-> 代表新的提交
- 在提交 E 中,文件包含了 GitHub 生成的 token
- 在提交 F 中,同样包含 GitHub 生成的 token
Bash
A -> ... -> E -> F (敏感信息在 E 中)
解决方案
1、逐一 rebase:可以参考背景 1 中的解决方案,先解决提交 E;然后,以同样的方式解决提交 F。
2、一同 rebase。(演示这种方案)
bug 复现&解决
1、进行两次提交
提交 E:在提交的文档中插入一个 token,用来演示后续的 push 操作被 GitHub 拒绝
![](https://i-blog.csdnimg.cn/direct/4d2715d2881945cfbcbda000ba13964f.png)
提交 F:在另一份文档中添加 token
![](https://i-blog.csdnimg.cn/direct/e909e99e743749feb595e66961483c96.png)
效果:提交 E 中含有 token;提交 F 相较于 E,除了含有 E 添加的 token 外,还含有自身添加的其他 token。
F 提交完以后,要将本地代码 push 到 GitHub,push 被 GitHub 拒绝:
在控制台找到被拒绝 push 的提交的信息:
![](https://i-blog.csdnimg.cn/direct/0b253f721eb6457caec5eb75f94d6034.png)
2、定位第一次包含 token 的提交
查看日志:
![](https://i-blog.csdnimg.cn/direct/598d662f30354ff78a362dd411d01aba.png)
第一个包含 token 的提交的哈希值为 5961957。
3、启动交互式 rebase,列出所有包含 token 的提交
执行如下命令,从提交 E 的前一个提交开始 rebase。之所以从前一次开始,是为了包含提交 E。
Bash
git rebase -i 5961957~1
终端如下图所示:
![](https://i-blog.csdnimg.cn/direct/755fa5e7b5fb48c789c3182c7dfa2896.png)
4、修改有问题的提交
将提交 E 和 F 的状态都修改为 edit:
![](https://i-blog.csdnimg.cn/direct/1e64c26631284415837e95a43d92c2cb.png)
当前,停止在提交 E 处:
![](https://i-blog.csdnimg.cn/direct/c2736ebe94e248719957b16188f44949.png)
删除提交 E 中含有的 token:
![](https://i-blog.csdnimg.cn/direct/3578e65315184d25b0b6ad5812eed696.png)
将修改提交:
Bash
# 先添加到暂存区
git add .
# 再提交更改,并覆盖原来的提交
git commit --amend
控制台打印如下:
![](https://i-blog.csdnimg.cn/direct/6a3101a1d9fb4c62a4c4ac88de69238c.png)
执行 git rebase --continue,继续 rebase。如下图所示,停止在了提交 F 上:
![](https://i-blog.csdnimg.cn/direct/51fcf49665094743bf7f57e564f84842.png)
开始 rebase 提交 F
在提交 F 中删除 token:
![](https://i-blog.csdnimg.cn/direct/57c1836e1bdd434e985ddcc702c42ca6.png)
将修改提交:
Bash
# 先添加到暂存区
git add .
# 再提交更改,并覆盖原来的提交
git commit --amend
控制台打印如下:
![](https://i-blog.csdnimg.cn/direct/373ad62f7d1644bab7c3bf2d2f78a455.png)
执行 git rebase --continue,继续 rebase。如下图所示,变基成功:
![](https://i-blog.csdnimg.cn/direct/825f6e0b18dd42e1b40e5509f2eb7018.png)
5、将最新的提交push到GitHub
![](https://i-blog.csdnimg.cn/direct/83f11177347a49d4b129ef1200e93b14.png)
成功!
参考: