救命啊,我需要 git push —force 救命手冊

內容目錄 顯示

就真的那麼剛好發生 force push 事故時, 時間就是一切。以下三種情境希望能解決到你的危急任務。讓你可以匆匆容容游刃有餘~

立即行動清單(黃金 5 分鐘,當然是越快越好)

發現錯誤的瞬間,立刻執行以下步驟:

步驟一:凍結操作(30 秒內)

Bash
# 1. 不要關閉終端機視窗!
# 2. 不要執行任何 git 命令!
# 3. 截圖或複製終端機輸出

為什麼: 終端機中的 force push 輸出包含關鍵的 commit hash,是恢復的重要線索。

步驟二:緊急通知團隊(1 分鐘內)

立即在團隊頻道發布緊急通知:

Bash
🚨 緊急通知 🚨
我剛才誤對 [分支名稱] 執行了 force push
請所有人:
1. 立即停止推送任何程式碼
2. 不要執行 git pull
3. 保持現有的本地分支狀態
我正在進行恢復操作,約需 5-10 分鐘

步驟三:評估損害範圍(2 分鐘內)

快速檢查影響範圍:

指令 1:查看本地操作歷史

Bash
git reflog | head -20

使用時機: 你是執行 force push 的操作者,需要查看本地的操作記錄

說明:

  • git reflog 顯示本地 HEAD 的移動歷史,包含所有操作(commit、checkout、reset 等)
  • | head -20 只顯示最近 20 筆記錄,避免輸出過多
  • 這個記錄是純本地的,即使遠端分支被覆蓋,本地的 reflog 仍然完整保存

輸出範例:

Bash
cf4c40e HEAD@{0}: commit: 正確的提交
e92a30e HEAD@{1}: commit: 前一個提交
f961cff HEAD@{2}: checkout: moving from main to feature-branch

關鍵資訊: 從這裡可以找到 force push 之前的正確 commit hash


指令 2:更新並檢查遠端當前狀態

Bash
git fetch origin
git log origin/<分支名稱> --oneline --decorate --graph -10

使用時機: 需要確認遠端分支現在的狀態(被覆蓋後的狀態)

說明:

  • git fetch origin 更新本地的遠端追蹤分支,但不會改變你的工作目錄
  • git log origin/<分支名稱> 查看遠端分支的提交歷史
  • -oneline 每個提交只顯示一行(簡短格式)
  • -decorate 顯示分支名稱和標籤
  • -graph 用圖形方式顯示分支結構
  • 10 只顯示最近 10 個提交

範例:

Bash
# 如果你 force push 的是 main 分支
git log origin/main --oneline --decorate --graph -10

# 如果是其他分支,例如 demo-branch
git log origin/demo-branch --oneline --decorate --graph -10

輸出範例:

Bash
* 42ce27d (origin/demo-branch) WRONG COMMIT: 錯誤的提交
* d48e20c Initial commit

關鍵用途: 確認遠端分支現在指向哪個錯誤的 commit,以及還剩下多少提交


指令 3:比較本地與遠端的差異

Bash
# 查看「遠端有,但本地沒有」的提交
git log HEAD..origin/<分支名稱> --oneline

# 查看「本地有,但遠端沒有」的提交
git log origin/<分支名稱>..HEAD --oneline

使用時機: 需要了解哪些提交在 force push 後消失了

說明:

第一條命令 (HEAD..origin/<分支名稱>):

  • 顯示遠端分支領先本地的提交
  • 如果輸出為空,表示遠端沒有本地不知道的新提交
  • 正常情況: 有其他人推送了新程式碼,你需要先 pull
  • force push 後: 通常為空(因為遠端被你覆蓋了)

第二條命令 (origin/<分支名稱>..HEAD):

  • 顯示本地分支領先遠端的提交
  • 這些是存在於你本地但遠端沒有的提交
  • force push 後: 如果輸出很多提交,表示那些是被你覆蓋掉的正確提交

範例:

Bash
# 假設你在 demo-branch 上執行了錯誤的 force push
git log HEAD..origin/demo-branch --oneline
# 輸出: (空的) - 表示遠端沒有新東西

git log origin/demo-branch..HEAD --oneline
# 輸出:
# cf4c40e Scenario1: 正常開發工作
# e92a30e feat: Add important feature
# f961cff Add user authentication
# 這些就是被覆蓋掉、需要恢復的提交!

關鍵用途: 快速識別哪些提交需要恢復


指令 4(可選):完整的視覺化比較

Bash
git log --oneline --all --decorate --graph -20

使用時機: 需要完整了解所有分支的關係和狀態

說明:

  • -all 顯示所有分支(本地 + 遠端追蹤分支)
  • 這會產生一個完整的分支圖,幫助你理解整個情況

輸出範例:

Bash
* 42ce27d (origin/demo-branch) WRONG COMMIT
| * cf4c40e (HEAD -> demo-branch) Correct commit
| * e92a30e Another correct commit
|/
* d48e20c Initial commit

關鍵用途: 視覺化地看到本地分支和遠端分支的分歧點


快速評估檢查清單

執行完上述指令後,你應該能回答以下問題:

  • [ ] 遠端分支現在指向哪個 commit?(錯誤的那個)
  • [ ] 正確的 commit hash 是什麼?(應該恢復的目標)
  • [ ] 有多少個提交消失了?
  • [ ] 這些消失的提交是誰的?(只有你的,還是包含其他人的)
  • [ ] 本地是否還保留完整的歷史?

根據答案選擇恢復策略:

  • 如果只有你的提交 → 使用「情境一」
  • 如果包含其他人的提交 → 使用「情境二」或「情境三」

情境一:你是最後推送者(最簡單)

適用條件: 在你 force push 之前,沒有其他人推送過新提交。

判斷方法:

  • 你剛執行完 force push,終端機視窗還開著
  • 從「步驟三:評估損害範圍」得知只有你自己的提交被影響
  • 你是最後一個推送到這個分支的人

步驟 1:從終端機輸出找回正確的 commit

關鍵線索:終端機的 force push 輸出

在終端機中查看 force push 的輸出:

Bash
+ deadbeef...f00f00ba main -> main (forced update)

