バージョン管理システムにおいて、並行して進行する開発作業は別ブランチで管理されることが一般的です。これらの作業成果を統合する際、git mergeと並んで頻繁に使用されるのがgit rebaseです。本記事では、リベースの内部動作原理、コミット履歴の変遷、および競合発生時の標準的なワークフローを技術的に解説します。
シナリオの構築と分岐の発生
まず、新規リポジトリを初期化し、ベースとなる構成ファイルを追加して最初のコミットを作成します。
mkdir project-root && cd project-root
git init
echo "app_name: sample_service" > app.conf
git add app.conf
git commit -m "init: baseline configuration"
この段階では、mainブランチ上に単一のコミットが存在する状態です。
次に、メインラインとは独立した機能開発ブランチを生成し、両ブランチで別々の修正を加えます。
# 機能開発ブランチの作成と切替
git checkout -b feature-auth
# mainブランチ上で設定値を更新
git checkout main
echo "timeout: 30s" >> app.conf
git commit -am "refactor: adjust timeout setting"
# 機能ブランチに戻り、独自の変更を追加
git checkout feature-auth
echo "auth_mode: oauth2" >> app.conf
git commit -am "feat: introduce oauth2 authentication"
リベース実行前の履歴構造
上記手順完了時点での両ブランチは、初期コミットを共通の基点として分岐した状態です。git log --oneline --graphで構造を確認すると、以下のようになります。
main:
* B (adjust timeout setting)
* A (baseline configuration)
feature-auth:
* C (introduce oauth2 authentication)
* A (baseline configuration)
この構造において、コミットCはコミットAを直接の親として参照しています。両ブランチを単純に統合すると、Gitはデフォルトでマージコミットを生成し、履歴が三角形状に分岐・結合する形になります。
リベースの動作メカニズム
リベースは、分岐ブランチのコミットを切り離し、指定されたベースブランチの先端に再適用する処理です。機能ブランチ上でベースを指定して実行します。
git rebase main
処理が正常に終了すると、履歴は直線的に再構成されます。
* C' (introduce oauth2 authentication) [NEW HASH]
* B (adjust timeout setting)
* A (baseline configuration)
コミットCは元の親Aとは無関係になり、代わりにBの上に新しいコミットC'として再作成されています。これは、GitがCに含まれる差分パッチを抽出し、mainブランチの最新状態(コミットB)に対して再適用した結果です。リベースの本質は、独立した変更セットを順序立てて連続的な変更履歴として再展開することにあります。
競合発生時の中断と再開フロー
リベース中は、再適用するコミットの差分とベースブランチの中間状態に矛盾が生じた場合、merge conflictを検出して処理を一時停止します。この際は以下のステップで進行管理を行います。
- 手動解決:エディタで競合ファイルを開き、不要な差分マーカー(
<<<<<<<、=======、>>>>>>>)を除去し、最終的なコード状態に修正します。 - ステージング:修正完了後、
git add <target_file>を実行し、Gitに解決済みを通知します。 - 再開:
git rebase --continueを実行し、残りのコミット適用処理を継続します。
解決が困難な場合や、試行錯誤中に元の分岐状態へ安全に復帰したいときは、以下のコマンドを使用します。
git rebase --abort:リベースセッション全体を破棄し、コマンド実行前のブランチ状態に完全にロールバックします。git rebase --skip:現在競合しているコミットの適用を完全に破棄し、次のコミット処理へと進みます。