用Git进行版本控制(五)撤销更改

前言:
教程来源于Udacity的免费教程-用Git进行版本控制,这里仅是个人的笔记,参考使用。

image.png

git commit --amend:
可以更改最近的提交,比如你忘记提交了某个文件,或是你的commit说明写错了。想要进行修改,就需要使用amend.
git revert:
还原指定的commit,传入指定的SHA,就可以撤销在该commit中的修改。
git reset:
删除提交的commit.

更改最后一个 commit

你已经使用 git commit 命令提交了大量的 commit。现在,借助 --amend 选项,你可以更改最近的 commit。

$ git commit --amend
如果你的工作目录没有内容(也就是仓库中没有任何未 commit 的更改),那么运行 git commit --amend 将使你能够重新提供 commit 消息。代码编辑器将打开,并显示原始 commit 消息。只需纠正拼错的单词或重新表述即可!然后保存文件并关闭编辑器,以便采用新的 commit 消息。

image.png

更改了上一次合并的commit,会弹出vim编辑窗口,可以进行一些修改,并关闭退出。

向 commit 中添加忘记的文件
此外,git commit --amend 使你能够包含忘记包含的文件(或文件更改)。假设你更新了整个网站的导航链接颜色。commit 了该更改,并以为完事了。但是后来发现深藏在页面上的一个特殊导航链接没有新的颜色。你可以执行新的 commit 并更新该链接的颜色,但是这样就会出现两个 commit 执行完全相同的任务(更改链接颜色)。

相反,你可以修改最后一个 commit(更新所有其他链接颜色的 commit)以包含这个忘记的链接。要包含忘记的链接,只需:

编辑文件
保存文件
暂存文件
运行 git commit --amend
你对必要的 CSS 和/或 HTML 文件作出了更改,以便修正被遗忘的链接样式,然后保存所有被修改的文件,并使用 git add 暂存所有被修改的文件(就像要提交新的 commit 那样!),但是你可以运行 git commit --amend 来更新最近的 commit,而不是创建新的 commit。

git commit --amend会将新修改的内容一并提交commit.

还原 commit

什么是还原?
当你告诉 git 还原(revert) 具体的 commit 时,git 会执行和 commit 中的更改完全相反的更改。我们详细讲解下。假设 commit A 添加了一个字符,如果 git 还原 commit A,那么 git 将创建一个新的 commit,并删掉该字符。如果删掉了一个字符,那么还原该 commit 将把该内容添加回来!

上节课最后讲解了合并冲突,并通过将标题设为 Adventurous Quest 解决了该冲突。假设现在仓库中有个 commit 将标题改为 Quests & Crusades。

git revert 命令
现在我创建了一个包含一些更改的 commit,我可以使用 git revert 命令还原它

$ git revert
因为最近的 commit 的 SHA 是 07212dc,要还原该 commit: 我需要运行 git revert 07212dc(随即弹出代码编辑器,以便编辑/确认提供的 commit 消息)

你看到 git revert 命令的输出结果是如何告诉我们它还原了什么吗?它输出了我要求它还原的 commit 的提交说明。同时值得注意的是,它创建了新的 commit。

image.png

按ESC,并输入:wq保存并退出vim,我们再回到资源编辑器里,比如我这里是Notepad++,发现字符串又恢复了,commit提交的内容被驳回。(创建新的commit)

revert 小结
总结下,git revert 命令用于还原之前创建的 commit:

$ git revert
此命令:

将撤消目标 commit 所做出的更改
创建一个新的 commit 来记录这一更改

重置 commit

重置与还原

初看,重置(reset) 似乎和 还原(revert) 相似,但它们实际上差别很大。还原会创建一个新的 commit,并还原或撤消之前的 commit。但是重置会清除 commit!

⚠️ 重置很危险 ⚠️

一定要谨慎使用 git 的重置功能。这是少数几个可以从仓库中清除 commit 的命令。如果某个 commit 不再存在于仓库中,它所包含的内容也会消失。

为了减轻你的压力,澄清下,git 会在完全清除任何内容之前,持续跟踪大约 30 天。要调用这些内容,你需要使用 git reflog 命令。请参阅以下链接以了解详情:

  • git-reflog (英)
  • 重写历史记录 (英)
  • reflog,你的安全屏障 (英)

