訪問博客查看 本文 最新內(nèi)容,排版更美觀ヾ(?ω?`)o 如有錯誤歡迎指出~
Git 系列學(xué)習(xí)筆記:
接上文,本文在命令行的基礎(chǔ)上介紹了常用指令的在本地的使用情景疮跑。
本文大部分內(nèi)容參考了 RCY 同學(xué)的教程静袖,部分參考了 廖雪峰教程-Git烂完,菜鳥教程-Git贾陷,以及 Git 官網(wǎng)文檔 Git-Documentation疮绷。
本地的 Git
預(yù)先準(zhǔn)備
在正式的操作前弧关,你需要先配置你的用戶信息盅安,這件事情通常在你的機器上只需要干一次即可,因為我們使用了全局配置世囊。在任意?個目錄打開你的終端别瞭,配置你的用戶名和郵箱:
$ git config --global user.name <username> # 替換為 GitHub 用戶名
$ git config --global user.email <email> # 替換為綁定的郵箱
# username 中如果有空格,需要加雙引號
基本操作
在想用 Git 管理的項目路徑下右鍵進入 Bash 終端株憾,然后將當(dāng)前目錄初始化為 Git 倉庫蝙寨。
$ git init
手動新建一個文件,隨便寫點什么嗤瞎,譬如一個 hello.md墙歪,此時文件存在于工作區(qū)。下面將其跟蹤并提交:
$ git add hello.md # 如果有更多文件贝奇,也可直接追加在后面
$ git commit -m "Init" # -m: 表示 message虹菲,備注本次提交信息
注意到,add 后面是文件名掉瞳,也可以是目錄毕源、通配符髓帽,因此也支持以下寫法:
$ git add . # 相對路徑中 . 表示當(dāng)前目錄,因此這一選項會添加當(dāng)前目錄下所有文件
$ git add *.cpp # * 代表任意字符串脑豹,所有 .cpp后綴的文件被添加
$ git add test?.cpp # ? 代表單個字符郑藏,不得為空,因此如 test1.cpp 將被添加瘩欺,而 test.cpp 等不會
$ git add dir/ # 添加路徑下的 d 文件夾內(nèi)全部文件必盖, / 有沒有皆可
此外,為了體現(xiàn)三個區(qū)俱饿,我們可以使用一些指令來對比:
$ git status # 查看工作區(qū)歌粥、暫存區(qū)的文件,一般前者是紅色拍埠,后者是綠色
$ git diff # 查看工作區(qū)和暫存區(qū)的差異失驶,適用于暫存后又修改的內(nèi)容
$ git diff --cached # 查看暫存區(qū)與 Git 倉庫的差異,適用于提交前查看
$ git diff HEAD # 同時查看其他兩個區(qū)和 Git 倉庫的差異
在工程中枣购,通常我們不用命令行查看差異嬉探,而是用 VSCode、GitHub Desktop棉圈、GitLab 等可視化工具涩堤。
如果對于已暫存的文件后悔了,也可以取消暫存:
$ git reset <filename> # 對于該命令分瘾,如果不帶參數(shù)則會清空暫存區(qū)
$ git restore --stage <filename>
在完成了幾次提交后胎围,可以查看提交歷史:
$ git log
提交歷史中,每個提交記錄都有對應(yīng)的 commit hash 值德召,唯一標(biāo)識了這次提交白魂,這是 Git 用 SHA-1 hash
生成的加密字符串。
注:如果 log 比較長或者窗口比較小上岗,這會觸發(fā)「導(dǎo)航」模式福荸,很多人第?次見到可能不知所措,不會退出該頁面液茎,此時可以:
- 上下鍵移動或 Page Up / Down 翻頁逞姿;
- 輸入
\
接字符來全局查找 ; - 輸入
q
退出捆等,與其他系統(tǒng)中的導(dǎo)航模式類似滞造; - 其他操作可以通過查詢關(guān)鍵字「Linux less 導(dǎo)航」來查到。
分支 (Branch)
有了前面的知識栋烤,我們已經(jīng)在腦海中想象?副快照變更圖了谒养,本節(jié)中我們將快照稱作「結(jié)點」,若干結(jié)點組織成了版本樹——Git 本身正是使用了紅黑樹對結(jié)點進行高效管理。
以下內(nèi)容強烈推薦結(jié)合 Git 分支在線教程 來學(xué)習(xí)买窟!
目前我們的結(jié)點樹基本是串行的(除了回退丰泊、重新提交會導(dǎo)致分叉),那么所謂的并行開發(fā)如何體現(xiàn)呢始绍?
注意到了反復(fù)有?個單詞 main 出現(xiàn)在命令行瞳购,這即是 Git 默認(rèn)的分支:主分支(舊版本叫 master)。main 即是這個分支的名字亏推,也是一個指針学赛,指向了該分支的最新結(jié)點。
和它?起的還有?個單詞 HEAD吞杭,這表示頭指針盏浇,指向當(dāng)前所處的結(jié)點。在你做分支相關(guān)的操作前芽狗,會有 HEAD -> main -> 最新結(jié)點
绢掰,直到你將它們分開。
創(chuàng)建分支
與初始化倉庫類似童擎,創(chuàng)建分支也很方便:
$ git branch test # 創(chuàng)建?個名為 test 的分?
注意:創(chuàng)建完后分支的即會指向當(dāng)前所在的結(jié)點滴劲,因此當(dāng)前最新結(jié)點同時被 main、test柔昼、HEAD 指向哑芹,即 HEAD -> main(test) -> 最新結(jié)點
。
對于已有的分支捕透,也可以通過以下命令查看、切換:
$ git branch # 查看本地分支碴萧,帶 * 的為 HEAD 指向的當(dāng)前分支
$ git checkout test # 檢出乙嘀,表示切換到 test 分支
# 上述的創(chuàng)建、切換操作也可以被縮減為一步操作
$ git checkout -b test # 創(chuàng)建?個名為 test 的分?并且切換過去
切換分支后破喻,可以看到出現(xiàn)在命令行右側(cè)的 main 已經(jīng)變成了 test虎谢。查看 log 也可發(fā)現(xiàn) HEAD -> test(main) -> 最新結(jié)點
。
合并分支
如果此時我們已經(jīng)在不同的分支提交了不同修改 C2 和 C3曹质,那么如何將分支合并到一起婴噩,使得并行開發(fā)結(jié)果匯總呢?
$ git checkout main # 切換到 main 分?
$ git merge test # 讓 main 分?合并 test 分?的結(jié)點
對于分支的合并羽德,Git 有專門的圖形化輸出命令來進行查看版本樹(也可以用其他可視化工具):
$ git log --graph --oneline --all
# oneline 表示用行來顯示記錄几莽,最上方的是最新的提交
# all 表示顯示所有分支,如果沒有則只顯示當(dāng)前分支及其祖先
# 當(dāng)然宅静,還有其他許多參數(shù)可以美化版本樹章蚣,這里貼一個大神的版本
$ git log --graph --all --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit --date=relative
為展示方便,這里使用在線教程里的圖例:
可以看到姨夹,合并后產(chǎn)生了一個新結(jié)點 C4纤垂,該結(jié)點具有雙父結(jié)點矾策,指向原來的 C2 和 C3。需要注意的是峭沦,該結(jié)點屬于 main 分支贾虽,是 main 分支的最新結(jié)點(被指針 main 所指),而 test 仍指向舊的 C2吼鱼。
如果要把 test 分支也同步到新結(jié)點蓬豁,只需要讓 test 分支合并 C4,也就是合并當(dāng)前的 main 分支:
$ git checkout test # 切換到 test 分?
$ git merge main # 讓 test 分?合并 main 分?的結(jié)點
而由于 main 分支的最新結(jié)點 C4 繼承自 C2蛉抓,此時的 Git 不會有任何操作庆尘,只是簡單地將指針 test 移動到指針 main 所在位置,即快速前移(fast-forward):
多數(shù)情況下巷送,我們會先用主分支合并從分支驶忌,如果之后需要再從分支繼續(xù)開發(fā),才會把從分支快速前移笑跛!
沖突的合并
如果 C2 和 C3 修改的代碼不在同一文件的同一處付魔,上述的 merge 是沒有問題的,但是一旦發(fā)生沖突飞蹂,git merge test
命令時就會提示錯誤几苍。
此時如果我們用 git status
查看,會發(fā)現(xiàn)工作區(qū)里有一個新的狀態(tài)「未合并的路徑」陈哑,里面就是沖突的文件妻坝。打開該文件,會發(fā)現(xiàn) Git 已經(jīng)在里面標(biāo)記出了雙方修改的內(nèi)容(用 VSCode 等 IDE 將看得更清楚)惊窖。
而我們只需要手動維護沖突刽宪,將該文件手動加入暫存區(qū),最后再提交界酒,就會生成一個新的結(jié)點圣拄,該結(jié)點無異于直接使用 git merge test
命令得到的。
變基 (Rebase)
一個來回穿插的版本樹是有點凌亂的毁欣,此時不得不提到第二種合并分支的辦法:變基 (rebase) 操作庇谆。Rebase 實際上就是取出從分支的提交記錄,「復(fù)制」它們凭疮,然后在主分支逐個的放下去饭耳。
Rebase 的優(yōu)勢就是可以創(chuàng)造更線性的提交歷史。如果兩個分支沒有沖突哭尝,如上文提到的第一種情況哥攘,直接 Merge 會出現(xiàn)一個新的結(jié)點(實際上該結(jié)點并沒有做出實質(zhì)的修改,反而使版本樹變得冗長)。
此時如果我們用 Rebase 操作逝淹,則可以簡化版本樹:
$ git checkout test # 切換到 test 分?
$ git rebase main # 讓 test 分?以 main 為基耕姊,因此 test 成為 main 的后代
注意,Rebase 操作通常是讓從分支變基到主分支栅葡,這與 Merge 操作是相反的茉兰!
觀察該圖,我們可以發(fā)現(xiàn) test 分支上的工作在 main 的最頂端欣簇,同時我們也得到了一個更線性的提交序列规脸。
而提交記錄 C2 依然存在(樹上那個半透明的節(jié)點),而 C2’ 是我們 Rebase 到 main 分支上的 C2 的副本熊咽,它們具有不同的 hash 值莫鸭,可以通過 log 查看。
之后我們也可以通過類似的操作横殴,把 main 快速前移到最新的結(jié)點:
$ git checkout main # 切換到 main 分?
$ git rebase test # 讓 main 分?以 test 為基被因,快速前移
# 當(dāng)然,z也可以用前面的 merge 操作
Rebase 操作在沒有沖突時將非常舒適衫仑,可以避免沒有意義的合并結(jié)點(尤其是涉及到遠(yuǎn)程倉庫時)梨与,但是一旦發(fā)生了沖突,操作將十分繁瑣文狱!這里不再贅述粥鞋,具體工程中如果遇到了請根據(jù) Git 的自動提示逐步操作。
而對于 Rebase 后的從分支上的結(jié)點瞄崇,就變成了所謂的「懸垂結(jié)點」呻粹,這些結(jié)點的訪問將十分復(fù)雜。此外苏研,如果這個從分支被廢棄尚猿,我們也可以用以下指令將其刪去:
$ git branch -d test # 刪除名為 test 的分?,用于合并后廢棄的分支
分叉 (Branch Diverged)
分叉是分支的一種特殊情況楣富,往往是因為某些「不友好」操作而產(chǎn)生,最終被廢棄掉伴榔。對于單人操作的倉庫纹蝴,其產(chǎn)生原因可能是:
- Reset 后舊分支:版本回退后重新提交,這種情況下往往是要棄用舊分支踪少。但如果舊的分支仍有需要保留的更改塘安,則需要 Cherry-Pick 等操作。
- Rebase 后從分支:上文介紹到援奢,Rebase 將創(chuàng)造更線性的主分支兼犯,但這樣做的代價是從分支將被廢棄,成為一個無用的分叉。
上述情形的發(fā)生往往可以人為進行控制切黔,而對于多人操作的倉庫砸脊,如果不同開發(fā)者同時對一個結(jié)點進行了更改,將會造成「不可控」的分叉纬霞,具體情形及解決方法將在下一節(jié)介紹凌埂。