git 还原到本地初始状态
与Git一起使用时,鲜为人知的方面之一就是回到原先的位置很容易,也就是说,即使撤消存储库中的重大更改也很容易。 在本文中,我们将快速查看如何重置,还原以及完全返回到以前的状态,所有这些都具有单个Git命令的简单性和优雅性。
重启
让我们从Git命令reset
。 实际上,您可以将其视为“回滚”,它会将您的本地环境指向上一次提交。 “本地环境”是指您的本地存储库,暂存区域和工作目录。
看一下图1。这里我们展示了Git中的一系列提交。 Git中的分支只是一个指向特定提交的命名可移动指针。 在这种情况下,我们的分支主机是指向链中最新提交的指针。
![具有存储库,暂存区和工作目录的本地Git环境 Local Git environment with repository, staging area, and working directory](https://i-blog.csdnimg.cn/blog_migrate/3e99f90f64333b6d2e08bfd4764fe25e.png)
图1:具有存储库,暂存区和工作目录的本地Git环境
如果我们现在查看master分支中的内容,那么可以看到到目前为止所做的提交链。
$ git log --oneline
b764644 File with three lines
7c709f0 File with two lines
9ef9173 File with one line
reset
命令为我们做到这一点。
例如,如果我们想将master重置为指向当前提交两次返回的提交,则可以使用以下方法之一:
$ git reset 9ef9173
(使用绝对提交SHA1值9ef9173)
要么
$ git reset current~2
(在“当前”标签之前使用相对值-2)
图2显示了此操作的结果。 此后,如果我们在当前分支( master )上执行git log
命令,则只会看到一个提交。
$ git log --oneline
9ef9173 File with one line
![重置后 After reset](https://i-blog.csdnimg.cn/blog_migrate/fece021ae03c306c9b29b9095da2c2fd.png)
图2: reset
git reset
命令还包括一些选项,这些选项可以使用最终的提交内容来更新本地环境的其他部分。 这些选项包括: hard
重置存储库中指向的提交,使用提交的内容填充工作目录,以及重置登台区域; soft
,仅重置存储库中的指针; 并mixed
(默认)以重置指针和临时区域。
在目标情况下,例如git reset --hard <commit sha1 | reference>
使用这些选项可能很有用。 git reset --hard <commit sha1 | reference>
.
这将覆盖您尚未提交的所有本地更改。 实际上,它会重置(清除)暂存区域,并使用您重置为提交的内容覆盖工作目录中的内容。 在使用hard
选项之前,请确保这是您真正想要做的,因为该命令会覆盖所有未提交的更改。
还原
git revert
命令的最终效果类似于reset,但其方法不同。 在reset
命令将分支指针移回链中(通常)以“撤消”更改的情况下, revert
命令在链末尾添加新的提交以“取消”更改。 再次查看图1,最容易看出效果。 如果我们在链中的每个提交中向文件添加一行,则仅返回两行即可返回该版本的一种方法是重置为该提交,即git reset HEAD~1
。
结束于两行版本的另一种方法是添加一个删除了第三行的新提交,从而有效地取消了该更改。 这可以通过git revert
命令完成,例如:
$ git revert HEAD
因为这会添加一个新的提交,所以Git将提示您输入提交消息:
Revert "File with three lines"
This reverts commit b764644bad524b804577684bf74e7bca3117f554.
# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
# On branch master
# Changes to be committed:
# modified: file1.txt
#
图3(下图)显示了revert
操作完成后的结果。
如果现在执行git log
,我们将看到一个新的提交,它反映了上一次提交之前的内容。
$ git log --oneline
11b7712 Revert "File with three lines"
b764644 File with three lines
7c709f0 File with two lines
9ef9173 File with one line
以下是工作目录中文件的当前内容:
$ cat <filename>
Line 1
Line 2
![](https://i-blog.csdnimg.cn/blog_migrate/926c3cca30139afa0935295d886cffa8.png)
还原还是重置?
您为什么选择通过reset
操作进行revert
? 如果您已经将提交链推送到远程存储库(其他人可能已经在其中提交了代码并开始使用它),那么还原是一种取消它们的更改的更好方法。 这是因为Git工作流程非常适合在分支的末尾拾取其他提交,但是当有人重置分支指针时,如果在链中不再看到一组提交,则可能会有挑战。
当以这种方式使用Git时,这使我们遵循了一条基本规则:在本地存储库中进行此类更改以编写尚未推送的代码就可以了。 但是,如果提交已被推送到远程存储库,并且其他人可能正在使用它们,则请避免进行更改以重写历史记录的更改。
简而言之,如果您回滚,撤消或重写其他人正在使用的提交链的历史记录,则当您的同事尝试根据他们拉出的原始链合并更改时,他们可能需要做更多的工作。 如果必须对已经推送的代码和他人正在使用的代码进行更改,请在进行更改之前考虑进行交流,并让人们有机会首先合并他们的更改。 然后,他们可以在侵权操作之后提取新副本,而无需合并。
您可能已经注意到,在进行重置后,原始的提交链仍然存在。 我们移动了指针,并将代码重置为上一次提交,但是它没有删除任何提交。 这意味着,只要我们知道所指向的原始提交,就可以通过简单地重置为分支的原始头来“恢复”到先前的点:
git reset <sha1 of commit>
当替换提交时,我们在Git中执行的大多数其他操作中也会发生类似的情况。 创建新的提交,并将适当的指针移至新链。 但是旧的提交链仍然存在。
变基
现在,让我们来看一个分支的基础。 考虑我们有两个branches- 主站和特征 -附下面的图4中所示的提交的链。 主链具有链C4->C2->C1->C0
, 功能链具有C5->C3->C2->C1->C0
。
![分支母版和功能的提交链 Chain of commits for branches master and feature](https://i-blog.csdnimg.cn/blog_migrate/1aea8df691b3ade4b743775bc39570d5.png)
图4:分支母版和要素的提交链
如果我们查看分支中的提交日志,它们可能如下所示。 (使用提交消息的C
指示符可以使此操作更容易理解。)
$ git log --oneline master
6a92e7a C4
259bf36 C2
f33ae68 C1
5043e79 C0
$ git log --oneline feature
79768b8 C5
000f9ae C3
259bf36 C2
f33ae68 C1
5043e79 C0
我告诉人们,在Git中将重新构建视为“与历史的融合”。 本质上,Git所做的是将一个分支中的每个不同提交提交并尝试将差异“重播”到另一个分支上。
因此,我们可以将要素重新建立到主要素上,以提取C4
(例如,将其插入要素链中)。 使用基本的Git命令,它看起来可能像这样:
$ git checkout feature
$ git rebase master
First, rewinding head to replay your work on top of it...
Applying: C3
Applying: C5
之后,我们的提交链将如图5所示。
![rebase命令后的提交链 Chain of commits after the rebase command](https://i-blog.csdnimg.cn/blog_migrate/cae6e89cbbd0c71e301f9dfe2555b85d.png)
图5: rebase
命令后的提交链
同样,查看提交日志,我们可以看到更改。
$ git log --oneline master
6a92e7a C4
259bf36 C2
f33ae68 C1
5043e79 C0
$ git log --oneline feature
c4533a5 C5
64f2047 C3
6a92e7a C4
259bf36 C2
f33ae68 C1
5043e79 C0
请注意,我们有C3'
和C5'
-新提交的‘之上’,在掌握现有链使得从原稿的变化而创建的。 但也请注意,“原始” C3
和C5
仍然存在-它们不再具有指向它们的分支。
如果我们进行了这种变基,那么就决定我们不喜欢结果并想要撤消它,它就像下面这样简单:
$ git reset 79768b8
通过这一简单的更改,我们的分支现在将指向与rebase
操作之前相同的提交集-有效地撤消了该提交(图6)。
![撤消变基后 After undoing rebase](https://i-blog.csdnimg.cn/blog_migrate/721ec634794c261b9433fb872f5a6781.png)
图6:撤消rebase
操作后
如果您不记得操作前提交的分支指向什么,该怎么办? 幸运的是,Git再次帮助了我们。 对于大多数以此方式修改指针的操作,Git都会为您记住原始的提交。 实际上,它将其存储在.git
存储库目录中名为ORIG_HEAD
的特殊引用中。 该路径是一个文件,其中包含修改前的最新参考。 如果我们cat
的文件中,我们可以看到它的内容。
$ cat .git/ORIG_HEAD
79768b891f47ce06f13456a7e222536ee47ad2fe
我们可以像以前一样使用reset
命令来指向原始链。 然后日志将显示以下内容:
$ git log --oneline feature
79768b8 C5
000f9ae C3
259bf36 C2
f33ae68 C1
5043e79 C0
获取此信息的另一个地方是reflog。 reflog是按播放列表,列出了本地存储库中的开关或对引用的更改。 要查看它,可以使用git reflog
命令:
$ git reflog
79768b8 HEAD@{0}: reset: moving to 79768b
c4533a5 HEAD@{1}: rebase finished: returning to refs/heads/feature
c4533a5 HEAD@{2}: rebase: C5
64f2047 HEAD@{3}: rebase: C3
6a92e7a HEAD@{4}: rebase: checkout master
79768b8 HEAD@{5}: checkout: moving from feature to feature
79768b8 HEAD@{6}: commit: C5
000f9ae HEAD@{7}: checkout: moving from master to feature
6a92e7a HEAD@{8}: commit: C4
259bf36 HEAD@{9}: checkout: moving from feature to master
000f9ae HEAD@{10}: commit: C3
259bf36 HEAD@{11}: checkout: moving from master to feature
259bf36 HEAD@{12}: commit: C2
f33ae68 HEAD@{13}: commit: C1
5043e79 HEAD@{14}: commit (initial): C0
然后,您可以使用在日志中看到的特殊相对命名格式,重置为该列表中的任何项目:
$ git reset HEAD@{1}
一旦您了解到Git会在操作“修改”链时保持原始的提交链,那么在Git中进行更改的恐惧就会大大减少。 这是Git的核心优势之一:能够快速轻松地尝试并在不起作用的情况下撤消它们。
Brent Laster将于7月16日至19日在俄勒冈州波特兰举行的第20届年度OSCON活动中展示Power Git:Rerere,Bisect,子树,Filter分支,工作树,子模块等 。有关在任何级别使用Git的更多提示和解释,结帐Brent的书“ Professional Git ”,可在亚马逊上找到。
翻译自: https://opensource.com/article/18/6/git-reset-revert-rebase-commands
git 还原到本地初始状态