相关 commit 引用
你已经知道可以使用 SHA、标签、分支和特殊的 HEAD 指针引用 commit。有时候这些并不足够,你可能需要引用相对于另一个 commit 的 commit。例如,有时候你需要告诉 git 调用当前 commit 的前一个 commit,或者是前两个 commit。我们可以使用特殊的“祖先引用”字符来告诉 git 这些相对引用。这些字符为:

^ – 表示父 commit
~ – 表示第一个父 commit
我们可以通过以下方式引用之前的 commit:

父 commit – 以下内容表示当前 commit 的父 commit
HEAD^
HEAD~
HEAD~1
祖父 commit – 以下内容表示当前 commit 的祖父 commit
HEAD^^
HEAD~2
曾祖父 commit – 以下内容表示当前 commit 的曾祖父 commit
HEAD^^^
HEAD~3

^ 和 ~ 的区别主要体现在通过合并而创建的 commit 中。合并 commit 具有两个父级。对于合并 commit,^ 引用用来表示第一个父 commit,而 ^2 表示第二个父 commit。第一个父 commit 是当你运行 git merge 时所处的分支,而第二个父 commit 是被合并的分支。

我们来看一个示例,这样更好理解。这是我的 git log 当前的显示结果:

* 9ec05ca (HEAD -> master) Revert "Set page heading to "Quests & Crusades""
* db7e87a Set page heading to "Quests & Crusades"
*   796ddb0 Merge branch 'heading-update'
|\  
| * 4c9749e (heading-update) Set page heading to "Crusade"
* | 0c5975a Set page heading to "Quest"
|/  
*   1a56a81 Merge branch 'sidebar'
|\  
| * f69811c (sidebar) Update sidebar with favorite movie
| * e6c65a6 Add new sidebar content
* | e014d91 (footer) Add links to social media
* | 209752a Improve site heading for SEO
* | 3772ab1 Set background color for page
|/  
* 5bfe5e7 Add starting HTML structure
* 6fa5f34 Add .gitignore file
* a879849 Add header to blog
* 94de470 Initial commit

我们来看看如何引用一些之前的 commit。因为 HEAD 指向 9ec05ca commit:

HEAD^ 是 db7e87a commit
HEAD~1 同样是 db7e87a commit
HEAD^^ 是 796ddb0 commit
HEAD~2 同样是 796ddb0 commit
HEAD^^^ 是 0c5975a commit
HEAD~3 同样是 0c5975a commit
HEAD^^^2 是 4c9749e commit(这是曾祖父的 (HEAD^^) 第二个父 commit (^2))
*HEAD^^^2 表示父commit,即向下数两个commit,而最后的2代表合并后的第二个分支,即被合并的分支,所以是heading-update分支的commit 4c9749e

哪一个 commit?
请使用此仓库回答以下练习问题:

* 9ec05ca (HEAD -> master) Revert "Set page heading to "Quests & Crusades""
* db7e87a Set page heading to "Quests & Crusades"
*   796ddb0 Merge branch 'heading-update'
|\  
| * 4c9749e (heading-update) Set page heading to "Crusade"
* | 0c5975a Set page heading to "Quest"
|/  
*   1a56a81 Merge branch 'sidebar'
|\  
| * f69811c (sidebar) Update sidebar with favorite movie
| * e6c65a6 Add new sidebar content
* | e014d91 (footer) Add links to social media
* | 209752a Improve site heading for SEO
* | 3772ab1 Set background color for page
|/  
* 5bfe5e7 Add starting HTML structure
* 6fa5f34 Add .gitignore file
* a879849 Add header to blog
* 94de470 Initial commit

*~ 表示第一个父 commit,数字(这里是 6)表示要往回数多少个父 commit。因此找到 HEAD 指向的 commit,并往回数六个 commit。
往回数6个父commit.

image.png

上道题答的不错,再试试这道题吧!对于相同的仓库,HEAD~4^2 引用的是哪个 commit?

HEAD~4 引用的是当前分支的第四个父 commit,然后 ^2 告诉我们它是合并 commit 的第二个父 commit(被合并的那个 commit !)

答案是:f69811c

