git 我提交了,但我又后悔了
背景
在实际的开发工作中,使用git commit 总会遇到一些令人抓狂的提交,一切源于手欠和脑子不清醒,把一些不该提交的东西一起提交了。
不过还在Git还是能给我们后悔的机会。
场景模拟
@PostMapping("/hhh")
public String test1(){
AbsBatch batch = mapper.selectByPrimaryKey("dd");
return batch.toString();
}
@PostMapping("/hhhh")
public String test3(){
AbsBatch batch = mapper.selectByPrimaryKey("dd");
return batch.toString();
}
在这一次的提交中,我本来只需要提交test1这个方法,但是一不小心把test3也一起提交了o((⊙﹏⊙))o
case 1 已提交未push
这种情况处理起来比较简单。如果你适用的开发工具是idea时,就显得尤为简单。
undo commit
直接就是 右键 -> undo commit
直接把本地的git提交记录给去掉了,但是你之前对代码的修改还在(✪ω✪) 然后你就可以回到未提交的状态,然后重新提交。
case 2 已提交 未push 或已push
reset
注意:
- 如果已经
push
了,再使用reset
命令时取消更改时。提交代码时,需要强制覆盖远程代码。而且请确保其他人没有pull你提交的错误代码,不然其他人再次提交并push时,错误代码又会上远程
。reset 的节点请正确选择,不要选到其他分支的提交记录。
git reset --soft,–hard的区别
git reset 命令可以将当前的HEAD重置到特定的状态。
首先要搞清楚下面几个概念
- HEAD: HEAD就是指向当前分支当前版本的游标
- Index: Index即为暂存区,当你修改了你的git仓库里的一个文件时,这些变化一开始是unstaged状态,为了提交这些修改,你需要使用git add把它加入到index,使它成为staged状态。当你提交一个commit时,index里面的修改被提交。
- working tree: 即当前的工作目录。
git reset 的用法
git reset [<mode>] [<commit>]
git reset 将当前分支的HEAD指向给定的版本,并根据模式的不同决定是否修改index和working tree。
常用的有三种模式,–soft, --mixed, --hard,如果没有给出则默认是–mixed
1 --soft
已经有未提交的更改:如果在执行软重置之前有未提交的更改,Git 可能会拒绝执行软重置操作。您可以通过提交或保存当前工作目录中的更改来解决此问题。
请一定注意这个
reset --soft相当于后悔药,给你重新改过的机会。对于上面的场景,就可以再次修改重新提交,保持干净的 commit 记录。
以上说的是还未 push 的commit。对于已经 push 的 commit,也可以使用该命令,不过再次 push 时,由于远程分支和本地分支有差异,需要强制推送git push -f来覆盖被 reset 的 commit。
还有一点需要注意,在reset --soft指定 commit 号时,会将该 commit 到最近一次 commit 的所有修改内容全部恢复,而不是只针对该 commit。
使用 --soft参数将会仅仅重置HEAD到制定的版本,不会修改index和working tree
三次提交
第一次:test1.txt
第二次:test2.txt
第三次:test3.txt
本地文件的内容并没有发生变化,而index中仍然有最近一次提交的修改,这时执行git status会显示这些修改已经在再暂存区中了,无需再一次执行git add。接下来就可以重新修改提交了。
2 --mixed
使用 --mixed参数与 --soft的不同之处在于, --mixed修改了index,使其与第二个版本匹配。index中给定commit之后的修改被unstaged。需要重新 git add .
3. --hard
使用–hard同时也会修改working tree,也就是当前的工作目录,如果我们执行git reset --hard HEAD~,那么最后一次提交的修改,包括本地文件的修改都会被清楚,彻底还原到上一次提交的状态且无法找回。所以在执行reset --hard之前一定要小心
不建议使用!
reset idea 页面操作版
如果你不习惯操作 命令行,那么直接使用idea也可以完成这一步。
- 第一步找到 git 提交记录,复制需要回退的版本号
- 项目根目录上找到 reset 入口
- 设置回退版本号
- 强制覆盖远程。(谨慎操作)
或 使用命令行
使用git push
命令并添加--force
或-f
参数,强制覆盖远程分支。
git push --force origin <branch-name>
例如,强制覆盖 main
分支:
git push --force origin main
revert
原理: git revert 是用于 “反做” 某一个版本,以达到撤销该版本的修改的目的。比如,我们 commit 了三个版本(版本一、版本二、 版本三),突然发现版本二不行(如:有 bug),想要撤销版本二,但又不想影响撤销版本三的提交,就可以用 git revert 命令来反做版本二,生成新的版本四,这个版本四里会保留版本三的东西,但撤销了版本二的东西。如下图所示:
适用场景: 如果我们想撤销之前的某一版本,但是又想保留该目标版本后面的版本,记录下这整个版本变动流程,就可以用这种方法。
具体操作:
举个例子,现在库里面有三个文件:READ.md、text.txt、text2.txt。
- 查看版本号:
可以通过命令行查看(输入 git log):
如图,最近的两个版本分别叫:“add text.txt”(即新增了文件 text.txt)、“add text2.txt”(新增了文件 text2.txt)。这个时候我们不需要 text.txt 这个文件了,那就是说不想要 “add text.txt” 那个版本的操作,那可以通过反做 “add text.txt” 这个版本来实现。
也可以通过 github 网站图形化界面查看版本号:
2. 使用 “git revert -n 版本号” 反做,并使用 “git commit -m 版本名” 提交:
(1)反做,使用 “git revert -n 版本号” 命令。如下命令,我们反做版本号为 8b89621 的版本:
git revert -n 8b89621019c9adc6fc4d242cd41daeb13aeb9861
注意: 这里可能会出现冲突,那么需要手动修改冲突的文件。而且要 git add 文件名。
(2)提交,使用 “git commit -m 版本名”,如:
git commit -m "revert add text.txt"
此时可以用 “git log” 查看本地的版本信息,可见多生成了一个新的版本,该版本反做了 “add text.txt” 版本,但是保留了 “add text2.txt” 版本:
3. 使用 “git push” 推上远程库:
git push
查看 github 上显示的远程库版本信息:
此时查看仓库的文件,剩下两个:READ.md、text2.txt
反做成功!
冲突
revert 如果之前和之后版本有改到同一个地方也会触发冲突(需要手动解决冲突)
idea 操作
选择要 revert 的版本进行 revert(是 revert 选中的本版)
也可以复制版本号进行 revert
管理端操作
在浏览器选择要回滚的版本进行回滚(示例 gitlab)
坑
比如某个需求开发完成后(A 分支)需要合并到主分支,却不小心把 B 分支合到主分支,B 分支还在开发中未能达到合并的要求,这种场景正常情况直接 reset 掉就行,假如选择了 revert 也可以达到目的。
但是后面 B 开发完成需要合并主分支后,发现主分支合不上,原因是之前已经有个 revert 的记录,遇到这种情况只需要把之前的 revert 记录再 revert 一次就行(不推荐这种操作行为)。所以正确的选择回滚的方式可以避免不必要的麻烦。
这一点 ,同样适用于开发到一半,误提交的情况下。revert 之后,开发完提交无法更新远程仓库
提交错分支了
git cherry-pick 是一个强大的 Git 工具,它的核心作用是将某个分支上的单个(或多个)提交“单独复制”到当前分支,而不是合并整个分支。以下我会用通俗易懂的方式解释它的应用场景、原理和实际案例。
一、应用场景:什么时候需要 cherry-pick?
- 跨分支应用特定修复
场景:你在develop
分支上修复了一个 Bug(提交abc123
),但这个 Bug 同样存在于master
分支上。你不想合并整个develop
分支到master
,而是只想将这一个修复提交应用到master
。
操作:切换到master
分支,运行git cherry-pick abc123
。这个提交会被“复制” 到 master 分支。 - 移植功能到旧版本
场景:你正在维护一个旧版本的代码分支(比如 v1.x),但某个新功能(提交def456
)在v2.x
分支上开发,现在需要单独将这个功能移植到旧版本。
操作:在v1.x
分支上运行git cherry-pick def456
。 - 避免合并无关代码
场景:你只想提取某个分支上的几个关键提交,而该分支上还有其他不想合并的代码(比如未完成的实验性代码)。
操作:用 cherry-pick 仅复制需要的提交。 - 修复误提交到错误分支的代码
场景:你本应在feature/login
分支开发,但不小心在main
分支上提交了代码。此时可以:
将误提交的提交git cherry-pick
到正确的分支。
从main
分支删除误提交(用git revert
或git reset
)。
二、cherry-pick 和 merge / rebase 的区别
merge:合并两个分支的所有差异,生成一个新的合并提交。
rebase:将当前分支的提交“重新播放”到目标分支上,改变提交历史。
cherry-pick:只复制特定提交的内容到当前分支,不关心其他提交。
👉 关键区别:cherry-pick 是精准的“复制粘贴”,而 merge 和 rebase 是“整体迁移”。
三、实际案例演示
假设有以下提交历史:
develop 分支:A → B → C (修复 Bug)
master 分支:X → Y → Z
现在需要将 develop 上的提交 C 复制到 master:
# 1. 切换到 master 分支
git checkout master
# 2. 复制提交 C
git cherry-pick C
结果:
master 分支:X → Y → Z → C'(C 的副本)
注意:C’ 的哈希值会改变,但内容与 C 相同。
四、cherry-pick 的注意事项
- 可能引发冲突:
如果复制的提交依赖其他提交的代码,可能会冲突。需要手动解决冲突后,运行git cherry-pick --continue
。 - 谨慎使用:
滥用 cherry-pick 可能导致代码重复或逻辑混乱(尤其是提交之间有依赖关系时)。 - 历史记录污染:
cherry-pick 会生成新的提交,可能导致提交历史冗余。建议在明确需要时才使用。
五、高级用法
复制多个提交:
git cherry-pick commit1 commit2 commit3
复制连续提交范围:
git cherry-pick start_commit^..end_commit
仅应用代码修改,不生成提交(需手动提交):
git cherry-pick -n commit_hash
六、总结
git cherry-pick
的核心价值是“精准移植代码”。它适用于需要从其他分支“摘取”特定提交的场景,但需注意避免滥用,尤其是当提交之间有依赖关系时。合理使用它能极大提升工作效率,尤其是在多分支协作开发中!