時(shí)間 | 更新備注 |
---|---|
2018-03-02 | 新建文章 |
2018-06-10 | 添加和revert&checkout的對(duì)比 |
2019-01-18 | 更新鏈接 |
目錄
- Git 筆記系列(一)—— Git簡(jiǎn)介
- Git 筆記系列(二)—— Git工作流程
- Git 筆記系列(三)—— Git常用命令-一覽
- Git 筆記系列(四)—— Git常用命令-Checkout
- Git 筆記系列(五)—— Git常用命令-Branch
- Git 筆記系列(六)—— Git常用命令-Reset
- Git 筆記系列(七)—— Git常用命令-Rebase
- Git 筆記系列(八)—— Git常用命令-Stash等
- Git 筆記系列(九)—— Git進(jìn)階
引言
在提交層面上腋颠,reset將一個(gè)分支的末端指向另一個(gè)提交山橄。這可以用來(lái)移除當(dāng)前分支的一些提交抄淑。比如褐望,下面這兩條命令讓 hotfix 分支向后回退了兩個(gè)提交慎冤。
git checkout hotfix
git reset HEAD~2
hotfix 分支末端的兩個(gè)提交現(xiàn)在變成了懸掛提交熙兔。也就是說(shuō)贬循,下次 Git 執(zhí)行垃圾回收的時(shí)候陶夜,這兩個(gè)提交會(huì)被刪除冀泻。換句話說(shuō)常侣,如果你想扔掉這兩個(gè)提交,你可以這么做弹渔。reset 操作如下圖所示:
如果你仔細(xì)研究reset命令本身就知道胳施,它本身做的事情就是重置HEAD(當(dāng)前分支的版本頂端)到另外一個(gè)commit。
Reset解惑
讓我們跟著 reset 看看它都做了什么肢专。它以一種簡(jiǎn)單可預(yù)見(jiàn)的方式直接操縱這三棵樹(shù)舞肆。它做了三個(gè)基本操作。 第 1 步:移動(dòng) HEAD
reset 做的第一件事是移動(dòng) HEAD 的指向博杖。這與改變 HEAD 自身不同(checkout 所做的); reset 移動(dòng) HEAD
指向的分支椿胯。這意味著如果 HEAD 設(shè)置為 master 分支(例如,你正在 master 分支上)剃根,運(yùn)行 git reset 9e5e64a
將會(huì)使master指向9e5e64a哩盲。
reset補(bǔ)充
總的來(lái)說(shuō),git reset
命令是用來(lái)將當(dāng)前branch重置到另外一個(gè)commit的,而這個(gè)動(dòng)作可能會(huì)將index以及work tree
同樣影響种冬。比如如果你的master branch
(當(dāng)前checked out)是下面這個(gè)樣子:
- A - B - C (HEAD, master)
HEAD和master branch
是在一起的镣丑,而你希望將master指向到B,而不是C娱两,那么你執(zhí)行
git reset B
以便移動(dòng)master branch到``B那個(gè)commit:
- A - B (HEAD, master) # - C is still here, but there's no branch pointing to it anymore
注意:git reset
和checkout是不一樣的莺匠。如果你運(yùn)行g(shù)it checkout B,那么你講得到:
- A - B (HEAD) - C (master)
這時(shí)HEAD和master branch
就不在一個(gè)點(diǎn)上了,你進(jìn)入detached HEAD State
. HEAD,work tree,index都指向了B十兢,但是master branch
卻依然指向C趣竣。如果在這個(gè)點(diǎn)上,你執(zhí)行一個(gè)新的commit D旱物,那么你講得到下面(當(dāng)然這可能并不是你想要的遥缕,你可能想要的是創(chuàng)一個(gè)branch做bug fix):
- A - B - C (master)
\
D (HEAD)
記住git reset
不會(huì)產(chǎn)生commits,它僅僅更新一個(gè)branch(branch本身就是一個(gè)指向一個(gè)commit的指針)指向另外一個(gè)commit(Head和branch Tip同時(shí)移動(dòng)保持一致).其他的僅剩對(duì)于index和work tree(working directory)
有什么影響。git checkout xxxCommit
則只影響HEAD宵呛,如果xxxCommit和一個(gè)branch tip是一致的話单匣,則HEAD和branch相匹配,如果xxxCommit并不和任何branch tip相一致宝穗,則git進(jìn)入detached HEAD
狀態(tài)户秤。
reset用法
重置命令(git reset
)是Git
最常用的命令之一,也是最危險(xiǎn)逮矛,最容易誤用的命令鸡号。來(lái)看看git reset
命令的用法。
用法一:git reset [-q] [<commit>] [--] <paths>...
用法二:git reset [--soft | --mixed | --hard | --merge | --keep] [-q] [<commit>]
上面列出了兩個(gè)用法须鼎,其中<commit>
都是可選項(xiàng)鲸伴,可以使用引用或者提交ID
,如果省略 <commit>
則相當(dāng)于使用了HEAD
的指向作為提交ID
晋控。
上面列出的兩種用法的區(qū)別在于汞窗,第一種用法在命令中包含路徑<paths>
。為了避免路徑和引用(或者提交ID
)同名而沖突赡译,可以在<paths>
前用兩個(gè)連續(xù)的短線(減號(hào))作為分隔仲吏。
第一種用法(包含了路徑<paths>
的用法)不會(huì)重置引用,更不會(huì)改變工作區(qū)捶朵,而是用指定提交狀態(tài)(<commit>
)下的文件(<paths>
)替換掉暫存區(qū)中的文件蜘矢。例如命令git reset HEAD <paths>
相當(dāng)于取消之前執(zhí)行的git add <paths>
命令時(shí)改變的暫存區(qū)狂男。
第二種用法(不使用路徑<paths>的用法)則會(huì)重置引用综看。根據(jù)不同的選項(xiàng),可以對(duì)暫存區(qū)或者工作區(qū)進(jìn)行重置岖食。參照下面的版本庫(kù)模型圖红碑,來(lái)看一看不同的參數(shù)對(duì)第二種重置語(yǔ)法的影響。
reset注意
當(dāng)你傳入HEAD
以外的其他提交的時(shí)候要格小心,因?yàn)?reset 操作會(huì)重寫當(dāng)前分支的歷史析珊。正如 rebase 黃金法則所說(shuō)的羡鸥,在公共分支上這樣做可能會(huì)引起嚴(yán)重的后果。
當(dāng)執(zhí)行 “git reset HEAD” 命令時(shí)忠寻,暫存區(qū)的目錄樹(shù)會(huì)被重寫惧浴,被 master 分支指向的目錄樹(shù)所替換,但是工作區(qū)不受影響奕剃。
reset Parameters
- soft
reset 做的第一件事是移動(dòng) HEAD 的指向衷旅。
--soft參數(shù)告訴Git重置HEAD到另外一個(gè)commit,但也到此為止纵朋。如果你指定--soft參數(shù)柿顶,Git將停止在那里而什么也不會(huì)根本變化。這意味著index,working copy都不會(huì)做任何變化操软,所有的在original HEAD和你重置到的那個(gè)commit之間的所有變更集都放在stage(index)區(qū)域中嘁锯。
- mixed(default默認(rèn)選項(xiàng))
接下來(lái),reset 會(huì)用 HEAD 指向的當(dāng)前快照的內(nèi)容來(lái)更新索引聂薪。
--mixed是reset的默認(rèn)參數(shù)家乘,也就是當(dāng)你不指定任何參數(shù)時(shí)的參數(shù)。它將重置HEAD到另外一個(gè)commit,并且重置index以便和HEAD相匹配胆建,但是也到此為止烤低。working copy不會(huì)被更改。所有該branch上從original HEAD(commit)到你重置到的那個(gè)commit之間的所有變更將作為local modifications保存在working area中笆载,(被標(biāo)示為local modification or untracked via git status)扑馁,但是并未staged的狀態(tài),你可以重新檢視然后再做修改和commit
- hard
reset 要做的的第三件事情就是讓工作目錄看起來(lái)像索引凉驻。如果使用 --hard 選項(xiàng)腻要,它將會(huì)繼續(xù)這一步。
Git reflog
--hard參數(shù)將會(huì)blow out everything.它將重置HEAD返回到另外一個(gè)commit(取決于~12的參數(shù))涝登,重置index以便反映HEAD的變化雄家,并且重置working copy也使得其完全匹配起來(lái)。這是一個(gè)比較危險(xiǎn)的動(dòng)作胀滚,具有破壞性趟济,數(shù)據(jù)因此可能會(huì)丟失!如果真是發(fā)生了數(shù)據(jù)丟失又希望找回來(lái)咽笼,那么只有使用:git reflog命令了顷编。makes everything match the commit you have reset to.你的所有本地修改將丟失。如果我們希望徹底丟掉本地修改但是又不希望更改branch所指向的commit剑刑,則執(zhí)行git reset --hard = git reset --hard HEAD
. i.e. don't change the branch but get rid of all local changes.另外一個(gè)場(chǎng)景是簡(jiǎn)單地移動(dòng)branch從一個(gè)到另一個(gè)commit而保持index/work區(qū)域同步媳纬。這將確實(shí)令你丟失你的工作双肤,因?yàn)樗鼘⑿薷哪愕膚ork tree!
working index HEAD target working index HEAD
----------------------------------------------------
A B C D --soft A B D
--mixed A D D
--hard D D D
--merge (disallowed)
working index HEAD target working index HEAD
----------------------------------------------------
A B C C --soft A B C
--mixed A C C
--hard C C C
--merge (disallowed)
限定reset重置范圍
前面講述了 reset 基本形式的行為钮惠,不過(guò)你還可以給它提供一個(gè)作用路徑茅糜。若指定了一個(gè)路徑,reset 將會(huì)跳 過(guò)第 1 步素挽,并且將它的作用范圍限定為指定的文件或文件集合蔑赘。這樣做自然有它的道理,因?yàn)?HEAD 只是一個(gè)指 針预明,你無(wú)法讓它同時(shí)指向兩個(gè)提交中各自的一部分米死。不過(guò)索引和工作目錄 可以部分更新,所以重置會(huì)繼續(xù)進(jìn)行 第2贮庞、3步峦筒。
現(xiàn)在,假如我們運(yùn)行git reset file.txt
(這其實(shí)是git reset --mixed HEAD file.txt
的簡(jiǎn)寫形 式窗慎,因?yàn)槟慵葲](méi)有指定一個(gè)提交的 SHA-1 或分支物喷,也沒(méi)有指定 --soft 或 --hard),它會(huì):
- 移動(dòng) HEAD 分支的指向 (已跳過(guò))
- 讓索引看起來(lái)像 HEAD (到此處停止)
所以它本質(zhì)上只是將 file.txt 從 HEAD 復(fù)制到索引中遮斥。
更進(jìn)一步峦失,我們可以不讓 Git 從 HEAD
拉取數(shù)據(jù),而是通過(guò)具體指定一個(gè)提交來(lái)拉取該文件的對(duì)應(yīng)版本术吗。我們只需運(yùn)行類似 于git reset eb43bf file.txt
的命令即可尉辑。
壓縮提交
我們來(lái)看看如何利用這種新的功能來(lái)做一些有趣的事情 - 壓縮提交。
假設(shè)你的一系列提交信息中有 “oops.”较屿、“WIP” 和 “forgot this file”隧魄,聰明的你就能使用 reset 來(lái)輕松快 速地將它們壓縮成單個(gè)提交,也顯出你的聰明隘蝎。(壓縮提交 展示了另一種方式购啄,不過(guò)在本例中用 reset 更簡(jiǎn) 單。)
假設(shè)你有一個(gè)項(xiàng)目嘱么,第一次提交中有一個(gè)文件狮含,第二次提交增加了一個(gè)新的文件并修改了第一個(gè)文件,第三次提
交再次修改了第一個(gè)文件曼振。由于第二次提交是一個(gè)未完成的工作几迄,因此你想要壓縮它。
那么可以運(yùn)行git reset --soft HEAD~2
來(lái)將HEAD分支移動(dòng)到一個(gè)舊一點(diǎn)的提交上(即你想要保留的第 一個(gè)提交):
注意冰评,這時(shí)候因?yàn)槭窃谏洗翁峤坏暮笥承玻?code>Index暫存區(qū)和Working Directory
是保持一致的坤溃,所以可以直接提交两入。
然后只需再次運(yùn)行git commit
:
現(xiàn)在你可以查看可到達(dá)的歷史诽俯,即將會(huì)推送的歷史喇喉,現(xiàn)在看起來(lái)有個(gè) v1 版 file-a.txt
的提交务荆,接著第二個(gè)提 交將 file-a.txt
修改成了 v3 版并增加了 file-b.txt
妆距。包含 v2 版本的文件已經(jīng)不在歷史中了。
reset和checkout
checkout這個(gè)命令做的不過(guò)是將HEAD移到一個(gè)新的分支函匕,然后更新工作目錄娱据。因?yàn)檫@可能會(huì)覆蓋本地的修改,Git 強(qiáng)制你提交或者緩存工作目錄中的所有更改盅惜,不然在 checkout 的時(shí)候這些更改都會(huì)丟失中剩。和 git reset 不一樣的是,git checkout 沒(méi)有移動(dòng)這些分支抒寂。這對(duì)于快速查看項(xiàng)目舊版本來(lái)說(shuō)非常有用结啼。
checkout對(duì)工作目錄是安全的,它會(huì)通過(guò)檢查來(lái)確保不會(huì)將已更改的文件吹走屈芜。
- checkout不會(huì)去修改你在Working Directory里修改過(guò)的文件
- checkout則把HEAD移動(dòng)到另一個(gè)分支
- reset會(huì)不做檢查把working directory里的所有內(nèi)容都更新掉
- reset把branch移動(dòng)到HEAD指向的地方
reset和revert
- git revert可以用在公共分支上郊愧,git reset應(yīng)該用在私有分支上.
git revert
用于記錄一些新的提交以反轉(zhuǎn)一些早期提交的影響(通常只是一個(gè)錯(cuò)誤的提交)。如果你想扔掉工作目錄中所有未提交的更改井佑,你應(yīng)該看到git-reset
属铁,特別是--hard選項(xiàng)。如果你想提取特定文件躬翁,就像在另一個(gè)提交中那樣焦蘑,你應(yīng)該看到git-checkout
,特別是git checkout <commit> -- <filename>
語(yǔ)法盒发。請(qǐng)謹(jǐn)慎使用這些替代方法例嘱,因?yàn)樗鼈兌紩?huì)丟棄工作目錄中的未提交更改。
reset是用來(lái)修改提交歷史的宁舰,想象這種情況蝶防,如果你在2天前提交了一個(gè)東西,突然發(fā)現(xiàn)這次提交是有問(wèn)題的明吩。
這個(gè)時(shí)候你有兩個(gè)選擇间学,要么使用git revert(推薦),要么使用git reset印荔。
上圖可以看到git reset
是會(huì)修改版本歷史的低葫,他會(huì)丟棄掉一些版本歷史。
而git revert
是根據(jù)那個(gè)commit逆向生成一個(gè)新的commit仍律,版本歷史是不會(huì)被破壞的嘿悬。
相比git reset
,它不會(huì)改變現(xiàn)在的提交歷史水泉。因此善涨,git revert可以用在公共分支上窒盐,git reset應(yīng)該用在私有分支上。
你也可以把git revert
當(dāng)作撤銷已經(jīng)提交的更改钢拧,而git reset HEAD
用來(lái)撤銷沒(méi)有提交的更改蟹漓。
就像git checkout
一樣,git revert
也有可能會(huì)重寫文件源内。所以葡粒,Git會(huì)在你執(zhí)行revert之前要求你提交或者緩存你工作目錄中的更改。
如果你的更改還沒(méi)有共享給別人膜钓,git reset
是撤銷這些更改的簡(jiǎn)單方法嗽交。當(dāng)你開(kāi)發(fā)一個(gè)功能的時(shí)候發(fā)現(xiàn)「糟糕,我做了什么颂斜?我應(yīng)該重新來(lái)過(guò)夫壁!」時(shí),reset 就像是 go-to 命令一樣沃疮。
除了在當(dāng)前分支上操作掌唾,你還可以通過(guò)傳入這些標(biāo)記來(lái)修改你的緩存區(qū)或工作目錄:
- --soft – 緩存區(qū)和工作目錄都不會(huì)被改變
- --mixed – 默認(rèn)選項(xiàng)。緩存區(qū)和你指定的提交同步忿磅,但工作目錄不受影響
- --hard – 緩存區(qū)和工作目錄都同步到你指定的提交
把這些標(biāo)記想成定義 git reset
操作的作用域就容易理解多了
相比 git reset糯彬,它不會(huì)改變現(xiàn)在的提交歷史。因此葱她,git revert 可以用在公共分支上撩扒,git reset 應(yīng)該用在私有分支上。
總結(jié)
簡(jiǎn)單總結(jié)一下吨些,其實(shí)就是--soft 搓谆、--mixed以及--hard是指代三個(gè)不同的恢復(fù)等級(jí)。使用--soft就僅僅將Head頭指針恢復(fù)豪墅,已經(jīng)add的緩存以及工作空間的所有東西都不變泉手。如果使用--mixed,就將Head頭指針恢復(fù)掉偶器,已經(jīng)add的緩存也會(huì)丟失掉斩萌,工作空間的代碼什么的是不變的。如果使用--hard屏轰,那么一切就全都恢復(fù)了颊郎,Head頭指針變,add的緩存消失霎苗,本地工作區(qū)的代碼的也恢復(fù)到指定之前版本的狀態(tài)姆吭。
命令 | 作用域 | 常用情景 |
---|---|---|
git reset | 提交層面 | 在私有分支上舍棄一些沒(méi)有提交的更改 |
git reset | 文件層面 | 將文件從緩存區(qū)中移除 |
git checkout | 提交層面 | 切換分支或查看舊版本 |
git checkout | 文件層面 | 舍棄工作目錄中的更改 |
git revert | 提交層面 | 在公共分支上回滾更改 |
git revert | 文件層面 | (然而并沒(méi)有) |
head index work dir wd safe
Commit Level
reset --soft [commit] REF NO NO YES
reset [commit] REF YES NO YES
reset --hard [commit] REF YES YES NO
checkout [commit] HEAD YES YES YES
File Level
reset (commit) [file] NO YES NO YES
checkout (commit) [file] NO YES YES NO
參考
- git reset soft,hard,mixed之區(qū)別深解
- Git - git-reset Documentation
- 代碼回滾:git reset、git checkout和git revert區(qū)別和聯(lián)系 - houpy - 博客園
- Pro Git(中文版)