Git 全景課 / 第三部分 協作與正本控制 · M9
M9 遠端倉庫:clone、fetch、pull、push
到目前為止,一切都發生在你自己的電腦上。協作的第一步,是明白「你的倉庫」和「GitHub 上的倉庫」是兩個獨立的倉庫,它們只在你執行 fetch、pull、push 時才交換東西。看懂
origin/main 這張標籤,遠端就不再神秘。
學完本單元,你能夠:
- 說出本機倉庫和遠端倉庫的關係,以及
origin、origin/main是甚麼; - 分清 fetch、pull、push 各自搬動了甚麼;
- 讀懂
git status的 ahead/behind/diverged,並決定下一步; - 處理被拒絕的 push,並在必要時安全地用
--force-with-lease。
1兩個獨立的倉庫 Two separate repositories
M0 說過,Git 是分散式的:每個人手上都有完整的倉庫。GitHub 上的倉庫只是另一個倉庫,恰好放在一部大家都連得到的伺服器上,並由團隊約定為正本。
2clone:取得一份完整的複本 Cloning
git clone git@github.com:you/report.git
Cloning into 'report'...
done.
git clone 做了四件事:① 下載遠端的全部物件(整段歷史,不只最新版本);② 把遠端記為 origin;③ 為遠端每條分支建立遠端追蹤分支 origin/main、origin/feature……;④ 建立本機的 main,設定它「追蹤」origin/main(稱為 upstream),並把檔案攤開在工作目錄。
之後 git switch feature 時,如果本機沒有 feature 但有 origin/feature,Git 會自動建立一條追蹤它的本機分支:
Switched to a new branch 'feature'
branch 'feature' set up to track 'origin/feature'.
3origin/main:你對遠端的最後記憶 Remote-tracking branches
origin/main 不是遠端本身,而是你電腦上的一張標籤,記着「上一次聯絡時,GitHub 的 main 在哪個 commit」。所以 git status 說的 ahead/behind,是和這份記憶比較:
git status 顯示 | 意思 | 下一步 |
|---|---|---|
up to date with 'origin/main' | 和記憶一致(但記憶可能已過時!) | 不確定時先 git fetch |
ahead of 'origin/main' by 1 commit | 你有 1 個 commit 還沒送出 | git push |
behind 'origin/main' by 1 commit, and can be fast-forwarded | 遠端有你還沒收的 commit | git pull |
have diverged | 兩邊各有對方沒有的 commit | git pull --rebase 或 --no-rebase,再 push |
「up to date」不代表遠端沒有新東西
git status 不會聯絡 GitHub。如果同事剛剛 push 了,在你 git fetch 之前,你的 origin/main 仍停在舊位置,status 會繼續說 up to date。
4fetch、pull、push The three sync verbs
| 命令 | 方向 | 改變了甚麼 | 會動你的分支和檔案嗎 |
|---|---|---|---|
git fetch | GitHub → 你 | 下載新 commit,更新 origin/* | 不會。完全安全 |
git pull | GitHub → 你 | fetch,然後把 origin/main 合併(或 rebase)進目前分支 | 會 |
git push | 你 → GitHub | 上傳 GitHub 沒有的 commit,移動 GitHub 上的分支,同時更新你的 origin/* | 不動你的,只動遠端的 |
push 只會在「遠端分支是你分支的祖先」時成功(等於讓遠端 fast-forward);否則就會被拒絕,以免蓋掉別人的 commit。第一次推送一條新分支時加 -u,設定它的 upstream,之後就能直接 git push、git pull。
5沙盒:你、GitHub、同學 Sandbox: you, GitHub and a classmate
6push 被拒絕的時候 When a push is rejected
被拒絕是 Git 在保護別人的工作,不是錯誤。按訊息判斷:
| 訊息 | 原因 | 做法 |
|---|---|---|
(fetch first) | 遠端有你從未見過的 commit | git pull --rebase(或 --no-rebase),再 push |
(non-fast-forward) | 你的分支不包含遠端的 tip:可能是分岔了,或你改寫了已推送的 commit | 分岔 → 同上;改寫了自己的個人分支 → --force-with-lease |
(stale info) | 用了 --force-with-lease,但遠端在你上次 fetch 之後又變了 | 先 fetch 看看別人推了甚麼,再決定 |
Need to specify how to reconcile divergent branches | git pull 時分岔了,Git 要你選合併方式 | --rebase、--no-rebase 或 --ff-only;也可用 git config pull.rebase true 設定預設 |
--force 與 --force-with-lease
--force 讓遠端無條件變成你的版本,期間別人推送的 commit 會被丟掉。--force-with-lease 多一個條件:「遠端現在仍是我上次看到的那個 commit」才覆蓋,否則拒絕(stale info)。改寫自己個人分支後需要推送時,一律用後者。受保護的 main 兩者都不允許(FAQ-002、M11)。
7容易混淆的對照 Easily confused
main | origin/main | |
|---|---|---|
| 是誰的 | 你的本機分支 | 你電腦上對遠端 main 的記錄 |
| 何時移動 | 你提交、合併、reset 時 | 只在 fetch、pull、push 時 |
| 能在上面提交嗎 | 可以 | 不行(switch 過去會進入 detached HEAD) |
常見誤解
- 「commit 之後,同事就看得到。」不會。commit 只在你的倉庫;要 push 之後,同事 fetch 才看得到。
- 「
git fetch沒有改變我的檔案,所以甚麼都沒做。」它更新了origin/*並下載了物件;用git log --oneline --all或git status就看得到。 - 「
git pull只是下載。」它是 fetch+合併(或 rebase),會改動你的分支和檔案。想先看看再決定,就只 fetch。 - 「origin 是 GitHub 的意思。」origin 只是 clone 時給遠端取的預設代號,可以有多個遠端,例如
upstream(M10 的 fork 流程)。
8命令速查 Cheat sheet
| 命令 | 作用 |
|---|---|
git clone <網址> | 取得完整複本,設定 origin 與追蹤關係 |
git remote -v | 列出遠端及其網址 |
git fetch | 下載新 commit,只更新 origin/* |
git pull --rebase / --no-rebase / --ff-only | fetch 後以 rebase/合併/只容許 fast-forward 收進來 |
git push | 推送目前分支到它的 upstream |
git push -u origin feature | 第一次推送新分支並設定 upstream |
git push --force-with-lease | 改寫個人分支後的安全強制推送 |
git branch -vv | 每條分支追蹤誰、ahead/behind 多少 |
git log --oneline --all | 連 origin/* 一起看 |
9小測 Quiz
本單元重點 Summary
- 你的倉庫和 GitHub 上的倉庫是兩個獨立的倉庫;origin 是遠端的代號。
origin/main是你對遠端 main 的最後記憶,只在 fetch、pull、push 時更新。- fetch 只下載;pull=fetch+合併/rebase;push 只在遠端可以 fast-forward 時成功。
- 被拒絕時先 fetch 或 pull;分岔時用
--rebase或--no-rebase明確選擇。 - 需要強制推送時用
--force-with-lease,而且只用在自己的個人分支。
延伸閱讀
- Pro Git 2.5 Working with Remotes
- Pro Git 3.5 Remote Branches
- git push 官方文件(--force-with-lease 一節)
- Learn Git Branching 的「Remote」關卡,可以練習 fetch、pull、push
- 有問題?記下來 → 學生問題庫