文章

Git 實用指南:常用命令及最佳實踐

本文收錄了作者本身,於實務上經常使用到的Git指令作為分享。

引言

過去版本控制系統 (Version Control System, VCS) 多是基於中央化的模型,例如CVS (Concurrent Versions System) 與稍後推出的SVN(Subversion)。 雖然這對軟體開發有很大的幫助,提供了基本的版本控制功能,比如版本追蹤和多人協作,但在使用過程中仍然存在一些缺陷和限制:

集中式版控的限制

集中式版控系統將專案資料儲存於單一伺服器內,一旦伺服器出現故障問題將影響整個團隊。高度依賴網路連接,一旦網路延遲或斷開將阻礙提交和更新。其分支管理不夠靈活導致開發人員經常避免使用分支。需要更嚴謹的版控權限,一旦受到資安威脅,則可能所有的專案資料都將洩漏。

分散式版控系統

GIT 引入了分散式架構,允許開發者即使在離線時也能進行提交和查看歷史,每個工作站同時作為資料備份。它的分支管理靈活高效,促進了開發和協作,且其先進的合併機制及SHA-1哈希確保數據完整性和安全。

開始之前要先學會的作業系統指令

Windows(PowerShell)Windows(命令提示字元)MacOS / Linux說明
Set-Locationcdcd切換目錄
Get-Locationchdirpwd取得目前所在的位置
Get-ChildItemdirls列出目前的檔案列表
mkdirmkdirmkdir建立新的目錄
New-Itemtouch建立檔案
Copy-Itemcopycp複製檔案
Move-Itemmovemv移動檔案
Remove-Itemdelrm刪除檔案
Clear-Hostclsclear清除命令視窗上的內容

Git 基礎

確認並安裝好Git後藉由TerminalGit Bash下達指令,即可開始你的版本控制之旅。

1
2
which git #查看當前Git安裝在哪個目錄
git --version #檢查是否已安裝Git及其版本

Windows 可至 Git 官網進行下載
Mac 可用Homebrew 執行安裝brew install git
基於Debian的 Linux 發行版 ,可以使用apt-get執行安裝sudo apt-get install git

工作流程命令

以下將介紹開發人員日常使用的基本工作流程,包含以下核心指令:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
git init      #為當前工作目錄建立一個".git"環境目錄 (不論是全新的工作目錄或是現有專案)
git clone     #從遠端儲存庫克隆專案 (與上述指令擇一使用即可)
git status    #查看工作目錄和暫存區狀態
git restore   #撤銷並還原變更至最新一次提交狀態
git add       #將變更添加到暫存區,準備提交
git commit    #將暫存區變更進行提交
git push      #將提交推送至遠端儲存庫上
git fetch     #從遠端儲存庫獲取最新的歷史紀錄
git pull      #從遠端儲存庫獲取最新的歷史紀錄並立即與當前分支合併
git branch    #以當前分支所在基準點建立新分支
git checkout  #切換至指定分支
git merge     #將當前分支變更合併至指定分支上
git rebase    #以指定分支將當前分支重定基準點
git diff      #可自由選定兩個<commit hash>進行差異比對,並產出.diff檔案至指定目錄

對於創建、切換、管理、合併、重定基準分支等操作可至 Learn Git Branching 網站嘗試操作。

初始化/克隆儲存庫 (git init/clone)

透過終端機(Terminal)指令操作至專案工作目錄(WorkFolder),使用先前已安裝至作業系統(OS)的環境變數Git,進入Git指令模式下達git init為你的工作目錄建立一個新的子資料夾.git,這個資料夾內的檔案構成了Git的完整版本控制所需的結構。

1
2
git init  #為當前工作目錄建立 ".git\"環境
git config --global init.defaultBranch main #修改預設分支名稱為"main"

假如你已經擁有一個遠端儲存庫(例如Github、Bucket Server、Gitea、Gitlab等),於遠端伺服器上投過GUI操作創建一個全新的儲存庫,並拿到儲存庫的Clone Url,即可使用git clone url進行本地克隆,而不必再使用git init指令來創建git工作目錄。

1
git clone https://github.com/yourUserName/yourRepository.git

而在git clone期間第一次需要你輸入並驗證你的使用者帳密,以確保你擁有訪問該儲存庫的權限。

環境配置 (git config)

在 Git 中,git config 是一個非常強大的命令,用於設置或讀取配置變數,這些變數控制 Git 的外觀和操作的方方面面。

層級說明查看配置實例
系統適用於系統上所有用戶和所有儲存庫的配置git config --system --list
全域僅適用於當前用戶的所有儲存庫的配置git config --global --list
本地僅適用於當前儲存庫的配置git config --local --list

自動化認證

1. 啟用 Credential Helper 工具
1
2
3
4
5
git config --global user.name "自行設定你的名字"
git config --global user.email "自行設定你的信箱"
git config user.name #查看當前儲存庫配置中的用戶名
git config user.email #查看當前儲存庫配置中的郵件地址
...

對於 Windows 而言,當使用 git config --global credential.helper wincred 命令設定 Credential Helper 時,Git 會使用 Windows 的認證管理員(Credential Manager) 來安全地存儲和管理認證信息。這樣,當你透過 HTTPS 訪問 Git 儲存庫且需要認證時,Git 會自動從認證管理員中獲取使用者名稱和密碼,而不需要你每次都手動輸入。

2. 使用 SSH Key 協議

SSH Key 通常儲存在用戶的家目錄下的 .ssh 目錄中。SSH Key 不是由 Git 直接管理,而是由 SSH 客戶端管理。

1
2
3
ssh-keygen -t rsa -b 4096 -C "your_email@example.com" #生成 SSH-pair (如果你還沒有SSH金鑰的話)
ls -al ~/.ssh #列出現有的 SSH Keys
cat ~/.ssh/yourId_Rsa.pub #查看你的 SSH 公鑰內容

然後將顯示的公鑰內容添加到 GitHub、GitLab 或其他 Git 服務提供商的 SSH Key 設定中。

yourId_Rsa.pub 替換為你的公鑰檔案名。這會顯示公鑰的內容。

