コンテンツにスキップ

3-3. コンフリクト・rebase・stash・.gitignore

3-1・3-2 で基本操作とチーム開発フローを学んだ。このセクションでは、実務でほぼ毎日遭遇する「詰まりやすいポイント」を集中して扱う。


2 つの branch が同じファイルの同じ行を別々に変更した状態で merge しようとすると、Git はどちらを正解とすべきか判断できない。これがコンフリクトである。

main branch : price = 1000;
feature branch : price = 1200;
→ merge しようとすると Git が「どちらを使う?」と止まる

コンフリクトマーカーの読み方

Section titled “コンフリクトマーカーの読み方”

コンフリクトが起きたファイルには、自動的に次の記号が挿入される。

<<<<<<< HEAD
price = 1000; ← HEAD(マージ先)の内容
=======
price = 1200; ← マージしようとしている branch の内容
>>>>>>> feature/pricing
  • <<<<<<< HEAD から ======= まで:現在いる branch(merge 先)の変更
  • ======= から >>>>>>> まで:merge しようとした branch の変更
1. git status でコンフリクト中のファイルを確認する
→ "both modified" と表示される
2. コンフリクトファイルをエディタで開く
3. マーカーを削除し、正しい内容だけを残す
(どちらか一方・両方採用・書き直しなど)
4. git add <file> ← 解消済みとしてステージ
5. git commit ← マージコミットを完成させる

例:解消後のバリエーション

// 変更前(コンフリクト中)
<<<<<<< HEAD
price = 1000;
=======
price = 1200;
>>>>>>> feature/pricing
// ① feature の値を採用する場合
price = 1200;
// ② main の値を採用する場合
price = 1000;
// ③ どちらでもなく書き直す場合
price = 1100; // 両者の議論で決めた別の値
// ④ 両方を残す場合(コード内容による)
const BASE_PRICE = 1000;
const NEW_PRICE = 1200;
  • branch の生存期間を短くする(長期間放置すると差分が積もる)
  • 1 つの branch では関心の近いファイルだけ変更する
  • こまめに main の最新を取り込む(後述の rebase や git merge main

【merge】
main: A - B - C - - - M ← M がマージコミット
\ /
feature: D - E
グラフに分岐・合流が残る。履歴は正確だが複雑になりやすい。
【rebase】
main: A - B - C ← main の先端
feature: D'- E' ← D, E を C の上に"付け替え"る
グラフが一直線になる。feature の commit は書き直されるので
Hash が変わることに注意。
Terminal window
# feature branch で作業中に main を取り込みたいとき
git switch main
git pull # まず main を最新状態にする
git switch feature/my-feature
git rebase main

これで「feature/my-feature の commit が main の先端から始まったかのように」歴史が書き換えられる。

rebase 中にコンフリクトが起きた場合

Section titled “rebase 中にコンフリクトが起きた場合”
Terminal window
# コンフリクト発生 → 解消 → 続行
git rebase main
# → CONFLICT と表示されたら
# → ファイルを手動編集して解消
git add <conflicted-file>
git rebase --continue
# やり直したいとき
git rebase --abort

インタラクティブ rebase — 直前 n commit を整理する

Section titled “インタラクティブ rebase — 直前 n commit を整理する”
Terminal window
git rebase -i HEAD~3 # 直前 3 commit を操作する

エディタが開き、次のような一覧が出る。

pick a1b2c3 Add price validation
pick d4e5f6 Fix typo
pick g7h8i9 Update price validation
# コマンド例
# pick = そのまま残す
# squash = 直前 commit にまとめる(メッセージ編集あり)
# fixup = 直前 commit にまとめる(メッセージ不要)
# reword = コミットメッセージだけ変える
# drop = この commit を消す

これにより「作業中の細かい commit を PR 前に 1 つにまとめる」「誤字 commit を消す」などが可能になる。

すでに push した(共有済みの)commit を rebase してはいけない

rebase は commit の Hash を書き換えるため、同じ branch を持っている他の人の履歴と食い違いが起き、強制 push が必要になる。 ローカルのみの branch に対して使うのが基本である。


「feature branch で作業中に、別の緊急修正 branch へ切り替えなければならないが、作業が中途半端でコミットできない」というとき、git stash で変更を一時退避できる。

作業中の状態(commit できない)
↓ git stash
退避されてキレイな状態
↓ 別 branch へ切り替え・作業
↓ 元の branch に戻る
↓ git stash pop
作業中の状態が復元される
Terminal window
# 変更を退避する(メッセージあり版が後で分かりやすい)
git stash push -m "価格バリデーション実装途中"
# 一覧を見る
git stash list
# stash@{0}: On feature/pricing: 価格バリデーション実装途中
# stash@{1}: On main: 別の退避
# 最新の退避を戻す(リストから削除)
git stash pop
# 特定の退避を戻す(リストから削除せず)
git stash apply stash@{1}
# 特定の退避を削除する
git stash drop stash@{1}
# 全退避を削除する
git stash clear
  • 追跡されていない新規ファイルは git stash だけでは含まれない → -u オプションで untracked も含める:git stash push -u -m "メッセージ"
  • stash pop 時にコンフリクトが起きることがある → 通常のコンフリクト解消と同じ手順

.gitignore はリポジトリのルートに置くテキストファイルで、「Git で追跡しないファイル・ディレクトリ」を指定する。

種類パターン例理由
ビルド成果物dist/, build/, *.class毎回生成できるため
パッケージnode_modules/, .gradle/インストールで再現できる
環境変数・秘密情報.env, .env.local漏洩防止
OS・エディタの自動生成.DS_Store, Thumbs.db, .vscode/不要なノイズ
ログ*.log, logs/大量かつ不要
.gitignore
# コメント
node_modules/ # ディレクトリごと除外(末尾 / はディレクトリを示す)
*.log # 拡張子で除外
.env # ファイル名で除外
!important.log # ! で除外のキャンセル(このファイルだけ追跡する)
/dist # ルート直下の dist だけ
src/**/*.tmp # src 以下の全 .tmp ファイル

すでに追跡中のファイルを除外したいとき

Section titled “すでに追跡中のファイルを除外したいとき”
.gitignore
# .gitignore に追加しても、すでに track されているファイルは無視されない
Terminal window
# 追跡を外す(ファイル自体は残す)
git rm --cached .env
# ディレクトリごと追跡を外す
git rm -r --cached node_modules/
# その後 .gitignore に追加して commit する

グローバル .gitignore(全リポジトリ共通)

Section titled “グローバル .gitignore(全リポジトリ共通)”

.DS_Store.vscode/ など、特定 OS・エディタ由来のファイルは個人の好みによるため、リポジトリの .gitignore より自分の環境専用ファイルに書くほうが望ましい。

Terminal window
# グローバル gitignore の設定
git config --global core.excludesfile ~/.gitignore_global

~/.gitignore_global に書く内容の例:

.gitignore
.DS_Store
Thumbs.db
.vscode/
*.swp

機能主な使い場面注意点
コンフリクト解消同じ行を別々の branch が変更した後の mergeマーカーを必ず全部消す
git rebase直線的な履歴にする・PR 前の整理共有済み commit には使わない
git rebase -icommit をまとめる・メッセージを直すローカル branch のみに限定
git stash作業途中で branch を切り替えたいuntracked は -u が必要
.gitignore生成物・秘密情報を除外する追跡済みは git rm --cached が先