【转】如何在Git中撤销一切

翻译:李伟 
审校:张帆
译自:Githubgit

任何一个版本控制系统中,最有用的特性之一莫过于 “撤销(undo)”操做。在Git中,“撤销”有不少种含义。github

当你完成了一次新的提交(commit),Git会及时存储当前时刻仓库(repository)的快照(snapshot);你可以使用Git将项目回退到任何以前的版本。安全

下文中,我将列举几个常见的、须要“撤销”的场景,而且展现如何使用Git来完成这些操做。app

1、撤销一个公共修改 Undo a "public" change编辑器

场景:你刚刚用git push将本地修改推送到了GitHub,这时你意识到在提交中有一个错误。你想撤销此次提交。oop

使用撤销命令:git revert学习

发生了什么:git revert将根据给定SHA的相反值,建立一个新的提交。若是旧提交是“matter”,那么新的提交就是“anti-matter”——旧提交中全部已移除的东西将会被添加进到新提交中,旧提交中增长的东西将在新提交中移除。翻译

这是Git最安全、也是最简单的“撤销”场景,由于这样不会修改历史记录——你如今能够git push下刚刚revert以后的提交来纠正错误了。3d

2、修改最近一次的提交信息 Fix the last commit message版本控制

场景:你只是在最后的提交信息中敲错了字,好比你敲了git commit -m "Fxies bug #42",而在执行git push以前你已经意识到你应该敲"Fixes bug #42"。

使用撤销命令:git commit –amend或git commit --amend -m "Fixes bug #42"

发生了什么:git commit –amend将使用一个包含了刚刚错误提交全部变动的新提交,来更新并替换这个错误提交。因为没有staged的提交,因此实际上这个提交只是重写了先前的提交信息。

3、撤销本地更改 Undo "local" changes

场景:当你的猫爬过键盘时,你正在编辑的文件刚好被保存了,你的编辑器也恰在此时崩溃了。此时你并无提交过代码。你指望撤销这个文件中的全部修改——将这个文件回退到上次提交的状态。

使用撤销命令:git checkout --

发生了什么:git checkout将工做目录(working directory)里的文件修改为先前Git已知的状态。你能够提供一个期待回退分支的名字或者一个确切的SHA码,Git也会默认检出HEAD——即:当前分支的上一次提交。

注意:用这种方法“撤销”的修改都将真正的消失。它们永远不会被提交。所以Git不能恢复它们。此时,必定要明确本身在作什么!(或许能够用git diff来肯定)

4、重置本地修改 Reset "local" changes

场景:你已经在本地作了一些提交(还没push),但全部的东西都糟糕透了,你想撤销最近的三次提交——就像它们从没发生过同样。

使用撤销命令:git reset或git reset --hard

发生了什么:git reset将你的仓库纪录一直回退到指定的最后一个SHA表明的提交,那些提交就像从未发生过同样。默认状况下,git reset会保留工做目录(working directory)。这些提交虽然消失了,可是内容还在磁盘上。这是最安全的作法,但一般状况是:你想使用一个命令来“撤销”全部提交和本地修改——那么请使用--hard参数吧。

5、撤销本地后重作 Redo after undo "local"

场景:你已经提交了一些内容,并使用git reset –hard撤销了这些更改(见上面),忽然意识到:你想还原这些修改!

使用撤销命令:git reflog和git reset, 或者git checkout

发生了什么:git reflog是一个用来恢复项目历史记录的好办法。你能够经过git reflog恢复几乎任何已提交的内容。

你或许对git log命令比较熟悉,它能显示提交列表。git reflog与之相似,只不过git reflog显示的是HEAD变动次数的列表。

一些说明:

1. 只有HEAD会改变。当你切换分支时,用git commit提交变动时,或是用git reset撤销提交时,HEAD都会改变。但当你用git checkout --时, HEAD不会发生改变。(就像上文提到的情形,那些更改根本就没有提交,所以reflog就不能帮助咱们进行恢复了)

2.  git reflog不会永远存在。Git将会按期清理那些“不可达(unreachable)”的对象。不要指望可以在reflog里找到数月前的提交记录。

3.  reflog只是你我的的。你不能用你的reflog来恢复其余开发者未push的提交。

blob.png

所以,怎样合理使用reflog来找回以前“未完成”的提交呢?这要看你究竟要作什么:

1. 若是你想恢复项目历史到某次提交,那请使用git reset --hard

2. 若是你想在工做目录(working direcotry)中恢复某次提交中的一个或多个文件,而且不改变提交历史,那请使用git checkout--

3. 若是你想确切的回滚到某次提交,那么请使用git cherry-pick。

6、与分支有关的那些事 Once more, with branching

场景:你提交了一些变动,而后你意识到你正在master分支上,但你指望的是在feature分支上执行这些提交。

使用撤销命令:git branch feature, git reset --hard origin/master, 和 git checkout feature

发生了什么:你可能用的是git checkout -b来创建新的分支,这是建立和检出分支的便捷方法——但实际你并不想马上切换分支。git branch feature会创建一个叫feature的分支,这个分支指向你最近的提交,可是你还停留在master分支上。

git reset --hard将master回退至origin/master,并忽略全部新提交。别担忧,那些提交都还保留在feature上。

最后,git checkout将分支切换到feature,这个分支原封不动的保留了你最近的全部工做。

7、事半功倍处理分支 Branch in time saves nine