別名設置

這對於要下很多--options是非常節省時間與開發效率的。

1
2
3
4
5
git config --global alias.st "status" #`st`就可以被Git視為`status`指令
git config --global alias.ci "commit"
git config --global alias.br "branch"
git config --global alias.co "checkout"
git config --global alias.lg "log --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit"

其他配置

1
2
3
4
5
6
7
8
git config --global core.editor "code --wait" #設定VSCode為預設編輯器
git config --global merge.tool vimdiff   #設定差異分析工具: 視覺化看到 git diff 輸出的工具
git config --global core.autocrlf true   #在Windows 上  (設定換行符處理)
git config --global core.autocrlf input  #在Linux/Mac 上(設定換行符處理)
git config --global --unset user.email   #刪除特定配置
git config --file /path/to/configfile user.name "自定義的另一個名字" #使用特定配置檔案
git config --global core.commentChar ";" #替換註解符號使commit訊息可以使用#字號開頭
...

查閱當前已配置的設定 git config --global --list
刪除不必要的設定 git config --global --unset <config>

狀態 (git status)

查看當前工作目錄與暫存區狀態

1
2
git status
git status -s #使用 -s 或 --short 選項可以獲得更簡潔的輸出格式

輸出解釋:

  • Changes to be committed(將要被提交的更改)
  • Changes not staged for commit(未暫存待提交的更改)
  • Untracked files(尚未追踪的文件)

如果使用-s選項則檔案狀態有以下幾種:

  1. A 表示新增
  2. M 表示修改
  3. D 表示刪除
  4. 空格 表示無變化
  5. ?? 表示未追蹤

還原 (git restore)

它專注於修改工作目錄暫存區的內容

  1. 撤銷並還原變更至最新一次提交狀態
    1
    2
    3
    
    git restore <filename>                     #還原單個檔案至最後一次提交
    git restore --staged <filename>            #從暫存區取回檔案至工作目錄
    git restore --staged --worktree <filename> #同時從暫存區和工作目錄撤銷
    
  2. 使用來源選項 --source: 從特定歷史提交文件恢復
    1
    2
    3
    4
    
    git restore --source=<commit> <filename> #將檔案還原到指定的來源狀態
    git restore --source=<branch> <filename> #從其他分支恢復檔案
    git restore --source=<tag> <filename>    #從指定標籤名來還原檔案
    git restore --source=<commit> <path>     #將指定工作目錄還原至指定歷史提交狀態
    
  3. 將整個工作目錄皆撤銷
    1
    2
    
    git restore <path>                     #撤銷指定工作目錄的變更
    git restore --staged --worktree <path> #撤銷指定工作目錄包含暫存區的變更
    

假設工作目錄位於 C:\\website\ 則替換 <path>

指令git restore屬於Git v2.23之後的版本才有,若是之前的版本請參考附錄一: 舊版本撤銷變更方法

重設 (git reset)

主要用於重設當前分支的 HEAD 到另一個指定的狀態,並根據需要修改暫存區和工作目錄。

1
2
3
git reset --soft <commit>  #移動HEAD指標到指定提交,但不改變暫存區或工作目錄
git reset --mixed <commit> #移動HEAD並更新暫存區以匹配指定提交,但不改變工作目錄
git reset --hard <commit>  #移動HEAD並使暫存區和工作目錄都匹配到指定提交,丟棄未提交的更改

如果只想撤銷對檔案的修改,建議使用git restore,如果需要調整分支的提交歷史則使用git reset 指令。

平常使用的git reset <commit>即是--mixed模式
git reset 可以操作提交歷史,這是其與 git restore 最大的不同。

使用git reset 指令需謹慎使用,它會更改操作歷史。

暫存區 (git add)

  • 將變更添加到暫存區,準備提交
    1
    2
    3
    4
    
    git add .   #將當前所在目錄及子目路的變更整個添加到暫存區 (只包含新文件&變更檔案)
    git add -A  #將當前工作目錄的所有變更整個添加到暫存區 (無論新增、修改、刪除皆添加) 
    git add <filename>              #添加單個檔案
    git add <filename1> <filename2> #添加多個檔案
    

    git add . 取決於執行此命令時當下所在目錄,只會添加該目錄及其子目錄的所有變更
    git add -A 不論當下所在目錄為何,整個環境的變更都會添加進暫存區

  • 使用 -p 指令批次且選擇性地提交修改
    1
    
    git add -p  #將所有工作目錄的檔案進行分割區塊(hunk),且由使用者自行決定是否添加。
    
    1. y 將此片段添加到暫存區。
    2. n 不將此片段添加到暫存區。
    3. q 退出 patch 模式,不再審查剩餘的片段。
    4. a 將此片段以及後續所有片段添加到暫存區,不再詢問。
    5. d 跳過此片段以及後續所有片段,不將它們添加到暫存區。
    6. s 將當前片段分割成更小的片段,以便更精細地審查變更。
    7. e 手動編輯當前片段。這允許你精確控制哪些行被標記為已暫存。

使用該指令有一些好處:

  • 當今天修改一個涵蓋很多功能的檔案時,可以只選擇性地添加片段,使當次Commit更加清晰。
  • Commit之前,可以透過此命令來審查修改,以免提交到不必要的除錯用程式碼。

提交 (git commit)

  • 將暫存區變更進行提交
    1
    2
    3
    
    git commit                 #省略 -m 選項則會開啟編輯器,撰寫多行提交訊息。
    git commit -m "提交訊息"    #提交訊息依據公司規範或個人喜好定義格式
    git commit -a -m "提交訊息" #該命令包含了git add -A的功能,並且也自動從暫存區提交至遠端
    
  • 使用 --amend 指令修改前一次提交的內容
    1
    2
    3
    4
    
    git add <filename>            #添加要提交的檔案至暫存區
    git commit --amend            #加入暫存區變更和開啟一個編輯器更改訊息
    git commit --amend -m "新訊息" #加入暫存區變更不開啟編輯器直接更改訊息
    git commit --amend --no-edit  #加入暫存區變更且不更改訊息
    

    建議在推送(push)至遠端儲存庫之前使用git commit --amend以避免需要強制推送進而影響其他團隊成員的潛在風險。

