博客 / 詳情

返回

GitFlow

GitFlow 分支管理模型

一、什麼是 GitFlow?

GitFlow 是一種成熟的 Git 分支管理策略,用於規範團隊協作中的代碼版本管理流程。

  • GitFlow 官方文檔

二、核心分支概念

GitFlow 模型包含五大核心分支,每個分支都有明確的職責和使用場景:

1. Master 分支(主分支)

  • 用途:存放穩定的生產代碼
  • 特性:始終保持可部署的穩定狀態
  • 管理:只接受來自 Release 分支或 Hotfix 分支的合併
  • 版本標記:每個提交都應該添加版本標籤(如 v1.0.0)

2. Develop 分支(開發分支)

  • 用途:集成所有正在開發的功能
  • 來源:基於 Master 分支創建
  • 特性:包含下一個版本要發佈的所有功能
  • 管理:所有 Feature 分支開發完成後都合併到 Develop 分支

3. Feature 分支(功能分支)

  • 用途:開發新功能或改進
  • 來源:基於 Develop 分支創建
  • 命名規範feature/功能名稱(如 feature/user-authentication
  • 管理
    • 功能開發完成後合併回 Develop 分支
    • 永遠不會直接與 Master 分支交互
    • push到遠程倉庫,發起 Pull Request/Merge Request 請求合併回 Develop 分支
    • 經過 Code Review(代碼審核)通過後方可合併

4. Release 分支(發佈分支)

  • 用途:準備新版本發佈
  • 來源:基於 Develop 分支創建
  • 命名規範release/x.y.z(如 release/1.2.0,x.y.z為版本號)
  • 管理
    • 用於修復發佈前的 bug
    • 不添加新功能,只處理 bug 修復和版本準備
    • 測試通過後同時合併回 Master 分支和 Develop 分支
    • 合併到 Master 分支後需要打版本標籤
    • 發佈完成後應及時刪除

5. Hotfix 分支(熱修復分支)

  • 用途:緊急修復生產環境中的問題
  • 來源:基於 Master 分支創建
  • 命名規範hotfix/x.y.z(如 hotfix/1.2.1,x.y.z 為版本號)
  • 管理
    • 僅用於緊急修復,不處理新功能
    • 修復完成後同時合併回 Master 分支和 Develop 分支
    • 合併到 Master 分支後需要打版本標籤
    • 修復完成後立即刪除

初始化倉庫

# 1. 初始化本地倉庫
git init

# 2. 添加遠程倉庫連接
git remote add origin <遠程倉庫URL>

# 3. 創建 Master 分支的首次提交
git add .
git commit --allow-empty -m "Initial commit"

# 4. 創建 Develop 分支
git checkout -b develop

# 5. 推送兩個核心分支到遠程
git push -u origin master
git push -u origin develop

# 6. 設置遠程倉庫的默認分支(可選但推薦)
git remote set-head origin master

--allow-empty 允許創建空提交

三、工作流程示例

展示適用於單人開發或無 PR 強制要求的場景流程示例
標準的現代流程為:推送功能分支到遠程 -> 在平台上創建 PR -> 審核通過後在網頁端點擊合併。

1. 功能開發流程

  1. 從 Develop 分支創建 Feature 分支
# 切換到 Develop 分支並確保是最新狀態
git checkout develop
git pull origin develop

git checkout -b feature/xxx develop
  1. 在 Feature 分支上進行功能開發
  2. 開發完成後,將 Feature 分支合併回 Develop 分支
git checkout develop
# 採用--no-ff參數,表示強制創建合併提交
# 合併時會創建一個新的合併提交,保留分支歷史
git merge --no-ff feature/xxx
git push
  1. 刪除本地 Feature 分支
git branch -d feature/xxx
# 如果之前推送了 Feature 分支,則需要刪除遠程分支
# git push origin --delete feature/xxx

2. 版本發佈流程

  1. 從 Develop 分支創建 Release 分支
# 切換到 Develop 分支並確保是最新狀態
git checkout develop
git pull origin develop

git checkout -b release/x.y.z develop
  1. 在 Release 分支上進行 bug 修復和版本準備
  2. 測試通過後,將 Release 分支合併回 Master 分支並打版本標籤
git checkout master
git merge --no-ff release/x.y.z
git tag -a tagName -m "Release x.y.z"
# 推送標籤到遠程倉庫
git push origin tagName
# 推送代碼到遠程倉庫
git push 
  1. 同時將 Release 分支合併回 Develop 分支,確保開發分支包含所有修復
git checkout develop
git merge --no-ff release/x.y.z
git push
  1. 刪除 Release 分支
git branch -d release/x.y.z
# 如果之前推送了 Release 分支,則需要刪除遠程分支
# git push origin --delete release/x.y.z

3. 熱修復流程

  1. 從 Master 分支創建 Hotfix 分支
# 切換到 Master 分支並確保是最新狀態
git checkout master
git pull origin master

git checkout -b hotfix/x.y.z master
  1. 在 Hotfix 分支上修復問題
  2. 測試通過後,將 Hotfix 分支合併回 Master 分支並打版本標籤
git checkout master
git merge --no-ff hotfix/x.y.z
git tag -a tagName -m "Hotfix x.y.z"
# 推送標籤到遠程倉庫
git push origin tagName
# 推送代碼到遠程倉庫
git push 
  1. 同時將 Hotfix 分支合併回 Develop 分支,確保開發分支包含修復
git checkout develop
git merge --no-ff hotfix/x.y.z
git push
  1. 刪除 Hotfix 分支
git branch -d hotfix/x.y.z
# 如果之前推送了 Hotfix 分支,則需要刪除遠程分支
# git push origin --delete hotfix/x.y.z

四、Git回退操作

有改動,無add,無commit

撤銷工作區改動
git checkout -- filename

有改動,有add,無commit

撤銷暫存區的改動
git reset HEAD filename

有改動,有add,有commit

從本地庫回退(已commit,未push)

操作描述 命令 效果
撤銷提交,但保留改動在工作區 git reset --mixed HEAD^ 提交消失,改動還在工作區
撤銷提交,但保留改動在暫存區 git reset --soft HEAD^ 提交消失,改動還在暫存區
徹底丟棄(真的不要了) git reset --hard HEAD^ 全部消失,慎用!

有改動,有add,有commit,有push

撤銷已推送的提交
創建一個新提交,用於撤銷指定提交
提交的新提交用來"抵消"(逆向操作)指定提交的更改。
revert後需要再次 git push 才能同步到遠程

git revert <commit-hash>

reset VS revert

操作 描述 影響
reset 通過移動分支指針來回退到指定提交 直接改變歷史
revert 創建一個新提交,用於來"抵消"(逆向操作)指定提交的更改 不改變歷史,而是在歷史記錄中增加一個新的,反向的提交

五、Git Stash(暫存區域)

臨時保存工作進度,快速切換上下文,而不需要創建臨時提交。

緊急修復bug時的上下文切換

# 正在功能分支開發,突然需要修復生產bug
$ git status
# 修改:src/feature.js
# 修改:src/feature.css

# 最佳實踐:使用描述性消息
$ git stash push -m "WIP: 用户頭像上傳功能開發中"

# 切換到主分支修復bug
$ git checkout main
$ git pull origin main
$ git checkout -b hotfix/login-bug
# ... 修復工作 ...

# 返回後繼續開發
$ git checkout feature/user-avatar
$ git stash list
# stash@{0}: WIP: 用户頭像上傳功能開發中

$ git stash pop
# 恢復工作進度

默認情況下,git stash 只能暫存已跟蹤文件(tracked files)的修改,但可以通過參數-u顯式包含未跟蹤文件。

git stash push -u -m "描述"
  • 始終使用 -m 添加描述性消息
  • git stash apply(僅恢復)
  • git stash pop(恢復並刪除)
  • 定期清理過期的 stash:git stash drop stash@
  • git stash clear(清空所有 stash)
  • 可以跨分支使用 stash

六、Git Cherry-Pick

將特定提交從一個分支複製到另一個分支,而不合並整個分支歷史。

  • 涉及單個提交
git cherry-pick -x <commit-hash>

-x 參數表示在提交信息中添加"cherry picked from commit"説明

  • 涉及多個提交
git cherry-pick -x <commit-hash1>..<commit-hash2> # 不包含起始提交
git cherry-pick -x <commit-hash1>^..<commit-hash2> # 包含起始提交
  • 衝突處理
  • git cherry-pick --continue # 繼續完成cherry-pick
  • git cherry-pick --abort # 撤銷並回到初始狀態

七、Git Fetch & Git Pull

fetch VS pull

操作 本質 對本地代碼的影響
fetch 僅下載遠程更新 不改變工作目錄和當前分支
pull 下載+自動合併(git fetch + git merge 的自動執行) 改變工作目錄和當前分支

決策樹

flowchart TD A[本地有未提交修改?] -->|是| B[必須用 git stash 或 commit] A -->|否| C[需要查看遠程更新?] C -->|是| D[git fetch + 手動merge/rebase] C -->|否| E[追求簡潔操作?] E -->|是| F[git pull --rebase] E -->|否| G[需要保留合併歷史?] G -->|是| H[git pull 默認merge] G -->|否| I[git pull --rebase] style A fill:#f9f,stroke:#333,stroke-width:2px style B fill:#bbf,stroke:#333,stroke-width:1px style C fill:#f9f,stroke:#333,stroke-width:2px style D fill:#bbf,stroke:#333,stroke-width:1px style E fill:#f9f,stroke:#333,stroke-width:2px style F fill:#bbf,stroke:#333,stroke-width:1px style G fill:#f9f,stroke:#333,stroke-width:2px style H fill:#bbf,stroke:#333,stroke-width:1px style I fill:#bbf,stroke:#333,stroke-width:1px
  • 默認 git pull = git fetch + git merge (產生合併提交)
  • git pull --rebase = git fetch + git rebase (歷史變平)

git pull --rebase
獲取遠程更新,將本地提交"嫁接"到遠程最新提交之上

八、Git Merge & Git Rebase

merge VS rebase

操作 本質 對本地代碼的影響 應用 描述
merge 合併兩個分支 改變工作目錄和當前分支 在「接收合併的目標分支」上執行 站在目標分支,合併進來
rebase 將指定分支的提交歷史,應用到當前分支 改變工作目錄和當前分支 在「被變基的分支」上執行 站在被變基分支,基於基準變

⚠️ 黃金法則:永遠不要對位於公共倉庫之外的提交(即已經 push 到遠程並被他人使用的提交)執行 Rebase。
只對尚未推送或僅屬於你個人的本地提交使用 Rebase。對公共歷史 Rebase 會導致團隊成員協作時產生混亂和代碼丟失。

可視化對比

分支初始狀態

graph TD A[提交 A] --> B[提交 B] B --> C[提交 C] C --> D[提交 D] D --> E[提交 E] C --> F[提交 F] F --> G[提交 G] classDef mainBranch fill:#4CAF50,stroke:#333,stroke-width:2px; classDef featureBranch fill:#2196F3,stroke:#333,stroke-width:2px; class A,B,C,D,E mainBranch; class F,G featureBranch; subgraph main 分支 A B C D E end subgraph feature 分支 F G end

Git Merge 可視化

git checkout main
git merge feature後狀態

graph TD A[提交 A] --> B[提交 B] B --> C[提交 C] C --> D[提交 D] D --> E[提交 E] E --> H[合併提交 H] C --> F[提交 F] F --> G[提交 G] G --> H classDef mainBranch fill:#4CAF50,stroke:#333,stroke-width:2px; classDef featureBranch fill:#2196F3,stroke:#333,stroke-width:2px; classDef mergeCommit fill:#FF9800,stroke:#333,stroke-width:2px; class A,B,C,D,E,H mainBranch; class F,G featureBranch; class H mergeCommit; subgraph main 分支 A B C D E H end subgraph feature 分支 F G end %% note right of H: 合併提交保留完整歷史
  • main 分支:指針移到新的合併提交 H,歷史新增合併記錄;
  • feature 分支:指針位置不變(仍指向 G),分支本身毫髮無損;

Git Rebase 可視化

git checkout feature
git rebase main後狀態

graph TD A[提交 A] --> B[提交 B] B --> C[提交 C] C --> D[提交 D] D --> E[提交 E] %% Rebase 後,main 分支依然在 E,feature 分支移動到了 F' 和 G' E --> FPrime[提交 F'] FPrime --> GPrime[提交 G'] classDef mainBranch fill:#4CAF50,stroke:#333,stroke-width:2px; classDef featureBranch fill:#2196F3,stroke:#333,stroke-width:2px; classDef rebasedCommit fill:#9C27B0,stroke:#333,stroke-width:2px; class A,B,C,D,E mainBranch; class FPrime,GPrime featureBranch; class FPrime,GPrime rebasedCommit; subgraph main 分支 A B C D E end subgraph feature 分支 FPrime GPrime end
  • main 分支:完全不受影響,指針仍指向 E;
  • feature 分支:指針從原來的 G 移到新的 G'(提交歷史被重寫)

附錄

  • 缺少 CI/CD 配置
user avatar
0 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.