摘要:通過(guò)搞清楚如何撤銷這個(gè)提交引入的更改,然后在最后加上一個(gè)撤銷了更改的新提交,而不是從項(xiàng)目歷史中移除這個(gè)提交。下面我們就用一個(gè)麻煩事回滾一個(gè)錯(cuò)誤的合并,來(lái)講解這個(gè)操作。回滾錯(cuò)誤的合并這個(gè)之后還繼續(xù)在開發(fā),另一波人也在從別的分支往合并代碼。
原文發(fā)表在知乎專欄 前端雜貨鋪, 歡迎關(guān)注我的專欄,轉(zhuǎn)載請(qǐng)注明出處
今天不說(shuō)前端,來(lái)聊聊git吧。
發(fā)現(xiàn)現(xiàn)在的小孩,玩框架一套一套的,等到玩點(diǎn)實(shí)質(zhì)的工程化的東西就不行了。
git 這么好的工具,培訓(xùn)班怎么可以忽視他的重要性呢?
很多人對(duì)Git究竟是一個(gè)怎樣的系統(tǒng),還是一知半解。
在這里強(qiáng)烈建議大家先理解git的核心思想和工作原理,有過(guò)subversion或者perforce使用經(jīng)驗(yàn)的人更是需要摒棄之前所見所學(xué),重新接受這樣一個(gè)新思想。
我們不再這里贅述其幾本原理,我們來(lái)介紹一下其簡(jiǎn)單工作流程。
Git以一個(gè)自有的思維框架管理著三個(gè)不同的盒子Commit History、INDEX、Working Directory。
Commit History 歷史記錄,存儲(chǔ)著所有提交的版本快照,并由當(dāng)前分支引用的指針HEAD指向該分支最新一條提交。
INDEX 索引,也叫暫存區(qū)域。它是一個(gè)文件,保存著即將提交的文件列表快照。
Working Directory 工作目錄,是從git倉(cāng)庫(kù)壓縮數(shù)據(jù)當(dāng)前版本中解包出來(lái)的文件列表。所以你在本地磁盤看到的你項(xiàng)目源碼的文件列表,其實(shí)就是git開放給你的一個(gè)沙盒。在你將文件的修改天道到暫存區(qū)域并將快照記錄到歷史之前,你可以隨意更改。
理解了這三者的含義后,我們?cè)囍鴣?lái)理解一下git的工作流程。
一切的開始,混沌之間,我們要干一件大事,在terminal里面敲打了幾下鍵盤
git init
混沌初開,幻化三界:HEAD、INDEX、Working Directory。這就是世界最開始的樣子git倉(cāng)庫(kù)仿佛就是掌管三界之神。而Working Directory就是他分配給你生產(chǎn)和工作的地方,你可以在這里肆意的創(chuàng)造。而為了安全和管理的有序我們需要把我們的添加與修改的文件交給git倉(cāng)庫(kù)。Git首先會(huì)將修改的文件標(biāo)記起來(lái)放入暫存區(qū)、然后git找到暫存區(qū)域的文件內(nèi)容將其永久性的存儲(chǔ)為快照到git倉(cāng)庫(kù),此時(shí)HEAD的指針指向這個(gè)最新的快照。
如圖,總結(jié)下三個(gè)步驟
在工作目錄中修改文件。
暫存文件,將文件的快照放入暫存區(qū)域。git add
提交更新,找到暫存區(qū)域的文件,將快照永久性存儲(chǔ)到 Git 倉(cāng)庫(kù)目錄 git commit
git 的基本工作流程就是在不斷的重復(fù)這三個(gè)步驟,最終git倉(cāng)庫(kù)目錄形成了一個(gè)快照堆棧,每產(chǎn)生一次新的版本,HEAD就會(huì)指向這個(gè)版本。
這里我們創(chuàng)建了下面這些文件:
├── README.md ├── v1.js ├── v2.js └── v3.js
形成了下圖的提交歷史
3aa5dfb v3 (<- HEAD) | 5aab391 v2 | ff7b88e v1 | 95d7816 init commit
下面我們來(lái)看看怎么利用checkout、reset、revert 來(lái)操作這個(gè)倉(cāng)庫(kù)目錄
checkout 、reset 還是 revert ? checkout版本控制系統(tǒng)背后的思想就是「安全」地儲(chǔ)存項(xiàng)目的拷貝,這樣你永遠(yuǎn)不用擔(dān)心什么時(shí)候不可復(fù)原地破壞了你的代碼庫(kù)。當(dāng)你建立了項(xiàng)目歷史之后,git checkout 是一種便捷的方式,來(lái)將保存的快照「解包」到你的工作目錄上去。
git checkout 可以檢出提交、也可以檢出單個(gè)文件甚至還可以檢出分支(此處省略)。
git checkout 5aab391
檢出v2,當(dāng)前工作目錄和5aab391完全一致,你可以查看這個(gè)版本的文件編輯、運(yùn)行、測(cè)試都不會(huì)被保存到git倉(cāng)庫(kù)里面。你可以git checkout master 或者 git checkout -回到原來(lái)的工作狀態(tài)上來(lái)。
git checkout 5aab391 v1.js
以檢出v2版本對(duì)于v1.js的改動(dòng),只針對(duì)v1.js這個(gè)文件檢出到5aab391版本。所以 它會(huì)影響你當(dāng)前的工作狀態(tài),它會(huì)把當(dāng)前狀態(tài)的v1.js文件內(nèi)容覆蓋為5aab391版本。所以除非你清楚你在做什么,最好不要輕易的做這個(gè)操作。但這個(gè)操作對(duì)于舍棄我當(dāng)前的所有改動(dòng)很有用:比如當(dāng)前我在v1.js上面做了一些改動(dòng),但我又不想要這些改動(dòng)了,而我又不想一個(gè)個(gè)去還原,那么我可以git checkout HEAD v1.js 或者 git checkout -- v1.js
reset 重置和 git checkout 一樣, git reset 有很多用法。
git reset
從暫存區(qū)移除特定文件,但不改變工作目錄。它會(huì)取消這個(gè)文件的緩存,而不覆蓋任何更改。
git reset
重置暫存區(qū),匹配最近的一次提交,但工作目錄不變。它會(huì)取消所有文件的暫存,而不會(huì)覆蓋任何修改,給你了一個(gè)重設(shè)暫存快照的機(jī)會(huì)。
git reset --hard
加上--hard標(biāo)記后會(huì)告訴git要重置緩存區(qū)和工作目錄的更改,就是說(shuō):先將你的暫存區(qū)清除掉,然后將你所有未暫存的更改都清除掉,所以在使用前確定你想扔掉所有的本地工作。
git reset
將當(dāng)前分支的指針HEAD移到
git reset --hard
將當(dāng)前分支的指針HEAD移到
可以看出,git reset 通過(guò)取消緩存或者取消一系列提交的操作會(huì)摒棄一些你當(dāng)前工作目錄上的更改,這樣的操作帶有一定的危險(xiǎn)性。下面我們開始介紹一種相對(duì)穩(wěn)妥的方式 revert
revert 撤銷git revert被用來(lái)撤銷一個(gè)已經(jīng)提交的快照。但實(shí)現(xiàn)上和reset是完全不同的。通過(guò)搞清楚如何撤銷這個(gè)提交引入的更改,然后在最后加上一個(gè)撤銷了更改的 新 提交,而不是從項(xiàng)目歷史中移除這個(gè)提交。
git revert
生成一個(gè)撤消了
例如:
81f734d commit after bug | 3a395af bug | 3aa5dfb v3 (<- HEAD) | 5aab391 v2 | ff7b88e v1 | 95d7816 init commit
我們?cè)?b>3a395af 引入了一個(gè)bug,我們明確是由于3a395af造成的bug的時(shí)候,以其我們通過(guò)新的提交來(lái)fix這個(gè)bug,不如git revert , 讓他來(lái)幫你剔除這個(gè)bug。
git revert 3a395af
得到結(jié)果
cfb71fc Revert "bug" | 81f734d commit after bug | 3a395af bug | 3aa5dfb v3 (<- HEAD) | 5aab391 v2 | ff7b88e v1 | 95d7816 init commit
這個(gè)時(shí)候bug的改動(dòng)被撤銷了,產(chǎn)生了一個(gè)新的commit,但是commit after bug沒(méi)有被清初。
所以相較于reset ,revert不會(huì)改變項(xiàng)目歷史,對(duì)那些已經(jīng)發(fā)布到共享倉(cāng)庫(kù)的提交來(lái)說(shuō)這是一個(gè)安全的操作。其次git revert 可以將提交歷史中的任何一個(gè)提交撤銷、而reset會(huì)把歷史上某個(gè)提交及之后所有的提交都移除掉,這太野蠻了。
另外revert的設(shè)計(jì),還有一個(gè)考量,那就是撤銷一個(gè)公共倉(cāng)庫(kù)的提交。至于為什么不能用reset,你們可以自己思考一下。
下面我們就用一個(gè)麻煩事(回滾一個(gè)錯(cuò)誤的合并),來(lái)講解這個(gè)操作。
相對(duì)于常規(guī)的commit,當(dāng)使用git merge
當(dāng)我們git show
commit 6dd0e2b9398ca8cd12bfd1faa1531d86dc41021a Merge: d24d3b4 11a7112 Author: 前端雜貨鋪 ...............
Merge: d24d3b4 11a7112 這行表明了兩個(gè)分支在合并時(shí),所處的parent的版本線索。
比如在上述項(xiàng)目中我們開出了一個(gè)dev分支并做了一些操作,現(xiàn)在分支的樣子變成了這樣:
init -> v1 -> v2 -> v3 (master) d1 -> d2 (dev)
當(dāng)我們?cè)赿ev開發(fā)的差不多了
#git:(dev) git checkout master #git:(master) git merge dev
這個(gè)時(shí)候形成了一個(gè)Merge Commit faulty merge
init -> v1 -> v2 -> v3 -- faulty merge (master) / d1 --> d2 (dev)
此時(shí)faulty merge有兩個(gè)parent 分別是v3 和 d2。
回滾錯(cuò)誤的合并這個(gè)merge之后還繼續(xù)在dev開發(fā),另一波人也在從別的分支往master合并代碼。變成這樣:
init -> v1 -> v2 -> v3 -- faulty merge -> v4 -> vc3 (master) / / d1 --> d2 --> d3 --> d4 (dev)/ / c1 --> c2 -------------------c3 (other)
這個(gè)時(shí)候你發(fā)現(xiàn), 媽也上次那個(gè)merge 好像給共享分支master引入了一個(gè)bug。這個(gè)bug導(dǎo)致團(tuán)隊(duì)其他同學(xué)跑不通測(cè)試,或者這是一個(gè)線上的bug,如果不及時(shí)修復(fù)老板要罵街了。
這個(gè)時(shí)候第一想到的肯定是回滾代碼,但怎么回滾呢。用reset?不現(xiàn)實(shí),因?yàn)樘髅ゲ徽f(shuō),還會(huì)把別人的代碼也干掉,所以只能用revert。而revert它最初被設(shè)計(jì)出來(lái)就是干這個(gè)活的。
怎么操作呢?首先想到的是上面所說(shuō)的 git revert
git revert faulty merge error: Commit faulty merge is a merge but no -m option was given. fatal: revert failed
這是因?yàn)樵噲D撤銷兩個(gè)分支的合并的時(shí)候Git不知道要保留哪一個(gè)分支上的修改。所以我們需要告訴git我們保留那個(gè)分支m 或者mainline.
git revert -m 1 faulty merge
-m后面帶的參數(shù)值 可以是1或者2,對(duì)應(yīng)著parent的順序.上面列子:1代表v3,2代表d2
所以該操作會(huì)保留master分支的修改,而撤銷dev分支合并過(guò)來(lái)的修改。
提交歷史變?yōu)?/p>
init -> v1 -> v2 -> v3 -- faulty merge -> v4 -> vc3 -> rev3 (master) / d1 --> d2 --> d3 --> d4 (dev)
此處rev3是一個(gè)常規(guī)commit,其內(nèi)容包含了之前在faulty merge撤銷掉的dev合并過(guò)來(lái)的commit的【反操作】的合集。
到這個(gè)時(shí)候還沒(méi)完,我們要記住,因?yàn)槲覀儝仐夁^(guò)之前dev合并過(guò)來(lái)的commit,下次dev再往master合并,之前拋棄過(guò)的其實(shí)是不包含在里面的。那怎么辦呢?
恢復(fù)之前的回滾很簡(jiǎn)單我們把之前master那個(gè)帶有【反操作】的commit給撤銷掉不就好了?
git checkout master git revert rev3 git merge dev
此時(shí)提交歷史變成了
init -> v1 -> v2 -> v3 -- faulty merge -> v4 -> vc3 -> rev3 -> rev3` -> final merge (master) / / d1 --> d2 --> d3 --> d4 --------------------------------(dev)總結(jié)
以上就是我想要講的關(guān)于git回滾代碼的一些操作,有不對(duì)的地方還望指正。另Git 是一門藝術(shù),是一種非常精妙的設(shè)計(jì),當(dāng)你使用上手后,你會(huì)發(fā)現(xiàn)越來(lái)越多好玩的東西,并為設(shè)計(jì)git的人默默點(diǎn)個(gè)贊。也希望在前端領(lǐng)域不論是初學(xué)還是深鑿者,在追逐流行框架的時(shí)候,都不要忘了學(xué)習(xí)這些基礎(chǔ)的工具。
參考《pro git》
重置揭秘
git-recipes
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/96323.html
摘要:的使用和操作在計(jì)算機(jī)科學(xué)中,俗稱殼,用來(lái)區(qū)別于核,是指提供使用者使用界面的軟件命令解析器。它接收用戶命令,然后調(diào)用相應(yīng)的應(yīng)用程序。命令行通過(guò)鍵盤輸入特定命令的方式,調(diào)用相應(yīng)的應(yīng)用程序,如系統(tǒng)的,系統(tǒng)的等。 git的使用和操作 shell 在計(jì)算機(jī)科學(xué)中,Shell俗稱殼,用來(lái)區(qū)別于Kernel(核),是指提供使用者使用界面的軟件(命令解析器)。它類似于DOS下的command和后來(lái)的...
摘要:掌握了命令行,使用圖形化工具如探囊取物。管理的文件狀態(tài)已修改已暫存已提交。由于我們使用了命令,但并未創(chuàng)建新的分支,所以創(chuàng)建了一個(gè)匿名分支。省略遠(yuǎn)程分支名表示將本地分支推送到與之存在追蹤關(guān)系的遠(yuǎn)程分支通常同名。概述此篇博文意在讓新手快速上手 Git,滿足工作中的基本需求,而非梳理細(xì)節(jié)。后續(xù)會(huì)再開一個(gè)系列,來(lái)探討 Git 細(xì)節(jié)問(wèn)題。一、Git 的安裝這部分網(wǎng)站上資料非常多,根據(jù)自己的系統(tǒng)版本查找...
閱讀 1236·2021-11-23 09:51
閱讀 683·2021-11-19 09:40
閱讀 1347·2021-10-11 10:58
閱讀 2356·2021-09-30 09:47
閱讀 3729·2021-09-22 15:55
閱讀 2165·2021-09-03 10:49
閱讀 1259·2021-09-03 10:33
閱讀 703·2019-08-29 17:12