Git 微操自救指南

2298 字
11 分钟
Git 微操自救指南
Git 微操自救指南

使用 Git 进行版本管理时,在通过 commit 提交并 push 了大量垃圾(bushi)后,然后看到提交历史,往往对自己的 push 垃圾的行为后悔不已。那么有对 git 不太熟悉的就要问了,主包主包,有什么悔棋的方法吗?

有的兄弟,有的。像这样的方法,这篇博客一共整理了 9 个。

目前仅整理了部分内容,学习 Git 不亚于新学一门编程语言,学不完,根本学不完。

Warning

!!!禁止修改多人共用的远端分支!!! 你在使用任何 Git 操作时都应该明确这一点,如果一条远端分支有多人共用,那么不要在上面执行 reset 、rebase 等会修改这条分支已经存在的 commit object 的命令。具体原因可以参考 understanding-git-rebase-and-the-golden-rule ;

一、修改最近一个提交#

开发过程中,提交完发现有一些临时的 log 等文件忘记去掉,或者有一些文件忘了提交,甚至 commit 信息打了错别字或者单纯写的不满意,这些情形是常有的。回滚回前一次提交再重新提交当然没啥问题,以下提供一种更清晰快捷的方法。

1.1 操作步骤#

假如此时你已经提交了部分内容(无论有没有推送到远程仓库)。比如:

Terminal window
# 修改了几个文件
git add a.py b.py
git commit -m "feat: 实现初始功能"
# 又改了 a.py,或者想把别的文件一起补进去上一次提交
git add a.py
git commit --amend # 然后输入新的提交信息

不过需要注意的是 --amend 会覆盖上一次提交,你可以观察到提交的哈希值变了,所以如果你已经推送到远程仓库,需要使用 git push --force 强推新的提交。

二、提交一个文件中的部分修改#

你可以使用 git add -p(patch 模式)或 git add -i(interactive 模式)来选择性地将文件的部分修改加入暂存区,而不是整个文件。这对于精细控制提交、按功能分组提交代码非常有用。

  1. 执行 git add -p <文件名> ;

你可以先执行 git add -i ,进入交互菜单,按数字键选择 patch 模式。(interactive 模式还提供多种操作,这里暂不展开)

  1. Git 会逐个展示该文件的修改块(hunk),你可以选择是否添加这个修改;

比如,某一次的修改提示为:

diff --git a/index.js b/index.js
@@ -1,6 +1,7 @@
function add(a, b) {
+ console.log('计算中...');
return a + b;
}
Stage this hunk [y,n,q,a,d,s,e,?]?

它会提供给你如下选项:

输入含义
y暂存这个 hunk(即这部分更改)
n不暂存这个 hunk
q退出
a暂存所有剩余的 hunk
d不暂存任何剩余的 hunk
s拆分这个 hunk 为更小的块(如果可能)
e手动编辑这个 hunk(精确选择添加哪些行)
?显示帮助信息

一般,如果你想要提交部分内容,推荐使用 s 进行拆分。

三、普通版:合并多个提交为一个提交#

大部分人合并可能都会用 git reset 命令实现:

Terminal window
git reset --soft HEAD~X && git commit -m 'xxx'

但是 git reset 只能合并最近的 X 个提交。如果你要整理整个提交历史呢?

git rebase 是 Git 中非常强大的命令,用于整理提交历史、合并分支时避免产生多余的 merge commit,甚至于还能对其中的一些提交进行排序,提高提交历史的清晰度。

3.1 操作步骤#

现在,假如你的提交历史如下:

A - B - C - D - E - F - G - H - I - J (HEAD)

如果你现在想要合并 C、D、E 这三个提交成一个提交。

  1. 首先,找到你所希望合并的提交中的最早一个提交的上一个提交(本情形中是 B);

可以使用该命令查看节点与其对应的哈希值:

Terminal window
git log --oneline

然后可以看到类似以下内容:

j9k8l7  J
i8h7g6  I
...
e5d4c3  E
d4c3b2  D
c3b2a1  C
b2a1z0  B   ← ← 这个是你需要的基准点
a1z0y9  A
  1. 执行 rebase 命令:git rebase -i b2a1z0 ;

在执行完以上命令后,终端会自动跳转到 Git 的 Vim 编辑器;

  1. 编辑提交列表;

Vim 编辑器会打开如下列表(从旧到新):

pick c3b2a1 C
pick d4c3b2 D
pick e5d4c3 E
pick ... (后面的如果不希望合并就不用管)

改为:

pick c3b2a1 C
squash d4c3b2 D
squash e5d4c3 E

然后保存;

  1. 编辑提交信息,输入你希望保留的新提交说明,保存退出即可完成;
  2. 推送到远程仓库:git push --force-with-lease ;

3.2 注意点#

  1. 如果你在上述操作过程中有任何失误,可以运行 git rebase --abort 及时恢复到变基以前的状态;
  2. ⚠️ rebase 会导致新的 commit 节点产生,所以切记不要对多人共用的远端分支进行 rebase 。

四、进阶版:合并多个提交为大于一个提交#

其实与上面的操作是类似的,只不过你不需要重复上述步骤整理单独一个提交,这就是 git rebase -i 的强大之处 —— 你可以精确控制哪些 commit 合并,哪些保留。

现在假设你的提交历史为:

A - B - C - D - E - F - G (HEAD)

你想把:

  • B+C+D 合并为一个提交
  • E+F 合并为另一个提交
  • 保留 G 不动
  1. 仍然找所有你希望变更的提交里最早的前一个提交并变基:

本情形中就是 git rebase -i <A的commit哈希> ;

然后会进入编辑提交列表,形如:

pick bbbbbbb B
pick ccccccc C
pick ddddddd D
pick eeeeeee E
pick fffffff F
pick ggggggg G
  1. 编辑提交历史;
pick bbbbbbb B
squash ccccccc C
squash ddddddd D
pick eeeeeee E
squash fffffff F
pick ggggggg G

说明:

  • 第一组:B(pick)+ C(squash)+ D(squash) → 合并成 1 个提交
  • 第二组:E(pick)+ F(squash) → 合并成另一个提交
  • 第三组:G 保持不动
  1. 编辑 commit message

Git 会依次提示你编辑每一组的合并 commit message ;

五、找回丢失的 commit 节点或分支#

5.1 原理#

首先,你需要知道,在你通过 Git 合并或删除一些提交或分支后,远程仓库和本地仓库的提交历史看似没有内容,确实删除了,但并非不可恢复。

分支和提交均有唯一的哈希值,虽然远程仓库不保存删除或覆盖的内容,但是 Git 的强大之处就在于,它提供了 Reflog 命令,它可以查看你本地 Git 仓库中所有分支引用的变动历史,即使是你已经被“删除”的 commit 或 reset 过的状态,它也能找到。(只要还没被垃圾回收,Git 默认在本地保留 reflog 数据 90 天)

5.2 具体步骤#

  1. 执行命令:git reflog ;

会输出以下信息:

8a2c28d (HEAD -> main) HEAD@{0}: reset: moving to origin/main
fbf9a61 HEAD@{1}: commit: 修复BUG
77d0d44 HEAD@{2}: commit: 添加功能A
e4d0c1b HEAD@{3}: rebase -i (finish): returning to refs/heads/main

其中,HEAD@{0} 是当前 HEAD 的变更位置(0 表示最新)。

  1. 找到你需要恢复的节点的哈希值,恢复该版本;

比如:

Terminal window
git reset --hard <commit哈希>

或者:

Terminal window
git checkout <commit哈希>
# 或者创建个分支保住它
git checkout -b rescued-branch <commit哈希>

六、获得干净的工作空间#

假如你在本地修改了一些文件,但又没必要上传,或者只是临时用的,在实际工作中仍需与远程仓库一致,这时候,你当然可以通过 git reset --hard HEAD 等直接丢弃本地修改,但万一等你重置完又想用了呢,这些文件并没有提交到 Git 历史,自然无法通过第三部分中的操作恢复。