任何直接對Git的歷史進行異動的行為,實際上都屬於重寫該分支的歷史。如果今天這個提交(commit)已被推送(push)至遠端儲存庫上,將會發生本地倉庫歷史與遠端上的歷史不匹配,如果在這樣的情況下還是需要推送(push)就必須使用git push --force強制推送並覆蓋遠端分支的歷史。

推送 (git push)

  • 將本地提交推送至遠端儲存庫上
    1
    2
    3
    4
    5
    6
    
    git push                            #推送至預設的遠端追蹤分支
    git push <remote> <branch>          #遠端儲存庫的名稱(ex:origin)與要推送的分支名稱
    git push --all <remote>             #將所有本地分支都推送上遠端儲存庫
    git push -f                         #強制推送當前分支至遠端儲存庫上並覆蓋歷史
    git push --force <remote> <branch>  #強制推送本地分支並覆蓋遠端分支的歷史
    git push <remote> --delete <branch> #刪除遠端儲存庫上的指定分支
    

    若無特別設定則<remote>預設為origin

  • 設定上游追蹤分支
    1
    
    git push -u origin <your-new-branch>
    
  • 刪除遠端分支
    1
    
    git push origin --delete <your-branch-name>
    

獲取 (git fetch)

從遠端儲存庫獲取最新的歷史紀錄

1
2
3
4
5
6
7
8
9
git fetch <remote>          #獲取遠端儲存庫的更新
git fetch <remote> <branch> #獲取特定分支的更新 (ex: git fetch origin main)
git fetch --all             #獲取所有遠端儲存庫的更新
git fetch --tags            #獲取遠端上的標籤以維持同步
git fetch --no-tags         #獲取遠端上的更新但排除標籤
git fetch --prune           #獲取遠端上的現存分支並自動刪除在本地端所有遠端儲存庫中已經不存在的分支
git fetch -p --prune-tags   #這將清除在遠程倉庫中已經不存在的本地標籤
git fetch --depth=<number>  #限制獲取的深度(筆數)以節省時間
git fetch --recurse-submodules #同時更新任何子模組的變化

直接下 git fetch --all --prune --recurse-submodules 來同時獲取所有更新、清理不存在的遠程分支並更新子模組。

拉取 (git pull)

從遠端儲存庫獲取最新的歷史紀錄(fetch)並立即與當前分支合併(merge)。

1
2
3
4
5
git pull                    #使用預設的遠端儲存庫和追蹤的上游分支
git pull <remote> <branch>  #指定遠端儲存庫和上游分支
git pull --rebase           #git pull 會使用 rebase 而不是 merge 的方式來整合更改
git pull --no-commit        #拉取更改但不自動創建一個合併提交,允許在合併前檢查然後手動提交
git pull --squash           #拉取的所有提交都會被壓縮成一個提交,有助於保持乾淨提交歷史

養成良好習慣每天開始工作之前,都先進行git pull再開始工作。

分支管理 (git branch)

介紹關於分支的命名規範、創建、切換、合併、刪除等指令方法。以下是一些核心的指令和操作:

分支類型命名規則例子
功能feature/<功能名稱>feature/user-authentication
修復fix/<修復的問題>fix/login-error
發布release/<版本號>release/1.0.0
熱修復hotfix/<熱修復的問題>hotfix/critical-login-issue
開發devdevelop通常只有一個 devdevelop
主分支mainmaster通常只有一個 mainmaster
任務ID<類型>/<任務ID>-<描述>feature/PROJ-123-user-profile-page

例如我有一個JS漏洞修補、以及一個JS套件升級的例子

1
2
3
4
5
git branch fix/vulnerability-CVE-XXXX-XXXX  #修補特定漏洞
git branch fix/multiple-vulnerabilities     #修補多個漏洞
git branch update/library-name              #升級特定庫
git branch update/library-name-to-v3.3.6    #升級特定庫(包含版本號)
git branch update/all-libraries             #升級所有庫

規範文件
許多組織會在其開發者文檔或專案貢獻指南中明確規定分支命名規範,新成員應該參考這些文檔以確保遵循最佳實踐。

創建分支

1
git branch branch_name #創建一個新的分支,但仍停留在當前分支

切換分支

1
2
git checkout branch_name #切換到指定的分支
git switch branch_name # ~2.23

Git 2.23 版本開始,git switch 是一個更直觀的方式來切換分支。

合併分支

1
git merge branch_name #將指定分支的歷史合併到當前分支。這通常用於將功能分支的更改合併到主分支

刪除分支

1
2
git branch -d branch_name #安全地刪除指定的分支。只有當分支已經被合併時才能刪除
git branch -D branch_name #強制刪除指定的分支,即使它尚未合併。

查看分支

1
2
git branch #列出本地所有分支,當前分支會以星號標記
git branch -a #顯示所有分支,包括遠端分支

創建並切換到新分支

1
2
git checkout -b feature_y #創建一個新分支並立即切換到該分支
git switch -c feature_y #使用 git switch 命令創建並切換到新分支

一致性和清晰性
保持分支名稱的一致性和清晰性至關重要,它應該能夠讓團隊成員一目了然分支的用途、涉及的功能、預期的發布版本等信息。

查看歷史提交 (git log)

介紹如何使用 Git 命令來查看儲存庫的提交歷史。這可以幫助用戶追蹤和審查過去的更改,包括每次提交的作者、日期、提交訊息以及具體變更。以下是一些核心的指令和操作:

1
2
3
4
5
git log               #顯示提交歷史,包括作者、日期和提交訊息
git log -p            #顯示每次提交的差異
git log -n 筆數       #只顯示最近的N筆提交
git log --oneline -3  #用一行來顯示最新N筆提交
git log -- README.md  #只顯示單一檔案的歷史提交訊息

其他進階使用請參考Git 進階語句第七項介紹

