Git rebase conflictの解決方法|初心者向け完全ガイド

Git

Git rebase conflictの解決方法|初心者向け完全ガイド

Gitを使ったバージョン管理において、rebase conflict(リベースコンフリクト)は多くの開発者が遭遇するエラーの一つです。特にチーム開発では、複数の開発者が同じファイルを編集することが頻繁にあり、rebase時にコンフリクトが発生することは珍しくありません。

この記事では、Git rebase conflictの原因から解決手順、実践的なコード例、そしてよくある間違いまで、初心者向けに詳しく説明します。

Git rebase conflictとは?原因の詳しい説明

まず、rebase conflictが何なのか、そしてなぜ発生するのかを理解することが重要です。

rebaseの基本概念

Gitのrebaseコマンドは、現在のブランチのコミット履歴を別のブランチの上に再適用するコマンドです。通常のmergeとの大きな違いは、コミット履歴を直線的に保つことができる点です。

例えば:

  • merge:異なるブランチの変更を統合する際に、マージコミットが新たに作成される
  • rebase:現在のブランチのコミットを別のブランチの最新の状態の上に「移動」させる

conflictが発生する仕組み

rebase conflictは、以下のような状況で発生します:

  1. あなたがブランチAで、同じファイルの同じ行を編集した
  2. その間にメインブランチでも、同じファイルの同じ行が編集されていた
  3. rebaseを実行しようとした時に、Gitはどちらの変更を優先するべきか判断できず、conflictが発生する

つまり、「同じ場所に対する異なる変更が衝突している状態」がrebase conflictなのです。

Git rebase conflictの解決手順

ステップ1:conflictの確認

まず、rebaseを開始する前に、現在のブランチの状態を確認しましょう。

$ git status
On branch feature/login
Your branch is ahead of 'origin/main' by 3 commits.

rebaseを実行します。

$ git rebase main

conflictが発生すると、以下のようなメッセージが表示されます。

CONFLICT (content): Merge conflict in src/auth.js
Auto-merging src/auth.js
CONFLICT (content): Merge conflict in src/config.js
Auto-merging src/config.js
error: could not apply a1b2c3d... Add login feature
Resolve all conflicts either by editing the files and using
  'git add <conflicted-files>',
or by using 'git rebase --abort'.

ステップ2:conflictの内容を確認

conflictが発生したファイルを開いて、実際の衝突内容を確認します。

$ git status

このコマンドで、conflictが発生しているファイルが一覧表示されます。

both modified:   src/auth.js
both modified:   src/config.js

ファイルの内容を確認すると、以下のような表示になっています。

// src/auth.js
function validatePassword(password) {
<<<<<<< HEAD
  // main ブランチの内容
  if (password.length < 8) {
    return false;
  }
=======
  // feature/login ブランチの内容
  if (password.length < 12) {
    return false;
  }
>>>>>>> a1b2c3d (Add login feature)
  return true;
}

ステップ3:conflictを手動で解決

conflictの内容を確認したら、どちらの変更を採用するか、または両方を結合するかを決めて、ファイルを編集します。

パターン1:一方の変更を完全に採用する

// src/auth.js
function validatePassword(password) {
  // feature/login ブランチの内容を採用
  if (password.length < 12) {
    return false;
  }
  return true;
}

パターン2:両方の変更を結合する

// src/auth.js
function validatePassword(password) {
  // より厳格な条件を採用
  const minLength = Math.max(8, 12); // 12文字が必須
  if (password.length < minLength) {
    return false;
  }
  return true;
}

ステップ4:変更をステージングする

conflictを解決したら、ファイルをgit addでステージングします。

$ git add src/auth.js
$ git add src/config.js

もう一度ステータスを確認します。

$ git status
On branch feature/login
All conflicts fixed but you are still rebasing.

ステップ5:rebaseを続行する

すべてのconflictを解決したら、rebaseを続行します。

$ git rebase --continue

複数のコミットがある場合、さらにconflictが発生することもあります。その場合は、ステップ2〜5を繰り返します。

rebaseが成功すると、以下のメッセージが表示されます。

Successfully rebased and updated refs/heads/feature/login.

実践的なコード例

例1:シンプルなテキストファイルのconflict解決

以下は、設定ファイルでのconflict例です。

# プロジェクト内で、config.json に conflict が発生した場合

$ git rebase main
CONFLICT (content): Merge conflict in config.json
Auto-merging config.json
error: could not apply a1b2c3d... Update configuration
// config.json(conflict 前)
{
  \"apiUrl\": \"https://api.example.com\",
<<<<<<< HEAD
  \"timeout\": 5000,
  \"retryCount\": 3
=======
  \"timeout\": 10000,
  \"retryCount\": 5,
  \"debug\": true
>>>>>>> a1b2c3d (Update configuration)
}

解決後:

// config.json(conflict 解決後)
{
  \"apiUrl\": \"https://api.example.com\",
  \"timeout\": 10000,
  \"retryCount\": 5,
  \"debug\": true
}

