【この記事の執筆者】
山岡滉治(やまおか こうじ)
LINEヤフー株式会社でAI駆動開発推進を担当。メール、カレンダーサービスのデータAI活用推進、社内生成AIコミュニティ運営、社内研修ギルドOrchestration Development Workshopの運営など、組織全体のAI活用文化醸成に取り組む。『開発系エンジニアのためのGit/GitHub絵とき入門』著者
X) https://x.com/kozzy0919
NEW! スキル
Claude Code、Codex、GitHub Copilotといった生成AIの登場で、開発のスタイルは大きく変わりました。エンジニア以外の人が開発に関わる場面も増え、それに伴ってGitを使う人の裾野も広がっています。
AIにコマンドを聞けばすぐに答えが返ってくるようにもなり、Git操作のハードルは一見、下がったように思えます。しかし、その便利さの裏側で「AIの指示通りにコマンドを打っているけど、正直、何をやっているのか分かってない」という人も多いようです。
「特にGitでは、AIの提案どおりに打ったらコンフリクトが起きた、よく分からない状態になった、とパニックになる話をよく耳にします」
そう話すのは、書籍『開発系エンジニアのためのGit/GitHub絵とき入門』(秀和システム新社)の著者であり、LINEヤフーでAI駆動開発を推進する山岡滉治さん。
本記事では、山岡さんの解説を通じて、開発現場で遭遇しがちな五つのGitトラブルへの対処法と、その背後にある仕組みを紐解きます。
AIの回答例を示しながら、Gitが内部で何をしているのかを追い、なぜそうなるのかを理解できるところまで踏み込んで解説していただきました。ツールに使われるのではなく、ツールの挙動の裏を理解するためのガイドとしても、ぜひ本稿を役立ててください。
【この記事の執筆者】
山岡滉治(やまおか こうじ)
LINEヤフー株式会社でAI駆動開発推進を担当。メール、カレンダーサービスのデータAI活用推進、社内生成AIコミュニティ運営、社内研修ギルドOrchestration Development Workshopの運営など、組織全体のAI活用文化醸成に取り組む。『開発系エンジニアのためのGit/GitHub絵とき入門』著者
X) https://x.com/kozzy0919
各事例は、状況説明、AIへの質問例、AIの回答例、内部動作の解説、実践ステップの順で進めます。
急いでコミットしたらメッセージに誤字が入っていた。あるいは、コミットした直後にファイルの入れ忘れに気付いた。どちらもよくある話です。
# 例:こんなコミットをしてしまった
git commit -m “ログイン機能のたいぽを修正” # 誤字入り
まだリモートにpushしていなければ、安全に修正できます。
ざっくり聞く場合
詳細に聞く場合
たいていは次のコマンドが返ってきます。
git commit –amend -m “ログイン機能のタイポを修正”
忘れていたファイルも含めたい場合は、こう案内されます。
# 忘れていたファイルを追加
git add forgotten_file.js
# 直前のコミットに含める
git commit –amend –no-edit
git commit –amend は直前のコミットを修正するコマンドですが、内部的には上書きではありません。新しいコミットを作成して、現在のブランチの参照先をそちらに切り替えています。
Gitのコミットは、SHA-1ハッシュによる一意のIDで管理されています。各コミットが保持する情報は次の通りです。
・親コミットのID(どのコミットから派生したか)
・変更内容のスナップショット
・作成者情報とタイムスタンプ
・コミットメッセージ
–amend を実行すると、これらの情報をもとに新しいコミットオブジェクトが生成されます。古いコミットは履歴から外れますが、すぐに消えるわけではなく、 git reflog で参照できます。
図解:amendの動作
新しいコミットID(def456)が生成され、HEADの参照先が切り替わります。
ステップ1:コミットメッセージのみを修正する
git commit –amend -m “正しいメッセージ”
ステップ2:ファイルを追加してからamendする
git add 追加したいファイル
git commit –amend –no-edit # メッセージは変更しない
ステップ3:エディタで詳細に編集する
git commit –amend
# デフォルトエディタが開き、メッセージを編集できます
すでにpushしたコミットにamendをかけると履歴が書き換わり、チームメンバーとの同期が崩れます。push済みの場合は git revert で打ち消しコミットを作る方が安全です。
# pushした後は、revertで取り消す
git revert HEAD
コミットメッセージは、将来の自分やチームが変更意図を理解するための重要なドキュメントです。丁寧なメッセージは、バグ調査やレビューの効率を大きく左右します。
チーム開発では、複数人が同じファイルを触ることは珍しくありません。mainに最新の変更が取り込まれた後、自分のfeatureブランチをマージしようとしたら、コンフリクトが起きた——という場面です。
git merge main
# Auto-merging src/app.js
# CONFLICT (content): Merge conflict in src/app.js
# Automatic merge failed; fix conflicts and then commit the result.
ざっくり聞く場合
詳細に聞く場合
おおむね次の手順が返ってきます。
1.コンフリクトが発生したファイルを確認する
git status
2.ファイルを開いて、コンフリクトマーカーを確認する
<<<<<<< HEAD
function login(user) {
console.log("ログイン処理");
}
=======
function login(user, password) {
console.log("ログイン認証");
}
>>>>>>> main
3.手動で修正する
// 両方の変更を統合
function login(user, password) {
console.log(“ログイン認証処理”);
}
4.解決したことをGitに伝える
git add src/app.js
git commit -m “mainブランチをマージ、ログイン処理のコンフリクトを解決”
コンフリクトが発生する理由
Gitのマージは、3-way merge(3方向マージ)で行われます。比較対象は三つです。
1.共通の祖先コミット(base)
2.現在のブランチ(HEAD、ours)
3.マージ対象のブランチ(theirs)
Gitはこの三つを突き合わせて自動で統合しますが、同じ箇所に異なる変更が入っていると、どちらを採用すべきか判断できません。ここでコンフリクトが発生します。
図解:3-way mergeの仕組み
共通祖先(base):
function login(user) {
console.log(“ログイン”);
}
現在のブランチ(ours / HEAD):
function login(user) {
console.log(“ログイン処理”); // “処理”を追加
}
マージ先(theirs / main):
function login(user, password) { // 引数を追加
console.log(“ログイン認証”); // “認証”を追加
}
この場合、Gitは自動マージできず、ファイル内にコンフリクトマーカーを挿入します。
コンフリクトマーカーの読み方
・ <<<<<<< HEAD から ======= までが、自分のブランチの変更
・ ======= から >>>>>>> main までが、マージ先ブランチの変更
ステップ1:コンフリクトしたファイルを確認する
git status
# On branch feature/login
# You have unmerged paths.
# (fix conflicts and run “git commit”)
#
# Unmerged paths:
# (use “git add <file>…” to mark resolution)
# both modified: src/app.js
ステップ2:ファイルを開いて手動で修正する
VSCodeなどのエディタには、コンフリクト解決を支援するUIがあります。
・Accept Current Change ── 自分の変更を採用
・Accept Incoming Change ── 相手の変更を採用
・Accept Both Changes ── 両方を残す
・Compare Changes ── 差分を比較
これらを使えばコンフリクトマーカーを手で消す手間が省けます。
ステップ3:解決をGitに伝える
git add src/app.js
git commit # エディタが開き、マージコミットメッセージを編集できます
ステップ4:マージの完了を確認する
# マージコミットが作成され、ブランチが統合されます
git log –oneline –graph
失敗1:マージを途中でやめたい
git merge –abort
# マージを中止し、マージ前の状態に戻ります
失敗2:どちらの変更を採用すべきか判断できない
チームメンバーに相談するのが一番確実です。GitHubのプルリクエスト上でコメントをやり取りするのもよいでしょう。
失敗3:マーカーを残したままコミットしてしまった
コードレビューやCI/CDで検出されることが多いですが、気付いた時点で git commit –amend で直せばよいでしょう。
コンフリクトは複数の開発者の意図が衝突している状態です。機械的に片方を選ぶのではなく、両方の意図を理解して統合することが重要です。繰り返しになりますが、判断に迷ったときはチームメンバーに相談することを優先しましょう。
試行錯誤しながら開発していると、コミット履歴がWIP、Fix、修正、といった一時的なメッセージで埋まっていきます。
git log –oneline
a1b2c3d WIP
b2c3d4e Fix
c3d4e5f 修正
d4e5f6g とりあえずコミット
e5f6g7h ログイン機能の実装開始
このままプルリクエストを出すと、レビュワーが変更の意図を追いにくくなります。pushする前に整理しておきたいところです。
ざっくり聞く場合
詳細に聞く場合
AIは git rebase -i 、いわゆるインタラクティブリベースをすすめてくることが多いです。
# 直近5つのコミットを整理
git rebase -i HEAD~5
このコマンドを実行するとエディタが開き、次のような画面が出ます。
pick e5f6g7h ログイン機能の実装開始
pick d4e5f6g とりあえずコミット
pick c3d4e5f 修正
pick b2c3d4e Fix
pick a1b2c3d WIP
# Commands:
# p, pick = use commit
# r, reword = use commit, but edit the commit message
# e, edit = use commit, but stop for amending
# s, squash = use commit, but meld into previous commit
# f, fixup = like “squash”, but discard this commit’s log message
# d, drop = remove commit
rebaseとは何か
rebase は、ブランチの基点を付け替える操作です。一連のコミットを、別のコミットから始まったかのように履歴を書き換えます。
mergeが二つのブランチを統合してマージコミットを作るのに対し、rebaseは履歴を一直線に保ちます。後から見返したときに変更の流れを追いやすくなります。
図解:mergeとrebaseの違い
インタラクティブリベースの仕組み
git rebase -i HEAD~5 を実行すると、過去五つのコミットを対象に、履歴を自由に組み替えられます。
・pick ── コミットをそのまま残す
・squash ── 前のコミットに統合する。コミットメッセージは両方残る
・fixup ── 前のコミットに統合する。このコミットのメッセージは捨てる
・reword ── コミットメッセージだけを書き換える
・drop ── コミットを削除する
ステップ1:整理対象のコミット数を確認する
git log –oneline -10 # 直近10件のコミットを表示
ステップ2:インタラクティブリベースを開始する
git rebase -i HEAD~5 # 直近5件を整理対象とする
ステップ3:エディタで整理方法を指定する
pick e5f6g7h ログイン機能の実装開始
fixup d4e5f6g とりあえずコミット # このコミットを前のコミットに統合
fixup c3d4e5f 修正
fixup b2c3d4e Fix
fixup a1b2c3d WIP
ファイルを保存してエディタを閉じると、Gitが履歴を書き換えます。
ステップ4:結果を確認する
git log –oneline
e5f6g7h ログイン機能の実装
五つのコミットが一つにまとまりました。
失敗1:rebase中にコンフリクトが発生した
# コンフリクトを解決する
git add conflicted_file.js
# rebaseを続行する
git rebase –continue
失敗2:途中でやめたくなった
git rebase –abort
# rebaseを開始する前の状態に戻ります
失敗3:push済みのコミットをrebaseしてしまった
⚠️ すでにpushしたコミットをrebaseすると、リモートとローカルで履歴が食い違います。チームで共有しているブランチでは git push –force が必要になり、他のメンバーの作業を壊しかねません。
pushする前にrebaseで整理し、pushした後は履歴を書き換えない。これが原則です。
履歴を整えるのは自分を良く見せるためではなく、チームのレビューコストを下げるためです。きれいな履歴はバグ調査やリバートの際にも役立ちます。1コミット1責務を理想として、変更の意図を明確にすることを心がけてください。ただし、チームによってコミットメッセージのルールがある場合もありますので、その場合は従うことを優先してください。
git reset –hard や git rebase の途中で、必要なコミットを消してしまうことがあります。もう戻れないと思うかもしれませんが、git reflogで復元できる可能性があります。
# 誤って強制リセット
git reset –hard HEAD~3
# 「しまった!必要なコミットが消えてしまった!」
ざっくり聞く場合
詳細に聞く場合
git reflog を使った復元方法が返ってきます。
# HEADの移動履歴を確認
git reflog
出力例:
a1b2c3d HEAD@{0}: reset: moving to HEAD~3
d4e5f6g HEAD@{1}: commit: 重要な機能の実装
c3d4e5f HEAD@{2}: commit: バグ修正
b2c3d4e HEAD@{3}: commit: テストコード追加
削除前のコミットIDが見つかったら、そこへ戻します。
git reset –hard d4e5f6g
# または、reflogのポインタを使って指定
git reset –hard HEAD@{1}
reflogとは何か
reflog(reference log)は、HEADやブランチの先端が移動した履歴を記録する仕組みです。コミット、チェックアウト、リセット、リベースなど、HEADの位置が変わるほぼ全ての操作が記録されます。
git logは現在のブランチから辿れるコミット履歴を表示します。一方、 git reflogはローカルリポジトリでの操作履歴を表示します。git logで見えなくなったコミットでも、reflogに残っていれば復元できます。
図解:reflogの仕組み
reflogには、削除されたDやCの情報が残っています。これを使って復元します。
reflogの保持期間
デフォルトではreflogのエントリは90日間保持されます。それを過ぎると git gc(ガベージコレクション)によって削除されることがあります。
ステップ1:reflogで履歴を確認する
git reflog –all
# すべての参照(ブランチなど)のreflogを表示
ステップ2:復元したいコミットを特定する
git reflog
# d4e5f6g HEAD@{1}: commit: 重要な機能の実装
ステップ3:コミットIDを使って復元する
# 方法1:現在のブランチを直接移動させる
git reset –hard d4e5f6g
# 方法2:安全のために新しいブランチを作成する
git branch recovered d4e5f6g
git switch recovered
ステップ4:復元を確認する
git log –oneline
# 削除したコミットが復元されていることを確認
失敗1:reflogに履歴が残っていない
90日以上経過しているか、手動で git gcを実行していると復元は難しくなります。大事な変更はこまめにリモートへpushしておくに越したことはありません。
失敗2:どのコミットが正しいか分からない
# 各コミットの内容を詳しく確認
git show d4e5f6g
# 2つのコミットの差分を確認
git diff d4e5f6g c3d4e5f
Gitは失われたデータを守る仕組みを持っています。gitの構造とgit reflogを知っていれば、パニックにならずに冷静に対処できます。ただし、定期的にリモートへpushしておくことが最も確実な保険です。
Claude Codeなどの生成AIツールを使えば、複数のissueを同時に高速実装できます。ただし、ブランチを切り替えるとAIのコンテキストが失われ、毎回状況を説明し直す必要があります。また、レビュー待ちの間に別の作業を始めたいこともあるでしょう。
従来のやり方だと、それぞれ課題があります。
# 方法1:ブランチを切り替える
git switch feature/review # レビュー待ちのブランチ
git switch main # 別の作業を始める
# → レビュー中のコードが作業ディレクトリから消えてしまう
# → AIのコンテキストも失われ、切り替えるたびに状況を説明し直す必要がある
# 方法2:リポジトリを複数クローンする
cd ~/project1 # レビュー待ちブランチ用のディレクトリ
cd ~/project2 # 新しい作業用のディレクトリ
# → ディスク容量を余分に消費し、管理が煩雑になる
git worktreeを使えば、一つのリポジトリで複数の作業ツリーを同時に持てます。
ざっくり聞く場合
詳細に聞く場合
git worktreeを使う方法が案内されることが多いです。
# 新しいworktreeを新しいブランチと共に作成
git worktree add ../project-feature feature/new-feature
# 既存のブランチからworktreeを作成
git worktree add ../project-hotfix hotfix/critical-bug
# worktreeの一覧を確認
git worktree list
# worktreeを削除
git worktree remove ../project-feature
worktreeとは何か
通常、Gitリポジトリが持つ作業ツリー(working tree)は一つだけです。ブランチを切り替えると、その作業ツリーの中身がまるごと入れ替わります。
git worktreeは、同じリポジトリに対して複数の作業ツリーを持てるようにする機能です。各worktreeは異なるブランチをチェックアウトでき、それぞれ独立して作業できます。
図解:通常のブランチ切り替え vs worktree
通常のブランチ切り替え:
project/
├── .git/
└── (作業ファイル) ← `git switch`で中身が入れ替わる
worktreeの場合:
project/ # メインの作業ツリー (mainブランチ)
├── .git/ # リポジトリ本体(全worktreeで共有)
└── (作業ファイル)
project-feature/ # 追加のworktree (feature/loginブランチ)
├── .git (シンボリックリンクまたはテキストファイル)
└── (作業ファイル)
project-hotfix/ # さらなる追加のworktree (hotfix/bugブランチ)
├── .git (シンボリックリンクまたはテキストファイル)
└── (作業ファイル)
各worktreeは独自の作業ディレクトリを持ちますが、リポジトリ本体の .gitディレクトリは共有されます。ディスク容量を無駄にしません。
worktreeを作成すると、メインリポジトリの .git ディレクトリ内にworktreeごとの情報が保存されます。
各worktreeは独立したHEADとindex(ステージングエリア)を持っているので、互いに干渉せず並行して作業できます。
ステップ1:新しいブランチでworktreeを作成する
# 現在のディレクトリ:~/project
git worktree add ../project-feature feature/new-login
# 新しいブランチ `feature/new-login` が作成され、
# `../project-feature` ディレクトリにworktreeが作成されます
ステップ2:既存のブランチでworktreeを作成する
# 既存のブランチを別のディレクトリに展開
git worktree add ../project-review feature/pending-review
ステップ3:worktree間を移動して作業する
# メインの作業
cd ~/project
# ここで `main` ブランチの作業を行う
# featureブランチの作業
cd ~/project-feature
# ここで `feature/new-login` の作業を行う
# レビュー対応
cd ~/project-review
# ここで `feature/pending-review` の確認や修正を行う
ステップ4:worktreeの状態を確認する
git worktree list
# 出力例:
# /Users/you/project abc123 [main]
# /Users/you/project-feature def456 [feature/new-login]
# /Users/you/project-review ghi789 [feature/pending-review]
ステップ5:不要になったworktreeを削除する
# worktreeを削除(ディレクトリも同時に削除される)
git worktree remove ../project-feature
# または、ディレクトリを手動で削除してから
rm -rf ../project-feature
git worktree prune # 孤立したworktree情報をクリーンアップ
シーン1:Claude Codeで複数issueを同時実装する
# issue #123: ユーザー認証機能を実装中
cd ~/project # `feature/user-auth` ブランチで作業中
# Claude Codeに「ユーザー認証機能を実装して」と指示
# issue #124: 通知機能も同時に進めたい
git worktree add ../project-notification feature/notification
cd ../project-notification
# 別のClaude Codeセッションで「通知機能を実装して」と指示
# issue #125: さらにダッシュボードも並行開発
git worktree add ../project-dashboard feature/dashboard
cd ../project-dashboard
# 3つ目のClaude Codeセッションで「ダッシュボードを実装して」と指示
# 各worktreeでAIのコンテキストが維持され、コンフリクトなく並行開発
# レビュー待ちの間も、別のworktreeで作業を継続できる
シーン2:本番環境とテスト環境のコードを同時に確認する
# 本番ブランチ用のworktree
git worktree add ../project-production production
# テストブランチ用のworktree
git worktree add ../project-staging staging
# ローカルサーバーを両方起動して動作を比較
cd ~/project-production && npm start # ポート3000で起動
cd ~/project-staging && npm start # ポート3001で起動
シーン3:緊急のバグ修正に対応する
# feature開発中に本番環境で緊急バグが発見された
cd ~/project # `feature/new-ui` で作業中
# 緊急対応用のworktreeを作成
git worktree add ../project-hotfix hotfix/critical-bug
cd ../project-hotfix
# バグ修正、テスト、push
git commit -am “重大なバグを修正”
git push origin hotfix/critical-bug
# 修正が終わったら、元のfeature開発に戻る
cd ~/project
# 中断していた作業をスムーズに再開
失敗1:同じブランチを複数のworktreeでチェックアウトしようとする
git worktree add ../project2 main
# fatal: ‘main’ is already checked out at ‘/Users/you/project’
一つのブランチを同時に複数のworktreeでチェックアウトすることはできません。別のブランチを使うか、既存のworktreeを削除してください。
失敗2:worktreeを削除せずにディレクトリだけ消してしまった
rm -rf ../project-feature
# worktreeの情報が.gitディレクトリ内に残ったままになる
git worktree prune で不要な情報をクリーンアップすればよいでしょう。
git worktree prune
# 孤立したworktree情報を削除
失敗3:worktreeがどこにあるか分からなくなった
git worktree list
# すべてのworktreeの場所とブランチの一覧を表示
Claude Codeなどを使った開発では、worktreeが複数issueの同時実装を可能にします。各ブランチが独立したディレクトリで並列稼働するため、AIのコンテキストを維持したまま複数の機能を高速に実装できます。ブランチ切り替えによるコンテキスト喪失やコンフリクトを避け、レビュー待ちの時間も有効活用することで、現場での効率向上につながります。
ブランチ切り替え不要で複数のブランチを同時に開けます。 .git ディレクトリを共有するので、リポジトリを複数クローンするよりディスクを節約できます。レビュー待ちの間に別のタスクを進めたり、緊急のバグ修正に対応するときに威力を発揮します。
ただし、worktreeを増やしすぎると、どのディレクトリで何をしているか分からなくなりがちです。ちょっと別のブランチを確認する程度なら git switch で十分でしょう。
worktreeの作成や移動をもっと手軽にしたいなら、関連ツールを知っておくと便利です。
・ghq ── 複数のGitリポジトリを統一的なディレクトリ構造で管理するツール。 ghq get でクローンし、 ghq list で一覧できます。URL: https://github.com/x-motemen/ghq
・fzf ── コマンドラインで使えるファジーファインダー。ブランチ名やディレクトリをあいまい検索で素早く選べます。URL: https://github.com/junegunn/fzf
・gwq ── git worktree を扱いやすくするための補助ツール。worktreeの作成や切り替えを、より少ない手数で実行できます。URL: https://github.com/d-kuro/gwq
たとえば、ghqでリポジトリを整理し、fzfで候補を選び、gwqでworktreeを作成・移動する、といった流れにすると、複数ブランチを並行して扱う作業がかなり軽くなります。
この記事では、AI活用の開発で遭遇しがちな五つのGitトラブルを扱いました。
1.コミットメッセージの修正 ── git commit –amendで直前のコミットを修正する
2.コンフリクトの解決 ── 3-way mergeの仕組みを知れば、手動の統合に迷わなくなる
3.コミット履歴の整理 ── git rebase -iで散らかった履歴をまとめる
4.削除したコミットの復元 ── git reflogで操作履歴を辿り、消えたコミットを救い出す
5.並行開発の効率化 ── git worktreeで複数ブランチを同時に扱う
AIはコマンドを教えてくれます。しかし、なぜそのコマンドが必要なのか、内部で何が起きているのかは、十分に説明してくれるとは限りません。
Gitは履歴を扱うツールです。誤った操作はチーム全体に波及します。AIの回答をそのまま実行する前に、次の点は押さえておきましょう。
・コミットは簡単には消えません。見えなくなっているだけで、reflogで復元できることが多いです
・コンフリクトはエラーではなく、開発者に判断を委ねている状態です
・拙著『開発系エンジニアのためのGit/GitHub絵とき入門』
https://www.shuwasystem.co.jp/book/b660992.html
・公式ドキュメント
https://git-scm.com/doc
・Pro Git
https://git-scm.com/book/ja/v2
Gitで困ったときにAIに聞いてみて、その回答をこの記事と照らし合わせれば、表面的な対処で終わらず、一歩深い理解につながるはずです。
執筆/山岡滉治(@kozzy0919)
協力/LINEヤフー
タグ