差異 (git diff)

  • 基本用法
    1
    2
    3
    4
    5
    6
    7
    8
    
    git diff #顯示自上次暫存以來工作目錄中的文件更改
    git diff --staged #顯示自上次提交之後,已暫存待提交的更改。
    git diff <commit1> <commit2> #顯示兩個提交之間的差異。
    git diff <branch1> <branch2> #比較不同分支的差異,這對於審查分支中的變更非常有用。
    git diff path/to/file #只顯示某個文件或目錄的差異
    git diff --stat #顯示差異的統計信息,如更改的文件數量、插入和刪除的行數。
    git diff HEAD #使用 HEAD 來表示當前分支的最新提交。
    git show <commit> #顯示某次提交的詳細信息:包括提交的內容差異,以及相關的元數據信息
    
  • 產出差異檔案
    1
    
    git diff commit1 commit2 > changes.diff #檔案名稱可自由定義,預設產出在工作目錄
    
  • 將有差異的檔案產出至指定目錄
    1
    2
    
    git diff --name-only commit1 commit2 #獲得更改的檔案清單
    git diff --name-only commit1 commit2 | xargs -I {} cp --parents {} </path>
    

    這裡xargs -I {}用於將git diff輸出視為cp的命令,
    cp --parents用於維持原始的目錄結構,最後搭配path產出至指定目錄。

Git 進階語句

“進階語句”在 Git 使用上指的是那些超出基本命令(如git addgit commitgit push等)的高級功能和技巧,它們可以讓你更有效率地管理代碼庫、解決複雜問題,以及優化你的工作流程。以下是一些實用的進階 Git 語句和實踐:

1. 使用 git stash 來暫存更改

當你需要快速切換分支,但又不想提交未完成的工作時,可以使用git stash來暫存你的更改:

1
2
3
4
git stash                     #將當前變更存放至暫存區
git checkout other-branch     #切換至其他分支並完成一些作業
git checkout original-branch  #切換回原來分支
git stash pop                 #從暫存區中取回前一次的暫存變更集合

2. 利用git rebase進行歷史整理

使用 git rebase 可以重新整理提交歷史,使其更清晰:

1
2
git config --global core.editor "code --wait" #修改編輯器為 Visual Studio Code
git rebase -i HEAD~5

上面的命令會打開一個交互式界面(文件編輯器),讓你可以選擇重新排序、合併或修改最近的5次提交。

如果變基過程中遇到衝突:

1
2
git rebase --continue #解決衝突後繼續執行變基
git rebase --abort    #不解決衝突全部捨棄回到原點

3. 修正最後一次提交

如果你需要修改最後一次提交(比如忘記添加某個文件,或者提交信息寫錯了),可以使用:

1
2
git commit --amend
git add #修改完成後提交

4. 使用 git cherry-pick 來選擇性合併提交

如果你想將某個分支上的特定提交引入當前分支,可以使用 git cherry-pick:

1
git cherry-pick <commit-hash-id>

5. 使用 git bisect 來找出導致錯誤的提交

當你不確定是哪次提交引入了某個錯誤時git bisect 可以幫助你快速定位:

1
2
3
4
git bisect start
git bisect bad                  #標記當前狀態為壞
git bisect good commit-hash-id  #標記一個你知道是好的提交
git bisect reset                #當找到導致問題的提交時,執行該命令並退出

然後 Git 會二分查找提交歷史來幫助你快速找到引入問題的提交。這對於在大量提交中定位錯誤非常有效。

  • 範例情境: 當你的專案出現難以追蹤的錯誤,但在上個版本或是幾週前是好的,但現在已有上百個提交。
    你可以遵循以下規則,直到找到問題: plaintext 1. 啟動bisect。 2. 標記目前的HEAD 為壞的。 3. 標記一個月前的某個提交為好的。 4. 依據自動檢出的每個提交運行你的測試腳本或手動測試。 5. 根據測試結果繼續標記好或壞,直到找到問題。

6. 使用 git reflog 恢復丟失的提交

1
git reflog

這個命令顯示你本地的操作歷史(包括已經用 git reset 丟棄的提交)。當你意外丟失更改時,可以使用它來恢復。

1
2
3
4
1a410ef HEAD@{0}: commit: Fix bug in your code
ab1afef HEAD@{1}: merge master: Fast-forward
34ac2e1 HEAD@{2}: commit: Add new feature
...

找到丟棄的提交後,使用git resetgit checkout到丟失的提交,即可救回丟失的更改。

使用git checkout <commit>可以先檢查該commit是否就是丟失的提交,若是則可以git checkout -b <newBranchName>創建一個新的分支繼續工作。
使用git reset --hard <commit>則是在你完全確定要徹底回到那個狀態時使用。

git reflog預設只保留90天內的紀錄
git reset --hard 一旦恢復到某個提交,則該提交之後的所有更改都會被捨棄,因此必須謹慎操作。

7. 利用 git log 進行高級篩選&輸出格式調整

git log 支持許多強大的選項,讓你可以根據作者、日期、內容,等進行過濾:

1
git log --author="Author Name" --grep="特定關鍵字" --since="2 weeks ago" --until="yesterday"

指令日期部分 --since等同--after,而--until等同--before
日期格式可以填入2 weeks ago2024-04-27yesterdaylast month

美化輸出:使用不同的格式化選項來美化 git log 的輸出

1
2
3
4
5
git log --pretty=format:"%h - %an, %ar : %s"
git log --pretty=format:"%H %nAuthor: %an %nDate: %ad %n%s%n"
git log --pretty=format:"%h %ad - %s" --date=format:"%Y-%m-%d"
git log --pretty=format:"%Cred%h%Creset - %s %Cgreen(%cr) %C(bold blue)<%an>%Creset"
git log --graph --decorate --pretty=format:"%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset"

可用格式選項:

語法描述語法描述
%H提交的完整哈希字串%h提交的簡短哈希字串
%T樹物件(tree)的完整哈希字串%t樹對象的簡短哈希字串
%P父物件(parent)的完整哈希字串%p父對象的簡短哈希字串
%an作者名字(author name)%ae作者信箱(author email)
%ad完整作者日期 (可用--date選項自訂格式)%ar相對作者日期(例如: 2 weeks ago
%cn提交者名字(committer name)%ce提交者信箱
%cd完整提交日期%cr相對提交日期
%s提交資訊標題  

返回Git基礎繼續閱讀

8. 子模塊(Submodules

當你的專案依賴於其他 Git 儲存庫的內容時,使用子模塊可以將這些依賴儲存庫作為你專案的一部分。這對於管理專案的外部依賴非常有用。

1
git submodule add <repository> <path>

通過這些進階語句和實踐,你可以更高效地使用 Git,解決複雜問題,並優化你的開發工作流程。不過,請記得,在使用這些進階功能時,特別是那些會改變歷史的命令(如git rebase和git commit –amend),應該格外小心,以免意外修改公共歷史或丟失數據。

Git 最佳實踐詳解

✅ 保持提交原子化

原子化提交意味著每一次的提交只包含一個功能的更改或一個問題的修復。

優點:

  • 這種做法使得修改更加透明,方便同事理解每一次提交的目的。
  • 如果需要回滾某個特定的更改,也讓這一過程變得簡單。
  • 可以避免一次提交中包含過多無關的更改,從而讓代碼審查更加高效。

假設我正在開發一個Web應用程式,今天遇到一個前台觸發的存檔功能要進行調整,依序調整順序為「Controller類別 > Services類別 > Model類別」這三個類別都各自有修改調整,那麼原子化提交可遵循以下步驟來實現:

步驟 1:計畫你的更改

仔細規劃每個類別的修改內容,確定每次提交都能夠獨立完成一個具體的功能或修復一個具體的問題

  • Controller:修改以接受前台的存檔請求並調用相應的服務。
  • Services:實現具體的存檔邏輯,如資料驗證、處理業務規則等。
  • Model:調整數據模型以符合新的業務規則或資料結構。

步驟 2:逐一進行更改並提交

按照功能的依賴順序(在這個例子中是 Model -> Services -> Controller),分步進行更改和提交。

  • 提交 Model 更改
    1
    2
    
    git add projectPath/models/abcModel.cs
    git commit -m "Update Model class to support new data structure for save function"
    
  • 提交 Services 更改
    1
    2
    
    git add projectPath/services/ServiceClass.cs
    git commit -m "Implement save logic in Service class for handling save requests"
    
  • 提交 Controller 更改
    1
    2
    
    git add projectPath/controllers/ControllerClass.cs
    git commit -m "Modify Controller to process save requests using the updated Service class"
    

步驟 3:進行測試

每次提交後,進行單元測試和集成測試以確保你的更改沒有引入任何問題。只有當確定每部分的更改都不會影響應用的其他部分時,這個提交才算完成。

步驟 4:重複審查

在完成所有更改並提交後,回顧整個更改過程,確保每個提交都遵循了原子化的原則,並且每次提交的訊息都足夠清晰。

📝 編寫有意義的提交信息

良好的提交信息是團隊溝通的重要一環。它應詳細描述所做更改的背景、原因及影響範圍,幫助團隊成員理解每次提交的背後邏輯。

優點:

  • 有意義的提交信息也有助於未來的問題排查和代碼維護,尤其當回顧特定更改的決策時。
  • 為了實現這一點,提交信息應包含清晰的標題、詳細的描述以及相關任務的鏈接或ID。
1
2
3
4
5
更新 UserModel 以支援其他用戶詳細信息

- 新增屬性「出生日期」和「電話號碼」以支援使用者設定檔增強。
- 調整「Validate」方法以包含對新屬性的檢查。
- 更新文件註解以反映變更。
1
2
3
4
5
在 UserService 中實作使用者設定檔更新的儲存邏輯

- 確保新欄位「出生日期」和「電話號碼」的資料驗證。
- 處理異常並為資料驗證失敗提供有意義的錯誤訊息。
- 更新單元測試以涵蓋保存方法中的新邏輯。
1
2
3
4
5
修改 UserController 以處理新的儲存請求格式

- 從儲存請求中解析新欄位「出生日期」和「電話號碼」。
- 使用更新的使用者資料模型呼叫「UserService.Save」方法。
- 根據儲存操作結果傳回適當的HTTP狀態碼。

提交訊息的結構:編寫有意義的提交信息通常遵循一定的結構,包括一個簡短且具描述性的標題,以及一個可選的詳細描述區塊,後者進一步說明更改的內容和原因。合理地使用這種結構可以使提交信息更加清晰和有用。

🌲 使用分支策略

合適的分支策略能夠促進團隊協作並提高開發效率。分支策略如 Git Flow 定義了一套明確的規則來管理不同類型的分支,如功能分支、發布分支和維護分支。這樣不僅幫助團隊成員理解每個分支的作用,也保障了代碼的穩定性和發布流程的順暢。選擇合適的分支策略並堅持使用,是實現高效協作的基礎。

步驟 1:確定適合的分支策略

根據你的團隊大小、專案類型和發布周期選擇一種分支策略。例如 GitHub Flow 是一個簡單且高效的選擇,它主要包括以下規則:

  • 主分支 (main 或 master) 保持隨時可發布的狀態。
  • 所有新功能和修復工作在新分支上進行。
  • 完成後通過 Pull Request (PR) 向主分支合併更改。
  • 在合併之前進行代碼審查和自動測試。

步驟 2:為存檔功能創建一個新分支

1
2
3
git checkout main                             #切換至主分之
git pull                                      #養成良好習慣先獲取最新歷史提交並合併
git checkout -b feature/save-function-update  #此時在執行創建新分支以確保是從最新的版本開始作業

步驟 3:在新分支上進行開發和提交

按照原子化提交的原則,在這個新分支上進行你的開發工作。為 Model、Services 和 Controller 的更新分別進行提交,並遵循編寫有意義的提交訊息的實踐。

步驟 4:推送分支並創建 Pull Request

開發完成後,推送你的分支到遠端儲存庫

1
git push -u origin feature/save-function-update

然後,在你的 Git 服務平台(如 GitHub、GitLab)上創建一個 Pull Request,將你的更改合併回主分支。這個過程中,你的團隊成員可以進行代碼審查,並執行自動測試以確保更改不會破壞現有功能。

步驟 5:合併並清理

一旦 Pull Request 獲得批准且所有測試通過,你就可以合併它到主分支了。合併後,不要忘記刪除功能分支,以保持儲存庫的整潔。

1
git branch -d feature/save-function-update

採用這樣的分支策略,不僅可以確保主分支的穩定和安全,還能促進團隊間的透明溝通和協作。通过定期進行代碼審查和自動化測試,團隊可以及時發現並修復錯誤,從而提高代碼質量。

🔄 定期拉取和推送

定期從遠端儲存庫拉取更新是保持本地工作副本與團隊其他成員同步的重要手段。這有助於及時發現並解決合併衝突,避免工作阻塞。同樣地,及時將本地的更改推送到遠端,可以讓團隊成員了解你的進展,並進行協作或代碼審查。這種互動確保了團隊工作的透明度和連貫性。

步驟 1:定期拉取遠端變更

在開始新一天的工作之前,或在開始進行新的開發任務之前,你應該從遠端儲存庫拉取最新的變更。這可以通過以下命令完成:

1
2
git checkout main
git pull origin main

步驟 2:在開發過程中定期提交

在開發功能或修復錯誤時,按照原子化提交的原則,定期將你的變更提交到你的本地儲存庫。這不僅有助於跟踪你的開發進度,也使回滾到之前的狀態變得容易。

1
2
git add .
git commit -m "具體描述你的變更"

步驟 3:定期推送到遠端儲存庫

在完成一個功能的開發或達到一個重要的開發階段後,應該將你的變更推送到遠端儲存庫。這樣,團隊成員可以看到你的進度,並根據需要進行代碼審查或進一步的開發。

1
git push origin feature/your-feature-branch

步驟 4:在發起 Pull Request 之前再次拉取

在你準備將你的分支合併到主分支之前,最好再次從主分支拉取最新的變更,以確保你的分支能夠順利合併。

1
2
3
4
git checkout main
git pull origin main
git checkout feature/your-feature-branch
git merge main #意味著從main分支將最新歷史提交合併至當前your-feature-branch分支上

如果有衝突,解決它們,然後再次提交。這樣做確保了你的分支是基於最新的主分支狀態。

步驟 5:推送合併後的分支

如果你在合併過程中進行了提交,記得將這些變更推送到遠端分支。

1
git push origin feature/your-feature-branch

通過遵循這些步驟,你可以確保你的工作與團隊保持同步,並且遠端儲存庫總是反映最新的工作狀態。這有助於維護專案的整體健康和促進有效的團隊協作。

更新步驟 4 和 5:使用 Git Rebase

1
2
3
4
5
git checkout main
git pull origin main
git checkout feature/your-feature-branch
git rebase main #以main分支將最新歷史提交作為基準點更新至當前分支上
git push -f origin feature/your-feature-branch #需透過`-f`或`--force`強制推送並覆蓋遠端分支的歷史

若要使用 git rebase 方式進行提交,切忌要注意是否會影響到同一分支上正在開發的其他使用者

🚫 處理合併衝突

合併衝突是多人協作時難以避免的,學會有效解決它們是必要的技能。首先,理解衝突的原因並溝通找到最佳解決方案是關鍵。使用 Git 提供的工具,如 git mergegit rebase,可以幫助處理衝突。維護良好的代碼結構和清晰的分支策略同樣能減少衝突的發生。當衝突解決後,徹底測試合併的結果以確保代碼質量。

步驟 1:識別衝突

在合併或變基過程中,如果 Git 提示存在衝突,首先需要確定哪些檔案受到影響:

1
git status

Git 會列出所有包含衝突的檔案。這些檔案的狀態會被標記為未合併(unmerged)。

步驟 2:手動解決衝突

打開包含衝突的檔案,尋找由 Git 標記的衝突區域,這些區域會被包含在 «««< 和 »»»> 之間。Git 會使用這些標記來指示不同分支上的變更:

1
2
3
4
5
<<<<<<< HEAD
// 這部分是你當前分支上的代碼
=======
// 這部分是嘗試合併進來的分支上的代碼
>>>>>>> feature/your-feature-branch

步驟 3:標記衝突為已解決

解決了檔案中所有的衝突後,使用 git add 命令將它們標記為已解決:

1
git add 解決衝突的檔案名稱

步驟 4:完成合併或變基

如果你正在進行變基,則使用以下命令:

1
git rebase --continue

如果你正在進行合併,一旦衝突解決,則使用以下命令:

1
git commit

Git 會打開一個編輯器讓你輸入合併提交的訊息。如果你沒有額外的訊息要添加,可以直接使用預設的合併提交訊息。

步驟 5:測試並驗證更改

在解決衝突並完成合併或變基後,務必執行應用的測試,以確保你的更改沒有引入任何問題。

📁 使用 .gitignore

.gitignore 文件允許你定義哪些文件和目錄應被 Git 忽略,不加入版本控制。這通常包括編譯產物、配置文件和日誌文件等。配置 .gitignore 是保持代碼庫整潔的關鍵步驟,避免不必要的文件被跟蹤會減少儲存庫大小並提高效率。為不同類型的專案選擇或定製合適的 .gitignore 模板是一個好習慣。

步驟 1:創建或檢查 .gitignore 文件

首先,檢查你的專案根目錄下是否已經有一個 .gitignore 文件。如果沒有,創建一個:

1
touch .gitignore

步驟 2:指定要忽略的文件和目錄

在 .gitignore 文件中,你可以指定要忽略的文件模式。每一行指定一個模式,例如:

1
2
3
4
5
*.log         #忽略所有 .log 檔案
/logs         #忽略特定目錄
settings.json #忽略特定文件
*             #忽略所有文件
!README.md    #但不忽略 README.md (透過!取反向)

步驟 3:針對 C# 專案添加通用規則

對於 C# 專案,你可能會想在 .gitignore 文件中添加一些通用的規則來忽略編譯產物、NuGet 包等。例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 忽略 Visual Studio 臨時文件和編譯產物
bin/
obj/
*.user
*.userosscache
*.suo
*.userprefs
*.pdb
*.ilk
*.aps

# 忽略所有 NuGet Packages
packages/*

# 忽略 Resharper 目錄和文件
_ReSharper*/
*.resharper

步驟 4:提交 .gitignore 文件

將 .gitignore 文件添加到你的 Git 儲存庫,並提交:

1
2
git add .gitignore
git commit -m "Add .gitignore file"

步驟 5:檢查現有的追蹤文件

如果在添加 .gitignore 文件後發現仍有應該被忽略的文件已被追蹤,你可以使用 git rm –cached 命令將它們從版本控制中移除,但保留在本地檔案系統中:

1
git rm --cached fileName

以上步驟幫助確保你的專案中不會追蹤不必要的文件,從而簡化你的代碼庫並避免潛在的合併衝突。

🔒 保護主分支

主分支(如 mainmaster)應該保持穩定性,因為它通常被視為專案的“生產”或“發布”版本。通過設定分支保護規則來限制對主分支的直接更改,並要求通過代碼審查的 Pull Request 來合併更改,是確保主分支質量的有效方法。這種做法增強了代碼質量控制並促進了團隊間的合作。

步驟 1:訪問專案設置

在 GitHub 專案頁面,點擊 “Settings”。 在側邊欄中,選擇 “Branches”。

步驟 2:設置分支保護規則

在 “Branch protection rules” 部分,點擊 “Add rule”。 在 “Branch name pattern” 欄位中,輸入主分支的名稱(如 main 或 master)。

步驟 3:配置保護規則

根據你的需求配置以下一個或多個選項:

規則描述
Require pull request reviews before merging要求至少一次代碼審查才能合併 Pull Request。
Require status checks to pass before merging要求所有配置的 CI/CD 管線狀態檢查通過才能合併。
Require branches to be up to date before merging要求在合併 Pull Request 前,目標分支(如 main)必須是最新的。
Include administrators將上述的保護規則也應用於具有管理員權限的用戶。

步驟 4:啟用分支保護

配置完畢後,點擊 “Create” 或 “Save changes” 來啟用這些規則。 這些規則現在將適用於所有嘗試對指定分支進行更改的操作。

通過這樣做,你將能夠保護你的主分支免受不經意的破壞性更改,確保所有進入主分支的更改都經過了嚴格的審查和測試。這不僅提高了代碼的質量,也有助於促進團隊成員之間的協作和溝通。這是一個極好的實踐,特別是對於大型團隊或在較長的開發周期中運作的專案。

🧹 定期維護代碼庫

隨著時間的推移,Git 儲存庫可能會累積大量的提交歷史、分支和標籤,可能導致性能下降。定期執行維護任務,如 git gc 來優化儲存庫性能,清理不再需要的分支,有助於保持儲存庫的效率和可管理性。

  1. 清理遠端已刪除的分支 當分支被合併並在遠端刪除後,本地副本可能仍然保留。定期執行以下命令來清理這些已不存在於遠端的分支:
    1
    
    git fetch --prune
    
  2. 刪除不再需要的本地分支 開發完成後,一些特性分支或修復分支可能不再需要保留。使用以下命令查看本地分支:
    1
    2
    3
    
    git branch                #列出當前所有本地分支
    git branch -d branch_name #刪除已經被合併過的分支
    git branch -D branch_name #不論是否已經被合併過 皆強制刪除分支 
    
  3. 壓縮和優化儲存庫 Git 提供了一個垃圾收集命令來優化儲存庫的性能:
    1
    
    git gc
    

    這個命令會清理不可達物件(即那些不再從任何分支或標籤的根部可達的物件)、壓縮檔案,並優化儲存庫的結構。這是一個好習慣,可以定期手動執行,特別是在進行了大量的 Git 操作後。

  4. 檢查和修復損壞的物件 雖然不常見,但 Git 儲存庫有時可能會因為各種原因出現損壞。定期檢查儲存庫的完整性是一個好習慣:
    1
    
    git fsck
    
  5. 定期備份儲存庫 除了定期維護之外,確保你有儲存庫的定期備份也是非常重要的。這可以通過簡單地複製整個儲存庫目錄到另一個安全位置來實現,或者使用專業的備份工具。

通過進行這些定期的維護任務,你可以幫助保持你的 Git 儲存庫在最佳狀態,提高工作效率,並減少未來可能遇到的問題。

📚 保持學習和更新

Git 是一個強大且持續發展的工具,新功能和改進不斷被添加。關注 Git 的最新發展,學習新的命令和特性,可以幫助你更有效地使用 Git。參加社區活動,閱讀相關的博客和文檔,是持續提升技能的好方法。

定期查看官方文檔

  • Git 官方文檔:定期閱讀 Git 官方文檔。Git 的官方文檔經常更新,提供最權威的指導和參考。

參與社區和論壇

  • Stack Overflow:參與 Stack Overflow 等技術論壇,這些平台上有豐富的問題討論和解決方案。
  • GitHub Community:加入 GitHub 社區論壇,參與討論和分享。

訂閱相關博客和新聞

  • 專業博客:訂閱專業博客和新聞網站,如 GitHub Blog,了解 Git 和開發工具的最新功能、技術趨勢和案例研究。
  • RSS 閱讀器:使用 RSS 閱讀器訂閱技術博客和新聞,以獲取及時更新。

參加研討會和工作坊

  • 技術會議:參加 Git 和軟件開發相關的研討會,例如 GitHub Universe,這些活動不僅提供最新的行業知識,還有機會與其他專業人士交流。
  • 在線課程:利用 Coursera、Udemy 和 YouTube 等平台上的在線課程和教程,學習 Git 的新特性和進階技術。

實踐和實驗

  • 實驗新功能:在側項目或沙盒環境中實驗 Git 的新版本和特性。這不僅有助於學習,還可以測試這些新功能對現有工作流程的影響。
  • 內部分享:定期在團隊會議中分享學習心得和最佳實踐,促進知識共享。

關注安全更新

  • 訂閱安全通告:關注 Git 和相關依賴項的安全更新和漏洞修復,確保及時應用安全補丁。

通過持續學習和更新,你可以確保自己和你的團隊能夠有效利用 Git 提高開發效率,同時確保代碼的安全和穩定。记住,技術是不斷进步的,透過持續學習,你可以保持在技術浪潮的前沿。

🔐 安全實踐

在使用 Git 時,確保你的認證信息安全是非常重要的。避免將敏感信息存儲在代碼庫中,使用 SSH 金鑰而不是明文密碼進行認證,定期更換金鑰和令牌。此外,關注和應用安全補丁,保持 Git 客戶端的更新,可以幫助保護你的工作免受安全威脅。

使用強密碼和雙因素認證(2FA)

  • 確保所有貢獻者的 Git 平台帳戶(如 GitHub、GitLab 等)使用強密碼和雙因素認證。這增加了帳戶安全性,防止未經授權的訪問。

使用 SSH 金鑰代替密碼

配置和使用 SSH 金鑰進行 Git 操作,代替密碼認證。SSH 金鑰提供更安全的數據傳輸和訪問控制。

1
ssh-keygen -t rsa -b 4096 -C "your_email@example.com" #然後,將生成的 SSH 公鑰添加到你的 Git 平台帳戶。

定期更新和審計依賴項

  • 定期使用工具如 npm audit 或 bundle audit 檢查和更新專案依賴項,以修復已知的安全漏洞。
  • 考慮使用依賴項管理服務(如 Dependabot)自動檢查和更新依賴項。

保持 Git 和相關工具的最新狀態

  • 定期更新 Git 客戶端和相關開發工具到最新版本,以利用最新的安全修復和改進。

審計代碼庫以識別敏感數據

  • 使用工具(如 git-secrets 或 truffleHog)審計代碼庫歷史和提交,以識別是否意外提交了敏感信息(如密碼、私鑰等)。
  • 如果發現敏感信息被提交,應立即採取措施撤銷和更換相關密碼或私鑰,並根據需要清理歷史記錄。

實施代碼審查

  • 強制實施代碼審查流程,確保所有代碼更改都經過至少一名其他開發者的審查。這有助於識別潛在的安全問題和漏洞。

建立安全開發政策和指南

  • 建立和維護一套安全開發政策和指南,教育團隊成員識別、預防和應對安全威脅。

通過實施這些安全實踐,你可以大大減少安全風險,保護你的代碼庫和用戶免受潛在威脅。安全是一個持續的過程,需要團隊的共同努力和持續的關注。

結論

通過本次對話,我們探討了使用 Git 進行版本控制時的一系列最佳實踐,包括如何有效地管理分支、保持代碼庫的整潔與安全、進行原子化提交,以及進階技巧的應用等。這些實踐和技巧對於任何規模的開發項目都是至關重要的,不僅可以提高個人和團隊的開發效率,還能確保代碼質量和項目的持續健康發展。

我們強調了代碼審查的重要性,這不僅有助於發現和修正錯誤,還能促進團隊間的知識共享和技術成長。此外,安全實踐的部分提醒我們,隨著技術的發展,持續關注和應用安全最佳實踐是保護代碼庫不受威脅的關鍵。

最後,”保持學習和更新”部分提醒我們技術是不斷進步的。為了保持競爭力並充分利用 Git 這一強大工具的潛力,我們必須致力於不斷學習,跟上最新的發展和最佳實踐。

總之,有效地使用 Git 不僅僅是學習命令和操作,更重要的是建立一套合理的工作流程,培養良好的開發習慣,並持續提升自身的技術素養。通過實踐這些最佳實踐,我們可以最大限度地發揮 Git 在項目管理和團隊協作中的價值。

📚 參考資源

在探索 Git 使用和最佳實踐的過程中,以下資源將為你提供豐富的學習材料和進一步的指導:

📖 官方文檔

  • Git 官方文檔Git Documentation
    • 提供 Git 的基礎知識、使用指南、參考手冊以及深入的主題討論。

🏫 在線教程和課程

  • Pro Git 書籍Pro Git
    • 這是一本免費的書,涵蓋從基礎到高級的所有 Git 主題,適合所有水平的讀者。
  • GitHub Learning SkillGitHub Learning Skill
    • 提供互動學習課程,幫助學習 GitHub 的使用和 Git 工作流。

📰 技術博客和文章

  • Atlassian Git 教程Atlassian Git Tutorials
    • 提供從基礎到進階的 Git 教程和實踐指南。
  • GitHub BlogGitHub Blog
    • 分享有關 GitHub 新功能、工作流最佳實踐以及社區故事的文章。

💬 論壇和社區

  • Stack OverflowStack Overflow
    • 一個技術問答網站,可以找到大量關於 Git 使用的問題和解答。
  • GitHub Community ForumGitHub Community Forum
    • GitHub 的官方社區論壇,適合討論 Git 使用技巧、尋求幫助和分享經驗。

🛠️ 工具和插件

  • SourceTreeSourceTree
    • 一個免費且強大的 Git 桌面客戶端,提供可視化的版本控制和更容易管理的 Git 命令。
  • GitKrakenGitKraken
    • 另一款流行的 Git GUI 客戶端,以其直觀的用戶界面和豐富的功能集著稱。

將這些資源納入你的學習路徑,能夠幫助你建立堅實的 Git 使用和管理基礎,並在日常開發實踐中實現更高效、更安全的代碼管理。

附錄一: 舊版本撤銷變更方法

請使用git reset HEAD來撤銷暫存區的更改,以及使用git checkout -- .來撤銷整個工作目錄的更改。

1
2
3
git reset HEAD              #撤銷暫存區的變更 (HEAD=最近一次提交的狀態)
git checkout -- .           #撤銷整個工作目錄
git checkout -- <file path> #撤銷指定檔案變更
  • 請注意使用git checkout -- <file>撤銷變更時,所有未暫存的變更都會被丟棄,此操作是逆不可的。
  • 如果檔案同時有暫存和未暫存的更改,則該指令僅取消未暫存區的工作目錄變更。
本文章以 CC BY 4.0 授權