*^ 和 ~ 的区别主要体现在通过合并而创建的 commit 中。合并 commit 具有两个父级。比如heading_update分支和master分支,通常我们是合并到master分支的,
合并后产生新的提交commit,该commit有两个父级。
^ 引用用来表示第一个父 commit,而 ^2 表示第二个父 commit。第一个父 commit 是当你运行 git merge 时所处的分支,而第二个父 commit 是被合并的分支。

所以第一个父级就是master(即要合并到的分支),而上面被合并的heading_update就是第二个分支了!所以答案是f69811c

git reset 命令
git reset 命令用来重置(清除)commit:

$ git reset
可以用来:

将 HEAD 和当前分支指针移到目标 commit
清除 commit
将 commit 的更改移到暂存区
取消暂存 commit 的更改

git reset 的选项
git 根据所使用选项来判断是清除、暂存之前 commit 的更改,还是取消暂存之前 commit 的更改。这些选项包括:

--mixed
--soft
--hard

默认是--mixed参数

image.png

如上图:
我们现在的HEAD指向master分支,最近的commit是3,如果这时候我执行
git reset HEAD~1,结果取决于后面的参数

首先他们都会回到commit-a上,但对于commit-3的处理却不一样:

默认是--mixed ,当执行了reset后,commit-3会回到工作区当中,下次你执行
git add .
会再次添加到暂存区域当中。
--soft:
会将commit-3放到暂存区当中,你下次可以继续进行提交。
--hard:
会删除当前的commit.

备份分支
注意,使用 git reset 命令将清除当前分支上的 commit。因此,如果你想跟着操作接下来出现的所有重置操作,需要在当前 commit 上创建一个分支,以便用作备份。

在进行任何重置操作之前,我通常会在最近的 commit 上创建一个 backup 分支,因此如果出现错误,我可以返回这些 commit:

$ git branch backup

reset 的 --mixed 选项
我们来看看每个选项。

  • 9ec05ca (HEAD -> master) Revert "Set page heading to "Quests & Crusades""
  • db7e87a Set page heading to "Quests & Crusades"
  • 796ddb0 Merge branch 'heading-update'
    使用上述示例仓库,其中 HEAD 指向 9ec05ca 上的 master,运行 git reset --mixed HEAD^ 会把 commit 9ec05ca 中做出的更改移至工作目录中。

回到正常状况
如果你在重置任何内容前创建了 backup 分支,那么你可以轻松地让 master 分支指向 backup 分支所指向的同一 commit。你只需:

从工作目录中删除未 commit 的更改
将 backup 合并到 master(这将导致快进合并并使 master 向上移到和 backup 一样的点)

git checkout -- index.html git merge backup

reset 的 --soft 选项
我们使用相同的几个 commit 并看看 --soft 选项的工作方式:

  • 9ec05ca (HEAD -> master) Revert "Set page heading to "Quests & Crusades""
  • db7e87a Set page heading to "Quests & Crusades"
  • 796ddb0 Merge branch 'heading-update'
    运行 git reset --soft HEAD^ 会把 commit 9ec05ca 中做出的更改直接移至暂存区。

reset 的 --hard 选项
最后再看看 --hard 选项(当然并非最不重要选项):

  • 9ec05ca (HEAD -> master) Revert "Set page heading to "Quests & Crusades""
  • db7e87a Set page heading to "Quests & Crusades"
  • 796ddb0 Merge branch 'heading-update'
    运行 git reset --hard HEAD^ 将清除 commit 9ec05ca 中做出的更改。

reset 小结
总结下,git reset 命令被用来清除 commit:

$ git reset
它可以用来:

将 HEAD 和当前分支指针移到引用的 commit
使用 --hard 选项清除 commit
使用 --soft 选项将 commit 的更改移至暂存区
使用 --mixed 选项取消暂存已被 commit 的更改
我们通常会用到祖先引用来指代之前的 commit。祖先引用包含:

^ – 表示父 commit
~ – 表示第一个父 commit

嗯,到此为上,git基本使用的咖啡课程结束了,还需要在实际的项目中多实践才好,还是挺消耗时间的,现在坐在雕刻时光的咖啡馆里,今天没有开空调,太热了,车已经充完了电,该回去了,拖延了一些事儿,回去搞定!!!

你可能感兴趣的:(用Git进行版本控制(五)撤销更改)