階:從安裝配置到 worktree 實(shí)戰(zhàn)避坑指南)
Git 這東西剛?cè)胄心菚何乙脖凰垓v得夠嗆。第一次看到fatal: not a git repository的時(shí)候我盯著屏幕愣了五分鐘心想“我不就在項(xiàng)目文件夾里嗎怎么就不是倉庫了”。后來帶過幾個(gè)新人發(fā)現(xiàn)大家卡住的地方驚人地相似裝完了不知道配置啥、clone下來不敢動、commit完了發(fā)現(xiàn)提交信息寫錯(cuò)了不知道怎么改、密鑰配了半天還是login failed。這篇東西就是把這些年踩過的坑、帶人時(shí)被問得最多的問題從頭到尾捋一遍。不管你是剛下載完 Git 還沒打開過還是已經(jīng)能敲幾個(gè)命令但一遇到報(bào)錯(cuò)就慌看完應(yīng)該都能有點(diǎn)收獲。我會從“Git 到底在干嘛”講起一直講到worktree這種稍微進(jìn)階一點(diǎn)的用法中間穿插大量實(shí)際會遇到的報(bào)錯(cuò)和排查思路。1. Git 到底解決了什么問題1.1 從“復(fù)制粘貼改文件名”說起沒有版本控制的時(shí)候我們是怎么管理文件版本的大概率是這樣方案.doc、方案_修改版.doc、方案_最終版.doc、方案_最終版_真的最終.doc。這種做法的死穴在于你根本說不清兩個(gè)版本之間到底改了什么想回退到某個(gè)中間狀態(tài)基本靠猜多人協(xié)作更是災(zāi)難——你把文件發(fā)給我我改完發(fā)給你你再改最后誰的版本是對的都不知道。Git 的核心價(jià)值就一句話它記錄的是每一次改動的快照和改動之間的差異而不是一堆孤立的文件副本。每次你提交commitGit 會把當(dāng)前所有文件的狀態(tài)拍一張“照片”存起來同時(shí)記錄這張照片和上一張之間哪些文件變了、怎么變的。這樣你隨時(shí)可以回到任何一張歷史照片也可以清楚地看到某一行代碼是誰在什么時(shí)候改的。我習(xí)慣用一個(gè)類比Git 就像給項(xiàng)目裝了一臺帶時(shí)間軸的監(jiān)控?cái)z像頭。你不需要手動保存“第 3 版”“第 5 版”攝像頭自動記錄每一幀你想看哪一幀就看哪一幀還能對比兩幀之間的差異。1.2 三個(gè)區(qū)域工作區(qū)、暫存區(qū)、本地倉庫理解 Git 最關(guān)鍵的一步是搞清楚它把文件分成了三個(gè)區(qū)域。很多人命令敲得挺溜但一出問題就懵根源就是沒搞明白這三個(gè)區(qū)的關(guān)系。工作區(qū)Working Directory你眼睛能看到、手能直接編輯的那些文件就是工作區(qū)。你在編輯器里改代碼改的就是工作區(qū)。暫存區(qū)Staging Area / Index一個(gè)中間緩沖區(qū)。你改完文件后用git add把想提交的改動“挑”進(jìn)暫存區(qū)。為什么要有這么個(gè)東西因?yàn)橐淮胃膭涌赡苌婕昂脦讉€(gè)文件但你可能只想先提交其中一部分暫存區(qū)就是讓你精確控制“這次提交到底包含哪些改動”的地方。本地倉庫Local Repositorygit commit之后暫存區(qū)里的內(nèi)容就被打包成一個(gè)永久的快照存進(jìn)本地倉庫。這個(gè)倉庫就在你項(xiàng)目目錄下的.git文件夾里是一個(gè)完整的、獨(dú)立的歷史記錄庫。這三個(gè)區(qū)的流轉(zhuǎn)關(guān)系是工作區(qū) →git add→ 暫存區(qū) →git commit→ 本地倉庫。反過來git checkout或git restore可以把倉庫或暫存區(qū)的內(nèi)容拉回工作區(qū)。搞懂這條鏈路后面 80% 的命令你都能自己推導(dǎo)出它是干嘛的。1.3 為什么是分布式而不是集中式早期的版本控制工具比如 SVN是集中式的所有歷史記錄存在一臺中央服務(wù)器上你本地只有當(dāng)前版本的文件。想提交得聯(lián)網(wǎng)。服務(wù)器掛了大家都別干活了。想看歷史得從服務(wù)器拉。Git 是分布式的意味著你clone下來的那一刻整個(gè)項(xiàng)目的完整歷史就已經(jīng)在你本地了。你可以在斷網(wǎng)的情況下提交、查看歷史、切換分支、對比差異所有操作都是本地完成的。只有當(dāng)你需要和別人同步時(shí)才需要聯(lián)網(wǎng)推送或拉取。這個(gè)設(shè)計(jì)帶來的好處是實(shí)打?qū)嵉奈以诟哞F上改代碼、提交完全不受網(wǎng)絡(luò)影響服務(wù)器出問題也不影響我本地繼續(xù)工作等恢復(fù)了再推上去就行。代價(jià)是初次clone會比較慢因?yàn)橐螺d全部歷史以及本地會占更多磁盤空間但對現(xiàn)在的硬盤容量來說這根本不是事。2. 安裝與配置把地基打牢2.1 Windows 上安裝 Git 的完整步驟Windows 用戶下載 Git 最穩(wěn)妥的渠道是官網(wǎng) git-scm.com下載頁會自動識別你的系統(tǒng)給出對應(yīng)安裝包。下載下來是一個(gè).exe文件雙擊運(yùn)行。安裝過程中有一堆選項(xiàng)新手最容易在這里犯迷糊。我挑幾個(gè)關(guān)鍵的說說選擇默認(rèn)編輯器默認(rèn)是 Vim但如果你不熟悉 Vim 的操作進(jìn)去容易出來難強(qiáng)烈建議改成 Notepad 或者 VS Code。這個(gè)編輯器是 Git 在你需要寫提交信息時(shí)調(diào)用的選一個(gè)你順手的能省很多事。調(diào)整 PATH 環(huán)境變量這一步選“Git from the command line and also from 3rd-party software”這樣你在 CMD、PowerShell、VS Code 的終端里都能直接用git命令。選錯(cuò)了的話只有在 Git Bash 里才能用 git會很別扭。換行符處理選“Checkout Windows-style, commit Unix-style line endings”。Windows 和 Unix 的換行符不一樣這個(gè)選項(xiàng)讓 Git 在檢出時(shí)自動轉(zhuǎn)成 Windows 格式、提交時(shí)轉(zhuǎn)成 Unix 格式能避免很多跨平臺協(xié)作時(shí)的“整個(gè)文件都顯示被修改了”的詭異問題。終端模擬器選 MinTTY默認(rèn)它比 Windows 自帶的 CMD 好用得多支持更好的顏色和字體。裝完之后在任意文件夾右鍵如果能看到“Git Bash Here”和“Open Git Bash here”說明裝好了。打開 Git Bash敲git --version能輸出版本號就萬事大吉。2.2 裝完必做的三項(xiàng)配置很多人裝完 Git 就直接開始用結(jié)果第一次commit就報(bào)錯(cuò)說不知道你是誰。Git 需要知道每次提交是誰做的所以裝完第一件事就是配置身份。git config --global user.name 你的名字 git config --global user.email 你的郵箱--global表示這是全局配置對你這臺電腦上所有倉庫生效。如果你某個(gè)項(xiàng)目想用不同的身份可以在那個(gè)項(xiàng)目目錄下不加--global再配一次項(xiàng)目級配置會覆蓋全局配置。第三項(xiàng)配置是設(shè)置默認(rèn)分支名。Git 早期默認(rèn)分支叫master現(xiàn)在業(yè)界普遍改用main。為了避免每次新建倉庫都要手動改名可以提前設(shè)好git config --global init.defaultBranch main配完之后用git config --list檢查一下能看到你剛設(shè)的三項(xiàng)就對了。這里有個(gè)小坑user.name里如果帶中文在某些終端里可能顯示亂碼建議用英文或者拼音反正這只是個(gè)標(biāo)識不影響功能。2.3 配置 Gitee 密鑰免密推送的正確姿勢每次推送都要輸密碼太煩用 SSH 密鑰可以免密。Gitee 和 GitHub 的配置邏輯一樣這里以 Gitee 為例。第一步生成密鑰對。在 Git Bash 里執(zhí)行ssh-keygen -t rsa -b 4096 -C 你的郵箱一路回車就行它會默認(rèn)在~/.ssh/目錄下生成兩個(gè)文件id_rsa私鑰絕對不能給別人和id_rsa.pub公鑰要貼到 Gitee 上去。第二步查看公鑰內(nèi)容cat ~/.ssh/id_rsa.pub把輸出的那一整串以ssh-rsa開頭復(fù)制下來。第三步登錄 Gitee進(jìn)入“設(shè)置” → “SSH 公鑰”把剛才復(fù)制的內(nèi)容粘貼進(jìn)去起個(gè)名字比如“我的筆記本”保存。第四步驗(yàn)證是否配置成功ssh -T gitgitee.com如果看到類似“Hi xxx! Youve successfully authenticated”的提示就說明配好了。如果提示Permission denied (publickey)八成是公鑰沒貼對或者你生成密鑰時(shí)改了默認(rèn)文件名導(dǎo)致 Git 找不到檢查一下~/.ssh/目錄下有沒有id_rsa和id_rsa.pub。注意私鑰文件id_rsa千萬不要發(fā)給任何人也不要用微信、郵件傳。它相當(dāng)于你倉庫的鑰匙泄露了別人就能以你的身份推送代碼。3. 日常使用從 clone 到 push 的完整鏈路3.1 拿到一個(gè)項(xiàng)目clone 的正確打開方式git clone是把遠(yuǎn)程倉庫完整復(fù)制到本地的命令包括所有歷史記錄和分支。git clone gitgitee.com:用戶名/倉庫名.git如果你用 HTTPS 地址https://gitee.com/...每次推送都要輸賬號密碼用 SSH 地址gitgitee.com:...則走密鑰免密。所以配好密鑰之后建議統(tǒng)一用 SSH 地址。clone 下來之后進(jìn)入項(xiàng)目目錄你會看到一個(gè).git文件夾默認(rèn)隱藏。這個(gè)文件夾就是本地倉庫的本體刪了它項(xiàng)目就變成普通文件夾了所有歷史記錄都沒了。所以千萬別手賤去刪它。有個(gè)常見報(bào)錯(cuò)fatal: not a git repository (or any of the parent directories): .git。這個(gè)報(bào)錯(cuò)的意思是“當(dāng)前目錄及其父目錄都找不到 .git 文件夾”也就是說你不在一個(gè) Git 倉庫里。解決辦法很簡單cd到正確的項(xiàng)目目錄或者確認(rèn)你是不是忘了git init。3.2 改完代碼怎么提交add、commit 的配合假設(shè)你改了兩個(gè)文件index.html和style.css?,F(xiàn)在想把它們提交上去。第一步看看當(dāng)前狀態(tài)git status它會告訴你哪些文件被修改了、哪些是新增的、哪些已經(jīng)進(jìn)了暫存區(qū)。這個(gè)命令我建議你養(yǎng)成習(xí)慣每次 add 和 commit 之前都敲一下心里有數(shù)。第二步把改動加入暫存區(qū)git add index.html style.css或者如果你確定所有改動都要提交可以偷懶用git add .注意后面有個(gè)點(diǎn)表示當(dāng)前目錄下所有改動。但我不太推薦無腦git add .因?yàn)橛袝r(shí)候你會改一些臨時(shí)文件、調(diào)試代碼不小心就一起提交上去了。第三步提交git commit -m 修復(fù)首頁輪播圖不顯示的問題-m后面跟的是提交信息。提交信息怎么寫有講究我的習(xí)慣是用一句話說清楚“這次改了什么、為什么改”不要寫“更新”“修改”這種沒信息量的詞。因?yàn)槿齻€(gè)月后你回頭看歷史唯一能幫你回憶起來的就是這行字。3.3 提交信息寫錯(cuò)了怎么辦commit --amend 實(shí)戰(zhàn)這是被問得最多的問題之一剛 commit 完發(fā)現(xiàn)提交信息打錯(cuò)字了或者漏加了一個(gè)文件怎么辦如果還沒推送到遠(yuǎn)程用git commit --amend就能補(bǔ)救。改提交信息git commit --amend -m 新的提交信息漏加了文件git add 漏掉的文件 git commit --amend --no-edit--no-edit表示沿用原來的提交信息只把新加的文件合并進(jìn)上一次提交。這里有個(gè)關(guān)鍵點(diǎn)--amend實(shí)際上是用一個(gè)新的提交替換掉上一個(gè)提交提交的哈希值會變。所以如果上一次提交已經(jīng)推送到遠(yuǎn)程了你 amend 之后再推會被拒絕因?yàn)楸镜睾瓦h(yuǎn)程的歷史不一致。這時(shí)候要么用git push --force危險(xiǎn)會覆蓋遠(yuǎn)程歷史多人協(xié)作時(shí)慎用要么就老老實(shí)實(shí)再提交一次。實(shí)操心得--amend只適合“還沒推送”的場景。一旦推上去了除非你確定這個(gè)分支只有你一個(gè)人在用否則別 force push容易把別人的工作覆蓋掉。3.4 推送到遠(yuǎn)程push 與 pull 的節(jié)奏本地提交完了要同步到遠(yuǎn)程git push如果是第一次推送這個(gè)分支可能需要指定上游git push -u origin main-u是--set-upstream的簡寫設(shè)置之后以后直接git push就行不用每次指定遠(yuǎn)程和分支。反過來如果別人推了新代碼你要拉下來git pullgit pull實(shí)際上是git fetch拉取遠(yuǎn)程最新加git merge合并到本地兩個(gè)操作的組合。我個(gè)人的習(xí)慣是先用git fetch看看遠(yuǎn)程有什么變化確認(rèn)沒問題再git merge這樣更可控。直接git pull有時(shí)候會觸發(fā)意料之外的合并沖突尤其是在你本地有未提交改動的時(shí)候。4. 那些讓人頭大的報(bào)錯(cuò)和疑難雜癥4.1 常見報(bào)錯(cuò)速查表我把這些年遇到的高頻報(bào)錯(cuò)整理成了一張表遇到問題先對號入座報(bào)錯(cuò)信息原因解決辦法fatal: not a git repository當(dāng)前目錄不是 Git 倉庫cd到項(xiàng)目目錄或git init初始化Please tell me who you are沒配置 user.name/user.email執(zhí)行 2.2 節(jié)的兩條 config 命令Permission denied (publickey)SSH 密鑰沒配好檢查公鑰是否貼到平臺、私鑰是否存在failed to push some refs遠(yuǎn)程有你本地沒有的提交先git pull再git pushYour local changes would be overwritten本地有未提交改動pull 會覆蓋先 commit 或 stash 再 pulllogin failed. check api token認(rèn)證信息過期或錯(cuò)誤重新配置密鑰或檢查 tokendetached HEAD處于游離頭指針狀態(tài)git switch -回到分支或新建分支4.2 密鑰配置失敗的排查思路login failed. check api token or gitlab version這類報(bào)錯(cuò)本質(zhì)是認(rèn)證沒通過。排查順序是這樣的先確認(rèn)你用的是 SSH 還是 HTTPS。如果是 HTTPS那走的是賬號密碼或 token跟 SSH 密鑰沒關(guān)系。很多人配了 SSH 密鑰卻用 HTTPS 地址 clone然后奇怪為什么還要輸密碼就是這個(gè)原因。如果是 SSH按這個(gè)順序查ssh -T gitgitee.com能不能通不通的話ssh -vT gitgitee.com加-v看詳細(xì)日志它會告訴你用了哪個(gè)密鑰文件、認(rèn)證到哪一步失敗了。常見原因是密鑰文件權(quán)限不對Linux/Mac 下~/.ssh應(yīng)該是 700私鑰應(yīng)該是 600或者你生成密鑰時(shí)自定義了文件名但沒在~/.ssh/config里配置對應(yīng)關(guān)系。還有一種情況是公司網(wǎng)絡(luò)環(huán)境對 SSH 端口有限制這時(shí)候可以改用 HTTPS 加 token 的方式。Gitee 和 GitHub 都支持在設(shè)置里生成個(gè)人訪問令牌token用 token 代替密碼進(jìn)行 HTTPS 認(rèn)證。4.3 合并沖突不要慌一步步來沖突是新手最怕的東西但其實(shí)它沒那么可怕。沖突的本質(zhì)是同一個(gè)文件的同一部分你和別人改了不一樣的內(nèi)容Git 不知道該聽誰的于是把決定權(quán)交給你。沖突發(fā)生時(shí)打開沖突文件你會看到這樣的標(biāo)記 HEAD 你本地的代碼 遠(yuǎn)程拉下來的代碼 branch-name和之間是你本地的版本和之間是遠(yuǎn)程的版本。你要做的就是手動決定保留哪個(gè)、刪掉哪個(gè)或者把兩者融合。編輯完之后把那些、、標(biāo)記全部刪掉保存文件然后git add這個(gè)文件再git commit完成合并。我的經(jīng)驗(yàn)是沖突文件多的時(shí)候用 VS Code 這類編輯器打開它會用顏色高亮沖突區(qū)域還提供“保留當(dāng)前”“保留傳入”“兩者都保留”的快捷按鈕比手動改快得多。5. 進(jìn)階但實(shí)用的幾個(gè)技巧5.1 git worktree同時(shí)處理多個(gè)分支git worktree是我近兩年用得越來越多的功能。場景是這樣的你正在feature-a分支上開發(fā)突然線上出了個(gè) bug 需要緊急修復(fù)。傳統(tǒng)做法是git stash存一下當(dāng)前改動切到main修 bug修完再切回來stash pop。但如果你當(dāng)前改動很多、很雜stash 來 stash 去很容易出錯(cuò)。worktree的思路是給同一個(gè)倉庫開一個(gè)“平行工作目錄”每個(gè)目錄可以檢出不同的分支互不干擾。git worktree add ../hotfix main這條命令會在當(dāng)前目錄的上一級創(chuàng)建一個(gè)叫hotfix的文件夾里面檢出main分支。你可以在那個(gè)文件夾里修 bug、提交、推送完全不影響你當(dāng)前目錄里的工作。修完之后git worktree remove ../hotfix就把那個(gè)工作目錄刪掉了。這個(gè)功能特別適合“手頭活沒干完又要緊急處理另一件事”的場景比 stash 干凈利落得多。5.2 小烏龜TortoiseGit不想敲命令的替代方案不是所有人都喜歡命令行。TortoiseGit俗稱“小烏龜”是 Windows 上的一個(gè) Git 圖形化客戶端裝完之后你的文件夾右鍵菜單里會多出一堆 Git 相關(guān)選項(xiàng)。它的好處是直觀改了哪些文件、每個(gè)文件改了什么都能在圖形界面里看得清清楚楚提交、拉取、推送都是點(diǎn)按鈕沖突了也有可視化的合并工具。對于剛?cè)腴T、還沒建立起命令行肌肉記憶的人來說小烏龜能幫你快速理解 Git 的工作流。但我的建議是圖形工具可以用但命令行的核心操作add、commit、push、pull、status、log還是要會。因?yàn)橐坏┏鰡栴}圖形界面能給你的信息往往不夠還是得回到命令行看詳細(xì)日志。而且很多服務(wù)器環(huán)境根本沒有圖形界面只能敲命令。5.3 IDEA 里怎么用 Git 提交代碼用 IDEA 開發(fā)的話其實(shí)不太需要單獨(dú)開終端敲 Git 命令I(lǐng)DEA 內(nèi)置了完整的 Git 集成。提交代碼的流程是改完代碼后在左側(cè)的 Commit 面板里能看到所有改動的文件。勾選你想提交的文件在下方輸入提交信息點(diǎn) Commit 就完成了本地提交。如果想直接推送到遠(yuǎn)程點(diǎn) Commit and Push。IDEA 的 diff 視圖很好用雙擊文件就能看到左右對比哪行改了、哪行刪了一目了然。沖突的時(shí)候它也有三欄合并工具比手動改文件舒服很多。不過有個(gè)坑要注意IDEA 默認(rèn)可能不會自動git add新文件你新建的文件需要手動勾選或者右鍵 Add 一下否則提交的時(shí)候會漏掉。6. 我踩過的坑和給你的建議6.1 提交前一定要 git status這個(gè)習(xí)慣能幫你避免 90% 的低級錯(cuò)誤。我見過太多人git add .之后直接 commit結(jié)果把.idea、node_modules、編譯產(chǎn)物、甚至包含密碼的配置文件一起提交上去了。這些東西一旦進(jìn)了歷史清理起來非常麻煩。正確的做法是在項(xiàng)目根目錄放一個(gè).gitignore文件把不需要版本控制的東西寫進(jìn)去node_modules/ .idea/ *.log .env dist/.gitignore要在項(xiàng)目初期就建好等文件已經(jīng)提交了再往.gitignore里加是沒用的Git 還是會繼續(xù)跟蹤那些文件。已經(jīng)提交了的話需要用git rm --cached 文件名把它從版本控制里移除但保留本地文件然后再提交一次。6.2 提交粒度要小信息要清楚一次提交只做一件事。不要把“修復(fù) bug”“重構(gòu)代碼”“調(diào)整格式”混在一個(gè)提交里。因?yàn)閷砣绻l(fā)現(xiàn)那個(gè) bug 修復(fù)有問題想回退你會把重構(gòu)和格式調(diào)整也一起退掉很麻煩。提交信息我習(xí)慣用這樣的格式第一行是簡短概括不超過 50 字空一行然后詳細(xì)說明改了什么、為什么改。雖然多寫幾個(gè)字但將來查歷史的時(shí)候你會感謝自己。6.3 不要害怕犯錯(cuò)Git 幾乎什么都能救回來新手最大的心理障礙是“怕把代碼搞丟”。實(shí)際上 Git 的設(shè)計(jì)非常保守只要你 commit 過的東西基本都能找回來。git reflog記錄了 HEAD 的所有移動歷史哪怕你誤刪了分支、reset 錯(cuò)了都能通過 reflog 找到那個(gè)提交的哈希值然后恢復(fù)。真正會丟數(shù)據(jù)的只有兩種情況一是你改了文件但從來沒 add 也沒 commit然后執(zhí)行了會覆蓋工作區(qū)的操作二是你 force push 覆蓋了遠(yuǎn)程歷史而本地又沒有備份。除此之外Git 都有辦法救。所以我的建議是大膽用多試。建個(gè)測試倉庫隨便折騰把各種命令都試一遍比看十篇教程都管用。Git 這東西光看是學(xué)不會的必須上手敲。6.4 關(guān)于 git 目錄泄露的提醒最后提一個(gè)安全相關(guān)的點(diǎn)。有些項(xiàng)目在部署的時(shí)候不小心把.git目錄也一起傳到了 Web 服務(wù)器上導(dǎo)致別人可以通過瀏覽器訪問你的域名/.git/config之類的路徑把整個(gè)倉庫的源碼和歷史記錄都下載下來。這是個(gè)很常見的安全疏漏。防范方法很簡單部署的時(shí)候確保.git目錄不在 Web 根目錄下或者在服務(wù)器配置里禁止訪問以.git開頭的路徑。如果你用的是自動化部署工具檢查一下它的配置確認(rèn)沒有把.git一起打包上傳。這個(gè)坑一旦踩了源碼泄露是小事如果歷史提交里有過密碼、密鑰那問題就大了。我自己現(xiàn)在的習(xí)慣是任何項(xiàng)目部署前都先確認(rèn)一遍.git目錄的位置寧可多花兩分鐘檢查也不要事后補(bǔ)救。