「あれ?このブランチ、間違ったブランチから作っちゃった!」
Gitで開発していると、こんな失敗をしてしまうことがあります。例えば、masterブランチから作るつもりが、別の作業ブランチから作ってしまった、というようなケースです。
でも安心してください。Gitには親ブランチ(派生元ブランチ)を変更する機能があります。この記事では、git rebase --ontoコマンドを使って、ブランチの親を変更する方法を初心者の方にも分かりやすく解説します。
親ブランチとは?
まず、「親ブランチ」という言葉の意味を確認しましょう。
親ブランチとは、あるブランチを作成したときの元となったブランチのことです。別の言い方をすると、「派生元ブランチ」とも呼ばれます。
具体例で理解しよう
例えば、以下のような操作をしたとします:
git checkout master
git checkout -b feature-login
この場合、feature-loginブランチの親ブランチはmasterです。
家系図で例えるなら、masterが親、feature-loginが子供という関係です。
どんなときに親ブランチの変更が必要?
実際の開発現場で、親ブランチの変更が必要になる場面をいくつか見てみましょう。
ケース1:間違ったブランチから作成してしまった
# developブランチにいるつもりで…
git checkout -b feature-search
# でも実際はfeature-loginブランチにいた!
# feature-searchの親がfeature-loginになってしまった
本来、feature-searchはdevelopから作りたかったのに、feature-loginから作ってしまった場合です。
ケース2:予定より早くマージしたくなった
master
└─ feature-A(まだ開発中)
└─ feature-B(完成した!)
最初はfeature-Aの後にfeature-Bをマージする予定だったけど、feature-Bだけ先にマージしたくなった場合です。このとき、feature-Bの親をfeature-Aからmasterに変更する必要があります。
ケース3:古いブランチから作ってしまった
# git fetchを忘れて、古いmasterブランチから作成してしまった
git checkout -b feature-profile
# 最新のmasterから作り直したい
ローカルのmasterブランチが古い状態で新しいブランチを作ってしまった場合です。
親ブランチを変更する方法
親ブランチを変更するには、主に2つの方法があります。
方法1:git rebase –onto(推奨)
最もスマートで効率的な方法です。
メリット:
- 一度のコマンドで完了する
- 必要なコミットだけを移動できる
- ブランチの履歴がきれいに保たれる
デメリット:
- すでにpushしたブランチの場合、強制プッシュ(
-f)が必要 - 他の人と共有しているブランチでは使えない
方法2:新しいブランチを作成してcherry-pick
安全だけど手間がかかる方法です。
メリット:
- 既存のブランチに影響を与えない
- 途中で失敗しても元に戻しやすい
デメリット:
- コミット数が多いと面倒
- プルリクエストを作り直す必要がある
この記事では、方法1のgit rebase --ontoを中心に解説します。
git rebase –onto の基本的な使い方
コマンドの形式
git rebase --onto <新しい親ブランチ> <今の親ブランチ> <作業ブランチ>
これが基本形です。それぞれの意味を見てみましょう:
- 新しい親ブランチ: 今後の親にしたいブランチ
- 今の親ブランチ: 現在の親ブランチ(間違って作ってしまった元のブランチ)
- 作業ブランチ: 親を変更したいブランチ(省略すると現在のブランチ)
具体例
状況:feature-searchブランチをfeature-loginから作ってしまったが、本来はdevelopから作りたかった。
現在の状態:
develop
└─ A --- B --- C
feature-login
└─ D --- E
feature-search(今ここ)
└─ X --- Y --- Z
やりたいこと:
develop
└─ A --- B --- C
└─ X' --- Y' --- Z'(feature-search)
feature-login
└─ D --- E
実行するコマンド:
git rebase --onto develop feature-login feature-search
または、feature-searchブランチにいる場合は、最後の引数を省略できます:
git checkout feature-search
git rebase --onto develop feature-login
これで、feature-searchブランチのコミット(X, Y, Z)がdevelopブランチの先端に移動します。
詳しい手順(実践編)
実際に親ブランチを変更する手順を、順を追って説明します。
ステップ1:現在の状況を確認
まず、ブランチの状態を確認しましょう。
# 現在のブランチを確認
git branch
# ブランチの関係を確認
git log --oneline --graph --all --decorate
これで、どのブランチがどこから分岐しているかが視覚的に分かります。
ステップ2:新しい親ブランチを最新にする
変更先の親ブランチを最新の状態にしておきます。
# リモートから最新情報を取得
git fetch origin
# 新しい親ブランチに移動
git checkout develop
# 最新の状態に更新
git pull origin develop
ステップ3:作業ブランチに移動
親を変更したいブランチに移動します。
git checkout feature-search
ステップ4:変更をコミットまたはstash
作業中の変更がある場合は、コミットするか一時保存します。
コミットする場合:
git add .
git commit -m "作業中の変更を保存"
一時保存(stash)する場合:
git stash
stashは、変更を一時的に退避させる機能です。後でgit stash popで戻せます。
ステップ5:rebaseを実行
いよいよ親ブランチを変更します。
git rebase --onto develop feature-login
実行すると、以下のようなメッセージが表示されます:
First, rewinding head to replay your work on top of it...
Applying: コミットメッセージ1
Applying: コミットメッセージ2
Applying: コミットメッセージ3
成功すれば、これで完了です!
ステップ6:コンフリクトの解消(必要な場合)
もしコンフリクト(競合)が発生した場合は、以下の手順で解消します。
6-1. コンフリクトが発生したファイルを確認
git status
both modifiedと表示されるファイルがコンフリクトしているファイルです。
6-2. エディタでコンフリクトを解消
ファイルを開くと、以下のような表示があります:
<<<<<<< HEAD
新しい親ブランチの内容
=======
あなたの変更
>>>>>>> コミットメッセージ
どちらを残すか、または両方を統合するかを決めて、<<<<<<<、=======、>>>>>>>の行を削除します。
6-3. 修正を追加
git add 修正したファイル名
6-4. rebaseを続行
git rebase --continue
コンフリクトが複数ある場合は、このステップを繰り返します。
途中でやめたい場合:
git rebase --abort
これで、rebase前の状態に戻ります。
ステップ7:リモートにプッシュ
ローカルで親ブランチの変更が完了したら、リモートにプッシュします。
初めてプッシュする場合:
git push -u origin feature-search
すでにプッシュ済みのブランチの場合:
git push -f origin feature-search
-fは「force(強制)」の意味です。rebaseするとコミットのハッシュ値(ID)が変わるため、通常のプッシュはエラーになります。強制プッシュすることで、リモートのブランチを上書きします。
⚠️ 重要な注意点:
強制プッシュは、他の人と共有しているブランチでは絶対にやってはいけません!自分だけが使っているブランチの場合のみ使用してください。
ステップ8:stashした変更を戻す(必要な場合)
ステップ4でstashした場合は、忘れずに戻しましょう。
# stashの一覧を確認
git stash list
# 最新のstashを適用
git stash pop
親ブランチの確認方法
「このブランチの親って、どれだっけ?」と思ったときの確認方法を紹介します。
方法1:git log で確認
git log --oneline --graph --all --decorate
これで、ブランチの分岐図が表示されます。
方法2:git show-branch で確認
git show-branch
より詳しい分岐情報が表示されます。
方法3:派生元のコミットを探す
git merge-base <ブランチ1> <ブランチ2>
このコマンドで、2つのブランチの共通の祖先コミットを確認できます。
rebaseとmergeの違い
親ブランチの変更を取り込む方法として、mergeもあります。違いを理解しておきましょう。
merge(マージ)
git checkout feature-search
git merge develop
特徴:
- 新しい「マージコミット」が作られる
- ブランチの履歴がそのまま残る
- 元のコミットのハッシュ値は変わらない
イメージ図:
develop
└─ A --- B --- C
\
feature-search \
└─ X --- Y --- Z --- M(マージコミット)
rebase(リベース)
git checkout feature-search
git rebase develop
特徴:
- マージコミットは作られない
- ブランチの履歴が一直線になる
- コミットのハッシュ値が変わる
イメージ図:
develop
└─ A --- B --- C --- X' --- Y' --- Z'(feature-search)
どちらを使うべき?
mergeがおすすめの場合:
- 他の人と共有しているブランチ
- ブランチの分岐履歴を残したい場合
- 安全性を重視する場合
rebaseがおすすめの場合:
- 自分だけが使っているブランチ
- 履歴を綺麗に保ちたい場合
- プルリクエストを出す前に最新の親ブランチに追従したい場合
一般的には、「公開済みのブランチではmerge、まだpushしていないローカルブランチではrebase」という使い分けが推奨されています。
よくある質問とトラブル対処
Q1. rebase中にコンフリクトが多すぎて大変です
コンフリクトが多い場合は、rebaseを中止して別の方法を試しましょう。
# rebaseを中止
git rebase --abort
# 新しいブランチを作成して、必要なコミットだけcherry-pickする
git checkout develop
git checkout -b feature-search-new
git cherry-pick <コミットハッシュ1>
git cherry-pick <コミットハッシュ2>
cherry-pickは、特定のコミットだけを別のブランチに適用するコマンドです。
Q2. 間違って強制プッシュしてしまいました
他の人が使っているブランチに強制プッシュしてしまった場合は、すぐにチームに連絡しましょう。
対処方法:
- チームメンバーに状況を説明する
- 元の状態に戻す(
git reflogで履歴を確認) - 今後は強制プッシュ前に必ず確認する
Q3. 「detached HEAD」状態になってしまいました
rebase中に予期しない操作をすると、「detached HEAD」という状態になることがあります。
# 現在のブランチに戻る
git checkout feature-search
# または、新しいブランチを作成
git checkout -b feature-search-recovered
Q4. rebase後にコミットが消えてしまいました
慌てないでください。Gitは簡単にはデータを失いません。
# 操作履歴を確認
git reflog
# 戻したいコミットのハッシュ値を確認して、ブランチをそこに戻す
git reset --hard <コミットハッシュ>
reflogは、Gitの操作履歴を表示するコマンドです。これを使えば、ほとんどの場合で元に戻せます。
Q5. すでにプルリクエストを出しているブランチの親を変更したいです
プルリクエスト(PR)を出しているブランチの場合、親を変更すると以下の影響があります:
- PRの差分が大きく変わる
- レビュアーが混乱する可能性がある
推奨される対応:
- PRをクローズする
- 親ブランチを変更する
- 新しいPRを作成する
- レビュアーに状況を説明する
または、親ブランチを変更せず、そのままマージすることも検討しましょう。
Q6. 複数のブランチから派生している場合はどうすれば?
develop
└─ feature-A
└─ feature-B
└─ feature-C(これの親を変更したい)
この場合、feature-Cだけをdevelopに付け替えたい場合は:
git checkout feature-C
git rebase --onto develop feature-B
ただし、feature-Aやfeature-Bの変更に依存している場合は、正常に動かない可能性があります。
安全に作業するためのヒント
親ブランチの変更は、間違えると大変なことになります。以下のポイントを守りましょう。
1. 作業前にバックアップを取る
# 現在のブランチのバックアップを作成
git branch feature-search-backup
これで、失敗しても元に戻せます。
2. 自分だけが使っているブランチか確認する
# リモートで誰かがこのブランチを使っているか確認
git branch -r | grep feature-search
他の人が使っている可能性がある場合は、事前に確認しましょう。
3. 少しずつ進める
いきなり本番環境で試さず、まずはローカルで試してみましょう。
4. チームのルールを確認する
プロジェクトによっては、rebaseを禁止している場合もあります。チームのGitワークフローを確認してください。
5. コミットメッセージを分かりやすくする
rebase後のコミットは、分かりやすいメッセージを付けておくと、後で振り返りやすくなります。
まとめ
Gitで親ブランチを変更する方法について解説しました。重要なポイントをまとめます。
親ブランチとは:
- ブランチを作成した元となったブランチ
- 「派生元ブランチ」とも呼ばれる
変更が必要な場面:
- 間違ったブランチから作成してしまった
- 予定より早くマージしたくなった
- 古いブランチから作ってしまった
基本的な変更コマンド:
git rebase --onto <新しい親> <今の親> <作業ブランチ>
実行手順:
- 現在の状況を確認
- 新しい親ブランチを最新にする
- 作業ブランチに移動
- 変更をコミットまたはstash
- rebaseを実行
- コンフリクトを解消(必要な場合)
- リモートにプッシュ(
-fが必要な場合あり) - stashした変更を戻す
重要な注意点:
- 他の人と共有しているブランチでは使わない
- 強制プッシュ(
-f)は慎重に - 作業前にバックアップを取る
- コンフリクトが多い場合は別の方法も検討
親ブランチの変更は、最初は難しく感じるかもしれません。でも、一度理解してしまえば、とても便利な機能です。
まずは自分だけのテストリポジトリで練習してみて、慣れてから実際のプロジェクトで使ってみることをおすすめします。失敗を恐れず、どんどん試してみてください!


コメント