6.1 原理#

git stash 是 Git 中的一个命令,用来临时保存当前工作目录的修改,让你可以切换分支或执行其他操作,而不会丢失当前更改。它非常适用于“你正在开发功能 A,突然需要切换到主分支修个 Bug,但你又不想提交当前没完成的代码。”的这种情形。

一句话概括,git stash 让你可以随时“藏”起当前代码进度,切分支不怕打断,再“拿”回来继续写。

6.2 具体步骤#

  1. 执行 git stash ,暂存当前修改(包括已暂存和未暂存的);

或者你也可以像 commit 一样,增加信息,方便区分:

Terminal window
git stash push -m "临时代码:XX模块"

还有一些参数可以选择:

Terminal window
git stash -k # 只保存未暂存的文件
git stash -u # 包含 untracked
git stash -a # 包含 untracked + ignored
  1. 恢复最近一次 stash (同时会将其从 stash list 中删除);
Terminal window
git stash pop

你也可以通过 git stash apply stash@{1} 恢复特定一次的 stash ;

  1. 可以通过 git stash list 查看 stash 列表;
  2. 如果希望删除某次 stash ,可以通过 git stash drop stash@{0} ;
  3. 如果希望清空所有 stash ,可以通过 git stash clear ;

七、占位#

八、占位#

九、占位#

文章分享

如果这篇文章对你有帮助,欢迎分享给更多人!

Git 微操自救指南
https://blog.yokumi.cn/posts/git-micro-operations-self-rescue-guide/
作者
Yokumi
发布于
2025-05-16
许可协议
CC BY-NC-SA 4.0
相关文章智能推荐
1
第一台 VPS 折腾小记
开发心得在使用本项目/教程/脚本之前,请务必仔细阅读以下声明。一旦您开始使用、复制或修改相关内容,即视为您已完全接受并同意本免责声明的所有条款。 请谨记:使用技术时请务必遵守您所在国家和地区的法律法规。 使用 IPv6-only VPS 低成本独享家宽,解锁 ChatGPT 不风控
2
2026.5 Live Repo
生活杂谈翻了翻相册才发现上一次已经是去年 9 月的羊文学了,哎gszm坏事做尽。 这回的出行方式是经典京沪高铁二等座,去年锅贴得意之作绿皮 D9 真给我坐麻了,这次果断 G 系列。不过被课表背刺,本来还可以买早一班(),结果就是快 0 点才到虹桥。好像是第一次这么晚到沪国。
3
在 Linux 下编译安装 Dynamips 和 Dynagen 搭建 Cisco 实验环境
开发心得最终还是花了一点时间把计网实践的实验平台搞到 Linux 下实现了,相比于 Windows,同样配置的路由器,Linux 下 CPU 占用可降低 50% 以上,应该也会更加稳定吧。 本文将详细介绍如何在 Linux (Ubuntu/Debian) 系统下(macOS 等也是类似的,一些依赖的安装略有不同罢了)从源码编…
4
Python 套件管理器 uv 使用介绍
开发心得Python 生态得益于大数据时代和大模型时代蓬勃发展,相应的包管理工具也不断涌现,这篇文章简单记录一下个人上手 Astral 团队开发的 uv 的一些基操和使用一段时间后的体验。当然其实官方文档[^1]已经很详细了。 下表转载自 Shoukai Huang 大佬的博客[^2] 整理的横向对比:
5
浅谈大型项目的模块化开发(Based on Python)
开发心得最近在进行大型项目开发时,自行实现的一些公共模块,包括工具库、API 客户端等,都被扔在一个文件夹下,需要时由该项目的控制代码进行调用,不过因为水平之菜,经过测试没什么问题的代码 PR 上去之后经常会出现需要反复修改的情况。而且另一方面,这些公共模块具有一定的可移植性,但只被一个项目使用,没什么性价比,肯定是需要进一…

评论区

文章目录