场景:你基于master新建了一个feature分支,可是master分支远远落后与origin/master。如今master分支与origin/master同步了,你指望此刻能在feature下马上commit代码,而且不是在远远落后master的状况下。

使用撤销命令:git checkout feature和git rebase master

发生了什么:你也许已经敲了命令:git reset(可是没用--hard,有意在磁盘上保存这些提交内容),而后敲了git checkout -b,以后从新提交更改,可是那样的话,你将失去本地的提交记录。不过,一个更好的方法:

使用git rebase master能够作到一些事情:

1.首先,它定位你当前检出分支和master之间的共同祖先节点(common ancestor)。

2.而后,它将当前检出的分支重置到祖先节点(ancestor),并将后来全部的提交都暂存起来。

3.最后,它将当前检出分支推动至master末尾,同时在master最后一次提交以后,再次提交那些在暂存区的变动。

8、批量撤销/找回 Mass undo/redo

场景:你开始朝一个既定目标开发功能,可是中途你感受用另外一个方法更好。你已经有十几个提交,可是你只想要其中的某几个,其余的均可以删除不要。

使用撤销命令:git rebase -i

发生了什么:-i将rebases设置为“交互模式(interactive mode)”。rebase开始执行的操做就像上文讨论的同样,可是在从新执行某个提交时,它会暂停下来,让你修改每一次提交。

rebase –i将会打开你的默认文本编辑器,而后列出正在执行的提交,就像这样:

blob.png

前两列最关键:第一列是选择命令,它会根据第二列中的SHA码选择相应的提交。默认状况下,rebase –i会认为每一个更改都正经过pick命令被提交。

要撤销一个提交,直接在编辑器删除对应的行就能够了。若是在你的项目再也不须要这些错误的提交,你能够直接删除上图中的第1行和3-4行。

若是你想保留提交但修改提交信息,你可使用reword命令。即,将命令关键字pick换成reword(或者r)。你如今可能想马上修改提交消息,但这么作不会生效——rebase –i将忽略SHA列后的全部东西。现有的提交信息会帮助咱们记住0835fe2表明什么。当你敲完rebase –i命令后,Git才开始提示你重写那些新提交消息。

若是你须要将2个提交合并,你能够用squash或者fixup命令,以下图:

blob.png

squash和fixup都是“向上”结合的——那些用了这些合并命令(编者按:指squash、fixup)的提交,将会和它以前的提交合并:上图中,0835fe2和6943e85将会合并成一个提交,而38f5e4e和af67f82将会合并成另外一个提交。

当你用squash时,Git将会提示是否填写新的提交消息;fixup则会给出列表中第一个提交的提交信息。在上图中,af67f82是一个“Ooops”信息,由于这个提交信息已经同38f5e4e同样了。可是你能够为0835fe2和6943e85合并的新提交编写提交信息。

当你保存并退出编辑器时,Git将会按照从上到下的顺序执行你的提交。你能够在保存这些提交以前,修改提交的执行顺序。若是有须要,你能够将af67f82和0835fe2合并,而且能够这样排序:

blob.png

9、修复早先的提交 Fix an earlier commit

场景:以前的提交里落下了一个文件,若是先前的提交能有你留下的东西就行了。你尚未push,而且这个提交也不是最近的提交,所以你不能用commit –amend。

使用撤销命令:git commit --squash和git rebase --autosquash -i

发生了什么:git commit –squash将会建立一个新的提交,该提交信息可能像这样“squash! Earlier commit”。(你也能够手写这些提交信息,commit –squash只是免得让你打字了)。

若是你不想为合并的提交编写信息,也能够考虑使用命令git commit --fixup。这种状况下,你可能会使用commit --fixup,由于你仅但愿在rebase中使用以前的提交信息。

rebase --autosquash –i将会启动rebase交互编辑器,编辑器会列出任何已完成的squash!和fixup!提交,以下图:

blob.png

当使用--squash和–fixup时,你或许记不清你想修复的某个提交的SHA码——只知道它可能在一个或五个提交以前。你或许可使用Git的^和~操做符手动找回。HEAD^表示HEAD的前一次提交。HEAD~4表示HEAD前的4次提交,加起来总共是前5次提交。

10、中止跟踪一个已被跟踪的文件 Stop tracking a tracked file

场景:你意外将application.log添加到仓库中,如今你每次运行程序,Git都提示application.log中有unstaged的提交。你在.gitignore中写上”*.log”,但仍旧没用——怎样告诉Git“撤销”跟踪这个文件的变化呢?

使用撤销命令: git rm --cached application.log

发生了什么:尽管.gitignore阻止Git跟踪文件的变化,甚至是以前没被跟踪的文件是否存在,可是,一旦文件被add或者commit,Git会开始持续跟踪这个文件的变化。相似的,若是你用git add –f来“强制”add,或者覆盖.gitignore,Git仍是会继续监视变化。因此之后最好不要使用–f来add .gitignore文件。

若是你但愿移除那些应当被忽略的文件,git rm –cached能够帮助你,并将这些文件保留在磁盘上。由于这个文件如今被忽略了,你将不会在git status中看到它,也不会再把这个文件commit了。

以上就是如何在Git上撤销的方法。若是你想学习更多Git命令用法,能够移步下面相关的文档:

原文地址:Github

译文地址:http://www.jointforce.com/jfperiodical/article/show/796?m=d03

相关文章
相关标签/搜索