輸出格式解析:

  • + 符號:表示這是一個強制更新(非 fast-forward)
  • deadbeef: 被覆蓋前的最後一個正確提交 ← 這是你要恢復的目標!
  • ...: 表示從 deadbeef 到 f00f00ba 之間有歷史變更
  • f00f00ba: 你錯誤推送的提交(現在遠端指向這個)
  • main -> main: 本地 main 分支推送到遠端 main 分支
  • (forced update): 確認這是強制更新

實際範例 (從我們的驗證測試):

Bash
+ cf4c40e...42ce27d wrong-branch -> demo-branch (forced update)
  • 要恢復的正確 commit: cf4c40e
  • 錯誤的 commit: 42ce27d

⚠️ 重要: 如果你已經關閉終端機,跳到「方式二」從 reflog 查找


步驟 2:立即恢復遠端分支

方式一:直接使用終端機輸出的 commit hash(最快速)

Bash
git push --force origin <正確的commit-hash>:<分支名稱>

使用時機: 你的終端機還開著,能看到 force push 的輸出

指令說明:

  • git push --force: 再次使用強制推送(這次是為了恢復)
  • origin: 遠端儲存庫名稱
  • <正確的commit-hash>: 從終端機輸出找到的正確 commit(例如 deadbeefcf4c40e
  • :<分支名稱>: 要更新的遠端分支名稱(例如 :main:demo-branch

實際範例:

Bash
# 從輸出 "+ cf4c40e...42ce27d" 得知要恢復 cf4c40e
git push --force origin cf4c40e:demo-branch

# 或者使用完整的 hash
git push --force origin cf4c40e01485d26fa11cc4058caf619de23e71d7:demo-branch

預期輸出:

Bash
+ 42ce27d...cf4c40e cf4c40e -> demo-branch (forced update)

這表示成功將遠端從錯誤的 42ce27d 恢復到正確的 cf4c40e

為什麼這樣做有效:

  • Git 的 commit 是永久不變的,只要知道 hash 就能直接引用
  • 你不需要在本地有那個 commit 的分支,直接推送 hash 即可
  • 這是最快的恢復方式,通常在 30 秒內完成

方式二:從本地 reflog 查找(如果終端機已關閉)

步驟 2.1:查看 HEAD 的 reflog

Bash
git reflog

使用時機: 你關閉了終端機,或者看不到 force push 的輸出

指令說明:

  • git reflog 顯示本地 HEAD 的所有移動記錄
  • 包含 commit、checkout、reset、merge 等所有操作
  • 預設保留 90 天的歷史記錄

輸出範例:

Bash
42ce27d HEAD@{0}: commit: WRONG COMMIT: 這是錯誤的提交
cf4c40e HEAD@{1}: commit: Scenario1: 正常開發工作 - 應該被保留的提交
e92a30e HEAD@{2}: commit: feat: Add important feature
f961cff HEAD@{3}: checkout: moving from main to demo-branch

如何找到正確的 commit:

  1. 找到 force push 操作的那一行(可能顯示為 push 或你執行 force push 前的最後一個 commit)
  2. 向上看一行,那就是被覆蓋前的正確狀態
  3. 記下那個 commit hash(例如 cf4c40e

步驟 2.2:查看遠端追蹤分支的 reflog(更精確)

Bash
git reflog show origin/<分支名稱>

使用時機: 需要更精確地找到遠端分支被覆蓋前的狀態

指令說明:

  • git reflog show origin/<分支名稱> 顯示本地對遠端分支的追蹤記錄
  • 這個記錄追蹤的是 git fetchgit push 如何改變遠端分支
  • git reflog 更精確,因為它專注於遠端分支的變化

範例:

Bash
# 查看 origin/main 的歷史
git reflog show origin/main

# 查看 origin/demo-branch 的歷史
git reflog show origin/demo-branch

輸出範例:

Bash
42ce27d refs/remotes/origin/demo-branch@{0}: push: forced update
cf4c40e refs/remotes/origin/demo-branch@{1}: fetch: forced update
e92a30e refs/remotes/origin/demo-branch@{2}: fetch: fast-forward

關鍵資訊:

  • @{0}: 最新狀態(錯誤的 force push 結果)
  • @{1}: 上一個狀態(這通常是正確的!)
  • 找到 @{1} 對應的 commit hash(例如 cf4c40e

步驟 2.3:執行恢復推送

Bash
git push --force origin <找到的commit-hash>:<分支名稱>

範例:

Bash
# 從 reflog 找到正確的 commit 是 cf4c40e
git push --force origin cf4c40e:demo-branch

步驟 3:驗證恢復結果

恢復完成後,必須驗證以確保一切正確。

驗證步驟 3.1:更新本地的遠端追蹤資訊

Bash
git fetch origin

使用時機: 每次恢復後的第一步

指令說明:

  • 從遠端下載最新的分支資訊
  • 更新本地的遠端追蹤分支(origin/mainorigin/demo-branch 等)
  • 不會改變你的工作目錄或當前分支
  • 這是一個安全的操作,只是同步資訊

預期輸出:

Bash
From gitlab.com:james-berget/git_demo
 + 42ce27d...cf4c40e demo-branch -> origin/demo-branch (forced update)

驗證步驟 3.2:檢查遠端分支的提交歷史

Bash
git log origin/<分支名稱> --oneline -5

使用時機: 確認遠端分支現在有正確的提交歷史

指令說明:

  • git log origin/<分支名稱> 顯示遠端分支的提交歷史
  • -oneline 每個提交顯示一行(簡潔格式)
  • 5 只顯示最近 5 個提交

範例:

Bash
git log origin/demo-branch --oneline -5

預期輸出 (應該看到所有正確的提交):

Bash
cf4c40e (origin/demo-branch) Scenario1: 正常開發工作 - 應該被保留的提交
e92a30e feat: Add important feature - CORRECT COMMIT TO PRESERVE
f961cff Add user authentication - CORRECT COMMIT
6d0a795 Add test file 1 - normal development
d48e20c Initial commit

驗證要點:

  • ✅ 最新的 commit 是你要恢復的那個(例如 cf4c40e
  • ✅ 所有應該存在的提交都在歷史中
  • ❌ 不應該看到錯誤的 commit(例如 42ce27d

驗證步驟 3.3:確認最新 commit 的完整 hash

Bash
git rev-parse origin/<分支名稱>

使用時機: 需要 100% 確認遠端分支指向正確的 commit

指令說明:

  • git rev-parse 將引用(分支名、標籤等)轉換為完整的 commit hash
  • 這是最精確的驗證方式

範例:

Bash
git rev-parse origin/demo-branch

預期輸出:

Bash
cf4c40e01485d26fa11cc4058caf619de23e71d7

驗證方式:

  • 將這個 hash 與你從終端機輸出或 reflog 找到的正確 commit 比對
  • 應該完全一致(至少前 7 個字元一致)

驗證步驟 3.4(可選):比對檔案內容

Bash
# 切換到本地的正確分支
git checkout <分支名稱>

# 比較本地和遠端的差異
git diff origin/<分支名稱>

使用時機: 想要確認檔案內容完全一致

指令說明:

  • git diff 比較兩個提交之間的差異
  • 如果恢復成功,應該沒有任何差異

預期輸出:

  • 如果完全一致:沒有任何輸出(這是好事!)
  • 如果有差異:會顯示不同的檔案和內容

步驟 4:通知團隊恢復完成

立即發送恢復成功通知

在團隊頻道發送以下訊息:

Bash
恢復完成 - 緊急事故已解決!

分支 [分支名稱] 已恢復到正確狀態

恢復資訊:
- 恢復的 commit: <commit-hash>
- 恢復時間: <實際花費時間,例如: 2鐘>
- 受影響範圍: 僅我個人的推送

後續行動:
- 大家可以繼續正常工作
- 如果你的本地分支與遠端有衝突,請執行:
  git fetch origin
  git reset --hard origin/<分支名稱>

如有任何問題請立即回報

實際範例

Bash
恢復完成 - 緊急事故已解決

分支 demo-branch 已恢復到正確狀態

恢復資訊:
- 恢復的 commit: cf4c40e (Scenario1: 正常開發工作)
- 恢復時間: 2分鐘
- 受影響範圍: 僅我個人的推送

後續行動:
- 大家可以繼續正常工作
- 如果你的本地分支與遠端有衝突,請執行:
  git fetch origin
  git reset --hard origin/demo-branch

如有任何問題請立即回報

情境一完整操作檢查清單

執行完所有步驟後,確認以下項目:

  • [ ] 從終端機輸出或 reflog 找到正確的 commit hash
  • [ ] 執行 git push --force origin <hash>:<branch> 恢復遠端分支
  • [ ] 執行 git fetch origin 更新本地資訊
  • [ ] 執行 git log origin/<branch> 確認提交歷史正確
  • [ ] 執行 git rev-parse origin/<branch> 確認 commit hash 正確
  • [ ] 通知團隊恢復完成
  • [ ] 所有團隊成員確認可以正常工作

如果所有項目都勾選,恭喜你!成功從 force push 災難中恢復了!🎉


情境二:其他人的提交也被覆蓋(中等難度)

適用條件: 在你 force push 之前,其他團隊成員已經推送了新提交,現在這些提交都消失了。

判斷方法:

  • 從「步驟三:評估損害範圍」發現遺失的提交包含其他人的工作
  • 執行 git log origin/<branch>..HEAD 看到多位開發者的提交
  • 團隊成員回報他們推送的程式碼消失了

與情境一的差異:

  • 情境一:只有你自己的提交,可以直接 force push 恢復
  • 情境二:包含多人的提交,需要建立恢復分支後合併(更安全)

步驟 1:找到所有遺失的提交

方法 A:使用本地 reflog 查找(最快速)

Bash
git reflog | head -20

使用時機: 你的本地儲存庫還保留完整的歷史

指令說明:

  • 查看本地 HEAD 的移動歷史
  • 找到 force push 之前的最後一個正確提交
  • 這是最快速的方法,因為不需要依賴遠端或其他人

輸出範例:

Bash
935ccec HEAD@{0}: commit: WRONG COMMIT: Accidental force push
04c363a HEAD@{1}: reset: moving to HEAD~3
c6e1b9e HEAD@{2}: commit: Developer C - Add analytics dashboard
eb0562b HEAD@{3}: commit: Developer B - Add order processing
370dbde HEAD@{4}: commit: Developer A - Add user login

關鍵資訊: c6e1b9e 是包含所有開發者提交的最後正確狀態


方法 B:使用遠端追蹤分支的 reflog

Bash
git reflog show origin/<分支名稱>

使用時機: 需要確認遠端分支被覆蓋前的狀態

指令說明:

  • origin/<分支名稱> 替換為實際的分支名(如 origin/main
  • 顯示本地對遠端分支的追蹤記錄
  • 可以看到 force push 前後的 commit hash

範例:

Bash
# 查看 main 分支的遠端追蹤歷史
git reflog show origin/main

# 查看其他分支
git reflog show origin/develop

輸出範例:

Bash
935ccec refs/remotes/origin/scenario2-test@{0}: update by push
c6e1b9e refs/remotes/origin/scenario2-test@{1}: update by push

分析: @{1} 位置的 c6e1b9e 就是要恢復的目標


方法 C:使用 GitHub/GitLab 網頁介面

使用時機: 本地 reflog 不可用,或需要從遠端平台獲取資訊

GitHub 操作步驟:

  1. 前往 Commits 頁面:
    • URL 格式: https://github.com/[組織]/[專案]/commits/[分支名稱]
    • 範例: https://github.com/myteam/myproject/commits/main
  2. 查看提交歷史:
    • 即使 commit 已經不在任何分支上,GitHub 仍會保留一段時間
    • 在頁面上找到 force push 之前的最後一個正常提交
    • 記下該 commit 的 hash(點擊提交可以看到完整 hash)
  3. 使用 GitHub API(進階方法):
Bash
# 查看最近的 push 事件
curl -H "Authorization: token YOUR_GITHUB_TOKEN" \
  <https://api.github.com/repos/[owner]/[repo]/events> \
  | jq '.[] | select(.type == "PushEvent")'

GitLab 操作步驟:

  1. 前往專案頁面RepositoryCommits
  2. 在右上角的分支選擇器查看歷史
  3. 或使用 GitLab API:
Bash
curl --header "PRIVATE-TOKEN: YOUR_TOKEN" \
  "<https://gitlab.com/api/v4/projects/[project_id]/events>"

方法 D:詢問團隊成員

使用時機: 無法從 reflog 或遠端找到完整資訊

在團隊頻道發送請求:

Bash
🔍 緊急協助請求

我剛才誤對 [分支名稱] 執行了 force push
需要找回被覆蓋的提交

請問有人在 [時間範圍,例如:今天下午 2 點後] 推送過程式碼嗎?
如果有,請執行以下命令並回報結果(不要執行其他 git 命令):

git log --oneline -10
git rev-parse HEAD

請將輸出截圖或複製到討論串,謝謝!

收集資訊:

  • 記錄所有團隊成員回報的 commit hash
  • 找出最新的那個 commit(通常是被覆蓋前的最後狀態)
  • 確認該 commit 包含所有人的工作

範例回報:

Bash
開發者 A: 我的最後一個 commit  370dbde
開發者 B: 我的是 eb0562b
開發者 C: 我的是 c6e1b9e (最新)

從回報可知 c6e1b9e 是要恢復的目標


步驟 2:建立恢復分支

找到正確的 commit hash 後,建立恢復分支來保存這些提交。

2.1 從本地建立恢復分支

Bash
git branch recovery-<分支名稱>-<期> <遺失的commit-hash>

使用時機: 你的本地儲存庫有完整的歷史記錄

指令說明:

  • git branch: 建立新分支的命令
  • recovery-<分支名稱>-<日期>: 恢復分支的命名規範
    • 使用 recovery- 前綴便於識別
    • 包含原始分支名稱
    • 加上日期(格式:YYYYMMDD)便於追蹤
  • <遺失的commit-hash>: 從步驟 1 找到的正確 commit hash

實際範例:

Bash
# 為 main 分支建立恢復分支
git branch recovery-main-20251017 c6e1b9e

# 為 develop 分支建立恢復分支
git branch recovery-develop-20251017 abc1234

# 使用完整的 hash 也可以
git branch recovery-main-20251017 c6e1b9e01485d26fa11cc4058caf619de23e71d7

預期輸出: (沒有輸出表示成功)

驗證分支已建立:

Bash
git branch -v | grep recovery

2.2 推送恢復分支到遠端

Bash
git push origin recovery-<分支名稱>-<期>

使用時機: 建立本地恢復分支後的下一步

指令說明:

  • 將恢復分支推送到遠端儲存庫
  • 讓團隊成員可以查看和驗證恢復內容
  • 作為遠端備份,避免本地資料遺失

範例:

Bash
git push origin recovery-main-20251017

預期輸出:

Bash
Enumerating objects: 10, done.
Counting objects: 100% (10/10), done.
Writing objects: 100% (9/9), 1.42 KiB | 1.42 MiB/s, done.
Total 9 (delta 4), reused 0 (delta 0)
To gitlab.com:myteam/myproject.git
 * [new branch]      recovery-main-20251017 -> recovery-main-20251017

2.3 如果本地沒有完整歷史

Bash
# 方法一:如果 GitHub 上還能看到該 commit
# 直接在 GitHub 網頁介面操作:
# 1. 前往該 commit 頁面
# 2. 點擊 commit hash 旁的 "<>" 按鈕
# 3. 在分支下拉選單輸入: recovery-main-20251017
# 4. 點擊 "Create branch"

# 方法二:從團隊成員的本地儲存庫獲取
# 請參考「情境三」的做法

使用時機: 你的本地儲存庫已經沒有完整的歷史記錄

GitHub 網頁操作優點:

  • 不需要本地有完整歷史
  • 操作簡單直觀
  • 立即在遠端建立分支

⚠️ 注意: 如果 GitHub 上也找不到該 commit,需要使用情境三的方法


步驟 3:驗證恢復分支內容

建立恢復分支後,必須驗證內容是否正確,避免恢復錯誤的版本。

3.1 切換到恢復分支

Bash
git checkout recovery-<分支名稱>-<期>

使用時機: 需要檢查恢復分支的內容

指令說明:

  • 切換工作目錄到恢復分支
  • 讓你可以查看檔案和提交歷史

範例:

Bash
git checkout recovery-main-20251017

預期輸出:

Bash
Switched to branch 'recovery-main-20251017'

3.2 檢查提交歷史

Bash
git log --oneline -10

使用時機: 確認恢復分支包含所有必要的提交

指令說明:

  • -oneline: 每個提交顯示一行,便於快速瀏覽
  • 10: 顯示最近 10 個提交(可根據需要調整)

預期輸出:

Bash
c6e1b9e feat: Developer C - Add analytics dashboard
eb0562b feat: Developer B - Add order processing system
370dbde feat: Developer A - Add user login feature
04c363a test: add synthetic record #15
...

驗證要點:

  • 看到所有開發者的提交
  • 提交順序正確
  • 提交訊息符合預期

3.3 比較恢復分支與事故分支的差異

Bash
git log --oneline <事故分支>..<恢復分支>

使用時機: 需要確認哪些提交在事故中遺失了

指令說明:

  • <事故分支>..<恢復分支>: 顯示恢復分支比事故分支多出的提交
  • 這些就是需要恢復的內容

範例:

Bash
# 比較 main 分支與恢復分支
git log --oneline main..recovery-main-20251017

# 比較當前分支(事故分支)與恢復分支
git log --oneline scenario2-test..recovery-scenario2-20251017

預期輸出:

Bash
c6e1b9e feat: Developer C - Add analytics dashboard
eb0562b feat: Developer B - Add order processing system
370dbde feat: Developer A - Add user login feature

分析: 這三個提交就是在 force push 中遺失的,需要恢復


3.4 查看檔案差異(可選)

Bash
git diff <事故分支>..<恢復分支>

使用時機: 需要查看具體的程式碼變更

指令說明:

  • 顯示兩個分支之間所有檔案的差異
  • 幫助確認恢復的內容是否正確

範例:

Bash
git diff main..recovery-main-20251017

輸出: 會顯示所有新增、修改、刪除的檔案內容

⚠️ 注意: 如果輸出很多,可以只檢查特定檔案:

Bash
git diff main..recovery-main-20251017 -- path/to/important/file.js

3.5 檢查檔案清單

Bash
ls -la

使用時機: 快速確認關鍵檔案是否存在

範例: 在驗證測試中,我們確認了以下檔案存在:

Bash
ls -la scenario2_*.txt

預期輸出:

Bash
scenario2_developer_A_feature.txt
scenario2_developer_B_feature.txt
scenario2_developer_C_feature.txt

步驟 4:將恢復分支合併回主分支

驗證完成後,使用合併方式將遺失的提交恢復到事故分支。

4.1 切回事故分支

Bash
git checkout <分支名稱>

使用時機: 驗證完恢復分支後,準備執行合併

指令說明:

  • 切回原本發生事故的分支
  • 在這個分支上執行合併操作

範例:

Bash
# 切回 main 分支
git checkout main

# 切回 scenario2-test 分支
git checkout scenario2-test

預期輸出:

Bash
Switched to branch 'main'
Your branch is up to date with 'origin/main'.

4.2 執行合併(使用 –no-ff)

Bash
git merge --no-ff recovery-<分支名稱>-<期> -m "Merge: 恢復因 force push 遺失的提交"

使用時機: 準備將恢復分支的提交合併回來

指令說明:

  • git merge: 合併分支的命令
  • -no-ff: 非常重要,強制建立合併提交
    • 保留完整的分支歷史
    • 清楚顯示這是一次恢復操作
    • 便於日後追蹤和審查
  • m "訊息": 指定合併提交的訊息

為什麼要使用 –no-ff:

  • 不使用 -no-ff: Git 可能使用 fast-forward,歷史看起來像直線
  • 使用 -no-ff: 保留分支結構,清楚顯示合併點

範例:

Bash
git merge --no-ff recovery-main-20251017 -m "Merge: 恢復因 force push 遺失的提交"

# 更詳細的訊息範例
git merge --no-ff recovery-main-20251017 -m "Merge: 恢復因 force push 遺失的提交

包含以下開發者的工作:
- Developer A: 使用者登入功能
- Developer B: 訂單處理系統
- Developer C: 資料分析儀表板

事故發生時間: 2025-10-17 14:00
恢復執行者: [你的名字]"

預期輸出:

Bash
Merge made by the 'ort' strategy.
 scenario2_developer_A_feature.txt | 13 +++++++++++++
 scenario2_developer_B_feature.txt | 13 +++++++++++++
 scenario2_developer_C_feature.txt | 13 +++++++++++++
 3 files changed, 39 insertions(+)
 create mode 100644 scenario2_developer_A_feature.txt
 create mode 100644 scenario2_developer_B_feature.txt
 create mode 100644 scenario2_developer_C_feature.txt

分析:

  • 合併成功
  • 三個檔案已加入
  • 顯示新增的行數

4.3 查看合併後的提交圖

Bash
git log --oneline --graph -10

使用時機: 合併後驗證分支結構

指令說明:

  • -graph: 以圖形方式顯示分支結構
  • 可以清楚看到合併點和分支關係

預期輸出:

Bash
*   01ab8ad (HEAD -> scenario2-test) Merge: 恢復因 force push 遺失的提交
|\
| * c6e1b9e (recovery-scenario2-20251017) feat: Developer C
| * eb0562b feat: Developer B
| * 370dbde feat: Developer A
* | 935ccec WRONG COMMIT
|/
* 04c363a Initial commit

分析:

  • 清楚顯示合併的分支結構
  • 錯誤提交(935ccec)保留在歷史中(用於事故追蹤)
  • 所有遺失的提交都已恢復

4.4 推送到遠端

Bash
git push origin <分支名稱>

使用時機: 合併完成後,將恢復結果推送到遠端

指令說明:

  • 這是正常的 push,不需要 -force
  • 因為我們使用合併方式,不是覆蓋歷史

範例:

Bash
git push origin main

# 如果遠端拒絕(不應該發生),檢查是否有分支保護
git push origin scenario2-test

預期輸出:

Bash
Enumerating objects: 4, done.
Counting objects: 100% (4/4), done.
Writing objects: 100% (2/2), 368 bytes | 368.00 KiB/s, done.
Total 2 (delta 1), reused 0 (delta 0)
To gitlab.com:myteam/myproject.git
   935ccec..01ab8ad  scenario2-test -> scenario2-test

分析:

  • 推送成功
  • 從 935ccec(錯誤提交)更新到 01ab8ad(合併提交)

步驟 5:驗證恢復結果

推送完成後,必須進行全面驗證確保恢復成功。

5.1 更新本地的遠端追蹤資訊

Bash
git fetch origin

使用時機: 推送後的第一步

指令說明:

  • 從遠端下載最新的分支資訊
  • 更新本地的遠端追蹤分支
  • 確保本地與遠端同步

預期輸出: 通常沒有輸出(因為剛推送過)


5.2 檢查遠端分支的提交歷史

Bash
git log origin/<分支名稱> --oneline --graph -10

使用時機: 確認遠端分支有正確的歷史

範例:

git log origin/main --oneline --graph -10

預期輸出:

驗證要點:

  • 看到合併提交
  • 所有遺失的提交都在歷史中
  • 分支結構正確

5.3 確認最新 commit 的 hash

Bash
git rev-parse origin/<分支名稱>

使用時機: 精確驗證遠端分支指向的提交

範例:

Bash
git rev-parse origin/main

預期輸出:

Bash
01ab8ad7f84ad3ba2b09d4a465ca8cb02352bb74

這應該是合併提交的完整 hash


5.4 比較本地與遠端(應該一致)

Bash
git diff origin/<分支名稱>

使用時機: 最後確認本地與遠端完全一致

預期輸出: (沒有任何輸出,表示完全一致)

如果有輸出,表示還有差異,需要檢查原因。


5.5 驗證所有檔案已恢復

Bash
# 列出相關檔案
ls -la

# 或檢查特定檔案是否存在
test -f important_file.txt && echo "檔案存在" || echo "檔案不存在"

使用時機: 確認關鍵檔案已經恢復


步驟 6:清理與通知團隊

6.1 發送恢復完成通知

在團隊頻道發送詳細的恢復報告:

Bash
恢復完成 - Force Push 事故已解決

分支 [分支名稱] 已成功恢復

恢復資訊:
- 事故發生時間: [時間]
- 恢復完成時間: [時間]
- 總耗時: [例如: 8 分鐘]
- 恢復的提交數: [例如: 3 個]
- 涉及開發者: [列出所有人]

恢復的提交:
- c6e1b9e: Developer C - 資料分析儀表板
- eb0562b: Developer B - 訂單處理系統
- 370dbde: Developer A - 使用者登入功能

後續行動:
1. 所有人請執行以下命令同步:
   git fetch origin
   git pull origin [分支名稱]

2. 如遇到衝突,請先備份本地工作:
   git stash save "備份-恢復前"

3. 驗證你的提交是否都在:
   git log --oneline -20

如有任何問題或發現遺漏的提交,請立即回報

6.2 清理恢復分支(可選)

Bash
# 刪除本地恢復分支
git branch -d recovery-<分支名稱>-<期>

# 刪除遠端恢復分支
git push origin --delete recovery-<分支名稱>-<期>

使用時機: 確認恢復成功且團隊驗證無誤後

範例:

Bash
git branch -d recovery-main-20251017
git push origin --delete recovery-main-20251017

⚠️ 建議: 保留恢復分支幾天,直到確定一切正常

保留恢復分支的理由:

  • 作為備份,以防需要重新恢復
  • 便於事後分析和學習
  • 提供清晰的審計記錄

情境二完整操作檢查清單

執行完所有步驟後,確認以下項目:

  • [ ] 從 reflog 或遠端找到所有遺失的提交 hash
  • [ ] 基於正確的 commit hash 建立恢復分支
  • [ ] 推送恢復分支到遠端
  • [ ] 切換到恢復分支並驗證內容
  • [ ] 使用 git log 確認所有遺失的提交都在恢復分支中
  • [ ] 使用 git diff 比較差異
  • [ ] 切回事故分支
  • [ ] 執行 git merge --no-ff 合併恢復分支
  • [ ] 使用 git log --graph 檢查合併後的分支結構
  • [ ] 推送合併後的分支到遠端(正常 push,不用 force)
  • [ ] 執行 git fetch origin 更新本地資訊
  • [ ] 確認遠端分支的提交歷史正確
  • [ ] 確認所有關鍵檔案已恢復
  • [ ] 發送詳細的恢復完成通知給團隊
  • [ ] 團隊成員驗證並同步
  • [ ] (可選)清理恢復分支

如果所有項目都勾選,恭喜你!成功從情境二的 force push 災難中恢復了!🎉


情境三:完全無法從遠端找回(這真的就有點麻煩了…)

適用條件: GitHub/GitLab 事件流中也找不到遺失的提交,需要從團隊成員的本地儲存庫恢復。

判斷方法:

  • 執行 force push 的操作者本地也沒有完整歷史(可能執行了 git reset --hard 等破壞性操作)
  • GitHub/GitLab 網頁介面找不到遺失的 commit
  • 執行 git reflog 顯示的歷史也不完整或已被清理
  • 這是最後的救援手段,必須依賴團隊成員的本地備份

與情境一、二的差異:

  • 情境一: 操作者本地有完整歷史,直接 force push 恢復
  • 情境二: 可從 reflog 或遠端平台找到 commit,建立恢復分支合併
  • 情境三: 操作者和遠端都無法找回,必須依賴團隊成員

步驟 1:發送緊急求助訊息

1.1 立即在團隊頻道發送請求

使用時機: 確認本地和遠端都無法找回完整歷史的第一時間

在團隊頻道發送以下訊息:

Bash
🆘 緊急求助 - Force Push 事故 🆘

我剛才誤對 [分支名稱] 執行了 force push
而且我的本地歷史也不完整,無法從 reflog 恢復

需要找到在 [具體時間,例如:今天下午 2 點] 之前
有執行過 git pull  git fetch 的成員

如果你符合條件,請執行以下命令並截圖回報:
  git log --oneline -10

⚠️ 重要:不要執行任何其他 git 命令!
⚠️ 特別是不要執行 git pull(會拉取損壞的遠端)

遺失的提交應該包含:
- [列出遺失的功能或開發者名稱]
- [例如:Developer A 的使用者認證系統]
- [例如:Developer B 的支付整合]

為什麼這樣做:

  • 清楚說明情況的嚴重性
  • 明確指示不要執行 git pull(避免團隊成員的歷史也被覆蓋)
  • 提供具體的時間範圍幫助成員判斷
  • 列出遺失的內容幫助成員確認他們的本地是否有

步驟 2:驗證團隊成員的本地歷史

2.1 團隊成員執行檢查命令

使用時機: 團隊成員收到求助訊息後,在自己的本地儲存庫執行

給團隊成員的指示:

Bash
# 步驟 2.1.1: 檢查本地提交歷史
git log --oneline -10

使用時機: 快速查看最近的 10 個提交

指令說明:

  • git log: 顯示提交歷史
  • -oneline: 每個提交顯示一行(簡潔格式)
  • 10: 只顯示最近 10 個提交

預期輸出:

Bash
d8d9535 feat: Developer C - 資料分析儀表板
7848581 feat: Developer B - 訂單處理系統
8cf0e5d feat: Developer A - 使用者認證系統
34d3940 test: add synthetic record #15
...

如何判斷:

  • ✅ 如果看到所有遺失的提交 → 你的本地有完整歷史!立即回報
  • ❌ 如果缺少某些提交 → 你的本地不完整,繼續等待其他成員

Bash
# 步驟 2.1.2: 獲取完整的 commit hash
git rev-parse HEAD

使用時機: 確認你的本地分支指向的確切 commit

指令說明:

  • git rev-parse: 將引用轉換為完整的 commit hash
  • HEAD: 當前分支的最新提交

預期輸出:

Bash
d8d9535e7503b8aeb9976e6e368188ba32305c6e

為什麼要記錄完整 hash:

  • 提供給事故處理者驗證
  • 確保恢復到正確的 commit
  • 作為恢復操作的審計記錄

Bash
# 步驟 2.1.3: 確認當前分支
git branch

使用時機: 確認你在正確的分支上

指令說明:

  • git branch: 列出所有本地分支
  • 當前分支會有 標記

預期輸出:

  main
* scenario3-test    ← 有 * 標記的是當前分支
  feature-x

關鍵檢查:

  • ✅ 確認 標記在事故分支上(例如 scenario3-test
  • ❌ 如果不是,請不要切換分支,先回報當前狀態

2.2 事故處理者驗證回報

使用時機: 收到團隊成員的回報後

驗證清單:

  • [ ] 該成員的 git log 輸出包含所有遺失的提交
  • [ ] 提交的順序和內容正確
  • [ ] 最新的 commit 是被覆蓋前的正確狀態
  • [ ] 該成員在正確的分支上

如果驗證通過,進入步驟 3


步驟 3:團隊成員執行恢復操作

⚠️ 關鍵警告: 以下操作由擁有完整歷史的團隊成員執行,不是事故處理者!

3.1 建立安全備份

Bash
git branch backup-before-fix-$(date +%Y%m%d-%H%M%S)

使用時機: 執行任何恢復操作之前的第一步

指令說明:

  • git branch: 建立新分支
  • backup-before-fix-$(date +%Y%m%d-%H%M%S): 分支名稱
    • backup-before-fix-: 前綴,表示這是恢復前的備份
    • $(date +%Y%m%d-%H%M%S): 自動插入當前日期時間(例如:20251017-143052)

預期輸出: (通常沒有輸出,表示成功)

驗證備份已建立:

Bash
git branch -v | grep backup

預期輸出:

Bash
  backup-before-fix-20251017-143052 d8d9535 feat: Developer C - 資料分析儀表板

為什麼要備份:

  • 如果恢復操作失敗,可以重新嘗試
  • 作為審計記錄,證明執行了備份
  • 提供心理安全感,降低操作壓力

3.2 最後確認檢查

使用時機: 備份完成後,執行 force push 之前

Bash
# 確認 1: 再次檢查當前分支
git branch

必須確認: 當前分支(有 * 標記)是事故分支


Bash
# 確認 2: 檢查提交歷史
git log --oneline -5

必須確認:

  • 最新的 commit 包含所有應該保留的工作
  • 沒有看到錯誤的 commit

Bash
# 確認 3: 查看當前 HEAD 指向
git rev-parse HEAD

必須確認: 這個 hash 與之前記錄的一致


3.3 執行恢復 Force Push

Bash
git push --force origin <分支名稱>

使用時機: 完成所有確認後,準備恢復遠端分支

指令說明:

  • git push: 推送到遠端
  • -force: 強制推送,覆蓋遠端的錯誤狀態
  • origin: 遠端儲存庫名稱
  • <分支名稱>: 要恢復的分支(例如 scenario3-testmain

實際範例:

Bash
# 恢復 scenario3-test 分支
git push --force origin scenario3-test

# 恢復 main 分支
git push --force origin main

預期輸出:

Bash
Enumerating objects: 86, done.
Counting objects: 100% (86/86), done.
Delta compression using up to 8 threads
Compressing objects: 100% (26/26), done.
Writing objects: 100% (69/69), 6.99 KiB | 6.99 MiB/s, done.
Total 69 (delta 47), reused 65 (delta 43), pack-reused 0
To gitlab.com:james-berget/git_demo.git
 + 8afeb60...d8d9535 scenario3-test -> scenario3-test (forced update)

關鍵資訊解析:

  • + 符號: 表示這是強制更新
  • 8afeb60...d8d9535: 從錯誤的 commit 恢復到正確的 commit
  • (forced update): 確認這是強制更新

⚠️ 為什麼使用 –force 而不是 –force-with-lease:

  • -force-with-lease 的問題:
Bash
# ❌ 這個命令可能會失敗
git push --force-with-lease origin scenario3-test
# 錯誤: ! [rejected] scenario3-test -> scenario3-test (stale info)

原因:

  • -force-with-lease 會檢查遠端分支是否與本地的遠端追蹤分支一致
  • 在情境三中,遠端已經被覆蓋,與團隊成員本地的追蹤分支不一致
  • 使用 -force-with-lease 會被拒絕,無法完成恢復
  • -force 的正當性:
  • 我們確定遠端分支已經損壞
  • 我們確認本地分支有完整的正確歷史
  • 這是緊急恢復操作,必須覆蓋遠端的錯誤狀態
  • 已經建立了備份分支,有安全保障

步驟 4:驗證恢復結果

4.1 事故處理者驗證遠端

使用時機: 團隊成員完成 force push 後立即執行

Bash
git fetch origin

使用時機: 更新本地的遠端追蹤分支資訊

指令說明:

  • git fetch origin: 從遠端下載最新的分支資訊
  • 不會改變你的工作目錄或當前分支
  • 只是更新本地的遠端追蹤分支(origin/mainorigin/scenario3-test 等)

預期輸出:

Bash
From gitlab.com:james-berget/git_demo
 + 8afeb60...d8d9535 scenario3-test -> origin/scenario3-test (forced update)

關鍵資訊:

  • + 符號: 遠端分支被強制更新
  • 8afeb60...d8d9535: 從錯誤狀態恢復到正確狀態
  • 這個輸出確認遠端已經被成功恢復

git log origin/<分支名稱> --oneline -10

使用時機: 確認遠端分支現在有正確的提交歷史

指令說明:

  • git log origin/<分支名稱>: 顯示遠端分支的提交歷史
  • -oneline: 簡潔格式,每個提交一行
  • 10: 顯示最近 10 個提交

範例:

Bash
git log origin/scenario3-test --oneline -10

預期輸出:

Bash
d8d9535 (origin/scenario3-test) feat: Developer C - 資料分析儀表板
7848581 feat: Developer B - 訂單處理系統
8cf0e5d feat: Developer A - 使用者認證系統
34d3940 test: add synthetic record #15
...

驗證要點:

  • 所有遺失的提交都已經回來
  • 提交順序正確
  • 沒有看到錯誤的 commit

Bash
git rev-parse origin/<分支名稱>

使用時機: 精確驗證遠端分支指向的 commit hash

指令說明:

  • git rev-parse: 將引用轉換為完整的 commit hash
  • origin/<分支名稱>: 遠端追蹤分支

範例:

Bash
git rev-parse origin/scenario3-test

預期輸出:

Bash
d8d9535e7503b8aeb9976e6e368188ba32305c6e

驗證方式:

  • 將這個 hash 與團隊成員回報的 hash 比對
  • 應該完全一致(或至少前 7 個字元一致)

4.2 同步本地分支

使用時機: 確認遠端已經正確恢復後

Bash
git reset --hard origin/<分支名稱>

使用時機: 將本地分支重置為遠端的狀態

指令說明:

  • git reset --hard: 重置當前分支並清除工作目錄的所有變更
  • origin/<分支名稱>: 重置到遠端追蹤分支的狀態

⚠️ 警告:

  • -hard永久刪除所有未提交的變更
  • 執行前確保沒有重要的本地修改
  • 如果有未提交的工作,先執行 git stash save "備份"

範例:

Bash
git reset --hard origin/scenario3-test

預期輸出:

Bash
HEAD is now at d8d9535 feat: Developer C - 資料分析儀表板

Bash
git log --oneline -5

使用時機: 驗證本地分支已經同步

預期輸出:

Bash
d8d9535 (HEAD -> scenario3-test, origin/scenario3-test) feat: Developer C - 資料分析儀表板
7848581 feat: Developer B - 訂單處理系統
8cf0e5d feat: Developer A - 使用者認證系統
34d3940 test: add synthetic record #15
212b5e8 test: add synthetic record #14

驗證要點:

  • HEAD -> scenario3-testorigin/scenario3-test 指向同一個 commit
  • 所有應該存在的提交都在歷史中

4.3 驗證檔案已恢復

Bash
ls -la

使用時機: 快速確認關鍵檔案是否存在

範例:

Bash
# 列出特定檔案
ls -la scenario3_*.txt

預期輸出:

Bash
scenario3_developer_A_feature.txt
scenario3_developer_B_feature.txt
scenario3_developer_C_feature.txt

驗證要點:

  • 所有應該存在的檔案都已恢復
  • 錯誤的檔案已經消失(如果有的話)

步驟 5:通知團隊並協助同步

5.1 發送恢復完成通知

使用時機: 驗證完成後立即發送

在團隊頻道發送以下訊息:

Bash
恢復完成 - Force Push 事故已解決

分支 [分支名稱] 已成功恢復

恢復資訊:
- 事故發生時間: [時間]
- 恢復完成時間: [時間]
- 總耗時: [例如: 10 分鐘]
- 恢復的提交數: [例如: 3 個]
- 協助成員: [感謝提供完整歷史的成員名字]

恢復的提交:
- d8d9535: Developer C - 資料分析儀表板
- 7848581: Developer B - 訂單處理系統
- 8cf0e5d: Developer A - 使用者認證系統

所有人請立即同步:

步驟 1: 保存當前工作(如果有未提交的變更)
  git stash save "備份-恢復前"

步驟 2: 更新遠端資訊
  git fetch origin

步驟 3: 重置本地分支
  git reset --hard origin/[分支名稱]

步驟 4: 驗證歷史完整性
  git log --oneline -10

步驟 5: 恢復之前保存的工作(如果有)
  git stash pop

⚠️ 如有任何問題或錯誤,請立即回報
⚠️ 請確認你的提交都在歷史中

5.2 團隊成員同步指令詳解

給所有團隊成員的詳細指示:

Bash
# 步驟 1: 保存當前工作(如果有未提交的變更)
git stash save "備份-恢復前"

使用時機: 如果你有未提交的變更需要保留

指令說明:

  • git stash save: 暫存當前的變更
  • "備份-恢復前": 暫存的描述訊息

⚠️ 注意: 如果沒有未提交的變更,這個命令會顯示 “No local changes to save”,這是正常的。


Bash
# 步驟 2: 更新遠端資訊
git fetch origin

使用時機: 所有人都必須執行

指令說明: 從遠端下載最新的分支資訊

預期輸出:

Bash
From gitlab.com:james-berget/git_demo
 + 8afeb60...d8d9535 scenario3-test -> origin/scenario3-test (forced update)

Bash
# 步驟 3: 檢視差異(可選但建議)
git log HEAD..origin/<分支名稱> --oneline

使用時機: 想要確認遠端有哪些提交是本地沒有的

指令說明:

  • HEAD..origin/<分支名稱>: 顯示遠端領先本地的提交
  • 這些就是恢復回來的提交

Bash
# 步驟 4: 重置本地分支
git reset --hard origin/<分支名稱>

使用時機: 確認要同步到遠端的狀態

⚠️ 警告: 這會刪除所有未提交的變更!確保已經執行了 git stash


Bash
# 步驟 5: 驗證歷史完整性
git log --oneline -10

使用時機: 重置後立即驗證

驗證要點:

  • 看到所有恢復的提交
  • 確認你自己的提交(如果有)也在歷史中

Bash
# 步驟 6: 恢復之前保存的工作(如果有)
git stash pop

使用時機: 如果步驟 1 執行了 git stash save

指令說明:

  • git stash pop: 恢復最近一次 stash 的變更
  • 如果有衝突,需要手動解決

情境三完整操作檢查清單

事故處理者清單:

  • [ ] 確認本地和遠端都無法找回完整歷史
  • [ ] 發送緊急求助訊息到團隊頻道
  • [ ] 明確指示團隊成員不要執行 git pull
  • [ ] 收集團隊成員的回報
  • [ ] 驗證回報成員的歷史完整性
  • [ ] 選擇有完整歷史的成員執行恢復

執行恢復的團隊成員清單:

  • [ ] 執行 git log --oneline -10 確認有完整歷史
  • [ ] 執行 git rev-parse HEAD 記錄 commit hash
  • [ ] 執行 git branch 確認在正確分支
  • [ ] 執行 git branch backup-before-fix-$(date +%Y%m%d-%H%M%S) 建立備份
  • [ ] 再次確認當前分支正確
  • [ ] 執行 git push --force origin <branch> 恢復遠端

所有團隊成員清單:

  • [ ] 執行 git stash save "備份-恢復前" 保存本地變更
  • [ ] 執行 git fetch origin 更新遠端資訊
  • [ ] 執行 git reset --hard origin/<branch> 同步本地
  • [ ] 執行 git log --oneline -10 驗證歷史
  • [ ] 執行 git stash pop 恢復本地變更(如果有)
  • [ ] 確認自己的提交都在歷史中
  • [ ] 向事故處理者確認同步成功

如果所有項目都勾選,恭喜你!成功從最困難的情境三中恢復了!!

如果連你的 reflog 都遺失了,不要絕望。只要有任何一位團隊成員在事故發生前拉取過最新的程式碼,他的電腦裡就有一份完整的歷史備份。

請他執行 git log 找到正確的 commit hash,然後你可以從他的電腦 git fetch,或者讓他建立恢復分支並推送到遠端。

記住,只要 commit 存在過,它幾乎永遠不會真正從 Git 中消失,只等你將它找回。

Ad Feature

這裡可以輸入標題、內文,也可以做粗體和斜體的變化,也可以插入圖片,或者是用 iframe 語法做任何操作也是可以的,如不需要可至精選功能 → 文末自訂廣告刪除。

分享此內容: