
オープンソースプロジェクトで、必要な機能のほとんどは備わっているけれど、すべてではない、というものに出会ったことはありませんか?私はいつもこれに遭遇します。自分の機能を追加して非公開にし、しかも正気を保ちながらアップストリームのバグ修正を取り込みたい、と。
GitHubの標準的なフォークワークフローは、公開された貢献にはうまく機能します。しかし、誰かのオープンソースの上に独自のものを構築している場合はどうでしょうか?その場合は、異なるアプローチが必要です。
いくつかの異なる戦略を試した結果、私がたどり着いた設定がこれです。派手ではありません。しかし、機能します。
最新のアップストリームの変更の上に、カスタムコミットをきれいに適用したプライベートフォークを維持する。マージノイズなし。手動での競合の悪夢なし。
まず、元のプロジェクトをクローンします。次に、リモートを入れ替えて、プライベートリポジトリがoriginになり、元のリポジトリがupstreamになるようにします。
git clone https://github.com/original-author/some-project
cd some-project
git remote set-url origin https://github.com/you/your-private-repo
git remote add upstream https://github.com/original-author/some-project
git push origin main
なぜこのようにするのか?理由は2つあります。まず、originはgitのデフォルトのリモートです。何も考えずにgit pushと入力すると、プライベートリポジトリに送られます。これは安全です。次に、元のリポジトリをupstreamとして保持することで、更新がどこから来るのかが明確になります。
これを行わずに、フォークのリモートに直接プッシュする人を見たことがあります。それもうまくいきます。しかし、私はどのリモートがどれであるかを明示的にするのが好きです。そうすることで、夜11時に間違った場所に何かをプッシュしてしまうのを防げます。
ここに実際のワークフローがあります。元のプロジェクトは動き続けています。カスタム作業を壊すことなく、それらの変更を取り込む必要があります。
git fetch upstream
git checkout main
git rebase upstream/main
git push origin main --force-with-lease
コマンドは4つだけです。それぞれの重要性について説明しましょう。
git fetch upstreamは、元のプロジェクトから最新のコミットをダウンロードします。それらを自分のブランチにマージすることはありません。これは意図的なものです。適用する前に、何が来るのかを確認したいのです。
以前は習慣でgit pull upstream mainを実行していました。悪い考えです。プルはフェッチと暗黙的なマージを行います。自分の変更と相手の変更がどのように結合されるかを制御できなくなります。
git rebase upstream/mainは、自分のコミットを巻き戻し、アップストリームの変更を適用し、その上に自分のコミットを再生します。カスタム作業は上に残り、アップストリームの変更は下に残ります。
マージコミットは、共同作業において目的を果たします。しかし、単独のフォーク同期の場合、それらはノイズです。git mergeを実行するたびに、「ブランチxをブランチyにマージしました」というコミットが作成されます。これを何十回も行うと、履歴が空のマージバブルの混乱になります。リベースは履歴を線形に保ち、読みやすくします。
リベースは履歴を書き換えます。コミットハッシュが変わります。通常のプッシュを試みると、Gitは警告を発します。そこで--force-with-leaseの出番です。
git push origin main --force-with-lease
これは単純な--forceよりも安全です。前回のフェッチ以降に他の誰もリモートブランチにプッシュしていないことを確認します。もしプッシュしていれば、gitはプッシュを拒否します。誤って誰かの作業を上書きすることはありません。
単純な--forceは盲目的にプッシュします。--force-with-leaseはあなたの安全ベルトです。常に使用してください。
リベースよりもマージを主張する人もいます。彼らは、リベースは履歴を書き換えるので悪いと言います。その主張は理解できます。複数の共同作業者がいる共有ブランチでは、私も同意します。マージコミットは、いつ何が起こったかの記録を作成します。
しかし、プライベートフォークの場合はどうでしょうか?作業しているのはあなただけです。クリーンな線形履歴は、すべての同期のタイムスタンプ付き記録よりも価値があります。6か月後に振り返って、アップストリームのリリースの上にきれいに積み重ねられたカスタムコミットを見たいと思うでしょう。マージバブルの絡み合ったウェブではありません。
フォークでチームと作業している場合は、マージを使用してください。チームとコミュニケーションを取りましょう。1つの戦略を選び、それを守りましょう。チームの半分がリベースし、もう半分がマージするという最悪の事態はありません。
リベースは時々競合を引き起こします。楽しいことではありませんが、管理可能です。
git rebase upstream/main
# git says: conflict in some-file.ts
# Fix the file manually
git add some-file.ts
git rebase --continue
重要な洞察:競合は彼らのコードではなく、あなたのコードで解決してください。アップストリームの変更は事実です。あなたのカスタム変更は、アップストリームのメンテナーが変更した内容に適応する必要があります。
競合が大きすぎる場合は、いくつかの選択肢があります。
git rebase --abortはすべてを元に戻します。時間がない場合はgit merge upstream/mainを試してください。アップストリームはあなたのフォークについて知りません。彼らが内部をリファクタリングすると、あなたのカスタムコードが壊れるでしょう。それは普通のことです。大規模なアップストリームリリース後には、競合解決のための時間を予算に組み込みましょう。
このアプローチは、すべての状況に適しているわけではありません。以下は、私が別の方法を選ぶ場合です。
フォークを非公開にする前に、アップストリームのライセンスを読んでください。MITとApache 2.0はプライベートフォークを許可します。GPLとAGPLは、ソフトウェアを配布する場合、変更を公開することを義務付けています。これはオプションではありません。ライセンスに違反すると、法的な問題に巻き込まれる可能性があります。
以下に、ワークフロー全体を1つのブロックにまとめました。ブックマークしてください。
# Initial setup
git clone https://github.com/original-author/some-project
cd some-project
git remote set-url origin https://github.com/you/your-private-repo
git remote add upstream https://github.com/original-author/some-project
git push origin main
# Each sync cycle
git fetch upstream
git checkout main
git rebase upstream/main
git push origin main --force-with-lease
私はこのワークフローを約2年間、いくつかのプロジェクトで使ってきました。完璧ではありません。競合はまだ発生します。大規模なリベースは面倒なこともあります。しかし、他の選択肢よりはましです。
他の選択肢はもっとひどいです。アップストリームと同期せず、フォークが腐ってしまうか、盲目的にマージして履歴が誰も読めないほど絡み合ってしまうか、あるいはプライバシーを諦めてすべてをオープンソースにするかです。
自分の状況に合ったアプローチを選びましょう。私にとって、定期的なリベース同期を伴うプライベートフォークは、最新の状態を保ちながらコントロールを維持するのに最適な方法でした。あなたの場合は異なるかもしれません。それで構いません。
あなたは`git rebase`ワークフローを熱心に使い、プライベートフォークをアップストリームのオープンソースプロジェクトと同期させています。ある日、rebase後に`git push origin main --force-with-lease`を試みましたが、プッシュが拒否されました。実際のシナリオで、この拒否の最も可能性の高い理由は何ですか?
AI CLIログの整形、Cron式の作成、JWTの解析、chmod権限の計算をオフラインで実行。

GitHub Actionsワークフローをセットアップし、テスト実行、dependencyのキャッシュ、Vercelへのデプロイ、secretsの処理、CIを遅くしたり不安定にしたりする一般的な間違いの回避など、典型的なWebプロジェクトにおけるCI/CDの実践的なウォークスルーを紹介します。
続きを読む
Google Cloud Platform (GCP) の全サービスを網羅したアーキテクチャハンドブックとチートシートで、エンタープライズ意思決定マトリクス、CLIレシピ、コストトラップ、本番環境配線までを解説します。
続きを読む
IstioAmbientMeshとSidecarアーキテクチャの比較、メモリオーバーヘッド、Ztunnel、ゼロトラストセキュリティについて、実戦で検証された本番環境の事例を交えながら詳細に解説するアーキテクチャガイドです。
続きを読む