戰(zhàn)指南)
1. 從“記不住命令”到“肌肉記憶”聊聊 Git 常用操作的核心思路很多開發(fā)者接觸 Git 都是邊用邊查今天commit忘了加-m明天push被拒絕不知道怎么辦。我當(dāng)年也是這樣直到把常用命令背后的邏輯理順之后才真正告別了“復(fù)制粘貼”——遇到問題腦子里面自動(dòng)會(huì)浮現(xiàn)出對(duì)應(yīng)的命令組合效率提升非常明顯。這篇文章想聊的就是 Git 日常開發(fā)中最高頻的那些操作提交、分支、遠(yuǎn)程同步、撤銷、查找歷史還有團(tuán)隊(duì)協(xié)作時(shí)最容易踩坑的場(chǎng)景。不會(huì)把 Git 的全部命令鋪開講只講你在真實(shí)項(xiàng)目里幾乎每周都會(huì)用到的那些配合我實(shí)戰(zhàn)中積累的一些經(jīng)驗(yàn)和教訓(xùn)。不管你是剛接觸 Git 的初學(xué)者還是已經(jīng)寫了幾個(gè)月項(xiàng)目但總感覺命令用得不夠順的開發(fā)者這篇內(nèi)容都能給你一些可落地的參考。當(dāng)然Git 命令光看不練沒有意義。我的建議是你把這篇文章當(dāng)成一份“操作地圖”在看的過程中隨手開一個(gè)測(cè)試倉庫把每條命令都敲一遍尤其是我標(biāo)了“容易踩坑”的地方親自跑一次比看十遍都管用。2. 安裝配置與初始化為什么這些“一次性操作”決定了后續(xù)體驗(yàn)2.1 全局配置兩條命令解決“我是誰”的問題先說最基礎(chǔ)但又是很多人忽略掉的部分。剛裝好 Git 或者換了新電腦之后需要做的第一件事就是配置用戶信息。git config --global user.name 你的名字 git config --global user.email 你的郵箱這兩條命令設(shè)置的user.name和user.email會(huì)寫進(jìn)每一次提交記錄里。也就是說將來團(tuán)隊(duì)里面所有人查看git log時(shí)看到的就是這里設(shè)置的署名。很多人圖省事隨便填或者干脆用一個(gè)像testtest.com這樣的假郵箱后面對(duì)接代碼托管平臺(tái)的時(shí)候就會(huì)很尷尬——你在平臺(tái)上的賬號(hào)和個(gè)人提交記錄對(duì)不上號(hào)貢獻(xiàn)圖也統(tǒng)計(jì)不到。另外提一句如果你想針對(duì)某個(gè)特定倉庫使用不同的用戶名和郵箱可以去掉--global參數(shù)在對(duì)應(yīng)倉庫目錄內(nèi)單獨(dú)執(zhí)行。我自己在工作電腦上就是這樣處理的個(gè)人項(xiàng)目用一個(gè)身份公司項(xiàng)目用另一個(gè)身份避免出現(xiàn)提交歷史里混著兩種身份的情況。2.2 初始化倉庫的三種場(chǎng)景git init是創(chuàng)建一個(gè)新倉庫的命令但實(shí)際操作中“我需要一個(gè)倉庫”的場(chǎng)景其實(shí)分三種本地新建項(xiàng)目還沒有任何版本管理在項(xiàng)目根目錄執(zhí)行g(shù)it init從遠(yuǎn)程克隆一個(gè)已有項(xiàng)目git clone 倉庫地址這個(gè)命令不僅會(huì)下載代碼還會(huì)自動(dòng)把遠(yuǎn)程倉庫地址配置好并且默認(rèn)創(chuàng)建一個(gè)本地分支跟蹤遠(yuǎn)端分支已有本地代碼想推送到遠(yuǎn)程先git init再添加遠(yuǎn)程地址git remote add origin 地址然后提交推送第三種場(chǎng)景中最容易出問題的是在已經(jīng)執(zhí)行過git init的目錄里又執(zhí)行一次。不會(huì)報(bào)錯(cuò)只是重復(fù)初始化而已但不建議養(yǎng)成這種習(xí)慣。另外每次執(zhí)行g(shù)it init之后建議用git status看一眼當(dāng)前狀態(tài)確認(rèn)一下自己確實(shí)是在正確的目錄里操作的。我就有過在錯(cuò)誤的路徑下執(zhí)行g(shù)it add .把一堆不該提交的文件全部加了進(jìn)去的經(jīng)歷等注意到的時(shí)候已經(jīng)晚了。2.3 忽略文件的正確打開方式每次初始化倉庫之后第一件該做的事其實(shí)是創(chuàng)建.gitignore文件。很多新手忽略了這一點(diǎn)直到提交了一堆node_modules或者編譯產(chǎn)物進(jìn)倉庫之后才開始后悔。.gitignore是一個(gè)純文本文件里面寫的是 Git 不需要跟蹤的文件或目錄規(guī)則。比如一個(gè)前端項(xiàng)目通常會(huì)忽略這些node_modules/ dist/ .env *.lognode_modules/表示忽略整個(gè)目錄*.log表示忽略所有以.log結(jié)尾的文件.env這種帶點(diǎn)號(hào)開頭的文件直接寫文件名就能忽略。這里有個(gè)值得注意的細(xì)節(jié).gitignore是支持通配符的*匹配任意內(nèi)容但不會(huì)跨越目錄層級(jí)如果想要匹配任意層級(jí)的某個(gè)文件需要寫成類似**/temp/的形式。踩坑提示如果某個(gè)文件已經(jīng)被 Git 跟蹤了再寫進(jìn).gitignore是不生效的。也就是說.gitignore只對(duì)“尚未被跟蹤”的文件有效。補(bǔ)救辦法是先執(zhí)行g(shù)it rm --cached 文件名把它從暫存區(qū)移除但保留本地文件然后再提交一次。3. 提交與狀態(tài)查閱讓時(shí)間線清晰可讀的基本功3.1 status 和 diff動(dòng)手提交前一定要做的事git status大概是整個(gè) Git 里使用頻率最高的命令了。它的作用就是告訴你當(dāng)前倉庫的狀態(tài)哪個(gè)分支、哪些文件被修改了、哪些文件暫存了、有沒有未跟蹤的新文件。我個(gè)人的習(xí)慣是在執(zhí)行任何可能改變倉庫狀態(tài)的操作之前先跑一次git status尤其是在多分支切換和代碼合并之前。比status更進(jìn)一步的是git diff它用來查看文件具體的改動(dòng)內(nèi)容。直接執(zhí)行g(shù)it diff查看的是工作區(qū)和暫存區(qū)之間的差異——說白了就是你改了但還沒git add的內(nèi)容。等你執(zhí)行了git add之后再想看暫存區(qū)和上一次提交之間的差異需要加一個(gè)參數(shù)git diff --cached很多新手不知道這兩個(gè)查看區(qū)域的區(qū)別經(jīng)常出現(xiàn)“我明明改了代碼為什么git diff什么都沒輸出”的疑問其實(shí)就是文件已經(jīng)被git add過了。git diff是一個(gè)非常適合用來做“提交前自查”的命令我每次打算提交時(shí)都會(huì)先看一眼改動(dòng)內(nèi)容防止把調(diào)試用的臨時(shí)日志或者意外修改一起提交上去。3.2 add 的三種常用方式與各自的坑git add負(fù)責(zé)把工作區(qū)的改動(dòng)添加到暫存區(qū)是構(gòu)成 Git 工作流的關(guān)鍵一環(huán)。最常見的三種用法是git add 具體文件名 git add . git add -ugit add 文件名最穩(wěn)妥按需添加精確控制到底哪些文件進(jìn)入暫存區(qū)適合改動(dòng)比較分散的時(shí)候使用。git add .是把當(dāng)前目錄下所有改動(dòng)和新文件全部加入暫存區(qū)用起來方便但也最容易把不該提交的東西提交上去。git add -u稍微冷門一點(diǎn)它只會(huì)把“已經(jīng)被 Git 跟蹤的文件”的改動(dòng)加入暫存區(qū)不會(huì)添加新文件。如果你項(xiàng)目里恰好有一些不想提交的新文件用這個(gè)命令會(huì)更安全。有一個(gè)實(shí)際遇到的坑想分享在倉庫根目錄執(zhí)行的git add .確實(shí)會(huì)遞歸添加所有子目錄的內(nèi)容但如果你在某個(gè)子目錄里面執(zhí)行同樣命令它只會(huì)添加當(dāng)前目錄下的內(nèi)容。有些同學(xué)在項(xiàng)目中的某個(gè)模塊目錄下執(zhí)行了git add .以為整個(gè)項(xiàng)目都提交了結(jié)果推送上去發(fā)現(xiàn)漏掉了一堆文件——這就是對(duì)“當(dāng)前目錄”這個(gè)概念理解不透徹導(dǎo)致的。3.3 commit 規(guī)范與 --amend 的妙用提交是 Git 操作里最核心的動(dòng)作之一執(zhí)行命令是git commit。最常用的兩種方式git commit -m 提交信息 git commit -am 提交信息git commit -a等價(jià)于先把所有已跟蹤文件的改動(dòng)加入暫存區(qū)再執(zhí)行提交。但它對(duì)新建文件是無效的新文件依然需要顯式git add。這個(gè)細(xì)節(jié)和git add -u是一致的因?yàn)?a內(nèi)部做的就是同樣的事情。另一個(gè)我強(qiáng)烈建議養(yǎng)成的習(xí)慣是使用git commit -m時(shí)提交信息不要隨便寫。fix bug和修復(fù)了用戶首次登錄時(shí)頭像無法加載的問題完全是兩種信息密度。等到你三個(gè)月后再看這段提交記錄前一種信息完全沒有任何幫助。比較好的格式是“動(dòng)詞 修改對(duì)象 原因或效果”比如修復(fù)訂單列表在低網(wǎng)速下重復(fù)請(qǐng)求的問題這樣一看就明白。如果提交完之后發(fā)現(xiàn)信息寫錯(cuò)了或者漏掉了一個(gè)文件不用慌張用--amend參數(shù)git commit --amend -m 修正后的提交信息這個(gè)命令會(huì)修改當(dāng)前分支的最新一次提交。注意是“修改”不是“新增加一條提交記錄”。如果這個(gè)提交已經(jīng)推送到了遠(yuǎn)程那么--amend之后需要強(qiáng)制推送才能更新遠(yuǎn)端記錄。在團(tuán)隊(duì)協(xié)作時(shí)如果沒有和隊(duì)友溝通好直接強(qiáng)制推送改寫歷史是有風(fēng)險(xiǎn)的——?jiǎng)e人的本地提交可能會(huì)因此出現(xiàn)沖突。所以我的建議是--amend只用于處理“還在自己本地還沒推送”的提交。3.4 log 與 status 的搭配使用技巧查詢歷史記錄主要靠git log。默認(rèn)的輸出格式是一長(zhǎng)串帶哈希值、作者、日期、提交信息的列表信息量足夠但看起來會(huì)比較密。我日常工作最常用的兩個(gè)變體是git log --oneline git log --oneline --graph--oneline把每次提交壓縮成一行清爽很多--graph會(huì)額外顯示分支合并的關(guān)系圖提交網(wǎng)絡(luò)一目了然。想要篩選某一個(gè)人的提交或者只看最近多少條記錄可以配合更多參數(shù)。還有一個(gè)組合拳我經(jīng)常用git log -p 文件名這條命令會(huì)顯示某個(gè)文件的完整修改歷史包括每次改動(dòng)前后的具體內(nèi)容。查“某行代碼是什么時(shí)候被改掉的”“那次改動(dòng)解決了什么問題”非常方便可以說是我排查歷史問題時(shí)的第一選擇。4. 分支操作并行開發(fā)和風(fēng)險(xiǎn)隔離的利器4.1 分支的創(chuàng)建、切換與刪除分支是 Git 最核心的設(shè)計(jì)之一它讓你可以在不影響主代碼的情況下并行開發(fā)新功能或修復(fù)問題。常用命令git branch # 查看本地分支列表 git branch name # 創(chuàng)建新分支 git checkout name # 切換分支 git checkout -b name # 創(chuàng)建并切換分支 git branch -d name # 刪除分支git checkout -b是創(chuàng)建并切換分支的快捷寫法也是我日常新建分支時(shí)最常用的命令?,F(xiàn)在新版本的 Git 也支持git switch系列命令功能上和checkout對(duì)分支的操作是等價(jià)的但switch更專注在“切換”這個(gè)語義上不容易和checkout恢復(fù)文件的功能混淆。如果你用的 Git 版本比較新可以試試git switch -c name創(chuàng)建并切換分支。刪除分支時(shí)需要注意一點(diǎn)git branch -d只能刪除“已合并”的分支。如果你嘗試刪除一個(gè)還沒合并的分支Git 會(huì)提示你使用-D強(qiáng)制刪除。這其實(shí)是 Git 在保護(hù)你的工作成果。我有一次在一個(gè)功能分支上寫了不少代碼還沒來得及合并進(jìn)主分支因?yàn)橹鼻謇矸种в昧藦?qiáng)制刪除后面發(fā)現(xiàn)有個(gè)功能需要參考那段代碼只能靠遠(yuǎn)程分支或者git reflog勉強(qiáng)找回來。所以刪除分支前建議三思。4.2 merge 和 rebase兩種合并思路的對(duì)比當(dāng)你在分支上完成了開發(fā)需要把改動(dòng)合并回主分支時(shí)有兩種主要方式git merge和git rebase。git merge的語義是“合并”它會(huì)創(chuàng)建一個(gè)新的合并提交節(jié)點(diǎn)保留兩條分支的完整歷史。優(yōu)點(diǎn)是不改動(dòng)原有提交記錄操作安全直觀缺點(diǎn)是合并次數(shù)多了以后提交歷史會(huì)出現(xiàn)很多分叉--graph看起來像一團(tuán)毛線。git rebase的語義是“變基”它會(huì)把當(dāng)前分支的提交“重新播放”到目標(biāo)分支的最新提交之上。執(zhí)行之后提交歷史會(huì)變成一條干凈的直線。但代價(jià)是提交的哈希值全部會(huì)變也就是重寫了歷史。我的使用建議是功能分支合并回主分支用merge保留合并痕跡方便回溯功能分支從主分支拉取最新代碼時(shí)用rebase讓本地提交始終基于最新代碼具體操作上把功能分支切換到需要并入的分支之后執(zhí)行g(shù)it merge 功能分支名如果發(fā)生沖突Git 會(huì)列出沖突文件你需要手動(dòng)編輯解決沖突之后再執(zhí)行g(shù)it add和git commit完成合并。注意沖突解決完之后是不需要git merge --continue的直接提交即可。4.3 解決沖突的三個(gè)原則合并沖突是 Git 使用中最頭疼的問題。出現(xiàn)沖突的本質(zhì)是兩個(gè)分支修改了同一個(gè)文件的同一個(gè)位置Git 不知道以誰為準(zhǔn)。解決方案只有一個(gè)——“人工判斷”。真實(shí)操作中沖突文件里會(huì)有這樣的標(biāo)記 HEAD 當(dāng)前分支的內(nèi)容 要合并進(jìn)來的分支的內(nèi)容 feature-branch你要做的就是把、、這些標(biāo)記連同其中一方的內(nèi)容一起刪掉保留正確的代碼。我處理沖突時(shí)的原則有三條不盲目保留任意一方。除非你對(duì)這塊代碼特別熟悉否則最好看看兩邊的上下文再?zèng)Q定涉及關(guān)鍵業(yè)務(wù)邏輯的沖突建議與相關(guān)開發(fā)者溝通確認(rèn)解決完畢之后立即運(yùn)行一次構(gòu)建或測(cè)試越早暴露問題越好5. 遠(yuǎn)程倉庫協(xié)作push、pull 和 fetch 的配合節(jié)奏5.1 remote 與 clone建立本地和遠(yuǎn)端的連接遠(yuǎn)程倉庫是團(tuán)隊(duì)協(xié)作的樞紐。把一個(gè)倉庫從遠(yuǎn)端復(fù)制到本地用的是git clone這個(gè)命令會(huì)自動(dòng)建立本地分支對(duì)遠(yuǎn)端分支的跟蹤關(guān)系。后面如果需要手動(dòng)添加遠(yuǎn)程地址用的命令是git remote add origin 倉庫地址 git remote -vgit remote -v用來查看當(dāng)前配置的遠(yuǎn)程地址。我建議每次換電腦、換網(wǎng)絡(luò)之后先跑一次這個(gè)命令確認(rèn)地址對(duì)不對(duì)因?yàn)楹芏唷巴扑褪 钡膯栴}其實(shí)根本原因是倉庫地址配錯(cuò)了。origin是 Git 對(duì)“默認(rèn)遠(yuǎn)程倉庫”的約定命名可以改成其他名字但沒有必要。多遠(yuǎn)程倉庫的場(chǎng)景確實(shí)存在比如同一份代碼需要推到兩個(gè)不同平臺(tái)但我個(gè)人覺得日常開發(fā)維護(hù)一個(gè)origin就足夠了太多遠(yuǎn)程地址反而容易操作失誤。5.2 push 的參數(shù)選擇與常見報(bào)錯(cuò)推送本地提交到遠(yuǎn)程倉庫是每個(gè)開發(fā)者都要頻繁做的操作git push origin 分支名 git push -u origin 分支名 git push --force-with-lease origin 分支名第一次推送新分支時(shí)建議加上-u參數(shù)作用是建立本地分支與遠(yuǎn)程分支的跟蹤關(guān)系。建立過跟蹤關(guān)系之后后續(xù)再推送或拉取時(shí)直接執(zhí)行g(shù)it push或git pull就可以不需要每次都寫清楚遠(yuǎn)端和分支名。推送時(shí)報(bào)錯(cuò)最經(jīng)典的是下面這種! [rejected] feature - feature (fetch first) error: failed to push some refs這通常意味著遠(yuǎn)程分支有你本地沒有的提交。正確做法是先git pull把遠(yuǎn)程更新拉到本地處理完可能的沖突再重新推送。這里提醒一下遇到這種報(bào)錯(cuò)千萬不要直接執(zhí)行g(shù)it push --force強(qiáng)行覆蓋。如果不是充分確認(rèn)遠(yuǎn)程分支可以被覆蓋的話強(qiáng)制推送可能把隊(duì)友的提交歷史全部沖掉后果相當(dāng)嚴(yán)重。如果必須強(qiáng)推優(yōu)先使用--force-with-lease它會(huì)檢查遠(yuǎn)程分支是否被別人更新過相對(duì)安全得多。5.3 pull 與 fetch理解“拉取”的兩個(gè)階段git pull是一條被很多人當(dāng)作“一鍵更新”來用的命令但它實(shí)際上做了兩件事先git fetch把遠(yuǎn)程的提交記錄下載到本地再執(zhí)行一次merge或rebase取決于你的配置把遠(yuǎn)端更新合并到當(dāng)前分支。git fetch單獨(dú)執(zhí)行時(shí)只是把遠(yuǎn)程的最新狀態(tài)下載到本地并不會(huì)修改你當(dāng)前的工作目錄和分支。所以想要先看看遠(yuǎn)程改了些什么再?zèng)Q定怎么處理時(shí)可以先git fetch然后使用git log origin/分支名查看遠(yuǎn)程分支的提交情況再?zèng)Q定用git merge還是其他方式合入。如果你想直接拉取并合并默認(rèn)執(zhí)行g(shù)it pull就行。但有些團(tuán)隊(duì)約定用 rebase 的方式來拉取以保持提交歷史的線性可以這樣設(shè)置git pull --rebase用--rebase拉取的原理是先把本地未推送的提交“暫存”到一邊然后合并遠(yuǎn)端最新代碼最后把你的提交再應(yīng)用到最新代碼之上。好處是歷史干凈壞處是遇到?jīng)_突時(shí)解決起來稍微復(fù)雜一點(diǎn)。建議在自己熟悉的項(xiàng)目里試驗(yàn)幾次找到適合自己的節(jié)奏。6. 撤銷與回滾從工作區(qū)到歷史記錄的“后悔藥”6.1 撤銷未提交的改動(dòng)checkout 和 restore日常開發(fā)中最常出現(xiàn)的誤操作就是“改錯(cuò)文件”或者“改砸了想回到原樣”。如果改動(dòng)還沒有g(shù)it add那么想撤銷工作區(qū)的修改可以git checkout -- 文件名 git restore 文件名這兩種寫法等價(jià)作用都是丟棄工作區(qū)里對(duì)某個(gè)文件的修改恢復(fù)到暫存區(qū)或上一次提交時(shí)的狀態(tài)。注意這個(gè)操作是不可逆的文件內(nèi)容一旦被覆蓋就找不回來。所以執(zhí)行前一定要確認(rèn)這個(gè)文件里沒有你想保留的代碼。如果改動(dòng)已經(jīng)執(zhí)行了git add想從暫存區(qū)撤銷而不動(dòng)工作區(qū)的內(nèi)容git restore --staged 文件名這條命令會(huì)把文件從暫存區(qū)退回到工作區(qū)但保留你當(dāng)前對(duì)它的修改。經(jīng)常在git add時(shí)誤加了文件或者目錄就可以用這個(gè)命令把它移出來。6.2 reset 的三種模式soft、mixed、hard當(dāng)你要撤銷“已經(jīng)提交”的改動(dòng)時(shí)git reset是核心工具。它有三種模式分別對(duì)應(yīng)不同的“回退深度”git reset --soft HEAD~1 git reset --mixed HEAD~1 git reset --hard HEAD~1HEAD~1表示上一次提交HEAD~2表示上上次以此類推。三種模式的區(qū)別在于--soft只移動(dòng) HEAD 指針到指定提交暫存區(qū)和工作區(qū)都不變。也就是說回退之后你剛才的提交內(nèi)容仍然在暫存區(qū)里可以直接重新提交。適合“提交完之后發(fā)現(xiàn)信息寫錯(cuò)了想重新提交”的場(chǎng)景--mixed默認(rèn)移動(dòng) HEAD 指針并且重置暫存區(qū)但保留工作區(qū)的內(nèi)容。效果是回退之后所有改動(dòng)回到工作區(qū)未暫存狀態(tài)--hardHEAD、暫存區(qū)、工作區(qū)全部回退到指定提交。這是最“重”的操作會(huì)直接丟棄所有后續(xù)修改。除非當(dāng)前分支完全不需要這些改動(dòng)否則不要輕易使用注意了如果一個(gè)提交已經(jīng)推送到了遠(yuǎn)程那么git reset只在本地有效想同步到遠(yuǎn)程還是要強(qiáng)制推送。這又會(huì)帶來前面說過的風(fēng)險(xiǎn)所以依然建議只對(duì)“還沒推送的提交”使用reset。6.3 revert安全地撤銷歷史提交另一種撤銷提交時(shí)更“安全”的方式是git revertgit revert commit哈希值revert不是把歷史回退而是“生成一個(gè)新的提交用這個(gè)新提交來抵消目標(biāo)提交的改動(dòng)”。也就是說歷史記錄里保留原提交同時(shí)多出一個(gè)反向提交。這種方式非常適合用在已經(jīng)推送到遠(yuǎn)程的公共分支上——因?yàn)椴桓淖兲峤粴v史隊(duì)友拉取時(shí)不會(huì)出現(xiàn)不一致的問題。在多人協(xié)作的項(xiàng)目里撤銷一個(gè)已經(jīng)合并到主分支的功能我強(qiáng)烈建議用revert而不是reset。即使之后需要恢復(fù)這個(gè)功能也可以再revert回去整個(gè)操作過程完全可追溯。6.4 reflog最后的救命稻草git reflog是所有“后悔”操作的最后一道保險(xiǎn)。它會(huì)記錄 HEAD 指針在本地倉庫中的所有變動(dòng)歷史包括被reset --hard丟棄的提交、被rebase改變位置的提交等等。比如不小心執(zhí)行了git reset --hard發(fā)現(xiàn)回退錯(cuò)了想找回丟失的提交可以先git reflog輸出里能看到每次操作對(duì)應(yīng)的哈希值和操作說明找到想要恢復(fù)的那次提交的哈希值然后git reset --hard 哈希值就能恢復(fù)過去的狀態(tài)。很多人以為reset之后代碼就徹底丟了其實(shí)本地倉庫里的對(duì)象還在reflog會(huì)保留一段時(shí)間。你得知道這個(gè)機(jī)制到了關(guān)鍵時(shí)刻能派大用場(chǎng)。7. 暫存與歷史查詢兩個(gè)容易被低估的高頻操作7.1 stash臨時(shí)保存當(dāng)前工作狀態(tài)開發(fā)過程中常常會(huì)遇到這種情況正在一個(gè)分支上改著代碼突然需要切到另一個(gè)分支處理緊急事務(wù)。如果此時(shí)直接切換分支Git 會(huì)阻止你因?yàn)橛形刺峤坏母膭?dòng)或者把這些改動(dòng)帶過去造成混亂。git stash就是專門解決這個(gè)問題的git stash # 暫存當(dāng)前所有修改 git stash list # 查看暫存列表 git stash pop # 恢復(fù)最新的暫存并刪除記錄 git stash apply # 恢復(fù)最新的暫存但保留記錄stash pop和stash apply的區(qū)別在于是否刪除暫存記錄。我個(gè)人的建議是如果只是應(yīng)急切換分支用pop就夠了恢復(fù)之后暫存列表清空免得后面忘記了自己還留著暫存。如果你懷疑暫存的代碼可能還需要在多個(gè)分支間復(fù)制使用那就用apply它會(huì)保留記錄可以反復(fù)恢復(fù)。和stash類似的還有一個(gè)git stash save 說明信息的寫法可以給暫存內(nèi)容加一句備注方便后面辨認(rèn)。我在一個(gè)多任務(wù)并行時(shí)期試過同時(shí)攢了三個(gè) stash如果沒有備注提示過幾天根本不知道哪個(gè)是哪個(gè)。7.2 log 的篩選組合與 blame 定位git log配合參數(shù)可以實(shí)現(xiàn)很多實(shí)用查詢場(chǎng)景。比如git log --author用戶名 git log --since2025-01-01 --until2025-06-01 git log -S某個(gè)關(guān)鍵字-S參數(shù)比較特別它用來查找“某個(gè)字符串被添加或刪除”的提交。想定位某行代碼是什么時(shí)候引入的時(shí)候這個(gè)參數(shù)效率很高。與定位代碼來源相關(guān)的還有g(shù)it blamegit blame 文件名它在每一行的前面標(biāo)出該行最后一次被修改的提交哈希和作者。查“這行代碼是誰寫的”“上次誰改過這里”時(shí)非常實(shí)用。不過逐行查看的方式在文件行數(shù)多的時(shí)候會(huì)比較累實(shí)際使用時(shí)建議先定位到具體行號(hào)范圍再用blame查看對(duì)應(yīng)區(qū)段。8. 項(xiàng)目實(shí)戰(zhàn)一套適合團(tuán)隊(duì)的 Git 工作流參考8.1 我常用的分支模型與命令組合說了這么多分散的命令最后把它們串成一整套實(shí)際可用的工作流。我日常參與的項(xiàng)目一般遵循這樣的分支模型main或master始終可部署的穩(wěn)定版本develop日常開發(fā)的主戰(zhàn)場(chǎng)feature/*功能分支按功能命名fix/*修復(fù)分支實(shí)際開發(fā)流程是這樣的從最新的develop拉一條功能分支git checkout develop git pull git checkout -b feature/xxx在功能分支上進(jìn)行開發(fā)頻繁提交git add . git commit -m 完成XX模塊的接口對(duì)接開發(fā)完成后先把自己的分支 rebase 到最新的developgit checkout develop git pull git checkout feature/xxx git rebase develop回到develop分支合并功能分支git checkout develop git merge feature/xxx推送并刪除功能分支git push git branch -d feature/xxx這套流程的好處是功能分支始終基于最新代碼開發(fā)減少合并沖突的概率合并到develop時(shí)用merge保留合并記錄方便回溯功能提交。8.2 一次完整的本地提交推送實(shí)戰(zhàn)記錄再以一個(gè)具體的場(chǎng)景走一遍。假設(shè)你在feature/login分支上完成了登錄功能的開發(fā)準(zhǔn)備提交推送# 查看當(dāng)前狀態(tài) git status # 查看修改的具體內(nèi)容 git diff # 添加需要提交的文件 git add src/pages/login.vue src/api/login.ts # 提交并寫好信息 git commit -m 完成登錄頁面的前端校驗(yàn)邏輯和接口對(duì)接 # 推送前先拉取遠(yuǎn)端最新代碼用 rebase 保持歷史整潔 git pull --rebase # 推送 git push -u origin feature/login如果pull --rebase時(shí)出現(xiàn)沖突按照沖突標(biāo)記解決后繼續(xù)執(zhí)行g(shù)it add 解決完的文件 git rebase --continue如果中途想要放棄 rebase可以用git rebase --abort回到 rebase 之前的狀態(tài)。8.3 快速保存修復(fù)現(xiàn)場(chǎng)的高效方式還有一種我經(jīng)常遇到的情況上線后發(fā)現(xiàn)緊急 bug需要一個(gè)“臨時(shí)補(bǔ)丁 干凈工作區(qū)”的組合。假設(shè)你的功能分支上正做著開發(fā)工作區(qū)有一堆改動(dòng)不想提交但必須立即切到fix分支去修 buggit stash save 登錄功能開發(fā)中待修復(fù)上線bug后繼續(xù) git checkout fix/xxx # 修復(fù)完之后 commit push git checkout feature/login git stash popstash在這里的價(jià)值是你不用為了一次臨時(shí)修復(fù)而做出一個(gè)半成品提交也不會(huì)因?yàn)榍袚Q分支把代碼搞亂。項(xiàng)目緊急時(shí)這套操作是救命級(jí)別的體驗(yàn)。9. 常見問題排查與經(jīng)驗(yàn)速查9.1 六類典型問題的判斷思路與解決方案這里整理了一張速查表是我自己在實(shí)際使用中反復(fù)驗(yàn)證過的你可以直接收藏問題現(xiàn)象可能原因排查思路常用解法push 被拒絕遠(yuǎn)端有本地沒有的提交先 pull 或 fetch 查看差異git pull 解決沖突后重新 pushGit 提示 “not a git repository”當(dāng)前目錄不在倉庫內(nèi)確認(rèn)路徑執(zhí)行g(shù)it statuscd到倉庫根目錄切換分支時(shí)提示工作區(qū)會(huì)被覆蓋有未提交修改看git status判斷改動(dòng)范圍commit 或 stash 后切換commit 之后發(fā)現(xiàn)漏了文件漏執(zhí)行了 add補(bǔ) add 并執(zhí)行--amendgit commit --amend誤 commit 想撤回提交未推送決定回退深度git reset --soft或--mixed誤 reset 后想找回提交本地引用被重置立即執(zhí)行 refloggit reset --hard 原哈希9.2 幾個(gè)減少低級(jí)失誤的小習(xí)慣第一提交前必看git status和git diff。哪怕只是一個(gè)小改動(dòng)花十秒鐘掃一眼狀態(tài)能規(guī)避大量的誤提交。第二大改動(dòng)盡量按模塊分多次提交不要一個(gè)git add .全塞進(jìn)一次提交。這樣出了問題定位時(shí)范圍更小代碼評(píng)審也更輕松。第三不確定遠(yuǎn)程分支狀態(tài)時(shí)多用git fetchgit log origin/分支名來觀察不要直接 pull 或 push尤其是公共分支。第四需要強(qiáng)制推送時(shí)永遠(yuǎn)優(yōu)先--force-with-lease而不是--force。前者相當(dāng)于加了安全帶防止你在不經(jīng)意間覆蓋掉同事剛推送的提交。10. 寫在最后的一點(diǎn)真實(shí)體會(huì)從我自己的經(jīng)歷來看Git 學(xué)會(huì)容易用好需要沉淀。初期總是記不住參數(shù)這太正常了。我的建議是從“先掌握一個(gè)場(chǎng)景的一個(gè)固定操作”開始比如先熟練git add、git commit、git push這三條形成肌肉記憶之后再逐步補(bǔ)上分支、合并、撤銷這些進(jìn)階操作。雙減到實(shí)際操作中有一個(gè)很真實(shí)的經(jīng)驗(yàn)想分享遇到問題不要急著去搜索“Git 報(bào)錯(cuò)怎么解決”先冷靜下來跑一遍git status再想想自己最近執(zhí)行的命令有哪些。80% 的 Git 問題都可以通過搞清楚“當(dāng)前狀態(tài)”和“目標(biāo)狀態(tài)”之間的差異來解決——所謂命令只不過是從一個(gè)狀態(tài)遷移到另一個(gè)狀態(tài)的方式而已。這篇文章提到的那些命令和流程都是我個(gè)人反復(fù)驗(yàn)證過的常規(guī)做法。你完全可以根據(jù)自己團(tuán)隊(duì)的習(xí)慣做適當(dāng)調(diào)整關(guān)鍵是把“用 Git 管理好每次改動(dòng)的來龍去脈”這個(gè)底層邏輯記在心里思路清晰了什么命令都是工具用什么順手、合理才是最重要的。