git 上常用的常规场景的 commit 和 reset --hard 用来提交和撤销提交,当遇到比较复杂的场景,常规的指令可能就不能满足需求了
1. git reset 的5种模式
在执行撤销操作时,指令 git reset 是git最常用的命令之一,也是最危险最容易误用的命令
git reset HEAD^|<commit_id>
撤销 commit 和 add 的,但保留修改内容的记录

HEAD^ 的代表上一个版本,也可以写成 HEAD~1
。如果进行了 2 次 commit,想都撤回,可以使用 HEAD~2

<commit_id> 代表某个版本的 commit_id,可通过 git log 或在 git 平台的 history 中查看

此时若需要重新提交,则需要重新执行 git add 和 git commit

git reset --soft HEAD^|<commit_id>
撤销 commit 提交,保留代码的修改和 add 的内容,目标版本可以是 HEAD 也可以使用 commit_id

若需要重新提交,则只需要重新执行 git commit

git reset --mixed HEAD^|<commit_id>
是 git reset 的默认模式,等同于 git reset,撤销 commit 和 add 记录,保留文件的修改记录,若需要再次提交,需要重新执行git add 和 git commit

git reset --hard HEAD^|<commit_id>
强制撤销 commit 和 add 记录,并撤销工作区对文件的修改,完全撤销到目标版本,是经常出现误操作指令
若对未push 的commit 进行撤销,则在当前版本下完全找不到撤销的 commit 的文件修改了,并且通过git log 也无法找到被撤销的记录了

git reset --merge HEAD^|<commit_id>
该指令在 git 版本 > 1.6.1 上生效
撤销合并(git merge)/修订(git commit)/变基(git rebase)操作,在需要提供ORIG_HEAD(git log 获取的 commit_id)
在执行撤销合并/ 变基时,效果等同于撤销合并(git merge --abort)/撤销变基(git rebase --abort)
在执行修订,效果等同于强制撤销(git reset --hard <commit_id>)
以撤销合并为例,当合并时遇到冲突并进行解决时,修改后重新提交 commit 但未执行 push 提交

此时若想要撤销合并使用 git merge --abort 将无法生效

可使用 git reset --merge <commit_id> 来进行撤销

git reset --keep HEAD^|<commit_id>
重置分支到指定的提交,保留当前工作目录和暂存区的更改。
介于git reset --hard 与 git reset --soft 之间的一个状态,保留当前做的未提交的更改,但不保留撤销之后提交的内容

当当前修改与撤销之后提交的内容有冲突时,该指令将无法执行

git 子模块
子模块创建可参考文档 Git中submodule的使用
在使用 git 子模块的场景下,执行reset 时会考虑到会不会同步执行撤销子模块的提交,此时会有两个属性用于针对子模块的场景
git reset --recurse-submodules
当使用子模块时,--recurse-submodules选项允许在重置时同时重置子模块的状态。这对于确保主项目和子模块之间的一致性非常重要。
git reset --no-recurse-submodules
与前一个选项相反,--no-recurse-submodules选项在重置时不会影响子模块的状态。
2. git commit 提交
git commit --amend
如果只是想修改commit的提交的 message 内容,在 vim 编辑器上进行修改

3. 查找提交的历史条目
git log
最常用的一种查看 git 提交历史的命令,可以获取有关仓库中提交的详细信息,包括提交者、提交日期、提交消息以及每个提交的哈希值

git reflog
查看本地仓库中的引用日志(reference log)。引用日志记录了分支、HEAD 和其他引用的变化历史,包括提交、重置、合并等操作。即使在分支指针发生变化后,你仍然可以通过 git reflog 查看引用的历史状态。

如在 git reset --hard / git merge --abort / git rebase --abort 之后,在 git log 中已经无法查看未 push 的 commit 记录了,但是可以通过 git reflog 中查看到并进行后续操作
reflog 保留与清理
git 提交历史是基于内容的,每个提交都会永久保留,但引用日志(reference log)不会。引用日志在 Git 仓库中保留一段时间,直到达到一定的条件后被清理。默认情况下 git 会保留 reflog 90天,上限条数250条,当达到任意上限时,较旧的记录会被自动清理。
若需要设置过期时间,可配置 gc.reflogExpire
参数,设置"now"使所有引用日志立即过期,而"never"则完全不过期
arduino
git config gc.reflogExpire 60.days // 设置60天过期
若需要设置上限条数,可配置 core.logAllRefUpdates
,设置为负数可以不限制记录的上限数量
arduino
git config core.logAllRefUpdates -1
此文背景:改了一堆代码,执行了 commit 但忘记 push 之后,执行了 git reset --hard 想看历史代码,回头发现 commit 记录没了