Git高级操作:reset,checkout,revert

git resetgit checkout,和git revert命令是Git工具箱中最有用的几个工具之一。他们都用来撤销仓库中的某种修改,其中前两个命令可以用来撤销针对提交或者单个文件的修改。

因为如此相似,在特定开发场景下很容易出现不知道该使用那个命令的情况。在本文中我们会比较git resetgit checkoutgit revert命令最常见的使用方式。希望在本文结束时读者能够在自己的项目中胸有成竹地使用对应的命令。

01.png

为了深入理解,我们需要考虑每个命令在Git仓库的三种状态管理机制中所产生的不同效果:工作目录,暂存快照,和提交历史。有时候这些状态被称为Git的三棵树。在阅读本文过程中请谨记这三种不同的工作机制。

checkout操作会将当前HEAD指针指向指定的提交。请参见下图:

01 git-sequence-transparent kopiera.png

上图展示了main分支中一系列的提交。此时的HEAD指针和main分支的当前指针都指向提交d。接下来执行git checkout b
02 git-checkout-transparent kopiera.png

这个操作会影响到“提交历史”树。git checkout命令可以用于提交,甚至在文件层级上执行。对于文件进行checkout操作会改变该文件的内容到某一次指定提交。

revert操作撤销指定提交并创建一个新的提交,其内容为指定提交的所有逆向修改。git revert只能运行在提交层面,不能对指定文件操作。

reset操作接受一次commit作为参数,并将git的三棵树状态重置到指定的这次commit的相同状态。reset操作可以在三棵树的不同状态下执行。

checkout和reset通常用于本地或者私有分支的撤销操作。修改之后的提交历史,在推送到共享的远程仓库时会引发冲突。反之revert操作的“公共撤销”通常被认为是安全的。因为revert操作会为撤销动作创建一次提交,而这个撤销历史也会被其他人得到,并且revert操作也不会覆盖团队其他成员可能依赖的提交历史。

Git Reset vs Revert vs Checkout

下表总结了这些命令的常用场景。

命令 影响范围 常见实用场景
git reset 提交 丢弃私有分支或者未提交的变更
git reset 文件 反暂存一个文件
git checkout 提交 切换分支或者查看历史快照
git checkout 文件 丢弃工作目录下的变更
git revert 提交 撤销公共分支的提交
git revert 文件 (N/A)

关于提交的操作

git resetgit checkout命令传递的参数决定了其影响范围。使用命令时不含文件路径则会让操作作用于整个提交。接下来这部分我们会主要讨论相关内容。注意git revert没有文件层面的操作。

重置指定提交

在提交层面,重置可以移动分支顶端到其他提交。基于这一特性,重置可以用于删除分支中的提交。比如,下面的命令将hotfix分支的顶端向前移动了两个提交。

git checkout hotfix git reset HEAD~2

hotfix分支的最后两个提交现在称为孤立的提交。这意味着下次Git执行垃圾回收时会删除他们。换句话说,如此操作意味着你要丢弃这些提交。这一过程可以通过下图表示:

03-04 Reset a specific commit.png

如此使用git reset撤销那些还未与他人共享过的变更相当简便。如果你开始开发一个功能做了几次提交之后,突然发现“卧槽,我在干嘛?从头来吧。”的时候可以直接使用这个命令。

除了移动当前分支以外,你还可以传递以下选项,用git reset来变更暂存快照或者工作目录:

  • --soft – The staged snapshot and working directory are not altered in any way.
  • --soft – 暂存快照和工作目录不会改变
  • --mixed – The staged snapshot is updated to match the specified commit, but the working directory is not affected. This is the default option.
  • --mixed – 暂存快照更新为指定提交,但是工作目录不受影响。这是默认选项。
  • --hard – The staged snapshot and the working directory are both updated to match the specified commit.
  • --hard – 暂存快照和工作目录都被更新为指定提交。

checkout旧提交

git checkout命令用于更新仓库状态到指定的项目提交历史。当传递的参数是一个分支名称,则用于切换分支。

 git checkout hotfix

在命令内部,以上所有命令都是移动HEAD指针到不同分支,并相应的更新工作目录。由于这一操作具有潜在的覆盖本地变更的可能性,因此Git会强制你在checkout操作之前执行commit或者stash命令,以便存储可能由于checkout丢失的变更。与git reset不同,git checkout不会移动分支本身的指针。

