學(xué)會(huì)這十種Git"撤銷"操作,走遍Git天下都不怕了。

譯自:Github

JF杰微刊出品:如何在Git中撤銷一切

JF杰微刊:如何在Git中撤銷一切


任何一個(gè)版本控制系統(tǒng)中,最有用的特性之一莫過(guò)于?“撤銷(undo)”操作。在Git中,“撤銷”有很多種含義。

當(dāng)你完成了一次新的提交(commit),Git會(huì)及時(shí)存儲(chǔ)當(dāng)前時(shí)刻倉(cāng)庫(kù)(repository)的快照(snapshot);你能夠使用Git將項(xiàng)目回退到任何之前的版本。

下文中,我將列舉幾個(gè)常見(jiàn)的、需要“撤銷”的場(chǎng)景,并且展示如何使用Git來(lái)完成這些操作。



一、撤銷一個(gè)公共修改 Undo?a?"public"?change

場(chǎng)景:你剛剛用git?push將本地修改推送到了GitHub,這時(shí)你意識(shí)到在提交中有一個(gè)錯(cuò)誤。你想撤銷這次提交。

使用撤銷命令:git?revert

發(fā)生了什么:git?revert將根據(jù)給定SHA的相反值,創(chuàng)建一個(gè)新的提交。如果舊提交是“matter”,那么新的提交就是“anti-matter”——舊提交中所有已移除的東西將會(huì)被添加進(jìn)到新提交中,舊提交中增加的東西將在新提交中移除。

這是Git最安全、也是最簡(jiǎn)單的“撤銷”場(chǎng)景,因?yàn)檫@樣不會(huì)修改歷史記錄——你現(xiàn)在可以git?push下剛剛revert之后的提交來(lái)糾正錯(cuò)誤了。



二、修改最近一次的提交信息 Fix?the?last?commit?message

場(chǎng)景你只是在最后的提交信息中敲錯(cuò)了字,比如你敲了git?commit?-m?"Fxies?bug?#42",而在執(zhí)行g(shù)it?push之前你已經(jīng)意識(shí)到你應(yīng)該敲"Fixes?bug?#42"。

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

發(fā)生了什么git?commit?–amend將使用一個(gè)包含了剛剛錯(cuò)誤提交所有變更的新提交,來(lái)更新并替換這個(gè)錯(cuò)誤提交。由于沒(méi)有staged的提交,所以實(shí)際上這個(gè)提交只是重寫了先前的提交信息。



三、撤銷本地更改 Undo?"local"?changes

場(chǎng)景當(dāng)你的貓爬過(guò)鍵盤時(shí),你正在編輯的文件恰好被保存了,你的編輯器也恰在此時(shí)崩潰了。此時(shí)你并沒(méi)有提交過(guò)代碼。你期望撤銷這個(gè)文件中的所有修改——將這個(gè)文件回退到上次提交的狀態(tài)。

使用撤銷命令git?checkout?--

發(fā)生了什么git?checkout將工作目錄(working?directory)里的文件修改成先前Git已知的狀態(tài)。你可以提供一個(gè)期待回退分支的名字或者一個(gè)確切的SHA碼,Git也會(huì)默認(rèn)檢出HEAD——即:當(dāng)前分支的上一次提交。

注意:用這種方法“撤銷”的修改都將真正的消失。它們永遠(yuǎn)不會(huì)被提交。因此Git不能恢復(fù)它們。此時(shí),一定要明確自己在做什么!(或許可以用git?diff來(lái)確定)



四、重置本地修改 Reset?"local"?changes

場(chǎng)景你已經(jīng)在本地做了一些提交(還沒(méi)push),但所有的東西都糟糕透了,你想撤銷最近的三次提交——就像它們從沒(méi)發(fā)生過(guò)一樣。

使用撤銷命令git?reset或git?reset?--hard

發(fā)生了什么git?reset 將你的倉(cāng)庫(kù)紀(jì)錄一直回退到指定的最后一個(gè)SHA代表的提交,那些提交就像從未發(fā)生過(guò)一樣。默認(rèn)情況下,git?reset會(huì)保留工作目錄 (working?directory)。這些提交雖然消失了,但是內(nèi)容還在磁盤上。這是最安全的做法,但通常情況是:你想使用一個(gè)命令來(lái)“撤銷”所有提 交和本地修改——那么請(qǐng)使用--hard參數(shù)吧。