例2:複数行のコード変更でのconflict解決

// src/api.js(conflict 前)
async function fetchUserData(userId) {
<<<<<<< HEAD
  const response = await fetch(`/api/users/${userId}`);
  const data = await response.json();
  return data;
=======
  try {
    const response = await fetch(`/api/users/${userId}`, {
      headers: { 'Authorization': 'Bearer token' }
    });
    if (!response.ok) throw new Error('Failed to fetch');
    const data = await response.json();
    return data;
  } catch (error) {
    console.error(error);
    return null;
  }
>>>>>>> a1b2c3d (Add error handling)
}

解決後(両方の変更を結合):

// src/api.js(conflict 解決後)
async function fetchUserData(userId) {
  try {
    const response = await fetch(`/api/users/${userId}`, {
      headers: { 'Authorization': 'Bearer token' }
    });
    if (!response.ok) throw new Error('Failed to fetch');
    const data = await response.json();
    return data;
  } catch (error) {
    console.error(error);
    return null;
  }
}

よくある間違いと対処法

間違い1:conflictを不完全に解決している

問題: conflictマーカー(<<<<<<<=======など)を削除し忘れて、そのままコミットしてしまう。

解決法: conflictを解決した後、必ずファイルを確認して、すべてのconflictマーカーが削除されていることを確認します。

$ grep -r \"<<<<<<<\" src/
# conflictマーカーが残っていないか確認

間違い2:誤ったブランチでrebaseを実行している

問題: 意図したブランチではなく、異なるブランチでrebaseを実行してしまう。

対処法: rebaseを実行する前に、必ず現在のブランチを確認します。

$ git branch
* feature/login  # アスタリスク付きが現在のブランチ
  main
  develop

間違い3:rebaseに失敗した時にパニックになる

問題: rebase中にエラーが発生しても、冷静に対応できずにランダムなコマンドを実行してしまう。

対処法: rebaseに失敗した時は、git rebase --abortで中止できます。

$ git rebase --abort
# rebase を中止して、元の状態に戻す

間repeated mistake 4:conflictを解決した後、git addを忘れる

問題: ファイルを編集しても、git addでステージングしないままgit rebase --continueを実行してしまう。

対処法: rebaseを続行する前に、必ずgit statusで確認します。

$ git status
On branch feature/login
You are currently rebasing.
  (fix conflicts and then run \"git rebase --continue\")
  (use \"git rebase --abort\" to cancel the rebase)

Changes not staged for commit:
  modified:   src/auth.js

$ git add src/auth.js
$ git rebase --continue

間違い5:公開済みのコミットに対してrebaseを実行する

問題: すでにpushされた公開済みのコミットに対してrebaseを実行すると、コミット履歴が大きく変わり、チーム全体に混乱をもたらします。

対処法: ローカルブランチでのみrebaseを実行し、pushする前に完了させます。もし誤ってpushしてしまった場合は、チーム全体に報告して慎重に対応します。

$ git rebase main
$ git status
On branch feature/login
Your branch and 'origin/feature/login' have diverged

# この場合、強制pushは避けて、チーム内で相談する
# または、merge を使用する方が安全

その他の便利なコマンド

対話的rebase(interactive rebase)を使用する

複数のコミットを整理しながらrebaseする方法:

$ git rebase -i main

このコマンドにより、各コミットに対して以下のアクションが選択できます:

pick a1b2c3d Commit message 1
pick b2c3d4e Commit message 2
pick c3d4e5f Commit message 3
  • pick:そのコミットを使用する
  • reword:コミットメッセージを編集する
  • squash:前のコミットに統合する
  • drop:そのコミットを削除する

conflictの3-way mergeを確認する

より詳しくconflictの原因を知りたい場合:

$ git log --oneline --graph --all
# コミット履歴を視覚的に確認

まとめ

Git rebase conflictは、初心者にとって不安になるエラーですが、正しい手順を理解すれば、決して難しいものではありません。重要なポイントをまとめます:

  1. 原因を理解する:conflictは、同じファイルの同じ箇所に異なる変更が加えられた時に発生します。
  2. 冷静に対応する:conflictが発生しても、焦らずgit statusで現状を確認し、ファイルを1つずつ編集します。
  3. 手動で解決する:conflictマーカーを確認して、どちらの変更を採用するか、または両方を結合するかを決めます。
  4. ステージングと続行:すべてのファイルをgit addでステージングした後、git rebase --continueで再開します。
  5. よくある間違いを避ける:conflictマーカーの削除忘れ、git addの忘れ、公開済みコミットへのrebaseなど、典型的なミスに注意します。

これらの知識を身に付ければ、Git rebase conflictはもう怖くありません。実際のプロジェクトで何度も経験することで、さらに対応が素早くなるでしょう。チーム開発においても、conflictの対応は必須スキルなので、ぜひ今回紹介した手順を参考にしてください。

タイトルとURLをコピーしました