git reset
糖赔、git checkout
和 git revert
是你的 Git 工具箱中最有用的一些命令萍丐。它們都用來撤銷代碼倉庫中的某些更改,而前兩個(gè)命令不僅可以作用于提交放典,還可以作用于特定文件逝变。
因?yàn)樗鼈兎浅O嗨疲晕覀兘?jīng)常會(huì)搞混奋构,不知道什么場(chǎng)景下該用哪個(gè)命令壳影。在這篇文章中,我們會(huì)比較 git reset
弥臼、git checkout
和 git revert
最常見的用法宴咧。希望你在看完后能游刃有余地使用這些命令來管理你的倉庫。
[圖片上傳失敗...(image-6ef1fb-1522748340286)]
Git 倉庫有三個(gè)主要組成——工作目錄醋火,緩存區(qū)和提交歷史悠汽。這張圖有助于理解每個(gè)命令到底產(chǎn)生了哪些影響。當(dāng)你閱讀的時(shí)候芥驳,牢記這張圖柿冲。
提交層面的操作
你傳給 git reset
和 git checkout
的參數(shù)決定了它們的作用域。如果你沒有包含文件路徑兆旬,這些操作對(duì)所有提交生效假抄。我們這一節(jié)要探討的就是提交層面的操作。注意,git revert
沒有文件層面的操作宿饱。
Reset
在提交層面上熏瞄,reset 將一個(gè)分支的末端指向另一個(gè)提交。這可以用來移除當(dāng)前分支的一些提交谬以。比如强饮,下面這兩條命令讓 hotfix 分支向后回退了兩個(gè)提交。
git checkout hotfix
git reset HEAD~2
hotfix 分支末端的兩個(gè)提交現(xiàn)在變成了懸掛提交为黎。也就是說邮丰,下次 Git 執(zhí)行垃圾回收的時(shí)候,這兩個(gè)提交會(huì)被刪除铭乾。換句話說剪廉,如果你想扔掉這兩個(gè)提交,你可以這么做炕檩。reset 操作如下圖所示:
[圖片上傳失敗...(image-17efb6-1522748340286)]
如果你的更改還沒有共享給別人斗蒋,git reset
是撤銷這些更改的簡(jiǎn)單方法。當(dāng)你開發(fā)一個(gè)功能的時(shí)候發(fā)現(xiàn)「糟糕笛质,我做了什么泉沾?我應(yīng)該重新來過!」時(shí)妇押,reset 就像是 go-to 命令一樣爆哑。
除了在當(dāng)前分支上操作,你還可以通過傳入這些標(biāo)記來修改你的緩存區(qū)或工作目錄:
- --soft – 緩存區(qū)和工作目錄都不會(huì)被改變
- --mixed – 默認(rèn)選項(xiàng)舆吮。緩存區(qū)和你指定的提交同步揭朝,但工作目錄不受影響
- --hard – 緩存區(qū)和工作目錄都同步到你指定的提交
把這些標(biāo)記想成定義 git reset
操作的作用域就容易理解多了。
[圖片上傳失敗...(image-f64e2a-1522748340286)]
這些標(biāo)記往往和 HEAD 作為參數(shù)一起使用色冀。比如潭袱,git reset --mixed HEAD
將你當(dāng)前的改動(dòng)從緩存區(qū)中移除,但是這些改動(dòng)還留在工作目錄中锋恬。另一方面屯换,如果你想完全舍棄你沒有提交的改動(dòng),你可以使用 git reset --hard HEAD
与学。這是 git reset
最常用的兩種用法彤悔。
當(dāng)你傳入 HEAD 以外的其他提交的時(shí)候要格外小心,因?yàn)?reset 操作會(huì)重寫當(dāng)前分支的歷史索守。正如 rebase 黃金法則所說的晕窑,在公共分支上這樣做可能會(huì)引起嚴(yán)重的后果。
Checkout
你應(yīng)該已經(jīng)非常熟悉提交層面的 git checkout
卵佛。當(dāng)傳入分支名時(shí)杨赤,可以切換到那個(gè)分支敞斋。
git checkout hotfix
上面這個(gè)命令做的不過是將HEAD移到一個(gè)新的分支,然后更新工作目錄疾牲。因?yàn)檫@可能會(huì)覆蓋本地的修改植捎,Git 強(qiáng)制你提交或者緩存工作目錄中的所有更改,不然在 checkout 的時(shí)候這些更改都會(huì)丟失阳柔。和 git reset
不一樣的是焰枢,git checkout
沒有移動(dòng)這些分支。
[圖片上傳失敗...(image-3980e4-1522748340286)]
除了分支之外舌剂,你還可以傳入提交的引用來 checkout 到任意的提交医咨。這和 checkout 到另一個(gè)分支是完全一樣的:把 HEAD 移動(dòng)到特定的提交。比如架诞,下面這個(gè)命令會(huì) checkout 到當(dāng)前提交的祖父提交。
git checkout HEAD~2
[圖片上傳失敗...(image-da5a1-1522748340286)]
這對(duì)于快速查看項(xiàng)目舊版本來說非常有用干茉。但如果你當(dāng)前的 HEAD 沒有任何分支引用谴忧,那么這會(huì)造成 HEAD 分離。這是非常危險(xiǎn)的角虫,如果你接著添加新的提交沾谓,然后切換到別的分支之后就沒辦法回到之前添加的這些提交。因此戳鹅,在為分離的 HEAD 添加新的提交的時(shí)候你應(yīng)該創(chuàng)建一個(gè)新的分支均驶。
Revert
Revert 撤銷一個(gè)提交的同時(shí)會(huì)創(chuàng)建一個(gè)新的提交。這是一個(gè)安全的方法枫虏,因?yàn)樗粫?huì)重寫提交歷史妇穴。比如,下面的命令會(huì)找出倒數(shù)第二個(gè)提交隶债,然后創(chuàng)建一個(gè)新的提交來撤銷這些更改腾它,然后把這個(gè)提交加入項(xiàng)目中。
git checkout hotfix
git revert HEAD~2
如下圖所示:
[圖片上傳失敗...(image-a2d811-1522748340286)]
相比 git reset
死讹,它不會(huì)改變現(xiàn)在的提交歷史瞒滴。因此,git revert
可以用在公共分支上赞警,git reset
應(yīng)該用在私有分支上妓忍。
你也可以把 git revert
當(dāng)作撤銷已經(jīng)提交的更改,而 git reset HEAD
用來撤銷沒有提交的更改愧旦。
就像 git checkout
一樣世剖,git revert
也有可能會(huì)重寫文件。所以笤虫,Git 會(huì)在你執(zhí)行 revert 之前要求你提交或者緩存你工作目錄中的更改搁廓。
文件層面的操作
git reset
和 git checkout
命令也接受文件路徑作為參數(shù)引颈。這時(shí)它的行為就大為不同了。它不會(huì)作用于整份提交境蜕,參數(shù)將它限制于特定文件蝙场。
Reset
當(dāng)檢測(cè)到文件路徑時(shí),git reset
將緩存區(qū)同步到你指定的那個(gè)提交粱年。比如售滤,下面這個(gè)命令會(huì)將倒數(shù)第二個(gè)提交中的 foo.py
加入到緩存區(qū)中,供下一個(gè)提交使用台诗。
git reset HEAD~2 foo.py
和提交層面的 git reset
一樣完箩,通常我們使用HEAD而不是某個(gè)特定的提交。運(yùn)行 git reset HEAD foo.py
會(huì)將當(dāng)前的 foo.py
從緩存區(qū)中移除出去拉队,而不會(huì)影響工作目錄中對(duì) foo.py
的更改弊知。
[圖片上傳失敗...(image-7d2ed3-1522748340284)]
--soft
、--mixed
和 --hard
對(duì)文件層面的 git reset
毫無作用粱快,因?yàn)榫彺鎱^(qū)中的文件一定會(huì)變化秩彤,而工作目錄中的文件一定不變。
Checkout
Checkout 一個(gè)文件和帶文件路徑 git reset
非常像事哭,除了它更改的是工作目錄而不是緩存區(qū)漫雷。不像提交層面的 checkout 命令,它不會(huì)移動(dòng) HEAD引用鳍咱,也就是你不會(huì)切換到別的分支上去降盹。
[圖片上傳失敗...(image-3fd75d-1522748340283)]
比如,下面這個(gè)命令將工作目錄中的 foo.py
同步到了倒數(shù)第二個(gè)提交中的 foo.py
谤辜。
git checkout HEAD~2 foo.py
和提交層面相同的是蓄坏,它可以用來檢查項(xiàng)目的舊版本,但作用域被限制到了特定文件丑念。
如果你緩存并且提交了 checkout 的文件剑辫,它具備將某個(gè)文件回撤到之前版本的效果。注意它撤銷了這個(gè)文件后面所有的更改渠欺,而 git revert
命令只撤銷某個(gè)特定提交的更改妹蔽。
和 git reset
一樣,這個(gè)命令通常和 HEAD 一起使用挠将。比如 git checkout HEAD foo.py
等同于舍棄 foo.py
沒有緩存的更改胳岂。這個(gè)行為和 git reset HEAD --hard
很像,但只影響特定文件舔稀。
總結(jié)
你現(xiàn)在已經(jīng)掌握了 Git 倉庫中撤銷更改的所有工具乳丰。git reset
、git checkout
和 git revert
命令比較容易混淆内贮,但當(dāng)你想起它們對(duì)工作目錄产园、緩存區(qū)和提交歷史的不同影響汞斧,就會(huì)容易判斷現(xiàn)在應(yīng)該用哪個(gè)命令。
下面這個(gè)表格總結(jié)了這些命令最常用的使用場(chǎng)景什燕。記得經(jīng)常對(duì)照這個(gè)表格粘勒,因?yàn)槟闶褂?Git 時(shí)一定會(huì)經(jīng)常用到。
命令 | 作用域 | 常用情景 |
---|---|---|
git reset | 提交層面 | 在私有分支上舍棄一些沒有提交的更改 |
git reset | 文件層面 | 將文件從緩存區(qū)中移除 |
git checkout | 提交層面 | 切換分支或查看舊版本 |
git checkout | 文件層面 | 舍棄工作目錄中的更改 |
git revert | 提交層面 | 在公共分支上回滾更改 |
git revert | 文件層面 | (然而并沒有) |