一份值得收藏的 Git 異常處理清單
前言
Git 作為一種分布式版本控制系統(tǒng)已經(jīng)成為現(xiàn)在開發(fā)的寵兒,不僅應(yīng)用在前端、后端、客戶端等開發(fā)場景中,也成為各行業(yè)互聯(lián)網(wǎng)企業(yè)分工協(xié)作的必備技能之一。
大家在使用過程中總會(huì)碰到這樣那樣的問題,本文主要針對以下經(jīng)常發(fā)生的幾種異常情況提供一些解決方案:
-
本地工作區(qū)文件修復(fù)
-
遠(yuǎn)程分支刪除后,刪除本地分支與其關(guān)聯(lián)
-
修改提交時(shí)的備注內(nèi)容
-
修改分支名,實(shí)現(xiàn)無縫銜接
-
撤回文件提交
-
撤銷本地分支合并
-
恢復(fù)誤刪的本地分支
-
不確定哪個(gè)分支有自己提交的 commit
(一)本地工作區(qū)文件修復(fù)
大家都知道,一個(gè)文件夾中的文件如果被刪掉了,那只有在垃圾箱里面找了。如果垃圾箱里面的也被刪掉了,以筆者的常識(shí)在不借助工具的情況下怕是就找不到了,emmmm。。。
不過,關(guān)聯(lián)了 Git 的文件和文件夾就不一樣了,有了本地倉庫和遠(yuǎn)程倉庫的雙重保護(hù),找到一個(gè)被刪除的文件也不過就分分鐘,一個(gè)命令行的事情吧。
語法: git checkout <filename/dirname>
命令: git checkout 1.js
這一命令主要用于本地工作區(qū)文件的撤回,下圖是一個(gè)工作區(qū)文件被刪除后的完美恢復(fù)過程。
(二)遠(yuǎn)程分支刪除后,刪除本地分支及關(guān)聯(lián)
為方便分支提交,一般情況下會(huì)用本地命令 git branch --set-upstream-to=origin/master master
建立本地分支與遠(yuǎn)程分支的關(guān)聯(lián),從 master 拉出的分支可以自動(dòng)建立與遠(yuǎn)程已有分支的關(guān)聯(lián),這樣可以很方便的使用 git pull
和 git push
拉取遠(yuǎn)程分支的代碼和將本地分支提交到遠(yuǎn)程。
Git 遠(yuǎn)程分支刪除之后,本地分支就無法成功推送到遠(yuǎn)程,想要重新建立與遠(yuǎn)程倉庫的關(guān)聯(lián),就需要先刪除其原本的與已刪除的遠(yuǎn)程分支的關(guān)聯(lián)。
如下圖所示,需要?jiǎng)h除的遠(yuǎn)程分支為 feature/test,使用 git push origin --delete feature/test
刪除掉對應(yīng)的遠(yuǎn)程分支之后,刪除本地分支關(guān)聯(lián)。
語法: git branch --unset-upstream <branchname>
命令: git branch --unset-upstream feature/test
刪除掉關(guān)聯(lián)關(guān)系之后,用 git branch -vv
命令可查看到本地分支與遠(yuǎn)程分支的關(guān)聯(lián)關(guān)系如下圖所示,可觀察到 feature/test 分支已經(jīng)沒有關(guān)聯(lián)的遠(yuǎn)程分支了。
(三)修改提交時(shí)的備注內(nèi)容
平時(shí)提交代碼很多時(shí)候因?yàn)檐娗榫o急,會(huì)在剛提交的時(shí)候填寫了自己不太滿意的備注,但筆者本人有點(diǎn)強(qiáng)迫癥,一定要把它改成想要的樣子咋辦。。。。,不要慌,還是有解決辦法滴!
想要修改最近一次提交的“修改xxx功能”的備注:
語法: git commit --amend
命令: git commit --amend
使用 git log --pretty=oneline
查看內(nèi)容,發(fā)現(xiàn)已經(jīng)成功修改啦。 需要注意的是此項(xiàng)命令會(huì)修改提交時(shí)的commit-id,即會(huì)覆蓋原本的提交,需要謹(jǐn)慎操作 。
(四)修改分支名,實(shí)現(xiàn)無縫銜接
開發(fā)中的大佬都是擁有極快手速的人,建了個(gè)分支一不小心打錯(cuò)了某個(gè)字母或者兩個(gè)字母打反了,可能就與本意存在較大誤差了,Git 提供一種已經(jīng)拉取了分支,在上面開發(fā)了不少的內(nèi)容,但后來發(fā)現(xiàn)原本拉的分支名字就有問題的修復(fù)方法。
需要修改的分支名字為 stor-13711, 如下圖所示:
語法: git branch -m <oldbranch> <newbranch>
命令: git branch -m feature/stor-13711 feature/story-13711
執(zhí)行完之后發(fā)現(xiàn)文件的工作區(qū)已修改內(nèi)容一點(diǎn)都沒有變化,真正的實(shí)現(xiàn)了無痛過渡,皆大歡喜!
(五)撤回文件提交
日常工作中,可能由于需求變更、對于文件的處理不同、提交了錯(cuò)誤的本不應(yīng)提交的內(nèi)容等原因?qū)е绿峤涣瞬贿m合提交的內(nèi)容,需要進(jìn)行撤銷操作。
需要撤回的提交中包含的內(nèi)容如下圖所示:
如下分析了各種原因撤銷的處理方式,主要包括:
-
需求變更等原因?qū)е碌某蜂N
-
代碼重構(gòu)等原因?qū)е碌某蜂N
-
個(gè)人疏失等原因?qū)е碌某蜂N
需求變更等原因?qū)е碌某蜂N
該需求仍然需要做,但進(jìn)行了某些改動(dòng),按照之前的提交已經(jīng)不合適,需要重新改動(dòng)之后一次性提交,以增加每一次提交的連續(xù)性和單個(gè)提交的完整性。
語法: git reset --soft [<commit-id>/HEAD~n>]
命令: git reset --soft HEAD~1
命令執(zhí)行完成后,查看文件變更記錄,可發(fā)現(xiàn)如下圖所示:
文件變更記錄與未提交之前的文件變更記錄是一致的,可判斷只是撤銷了 commit 的操作。
文件撤回到工作區(qū)后,可能發(fā)生部分之前上傳的文件不再需要,但是本地又不想刪除或是不能刪除(譬如 node_modules 文件)。
Git 提供了一個(gè)完整的解決方案,在本地項(xiàng)目根目錄下新建 .gitignore
文件,使用文件的特定語法(如下圖所示)即可以保證文件不被上傳到本地或遠(yuǎn)程倉庫。其實(shí)現(xiàn)原理主要是在本地保存的文件快照中刪除 .gitignore
文件中定義的文件,以實(shí)現(xiàn)不被上傳的效果。
- # 忽略 .a 文件
- *.a
- # 但否定忽略 lib.a, 盡管已經(jīng)在前面忽略了 .a 文件
- !lib.a
- # 僅在當(dāng)前目錄下忽略 TODO 文件, 但不包括子目錄下的 subdir/TODO
- /TODO
- # 忽略 build/ 文件夾下的所有文件
- build/
- # 忽略 doc/notes.txt, 不包括 doc/server/arch.txt
- doc/*.txt
- # 忽略所有的 .pdf 文件 在 doc/ directory 下的
- doc/**/*.pdf
代碼重構(gòu)等原因?qū)е碌某蜂N
本地需要進(jìn)行某些優(yōu)化,對于單次提交上傳的不同文件有不同的處理需求,為使提交記錄更易于閱讀和便于區(qū)分所進(jìn)行的撤銷。
語法: git reset --mixed [<commit-id>/HEAD~n>]
命令: git reset --mixed HEAD~1
命令執(zhí)行完成后,查看文件變更記錄,可發(fā)現(xiàn)如下圖所示:
已變更的文件都未添加到暫存區(qū),撤銷了 commit 和 add 的操作。
個(gè)人疏失等原因?qū)е碌某蜂N
本地需要將完全錯(cuò)誤的本不應(yīng)提交的內(nèi)容提交到了倉庫,需要進(jìn)行撤銷。
語法: git reset --hard [<commit-id>/HEAD~n>]
命令: git reset --hard HEAD~1
命令執(zhí)行完成后,查看文件變更記錄,可發(fā)現(xiàn)如下圖所示:
已追蹤文件的變更內(nèi)容都消失了,撤銷了 commit 和 add 的操作,同時(shí)撤銷了本地已追蹤內(nèi)容的修改;未追蹤的內(nèi)容不會(huì)被改變。從上面的效果可以看到,文件的修改都會(huì)被撤銷掉, --hard
的模式下需要謹(jǐn)慎操作 。
(六)撤銷本地分支合并
實(shí)際操作中,總會(huì)有很多的干擾,導(dǎo)致我們合并了并不該合并的分支到目標(biāo)分支上。解決這種問題的方式有兩種, git reset
和 git revert
。 reset 的語法和命令之前已經(jīng)介紹過,不做贅述, revert 的語法和命令和 reset 一致。但是產(chǎn)生的實(shí)際效果會(huì)有不同。
可以先來看下 revert 操作的實(shí)際效果,合并分支之后的效果如下圖所示:
撤銷合并:
語法: git revert <commit-id>
命令: git revert 700920
下圖為執(zhí)行命令后的效果:
經(jīng)過前后對比可知,revert 執(zhí)行之后會(huì)在原本的記錄中新增一條提交記錄。
reset 如上 “本地文件撤銷” 例子所述,會(huì)刪除掉原本已有的提交記錄,在合并分支中,會(huì)刪除原本合并分支的記錄。revert 則有不同,會(huì)保留原本合并分支的記錄,并在其上新增一條提交記錄,便于之后有需要仍然能夠回溯到 revert 之前的狀態(tài)。
從需要提交到遠(yuǎn)程分支的角度來講,reset 能夠毀尸滅跡,不讓別人發(fā)現(xiàn)我們曾經(jīng)錯(cuò)誤的合并過分支;revert 則會(huì)將合并分支和撤回記錄一并顯示在遠(yuǎn)程提交記錄上。
從代碼撤銷之后仍然需要合并的角度來講,revert 能夠?qū)崿F(xiàn)多分支合并之后,仍然能夠只撤銷本合并分支的內(nèi)容;reset 則需要使用 git reflog
命令 reset 到第一個(gè)沒有出錯(cuò)的內(nèi)容的 commit-id
,然后再逐個(gè)進(jìn)行合并(前提是這些分支的合并是有順序要求的)。
換言之,需要合并多個(gè)分支,如果合并的某個(gè)分支在操作過程中修改了內(nèi)容,revert 能夠?qū)崿F(xiàn)仍然合并修改之前的所有分支,reset 則只能合并某一個(gè)修改之前的分支。
(七)恢復(fù)誤刪的本地分支
本地分支被拉出之后,由于某種疏忽被錯(cuò)誤的刪除了,而且本地的分支并沒有被同步到遠(yuǎn)程分支上,此時(shí)想要恢復(fù)本地分支。
誤刪的分支為 feature/delete,使用 git reflog
命令可查看到該倉庫下的所有歷史操作,如下圖所示:
語法: git checkout -b <branch-name> <commit-id>
命令: git checkout -b feature/delete HEAD@{2}
命令執(zhí)行完成后,分支恢復(fù)到 HEAD@{2} 的快照,即從 master 分支拉取 feature/delete 分支的內(nèi)容,仍然缺少“新增xxx文件”的提交,直接將文件內(nèi)容恢復(fù)到最新的提交內(nèi)容,使用命令 git reset --hard HEAD@{1}
即可實(shí)現(xiàn)硬性覆蓋本地工作區(qū)內(nèi)容的目的。 git reflog
命令獲取到的內(nèi)容為本地倉庫所有發(fā)生過的變更,可謂恢復(fù)利器,既可向前追溯,亦可向后調(diào)整,滿滿的時(shí)光追溯器的趕腳啊。。。
(八)不確定哪個(gè)分支有自己提交的 commit
工作中會(huì)經(jīng)常碰到一種場景,在一個(gè)分支中修改了某個(gè)東西,進(jìn)行了提交,因?yàn)榘姹鞠群蟮葐栴},先后合并到了各個(gè)分支上,但后來發(fā)現(xiàn)提交的這個(gè)修改的東西是有問題的,需要全項(xiàng)目排查,到底哪個(gè)分支有用這個(gè)提交,然后將其修復(fù)掉。
需要先確定有問題的提交的 commit-id :
然后查看本地所有的分支:
可以看到本地有 4 個(gè)分支,本地的分支數(shù)量非人為控制的,在使用狀態(tài)的分支直接刪掉也不合適,分支數(shù)量達(dá)到一定程度,一個(gè)一個(gè)分支查找也不現(xiàn)實(shí)。Git 提供了一種能夠直接通過 commit-id 查找出包含該內(nèi)容分支的命令。
語法: git branch --contains <commit-id>
命令: git branch --contains 700920
命令執(zhí)行后可以看到包含該問題提交的分支如下圖所示,就可以很方便的在對應(yīng)分支上修復(fù)內(nèi)容啦。
總結(jié)
本文介紹的是實(shí)際工作場景中可能出現(xiàn)的幾種異常情況及解決方式,希望能夠?qū)Υ蠹矣兴鶐椭蛔阒幘凑堉刚?。?shí)際上現(xiàn)在已經(jīng)有很多 Git 操作對應(yīng)的工具可以使用,需要明白的是工具中的每個(gè)操作等同于 Git 命令行的哪個(gè)命令,會(huì)有什么樣的結(jié)果,以避免一些不必要發(fā)生的錯(cuò)誤。