git 后悔药

git 后悔药

前言

lilac 按:

错事常常有,后悔药却难寻。你是不是经常因为做错事而搜寻不到后悔药而苦恼万分呢?生活中很多事情确实无后悔药可吃,可是在git的世界里,只要用心思考,还是能找到不少好用的后悔药的。

如下几副后悔药为lilac思考总结的成果,如有不当,还请大佬们留言探讨,lilac先行谢过~

git 后悔药_第1张图片
gitrepo-1.jpeg
  • Workspace:工作区
  • Index / Stage:暂存区
  • Repository:仓库区(或本地仓库)
  • Remote:远程仓库

git 中存在工作区,暂存区,本地仓库和远程仓库几个概念,上图比较直观地说明了这几个区之间的操作(图片来源于网络)。平时通过git进行代码管理时,一般也是将改动在这几个区之间进行变换。

P.S. 划重点啦!!!

在 Git 中任何 已提交的 东西几乎总是可以恢复的,甚至那些被删除的分支中的提交或使用 --amend 选项覆盖的提交或使用reset --hard命令重置后的提交等都是可以恢复的。 然而,任何你未提交的东西丢失后很可能再也找不到了。

因此在使用git时,比较保险的做法是多采用commit命令提交代码,即便你有代码洁癖,想维持提交记录的清晰可读性,也可以多次提交,反正有办法可以从历史提交记录中去掉某个指定的提交,也可以将多个提交合并为一个提交。

接下来分情况讨论下git的各种后悔药吧,下文的分析脉络如下面的脑图所示。

备注:所有没有被git托管的文件如果没有自己手动进行备份的话,是不存在后悔药的,故不做讨论。

git 后悔药_第2张图片
Git后悔药.png

约定: 后文中英文方括号[]中的内容表示可选内容,当可选内容为commitID时,若不填,则表示采用HEAD所指向的提交。

示例

下面通过一个具体的实例演示各种后悔药的服用方法。

入职几天后,Jack 对组内工作已略微熟悉,找 mentor 接手了一个小任务,想要好好表现一番。

他首先加入了项目组的中央仓库,并拉取了组内前辈们的代码,然后便开始了边听歌,边写代码实现功能的节奏。

情况一 撤销工作区的修改

写到一半,突然想到有个文件的代码修改考虑不周,需要推翻重写。可是这份代码是在已有文件上进行修改的,不能删除重来,并且修改量还挺大的,一行一行修改回来的话,实在是有点傻。Jack 开始烦恼了。。。

此时可以服用第一方后悔药:撤销工作区的修改

在某个 git 仓库中,修改了文件 file 后,发现此次修改不对,想要放弃此次修改,或者想要将 file 恢复到某个指定的状态,此时可以用 git checkout 命令

git checkout [commitID] [file1][file2]...

如:
git checkout file1          # 将文件 file1 恢复到 HEAD 所指向的提交的状态,即上一次提交的时候的状态
git checkout commit file1       # 将文件 file1 恢复到 commitID 所指向的提交的状态

采用 git checkout 命令将吴修改的文件恢复到修改前的状态后,Jack 又可以愉快地开始写代码了。。。

情况二 撤销暂存区的修改

写着写着,午饭时间到了,同事们叫 Jack 一起去吃午饭。Jack 心想着,还是先提交一下,备份下代码吧,万一吃完饭回来,手欠将好不容易写好的代码给弄没了呢。于是 git add . 了一下,哈哈,这个命令可还是不要随便用的好呀,这不,一不小心就把不该提交的修改内容添加到暂存区了吧。

第二方后悔药:撤销提交到暂存区的修改

git reset [commitID] [file1] [file2]...
​
如:
git reset commitID       # 将暂存区和本地仓库的内容重置为 commitID 所指向的提交
git reset file1 file2    # 撤销 file1 file2 的 add 操作

情况三 撤销已提交到本地仓库的修改

撤销完本不应该添加到暂存区的文件后,Jack 运行 git commit 命令完成了第一次提交,终于可以安心地去吃午饭了。饭后,Jack 又完成了几次提交,在某一次提交后,发现有几次提交的内容不太恰当,想要撤销这几次提交,重新提交。

第三方后悔药:撤销已提交到本地仓库的修改

若此次提交还未 push 到远程仓库,那么可以调用 git reset 命令重置 HEAD 指针,或调用 git revert 命令撤销此次提交的修改。若此次提交已经 push 到远程仓库,那么只能调用 git revert 命令撤销此次修改,然后重新提交正确的内容,并 push 到远程仓库。此时如果调用 git reset 命令重置 HEAD 指针的话,会导致本地 HEAD 指针落后于远程仓库的 HEAD 指针,从而在 push 时出错。

git reset [commitID]
git revert commitID

如:
git reset commitID    # 重置 HEAD 指针,使其指向 commitID 对应的提交
git reset HEAD^       # 重置 HEAD 指针,使其指向 HEAD 的父提交
git revert commitID   # 创建一个新的提交,其内容为撤销 commitID 对应的修改
git revert HEAD       # 创建一个新的提交,其内容为撤销 HEAD 指向的提交对应的修改

情况四 撤销已提交到远程仓库的修改

一般来说,不能撤销已经提交到远程仓库的修改,尤其是那些已经提交到一个与他人合作的分支的修改。但是,可以通过 git revert 命令创建一个新的提交,撤销上一个提交的修改,然后重新 push 到远程仓库来变相完成撤销已提交到远程仓库的修改。

P.S. 如果想要撤销的提交位于一个自己独有的远程分支上的话,也可以采用硬重置远程分支的方式来强制撤销已提交到远程仓库的修改。不过此类操作有风险,需谨慎执行。

示例:

假设远程分支 A 是一个 Jack 独有的分支,此时 Jack 想要撤销分支 A 上的一个提交,那么他可以按照如下操作

git checkout HEAD^ -b AR  # 创建本地分支 AR,指向本次提交的上一次提交,也就撤销了本次提交的修改
git log --oneline --all   # 查看所有分支的日志,每次提交一行,以确保 AR 指向的提交符合自己想要的状态
git push origin :A        # 如果 AR 指向的提交满足自己的需求,那么可以通过该命令删除远程分支 A
git push origin AR:A      # 将本地分支 AR 指向的提交提交到远程分支 A

至此,便真正撤销了已提交到远程仓库的修改

扩展知识

git checkout 命令详解

git checkout 命令有两项功能:

  • 切换到指定分支;

  • 将工作区的文件恢复到指定提交对应的状态;

使用语法:
git checkout [-q] [-f] [-m] []
git checkout [-q] [-f] [-m] --detach []
git checkout [-q] [-f] [-m] [--detach] 
git checkout [-q] [-f] [-m] [[-b|-B|--orphan] ] []
git checkout [-f|--ours|--theirs|-m|--conflict=