五、撤銷本地后重做 Redo?after?undo?"local"

場(chǎng)景你已經(jīng)提交了一些內(nèi)容,并使用git?reset?–hard撤銷了這些更改(見(jiàn)上面),突然意識(shí)到:你想還原這些修改!

使用撤銷命令git?reflog和git?reset,?或者git?checkout

發(fā)生了什么git?reflog是一個(gè)用來(lái)恢復(fù)項(xiàng)目歷史記錄的好辦法。你可以通過(guò)git?reflog恢復(fù)幾乎任何已提交的內(nèi)容。

你或許對(duì)git?log命令比較熟悉,它能顯示提交列表。git?reflog與之類似,只不過(guò)git?reflog顯示的是HEAD變更次數(shù)的列表。

一些說(shuō)明

1.?只 有HEAD會(huì)改變。當(dāng)你切換分支時(shí),用git?commit提交變更時(shí),或是用git?reset撤銷提交時(shí),HEAD都會(huì)改變。但當(dāng)你用 git?checkout?--時(shí),?HEAD不會(huì)發(fā)生改變。(就像上文提到的情形,那些更改根本就沒(méi)有提交,因此reflog就不能幫助我們進(jìn)行恢復(fù) 了)

2.??git?reflog不會(huì)永遠(yuǎn)存在。Git將會(huì)定期清理那些“不可達(dá)(unreachable)”的對(duì)象。不要期望能夠在reflog里找到數(shù)月前的提交記錄。

3.??reflog只是你個(gè)人的。你不能用你的reflog來(lái)恢復(fù)其他開(kāi)發(fā)者未push的提交。

JF杰微刊出品:如何在Git中撤銷一切

因此,怎樣合理使用reflog來(lái)找回之前“未完成”的提交呢?這要看你究竟要做什么:

1.?如果你想恢復(fù)項(xiàng)目歷史到某次提交,那請(qǐng)使用git?reset?--hard

2.?如果你想在工作目錄(working?direcotry)中恢復(fù)某次提交中的一個(gè)或多個(gè)文件,并且不改變提交歷史,那請(qǐng)使用git?checkout--

3.?如果你想確切的回滾到某次提交,那么請(qǐng)使用git?cherry-pick。



六、與分支有關(guān)的那些事 Once?more,?with?branching

場(chǎng)景你提交了一些變更,然后你意識(shí)到你正在master分支上,但你期望的是在feature分支上執(zhí)行這些提交。

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

發(fā)生了什么你可能用的是git?checkout?-b來(lái)建立新的分支,這是創(chuàng)建和檢出分支的便捷方法——但實(shí)際你并不想立刻切換分支。git?branch?feature會(huì)建立一個(gè)叫feature的分支,這個(gè)分支指向你最近的提交,但是你還停留在master分支上。

git?reset?--hard將master回退至origin/master,并忽略所有新提交。別擔(dān)心,那些提交都還保留在feature上。

最后,git?checkout將分支切換到feature,這個(gè)分支原封不動(dòng)的保留了你最近的所有工作。



七、事半功倍處理分支 Branch?in?time?saves?nine

場(chǎng)景:你基于master新建了一個(gè)feature分支,但是master分支遠(yuǎn)遠(yuǎn)落后與origin/master。現(xiàn)在master分支與origin/master同步了,你期望此刻能在feature下立刻commit代碼,并且不是在遠(yuǎn)遠(yuǎn)落后master的情況下。

使用撤銷命令git?checkout?feature和git?rebase?master

發(fā)生了什么你也許已經(jīng)敲了命令:git?reset(但是沒(méi)用--hard,有意在磁盤上保存這些提交內(nèi)容),然后敲了git?checkout?-b,之后重新提交更改,但是那樣的話,你將失去本地的提交記錄。不過(guò),一個(gè)更好的方法:

使用git?rebase?master可以做到一些事情:

1.首先,它定位你當(dāng)前檢出分支和master之間的共同祖先節(jié)點(diǎn)(common?ancestor)。

2.然后,它將當(dāng)前檢出的分支重置到祖先節(jié)點(diǎn)(ancestor),并將后來(lái)所有的提交都暫存起來(lái)。

3.最后,它將當(dāng)前檢出分支推進(jìn)至master末尾,同時(shí)在master最后一次提交之后,再次提交那些在暫存區(qū)的變更。



八、批量撤銷/找回 Mass?undo/redo

場(chǎng)景你開(kāi)始朝一個(gè)既定目標(biāo)開(kāi)發(fā)功能,但是中途你感覺(jué)用另一個(gè)方法更好。你已經(jīng)有十幾個(gè)提交,但是你只想要其中的某幾個(gè),其他的都可以刪除不要。

使用撤銷命令git?rebase?-i

發(fā)生了什么-i將rebases設(shè)置為“交互模式(interactive?mode)”。rebase開(kāi)始執(zhí)行的操作就像上文討論的一樣,但是在重新執(zhí)行某個(gè)提交時(shí),它會(huì)暫停下來(lái),讓你修改每一次提交。

rebase?–i將會(huì)打開(kāi)你的默認(rèn)文本編輯器,然后列出正在執(zhí)行的提交,就像這樣:

JF杰微刊出品:如何在Git中撤銷一切


前兩列最關(guān)鍵:第一列是選擇命令,它會(huì)根據(jù)第二列中的SHA碼選擇相應(yīng)的提交。默認(rèn)情況下,rebase?–i會(huì)認(rèn)為每個(gè)更改都正通過(guò)pick命令被提交。

要撤銷一個(gè)提交,直接在編輯器刪除對(duì)應(yīng)的行就可以了。如果在你的項(xiàng)目不再需要這些錯(cuò)誤的提交,你可以直接刪除上圖中的第1行和3-4行。

如 果你想保留提交但修改提交信息,你可以使用reword命令。即,將命令關(guān)鍵字pick換成reword(或者r)。你現(xiàn)在可能想立刻修改提交消息,但這 么做不會(huì)生效——rebase?–i將忽略SHA列后的所有東西。現(xiàn)有的提交信息會(huì)幫助我們記住0835fe2代表什么。當(dāng)你敲完rebase?–i命令 后,Git才開(kāi)始提示你重寫那些新提交消息。

如果你需要將2個(gè)提交合并,你可以用squash或者fixup命令,如下圖:

JF杰微刊出品:如何在Git中撤銷一切

squash和fixup都是“向上”結(jié)合的——那些用了這些合并命令(編者按:指squash、fixup)的提交,將會(huì)和它之前的提交合并:上圖中,0835fe2和6943e85將會(huì)合并成一個(gè)提交,而38f5e4e和af67f82將會(huì)合并成另一個(gè)提交。

當(dāng) 你用squash時(shí),Git將會(huì)提示是否填寫新的提交消息;fixup則會(huì)給出列表中第一個(gè)提交的提交信息。在上圖中,af67f82是一個(gè) “Ooops”信息,因?yàn)檫@個(gè)提交信息已經(jīng)同38f5e4e一樣了。但是你可以為0835fe2和6943e85合并的新提交編寫提交信息。

當(dāng)你保存并退出編輯器時(shí),Git將會(huì)按照從上到下的順序執(zhí)行你的提交。你可以在保存這些提交之前,修改提交的執(zhí)行順序。如果有需要,你可以將af67f82和0835fe2合并,并且可以這樣排序:

JF杰微刊出品:如何在Git中撤銷一切



九、修復(fù)早先的提交 Fix?an?earlier?commit

場(chǎng)景之前的提交里落下了一個(gè)文件,如果先前的提交能有你留下的東西就好了。你還沒(méi)有push,并且這個(gè)提交也不是最近的提交,因此你不能用commit?–amend。

使用撤銷命git?commit?--squash和git?rebase?--autosquash?-i

發(fā)生了什么:git?commit?–squash將會(huì)創(chuàng)建一個(gè)新的提交,該提交信息可能像這樣“squash!?Earlier?commit”。(你也可以手寫這些提交信息,commit?–squash只是省得讓你打字了)。

如果你不想為合并的提交編寫信息,也可以考慮使用命令git?commit?--fixup。這種情況下,你可能會(huì)使用commit?--fixup,因?yàn)槟銉H希望在rebase中使用之前的提交信息。

rebase?--autosquash?–i將會(huì)啟動(dòng)rebase交互編輯器,編輯器會(huì)列出任何已完成的squash!和fixup!提交,如下圖:

JF杰微刊出品:如何在Git中撤銷一切

當(dāng) 使用--squash和–fixup時(shí),你或許記不清你想修復(fù)的某個(gè)提交的SHA碼——只知道它可能在一個(gè)或五個(gè)提交之前。你或許可以使用Git的^和~ 操作符手動(dòng)找回。HEAD^表示HEAD的前一次提交。HEAD~4表示HEAD前的4次提交,加起來(lái)總共是前5次提交。



十、停止跟蹤一個(gè)已被跟蹤的文件 Stop?tracking?a?tracked?file

場(chǎng)景:你意外將application.log添加到倉(cāng)庫(kù)中,現(xiàn)在你每次運(yùn)行程序,Git都提示application.log中有unstaged的提交。你在.gitignore中寫上”*.log”,但仍舊沒(méi)用——怎樣告訴Git“撤銷”跟蹤這個(gè)文件的變化呢?

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

發(fā)生了什么:盡 管.gitignore阻止Git跟蹤文件的變化,甚至是之前沒(méi)被跟蹤的文件是否存在,但是,一旦文件被add或者commit,Git會(huì)開(kāi)始持續(xù)跟蹤這 個(gè)文件的變化。類似的,如果你用git?add?–f來(lái)“強(qiáng)制”add,或者覆蓋.gitignore,Git還是會(huì)繼續(xù)監(jiān)視變化。所以以后最好不要使用 –f來(lái)add?.gitignore文件。

如果你希望移除那些應(yīng)當(dāng)被忽略的文件,git?rm?–cached可以幫助你,并將這些文件保留在磁盤上。因?yàn)檫@個(gè)文件現(xiàn)在被忽略了,你將不會(huì)在git?status中看到它,也不會(huì)再把這個(gè)文件commit了。

以上就是如何在Git上撤銷的方法。如果你想學(xué)習(xí)更多Git命令用法,可以移步下面相關(guān)的文檔:

· checkout

· commit

· rebase

· reflog

· reset

· revert

· rm



原文地址:Github

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


如果您發(fā)現(xiàn)這篇譯文的任何問(wèn)題,可隨時(shí)與我們聯(lián)系。

我們水平有限,但理想高遠(yuǎn)。我們旨在分享優(yōu)質(zhì)的內(nèi)容。


我們也同樣期待理想的您對(duì)這個(gè)世界的貢獻(xiàn)。歡迎任何目的的聯(lián)系。


我們的郵箱是:weikan@jointforce.com。


我們的QQ是:3272840549。



[轉(zhuǎn)載請(qǐng)保留原文出處、譯者和審校者。

可以不保留我們的鏈接]

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容

  • Git是目前最流行的版本管理系統(tǒng),也是最先進(jìn)的分布式版本控制系統(tǒng)(distributed version cont...
    pro648閱讀 5,752評(píng)論 1 17
  • git常用命令 GIT常用命令備忘:http://stormzhang.com/git/2014/01/27/gi...
    新篇章閱讀 8,540評(píng)論 1 26
  • 往生酒,往生已舊。 這不僅僅只是一杯酒。 它由世間所有游魂的執(zhí)念釀成,又能夠消解所有的執(zhí)念。 每個(gè)來(lái)到幽冥司的游魂...
    千琉閱讀 1,474評(píng)論 5 9
  • 你說(shuō)你有理想 說(shuō)你自己有面包和茶 還說(shuō)了要嫁就嫁給幸福 為什么你卻帶來(lái)了這樣一個(gè)他 奔馳是他的座駕 別墅是他的家 ...
    讀云軒札記閱讀 281評(píng)論 0 0
  • 看看王者榮耀帶給了我那些收獲 今年一個(gè)偶然的機(jī)會(huì)開(kāi)始下下載了王者榮耀游戲,一開(kāi)始覺(jué)得這游戲挺好玩的,輕輕松松的就上...
    簡(jiǎn)明估閱讀 807評(píng)論 0 0