TL;DR:git fetch upstream && git merge upstream/main && git push origin main を実行すれば、フォークは最新に追いつきます。 これが「git sync fork with upstream」ワークフローの全容です。フォーク元のプロジェクトから変更を引き取り、自分のコピーへpushします。リニアな履歴を好むなら merge を git rebase upstream/main に替えてforce-pushしてください。そして upstream リモートをまだ設定したことがないなら、下のステップ1から始めましょう。その欠けたリモートこそ、フォークが「同期できない」理由の第1位です。それ以外、つまりrebase、GitHubのSyncボタン、pushできない分岐したブランチは、すべてこの3コマンドの上の詳細に過ぎません。
フォークをupstreamと同期するとはどういうことか?
フォークとは、GitHub上の他人のリポジトリを自分用にコピーしたものです。原本が upstream、自分のコピーが origin です。GitHubのフォークは自動更新されません。メンテナーがプルリクエストをマージしても、あなたのコピーには昨日のコードが入ったままです。フォークの同期とは、upstreamの新しいコミットをフォークへ引き込み、自分のブランチをプロジェクトの現状と一致、少なくとも包含させることです。
これが重要な理由は2つです。第1にコントリビューション:古いフォークから出すプルリクエストは余計なノイズを運び、メンテナーはマージ前に更新を求めてきます。第2にセルフホスティングやコードリーディング:フォークを実運用するにもコードを読むにも、1ヶ月古いフォークは、あなたが持っていない1ヶ月分のバグフィックスです。
コマンドラインでフォークをupstreamと同期するには?
3ステップです。upstreamを一度宣言し、fetchし、mergeしてpushします。配線は恒久なので、次回からはステップ2と3だけを打ちます。
ステップ1 — upstreamリモートを追加する(クローンごとに1回)。
# フォークのローカルクローンの中で
git remote add upstream https://github.com/ORIGINAL_OWNER/REPO.git
git remote -v # 確認: origin -> 自分のフォーク、upstream -> 原本 正しいURLは原本リポジトリのページ、緑の Code ボタンで見つかります。よくあるミスは、両方のリモートを自分のフォークに向けることです。すると「同期」は静かに何もしません。自分のコピーと同じく古いものからfetchしたことになるからです。
ステップ2 — upstreamブランチをfetchしてmergeする。
git checkout main
git fetch upstream
git merge upstream/main
git push origin main 「git sync fork with upstream コマンドライン」への標準的な答えがこれです。通常の結果はfast-forwardです。フォーク側の main に新着がなければ、そのまま upstream/main へスライドし、マージコミットは作られません。
ステップ3 — 必要なときに繰り返す。 覚えることは git fetch upstream && git merge upstream/main && git push origin main だけです。mergeする前にどれだけ遅れているかを見るには、fetchのあとで git rev-list --count main..upstream/main を実行します。
フォークの同期はrebaseとmerge、どちらを使うべき?
どちらも同じコードをフォークに落とします。違うのは、あとに残る履歴です。リポジトリごとに方針を1つ決めて、それを貫いてください:
| 方法 | コマンド | 履歴の結果 | 向いている場面 |
|---|---|---|---|
| merge | git merge upstream/main | 分岐していた場合はマージコミットが1つ増える | openなPRのあるフィーチャーブランチ — 何も書き換えない |
| rebase | git rebase upstream/main | 自分のコミットを上に再生、リニアな履歴 | フォークの main をきれいに保つ、リセットしたい分岐フォーク |
| GitHub UI | Sync branchボタン / PRマージ | mergeと同じ | クローンを開かずに手早く追いつかせる |
同期のrebase版はこうです:
git fetch upstream
git rebase upstream/main
git push --force-with-lease origin main force pushは必須です。rebaseはコミットIDを書き換えるため、フォークのリモートブランチがローカルの子孫でなくなるからです。必ず --force ではなく --force-with-lease を使いましょう。誰か(あるいはあなたの別のマシン)が間にpushしていたら上書きを拒否してくれます。危険なコマンドがデフォルトで安全になります。
手首に刺したいルールが1つあります。openなプルリクエストのあるブランチは、自分が何をしているか分かっていない限りrebaseしない こと。rebaseはコミットIDを変えるため、openなPRがコミット群から引き剥がされることがあります。main はmergeで同期し(rebaseするなら新しい作業を始める前に)、PR用のブランチはこれに巻き込まないでください。
フォークがupstreamと同期できないのはなぜ?
よくある容疑者は4人です。実際の端末で出会う順に:
upstreamリモートがない —git remote -vにoriginしか表示されない。症状:git fetch upstreamが'upstream' does not appear to be a git repositoryで失敗する。対処:上のステップ1。- fetchしたがmergeしていない — fetchはローカルの
upstream/mainを更新しますが、作業ブランチには触れません。症状:fetchが成功したのにgit logが古いまま。対処:git merge upstream/main。 - 履歴が分岐している — フォークの
mainにコミットしたら、upstreamも先へ進んでいた。git pullが「unrelated histories」を言い出すか、マージを強制してきます。upstreamに合わせたい場合の対処:git reset --hard upstream/main(ローカルのmainだけのコミットは捨てられます。先にgit stash listを確認するかブランチで退避を。すでに悪いresetが起きているなら、復旧の手順はgit undo last commitと同じです。git reflogはまだ古い先端を覚えています)。 - rebase後のpushがnon-fast-forwardで拒否される — rebaseしたのに普通にpushした場合です。対処:
git push --force-with-lease origin main。
5つ目の稀なケース。upstreamリポジトリのリネームか削除で、ステップ1のURLまで404になるパターンです。GitHubはリネームされたリポジトリをリダイレクトするので、本物の失敗はたいてい削除か非公開化を意味します。同期する先がもう存在しないのです。
GitHubのWebサイトからフォークを同期できる?
はい。フォークのページでは、ブランチが遅れているときに Sync fork ボタンが表示され、1クリックでupstreamを引き込めます。その下では、同じことがプルリクエストとしても動きます。upstream/main からフォークの main へPRを出してマージするだけです。
ボタンの限界が、CLIへフォールバックすべき時を教えてくれます。fast-forwardかmergeしか行わず、rebaseはしないし、ブランチが分岐しているとはっきり拒否してコミット破棄かコマンドラインを促します。同期するのもデフォルトブランチだけです。単純な追いつきを超える作業なら、上の3コマンドが道具になります。
フォークはどのくらいの頻度で同期すべき?
正直な答えは「新しい仕事ごとに」です。新鮮な main からブランチを切れば、出すPRが「3週間前のバージョンが土台です」で始まることはありません。活発にコントリビュートするフォークなら、main を毎日かセッションごとに同期するのは数秒の作業です。読むだけ、あるいはデプロイするだけのフォークなら、upstreamが欲しいものを出したときに同期します。原本リポジトリのリリースフィードを購読して、リリースで同期しましょう。同期が安いのは、ルーチンだからです。6ヶ月遅れのフォークはマージではなく外科手術になり、「フォークを同期して」が午後まるごとの仕事に化けます。
チートシート
# 1回だけのセットアップ
git remote add upstream https://github.com/ORIGINAL_OWNER/REPO.git
# 日常の同期(merge方針)
git fetch upstream && git merge upstream/main && git push origin main
# 日常の同期(rebase方針、リニアな履歴)
git fetch upstream && git rebase upstream/main && git push --force-with-lease origin main
# どれだけ遅れているか?
git fetch upstream && git rev-list --count main..upstream/main
# 修復不能に分岐した — mainをupstreamと同一にする(破壊的)
git fetch upstream && git reset --hard upstream/main && git push --force-with-lease origin main 日常の2ライナーを筋肉に刻んでおけば、分岐したフォークは「直す問題」ではなく「読んで済む珍事」のままです。gitの家事がサーバーまで及ぶなら、journalctlチートシートがマシンの履歴を読みやすく保つ残り半分を扱っています。
— mrsaynothing
Get the next one by email
One email per post. No spam, no algorithms.
what is this?ss vs netstat:どちらのLinuxポート確認コマンドを使うべきか
記事を楽しんでいただけましたか?私の本業はこのような構築です。 採用のご相談