GitHubで公開リポジトリにプライベートなブランチを生やす
TL;DR
次のとおりです。個人的に「二重リポ戦略」と呼んでいます。
- 同じ内容のリポジトリを2つ作成し、片方をプライベートにする
-
private/main
ブランチを作成し、プライベート側のリポジトリにのみPushする - 誤って
private/main
ブランチを公開リポジトリ/ブランチにPush/Mergeしないよう、commit hookを設定する。
Motivation
最近、機械学習を利用した個人開発サービス「Turntable」をリリースしました。
TurntableではBlender拡張もあるのですが、そちらはオープンソースで公開しています。
一方で、キャラクターを360度回転させるためのモデル(具体的にはFramePackのLoRA)は非公開です。どのようなビジネスの機会があるか分からないので、そうした判断をしています。
しかし、開発自体はモノリポの方が捗るのも事実です。そこで、前述の「二重リポ戦略」を用いて、一部のコードをOSSにしつつ、モデルや学習データ・推論コードをクローズドに保っています。
それ以外にも、例えば「フロントエンドはOSSにしたいが、バックエンドは非公開にしたい」のような場合でも役に立つと思います。
やり方
まずリポジトリを2つ、公開と非公開で新規作成します。私の場合、非公開の方は頭にprivate
を付けています。
次に、ローカルのリポジトリにprivate/main
ブランチを作成します。この時単にprivate
ブランチを作成すると、あとあとprivate/hogehoge
が作成できなくなるので注意です。
それが完了したら、private/main
のremoteをprivate
という名前で非公開のリポジトリに設定します。また、誤って公開リポジトリ側にPush / 公開ブランチ側にMergeしないように、commit hookを設定します。
Commit Hook
まず、.githooks
を作成します。私のリポジトリからコピーするのが手っ取り早いかもです。
(コードはClaudeで書きました)
なお、~/.git-template/hooks/
を活用する方法もあります。非公開リポジトリの存在さえ明かせない場合は、そちらを検討してもいいかもしれません。私の場合は機械学習モデルの訓練のためにGPUサーバーにリポジトリをclone
する機会が多いため、.githooks
を使っています。
% chmod +x .githooks/*
% git config core.hooksPath .githooks
% git config core.hooksPath
.githooks
動作確認
pre-push
は次のように動作します。
% git push
Error: Attempting to push private branch 'private/test' to remote 'origin'
Private branches can only be pushed to the 'private' remote
Use: git push private private/test
error: failed to push some refs to 'ssh://github.com/xhiroga/blender-mcp-senpai'
post-merge
は次のように動作します。なお、Mergeを防ぐ効果はないのでご注意ください(警告のみ)
% git merge private/test
Merge made by the 'ort' strategy.
TEST.md | 0
1 file changed, 0 insertions(+), 0 deletions(-)
create mode 100644 TEST.md
⚠️ WARNING: You just merged a private branch into public branch 'feat/hooks'
This may expose private code to the public repository!
To undo this merge, run:
git reset --hard HEAD^
別の環境にCloneする場合
まず公開リポジトリをCloneし、次に非公開リポジトリからprivate/
ブランチをPullするのがおすすめです。
% git clone git@github.com:owner/my-repo.git
% cd my-repo
# 非公開リポジトリを remote として追加
% git remote add private git@github.com:owner/private-my-repo.git
# 非公開リポジトリの private/* ブランチを取得
% git fetch private
# 例: private/main をローカルに作成して追跡
% git switch -c private/main --track private/private/main
プライベートブランチの内容を公開ブランチに取り込む場合
公開ブランチとプライベートブランチで共通のファイルの内容を、プライベートブランチ側で編集してしまった、ということがあると思います。次のように取り込むと、コミットは手動にできるので安全でオススメです。
git switch main
git restore --source <feature-branch> --staged --worktree --pathspec-from-file=<(git diff --name-only --diff-filter=M main...<feature-branch>
注意事項
Commit Hookで予防を試みているものの、開発効率 > 公開リスク である場合(研究・小規模サービス)のみ適用するようにしてください。
企業で開発しているプロダクトや、個人情報・課金情報を扱うようなプロダクトでは、素直に別リポジトリに分けるのが良いと思います。
まとめ
モノリポの一部機能を隠したまま、OSSにすることができます。皆さんもぜひOSS活動をしてください!
Discussion