こんにちは!日々の開発、本当にお疲れ様です。
新しいツールや技術に触れるときって、ワクワクする半面、「これ、本当に自分のプロジェクトの役に立つのかな?」と少し不安になりますよね。
今回は、複数のリポジトリ間でコードをスマートに共有・同期するための秘密兵器、「Git Subtree(サブツリー)」と、それをGitHub Actionsで完全に自動化するテクニックについてお話しします。
「共通のライブラリや設定ファイルを3つのリポジトリで使っているけれど、修正するたびにコピペしたり、Submoduleで地獄を見たりした……」そんな苦い経験はありませんか?
これをマスターすれば、コードの共有と同期に悩む時間は一切なくなります。一緒に優しく、確実にお仕事の効率を爆上げしていきましょう!
—
1. なぜ「Git Submodule」ではなく「Git Subtree」なのか?
複数のリポジトリでコードを共有する機能として、Gitにはもともと Git Submodule(サブモジュール) という機能があります。しかし、これがなかなか曲者です。
- Submoduleの悲劇:
参照先のコミットハッシュがガチガチに固定されるため、親リポジトリ側で「ちょっとこのバグだけ直して親にすぐ反映したい」と思っても、HEADがデタッチしたり、更新を忘れて「あれ、動かない?」とチームメンバー全員が混乱する原因になります。
- Subtreeの優しさ:
一方、今回紹介する Git Subtree は、「別リポジトリのコードを、自分のリポジトリの『ただのフォルダ』として取り込む」 技術です。
取り込んでしまえば、もう中身はあなたのプロジェクトの一部。普通のコードと同じように修正して、そのままコミット・プッシュできちゃいます。もちろん、本家(元リポジトリ)のアップデートを取り込むことも自由自在です。
初心者の方こそ、この直感的に扱えるGit Subtreeから入るのが圧倒的に幸せになれますよ。
—
2. 基礎のセットアップ:Git Subtreeを動かしてみよう
まずは、自分の手元(ローカル環境)で、別のリポジトリからコードを取り込む基本の動きを体験してみましょう。特別なインストールは不要です。お使いのGitに標準で備わっています。
前提シナリオ
- 親リポジトリ(Main Repo): あなたが今作っているプロダクト。
- 子リポジトリ(Sub Repo): 複数のプロジェクトで使い回したい共通コンポーネントや設定ファイル群。
ステップ1:子リポジトリを「サブツリー」として追加する
親リポジトリのルートディレクトリで、以下のコマンドを叩くだけです。これだけで、指定したフォルダに別リポジトリの全コードが降ってきます。
git subtree add –prefix=<取り込み先フォルダ名> <子リポジトリのURL> <ブランチ名> –squash
git subtree add –prefix=shared-components https://github.com/your-name/shared-lib.git main –squash
- `–prefix`: 親リポジトリのどこに配置するかを指定します(例: `shared-components`)。
- `–squash`: 子リポジトリの膨大なコミット履歴を親側に持ち込まず、1つの綺麗なコミットにまとめて取り込む魔法のオプションです。これをつけると履歴が汚れません。優しさですね!
これで、`shared-components` というフォルダが生まれ、中身のコードを自由に編集できるようになりました。
ステップ2:子リポジトリの最新変更を「取り込む(プルする)」
本家(子リポジトリ)側でコードがアップデートされたら、親リポジトリ側へは以下のコマンドで同期します。
git subtree pull –prefix=shared-components https://github.com/your-name/shared-lib.git main –squash
これだけで、最新の変更が手元のプロジェクトにマージされます。すごく直感的ですよね!
—
3. GitHub Actionsで「同期」を完全に自動化する
さて、ここからが本番です。
「手動で `git subtree pull` を打つのは面倒くさい!」
「本家リポジトリを更新した瞬間に、すべての派生リポジトリ(子を使う側)へ自動で反映させたい!」
これをGitHub Actionsの力で実現しましょう。今回は、「本家ライブラリが更新されたら、自動で宛先リポジトリにPull Requestを送る」または「直接プッシュする」ワークフローを構築します。
以下のYAMLファイルを、本家リポジトリの `.github/workflows/sync.yml` として配置してください。
name: Sync to Derivative Repositories
on:
push:
branches:
- main # 本家のmainブランチが更新されたら発火
jobs:
sync:
runs-on: ubuntu-latest
steps:
# 1. 本家リポジトリのコードをチェックアウト
- name: Checkout Source Repository
uses: actions/checkout@v4
with:
fetch-depth: 0 # 履歴全体を取得
# 2. 派生リポジトリへ変更を伝搬させる公式アクションを使用
- name: Rsync / Push to Target Repo
uses: cpina/github-action-push-to-another-repository@main
env:
API_TOKEN_GITHUB: ${{ secrets.ACCESS_TOKEN }} # 権限を持ったPersonal Access Token
with:
source-file: “shared-code/” # 共有したいフォルダ
destination-repo-path: “shared-code/” # 派生側での配置先フォルダ
destination-repository: “your-name/derivative-project-repo” # 派生リポジトリ名
user-email: “bot@example.com”
commit-message: “chore: sync shared-components from upstream”
target-branch: main
このワークフローの優しさポイント
1. 完全自動化: あなたが本家リポジトリに `git push` をした瞬間、裏側でGitHubが勝手に派生リポジトリ側のコードを最新に書き換えてくれます。コピペ作業とはもうサヨナラです。
2. アクセストークン(Secrets): 別リポジトリを操作するため、あらかじめGitHubのSettingsで発行した `ACCESS_TOKEN` をRepository Secretsに登録しておくだけのセキュア設計です。
—
4. トラブルシューティング:初心者つまずきポイント
最後に、Git Subtreeを使い始めた開発者がよくハマる「優しくない罠」とその抜け出し方をこっそり共有しておきますね。
- Q. マージコンフリクトが起きてしまったら?
- A. 恐れることはありません。通常のGitのマージコンフリクトと全く同じです。衝突したファイルを開いて修正し、`git add` して `git commit` すれば何事もなかったかのように進められます。Subtreeだからといって特別なコマンドは必要ありません。
- Q. フォルダ構成を途中で変えたくなったら?
- A. `git mv` を使って普通にフォルダを移動し、いつも通りコミットすればGitが賢く追跡してくれます。
—
おわりに
いかがでしたでしょうか?
Git SubtreeとGitHub Actionsを組み合わせることで、「コードの共有」という多くの開発者が頭を悩ませるストレスが、驚くほどエレガントに解決できます。
「複数のリポジトリを管理するのが面倒で、ついコードをコピペしちゃっていた……」
そんな過去の自分とは今日でオサラバしましょう。
これをマスターすれば、あなたの毎日の開発作業が劇的に、そして圧倒的に楽になりますよ。ぜひ、次のプロジェクトの共通パーツ管理に取り入れてみてくださいね!