程式碼管理工具-Git基礎介紹及常用技巧

北冥蝦發表於2022-04-27


前言:本文主要介紹git的原理、使用和一些技巧,目的在於使讀者對git的瞭解不僅僅侷限於簡單的使用push、pull命令,而要做到知其然且知其所以然。當然,本文並不會深入去探討諸如git的實現原理之類的深層次東西,畢竟它只是一個程式碼管理工具罷了,作為使用者,我們只要達到真正熟練使用的地步就夠了,至於更深層次的東西,諸位有興趣的可以自行學習研究。

另外,本文分支相關圖片取自learngitbranching,這是個用遊戲的方式,圖文結合學習git分支的網站,相當nice,推薦大家去完整過一遍,相信對於git的理解會更上一層樓。

Git起源

git是由Linux的作者Linus花兩週時間寫出的分散式版本控制軟體。在這之前,Linux社群使用BitKeeper作為版本控制系統,但是由於社群中有人試圖破解BitKeeper的協議,這惹惱了BitKeeper的東家BitMover公司,於是BitMover決定收回linux社群的免費使用權。

在這樣的背景下,Linus花了兩週時間寫出了git,在一個月內替換了BitKeeper,作為Linux的版本控制工具,並在後面不斷完善,最終成為了現在程式碼版本控制的首選

基本概念

三種工作域:

  • git目錄(git direcdtory):即倉庫(Repository),儲存專案中所有版本和相關資訊,是git存放資料和資訊的地方
  • 工作目錄(work directory): 是對應專案的某個版本的檔案集合,對應從 git 目錄中解壓出來的供使用者進行操作和修改的資料和資訊
  • 暫存目錄(staging area):用於記錄下次commit時需要儲存的檔案列表

三種檔案狀態:

  • committed:已提交狀態,表示資料檔案已經被儲存至本地資料倉儲中。
  • modified:修改狀態,表示檔案已被修改,但是尚未被提交(儲存)。
  • staged:暫存狀態,表示是被標記了的被修改檔案,在下次提交時會將所有標記過的修改儲存。
    另外新增的檔案為untracked file,未在git管理範圍內,需要先通過git add新增到暫存目錄,然後其狀態會變為staged
    git
    如圖所示,git分為遠端和本地。遠端遠端伺服器儲存了倉庫資訊,而本地則是三種工作域都有。

Branch、HEAD和Commit tree

本地提交程式碼到遠端的一般流程:

  1. git add,將修改儲存到暫存區(stage area)
  2. git commit,將暫存區中的檔案推送到本地分支,本地倉庫更新
  3. git push,將本地倉庫的更改推送到遠端倉庫,遠端倉庫更新

可以看到,想要更新程式碼,commit是必不可少的。每次commit都會生成一個工作目錄的快照(前提是有修改),在git中,這些commit的快照資料使用樹(tree)結構來管理,稱為提交樹(Commit Tree)或者工作樹(Work Tree)。

Git 的分支(Branch),其實本質上僅僅是指向提交物件的可變指標。分支是git的核心所在,因為分支的存在,工作樹才是工作樹而不是工作"線"。可以將每個分支看作工作樹的分叉,專案可以在不同分支上並行開發,然後在合適的時機又可以合併在一起,這都是分支的作用。

HEAD表示當前所處提交位置。通常來說,HEAD是指向某個分支的,當然也可以手動切換將HEAD指向工作樹中的任意commit(這種情況稱為HEAD分離)。
Branch
圖中一共有c0-c4四個提交,main、bugFix和feature三個分支,三個分支分別指向C1、C3、C4三個提交,HEAD處於分離狀態,指向C2
瞭解了以上的基礎概念以後,讓我們來探討一下git分支相關內容。

Git分支

之前說了,分支的存在是為了並行開發,每一個分支都會指向一個具體的提交。需要多人協作的專案離不開對分支的操作。
通常來說,新建一個專案時預設分支為master,可以根據需要新建develop、release等分支。