05-06 Checkout old commits.png

你也可以通过传递提交引用作为参数,checkout出指定提交而不是分支。其内部执行方式与checkout分支一摸一样:移动HEAD指针到指定提交。举个例子,下面的命令会checkout出当前提交的祖父节点。

 git checkout HEAD~2

07.png

这一操作经常用于查看某一个旧版本的项目快照。然而由于当前HEAD指针并不指向任何分支,这一操作会让你处于游离HEAD状态。由于在这个状态下提交新的更新之后,当切换为其他分支之后无法在回到新的提交,所以在游离状态下新建提交是危险的。基于这个原因,如果希望在游离状态下做新的提交,应该先基于此提交创建新的分支。

使用revert撤销公共提交

revert命令通过新建一个提交来撤销之前的一个提交。因为这一操作不会重写提交历史所以被认为是一种安全的撤销操作。比如下面的例子中,Git会搞清楚倒数第二次提交的内容,然后创建一个新的提交用于撤销这些内容,并且将新提交的撤销动作提交到当前的项目中。

git checkout hotfix git revert HEAD~2

此过程图示如下:

08-09 Undo public commits.png

git reset相反,git revert没有改变已有提交历史。基于此,git revert应该被用于撤销公共分支上的变更,而git reset应该被限制于撤销私有分支的变更。

你也可以理解为git revert用于撤销已提交的变更,git reset用于撤销未提交的变更。

git checkout一样,git revert操作也会导致潜在的文件覆盖,所以Git也会要求在revert之前先进行commit或者stash操作。

关于文件的操作

git resetgit checkout命令也接受文件路径作为可选参数。这也让其行为与上面所介绍的功能完全不一样。相比于操作整个快照,附加的文件路径参数限制相应操作的影响范围到单个文件。

Git-reset指定文件

当附加了文件路径作为参数时,git reset会根据指定提交更新暂存快照。比如下面的命令会获取foo.py文件在倒数第二次提交时的快照,根据快照内容变更文件,并暂存它,等待下一次提交:

git reset HEAD~2 foo.py

相比于针对提交的git reset命令,上面的命令更多地用于HEAD。执行git reset HEAD foo.py会取消foo.py的暂存,但其中的改变仍然在工作目录中。

07 (1).png

--soft--mixed--hard选项在文件层面的git reset操作没有任何作用,因为暂存快照总是最新的,并且工作目录总是不更新的。

Git checkout 文件

checkout一个文件与使用git reset命令传递文件路径类似,除了checkout更新的是工作目录,而不是更新暂存快照。另外,与执行checkout命令关于提交的操作不同,checkout一个提交会改变HEAD的指向,而checkout文件不改变HEAD,仅改变文件内容。也就意味着执行这个命令不会切换分支。

08.png

比如下面的命令更新工作目录中的foo.py文件,将其内容同步为倒数第二次提交时的样子。

git checkout HEAD~2 foo.py

就像操作提交时使用git checkout,这也可以用于查看项目的旧版本——不过这次是查看指定文件的旧版本。

如果你暂存并提交了checkout出来的旧版本文件,其执行结果也含有将指定文件revert到旧版本的效果。不过请注意这一操作也同时移除了该文件从旧版本之后的所有后续变更历史,然而revert命令仅撤销指定提交的变更。

就像git reset,这种情况也通常与HEAD搭配使用。比如,git checkout HEAD foo.py执行的结果就含有丢弃foo.py文件未暂存的变更的效果。这个行为类似于git reset HEAD --hard,只不过影响范围仅限于指定文件。

总结

到现在为止,你应该已经拥有用于在Git仓库中撤销变更所需的所有知识了。git resetgit checkout,和git revert命令容易混淆,但是当你考虑到他们分别在工作目录,暂存快照和提交历史上的可能产生的影响,就不难在开发中分辨出应该使用哪个命令。

原文地址:https://www.atlassian.com/git/tutorials/resetting-checking-out-and-reverting

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,014评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,796评论 3 386
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,484评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,830评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,946评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,114评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,182评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,927评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,369评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,678评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,832评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,533评论 4 335
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,166评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,885评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,128评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,659评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,738评论 2 351

推荐阅读更多精彩内容