Git 微操自救指南

使用 Git 进行版本管理时,在通过 commit 提交并 push 了大量垃圾(bushi)后,然后看到提交历史,往往对自己的 push 垃圾的行为后悔不已。那么有对 git 不太熟悉的就要问了,主包主包,有什么悔棋的方法吗?
有的兄弟,有的。像这样的方法,这篇博客一共整理了 9 个。
目前仅整理了部分内容,学习 Git 不亚于新学一门编程语言,学不完,根本学不完。
!!!禁止修改多人共用的远端分支!!! 你在使用任何 Git 操作时都应该明确这一点,如果一条远端分支有多人共用,那么不要在上面执行 reset 、rebase 等会修改这条分支已经存在的 commit object 的命令。具体原因可以参考 understanding-git-rebase-and-the-golden-rule ;
一、修改最近一个提交
开发过程中,提交完发现有一些临时的 log 等文件忘记去掉,或者有一些文件忘了提交,甚至 commit 信息打了错别字或者单纯写的不满意,这些情形是常有的。回滚回前一次提交再重新提交当然没啥问题,以下提供一种更清晰快捷的方法。
1.1 操作步骤
假如此时你已经提交了部分内容(无论有没有推送到远程仓库)。比如:
# 修改了几个文件git add a.py b.pygit commit -m "feat: 实现初始功能"
# 又改了 a.py,或者想把别的文件一起补进去上一次提交git add a.pygit commit --amend # 然后输入新的提交信息不过需要注意的是 --amend 会覆盖上一次提交,你可以观察到提交的哈希值变了,所以如果你已经推送到远程仓库,需要使用 git push --force 强推新的提交。
二、提交一个文件中的部分修改
你可以使用 git add -p(patch 模式)或 git add -i(interactive 模式)来选择性地将文件的部分修改加入暂存区,而不是整个文件。这对于精细控制提交、按功能分组提交代码非常有用。
- 执行
git add -p <文件名>;
你可以先执行 git add -i ,进入交互菜单,按数字键选择 patch 模式。(interactive 模式还提供多种操作,这里暂不展开)
- 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 命令实现:
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 这三个提交成一个提交。
- 首先,找到你所希望合并的提交中的最早一个提交的上一个提交(本情形中是 B);
可以使用该命令查看节点与其对应的哈希值:
git log --oneline然后可以看到类似以下内容:
j9k8l7 Ji8h7g6 I...e5d4c3 Ed4c3b2 Dc3b2a1 Cb2a1z0 B ← ← 这个是你需要的基准点a1z0y9 A- 执行 rebase 命令:
git rebase -i b2a1z0;
在执行完以上命令后,终端会自动跳转到 Git 的 Vim 编辑器;
- 编辑提交列表;
Vim 编辑器会打开如下列表(从旧到新):
pick c3b2a1 Cpick d4c3b2 Dpick e5d4c3 Epick ... (后面的如果不希望合并就不用管)改为:
pick c3b2a1 Csquash d4c3b2 Dsquash e5d4c3 E然后保存;
- 编辑提交信息,输入你希望保留的新提交说明,保存退出即可完成;
- 推送到远程仓库:
git push --force-with-lease;
3.2 注意点
- 如果你在上述操作过程中有任何失误,可以运行
git rebase --abort及时恢复到变基以前的状态; - ⚠️
rebase会导致新的 commit 节点产生,所以切记不要对多人共用的远端分支进行 rebase 。
四、进阶版:合并多个提交为大于一个提交
其实与上面的操作是类似的,只不过你不需要重复上述步骤整理单独一个提交,这就是 git rebase -i 的强大之处 —— 你可以精确控制哪些 commit 合并,哪些保留。
现在假设你的提交历史为:
A - B - C - D - E - F - G (HEAD)你想把:
- B+C+D 合并为一个提交
- E+F 合并为另一个提交
- 保留 G 不动
- 仍然找所有你希望变更的提交里最早的前一个提交并变基:
本情形中就是 git rebase -i <A的commit哈希> ;
然后会进入编辑提交列表,形如:
pick bbbbbbb Bpick ccccccc Cpick ddddddd Dpick eeeeeee Epick fffffff Fpick ggggggg G- 编辑提交历史;
pick bbbbbbb Bsquash ccccccc Csquash ddddddd Dpick eeeeeee Esquash fffffff Fpick ggggggg G说明:
- 第一组:B(pick)+ C(squash)+ D(squash) → 合并成 1 个提交
- 第二组:E(pick)+ F(squash) → 合并成另一个提交
- 第三组:G 保持不动
- 编辑 commit message
Git 会依次提示你编辑每一组的合并 commit message ;
五、找回丢失的 commit 节点或分支
5.1 原理
首先,你需要知道,在你通过 Git 合并或删除一些提交或分支后,远程仓库和本地仓库的提交历史看似没有内容,确实删除了,但并非不可恢复。
分支和提交均有唯一的哈希值,虽然远程仓库不保存删除或覆盖的内容,但是 Git 的强大之处就在于,它提供了 Reflog 命令,它可以查看你本地 Git 仓库中所有分支引用的变动历史,即使是你已经被“删除”的 commit 或 reset 过的状态,它也能找到。(只要还没被垃圾回收,Git 默认在本地保留 reflog 数据 90 天)
5.2 具体步骤
- 执行命令:
git reflog;
会输出以下信息:
8a2c28d (HEAD -> main) HEAD@{0}: reset: moving to origin/mainfbf9a61 HEAD@{1}: commit: 修复BUG77d0d44 HEAD@{2}: commit: 添加功能Ae4d0c1b HEAD@{3}: rebase -i (finish): returning to refs/heads/main其中,HEAD@{0} 是当前 HEAD 的变更位置(0 表示最新)。
- 找到你需要恢复的节点的哈希值,恢复该版本;
比如:
git reset --hard <commit哈希>或者:
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 具体步骤
- 执行
git stash,暂存当前修改(包括已暂存和未暂存的);
或者你也可以像 commit 一样,增加信息,方便区分:
git stash push -m "临时代码:XX模块"还有一些参数可以选择:
git stash -k # 只保存未暂存的文件git stash -u # 包含 untrackedgit stash -a # 包含 untracked + ignored- 恢复最近一次 stash (同时会将其从 stash list 中删除);
git stash pop你也可以通过 git stash apply stash@{1} 恢复特定一次的 stash ;
- 可以通过
git stash list查看 stash 列表; - 如果希望删除某次 stash ,可以通过
git stash drop stash@{0}; - 如果希望清空所有 stash ,可以通过
git stash clear;
七、占位
八、占位
九、占位
文章分享
如果这篇文章对你有帮助,欢迎分享给更多人!