下面介紹一些常見的git分支相關命令

  • git commit。以當前提交為父節點生成新的子提交節點,並且當前HEAD/Branch會指向新生成的節點
  • git branch。單獨使用時檢視所有分支,後面加一個branchName表示在當前節點建立名為branchName的新分支,git checkout -b branchName也能達到相同的效果,區別在於後者會將HEAD指向新建立的分支
  • git checkout BRANCH/COMMIT。切換到對應的分支或者提交節點,之前有提過,直接切換到提交節點就是HEAD分離狀態
  • git merge BRANCH/COMMIT。將當前提交節點和指定提交節點合併並生成新的提交節點,新生成的節點有兩個父節點。
  • git rebase BRANCH/COMMIT。將當前分支所在節點及之前的節點中有且在另一分支/節點中所沒有的節點全部按序複製一份到目標分支,然後將當前BRANCH/HEAD移到目標位置。
  • git reset COMMIT.撤銷commit到指定提交,與git branch -f CURRENT_COMMIT COMMIT效果是一樣的。但是這種更改只能在本地體現,是不能同步到遠端的
  • git revert COMMIT。要想撤銷能同步到遠端,就得用到git revert COMMIT。此命令會通過新生成提交節點的方式撤銷指定的提交節點
  • git cherry-pick COMMIT_1 COMMIT_2 ... 。選取指定的一些節點按序rebase到當前分支

git merge 和 git rebase 的區別和抉擇

git rebase:

  • 優點:提交樹呈線性,乾淨簡單
  • 缺點:修改了提交樹的歷史

git merge:

  • 優點:提交記錄順序正確,不會造成迷惑
  • 缺點:多分支時顯得看起來很複雜

兩者如何抉擇,該使用哪條命令來合併,這取決於使用者的習慣,如果對提交歷史順序的正確性有要求,就用git merge,否則使用git rebase

與遠端倉庫的互動

通常來說,開發的一般流程是在遠端建立一個倉庫,然後開發人員各自在本地clone倉庫,新建自己的分支進行開發,開發完成後再推送到遠端分支,再合併到主分支。
當我們clone遠端倉庫到本地時,會在本地複製一份倉庫資訊和工作目錄
Clone
注意,本地多了一個origin/main的分支,這個分支叫做遠端分支,它反映了你最近一次操作遠端倉庫的狀態。遠端分支比較特殊,不能像普通分支一樣通過checkout、branch等命令直接操控,必須通過pull、push、fetch等命令與遠端同步。

下面介紹一些常見的與遠端倉庫互動的命令:

  • git clone Repository。將遠端倉庫克隆到本地,可通過git clone -b branchName Repository命令指定克隆的分支。
  • git fetch,下載遠端分支的最新狀態到本地,只會更新本地的遠端分支,不會更改HEAD和本地分支遠端
    有更新
    Clone1
    使用 git fetch後
    Clone2
  • git pull。可以視作 git fetch + git merge 的縮寫。存在下列常見情況,遠端分支有人提交了更新,自己本地也提交了更新,這時候就需要先拉取最新的程式碼
    使用git pull,第一步是下載最新分支更改,即git fetch
    ![git pull1](https://img2022.cnblogs.com/blog/1491971/202204/1491971-20220427100530923-1663855047.png
    第二步合併當前分支與遠端分支,即git merge o/main
    git pull2
  • git push。將當前分支的本地更改推送到遠端分支。git push會更新本地的遠端分支,並且將修改同步到遠端對應的分支
    git push1
    使用git push推送
    git push2

關於一些實際開發場景的問題和解決方式

前提:團隊目前使用develop分支作為本地測試環境,Release分支作為程式碼備份,通過手動部署的方式部署到真實環境,也就是說,每次開發完成以後會將develop分支合到release分支。

問題:在某次開發後develop最新提交為C2,但是C2忘了合併到release分支,然後又另外建立了個分支nas開發nas相關功能C3,現在nas已經合併到develop分支。這時候nas相關功能由於存在一些問題,需要回滾,請問這時候應該怎麼做?
Condition

解決方案:使用git revert C3回滾
Resolve1

在回滾以後另外新建分支開發新功能C4,之後又想將nas分支的功能重新合併到develop,應該怎麼做?
Resolve2

解決方案: git revert C3'
Resolve3

相關文章