コンテンツにスキップ

3-3. 演習問題


1-1. コンフリクトが発生する主な原因として最も適切なのはどれか。

  • A. 2 つの branch が同じファイルの同じ行を別々に変更して merge しようとした
  • B. git add を忘れた
  • C. リモートリポジトリへのアクセス権がない
解答と解説

正解:A. 2 つの branch が同じファイルの同じ行を別々に変更して merge しようとした

  • A が正しい。Git は同じ箇所への変更がどちらを正とすべきか判断できないため、人間に判断を委ねる。
  • B は誤り。git add を忘れると commit ができないが、コンフリクトとは関係ない。
  • C は誤り。アクセス権の問題は push/pull 時のエラーであり、コンフリクトとは別の話である。

コンフリクトは悪いことではなく、「両方の変更を人間が確認して統合する機会」である。

1-2. コンフリクト中のファイルに挿入されるマーカーとして最も適切な組み合わせはどれか。

  • A. <<<<<<<=======>>>>>>>
  • B. [CONFLICT][RESOLVED]
  • C. ---+++@@
解答と解説

正解:A. <<<<<<<=======>>>>>>>

  • A が正しい。<<<<<<< HEAD から ======= が現在いる branch(マージ先)の内容、======= から >>>>>>> がマージしようとしている branch(マージ元)の内容を示す。
  • B は誤り。そのような記法は Git には存在しない。
  • C は誤り。それは git diff の出力形式(unified diff)である。

コンフリクト解消後は必ず 3 種類のマーカーを全部消してから git add すること。

1-3. git rebasegit merge の違いとして最も適切なのはどれか。

  • A. rebase は commit を付け替えて直線的な履歴にし、merge は合流 commit を作る
  • B. rebase は変更を削除し、merge は変更を保持する
  • C. rebase はリモートにのみ、merge はローカルにのみ使う
解答と解説

正解:A. rebase は commit を付け替えて直線的な履歴にし、merge は合流 commit を作る

  • A が正しい。rebase は feature の commit を base branch の先端から始まり直したように書き換える。結果、グラフが一直線になる。
  • B は誤り。rebase も merge も変更内容は保持する。commit の「歴史のつながり方」が違うだけである。
  • C は誤り。両方ともローカルでもリモートでも使える。

git log --oneline --graph で merge と rebase 後の違いを比較すると視覚的に理解しやすい。

1-4. rebase の「黄金律」として最も適切なのはどれか。

  • A. すでに push して共有済みの commit を rebase してはいけない
  • B. main branch では rebase できない
  • C. rebase するには管理者権限が必要である
解答と解説

正解:A. すでに push して共有済みの commit を rebase してはいけない

  • A が正しい。rebase は commit の Hash を書き換えるため、他の人が持っている履歴と食い違いを起こし、force push が必要になって混乱を招く。
  • B は誤り。main branch に対して rebase を実行することは可能である(ただし共有済みなので通常は行わない)。
  • C は誤り。rebase は権限とは関係ない。

ローカルのみの branch で作業中に git rebase main を使ってコンフリクトを最新状態で解消する、というのが典型的な安全な使い方である。

1-5. git stash を使う主な目的として最も適切なのはどれか。

  • A. 作業途中の変更を一時的に退避して、他の branch へ切り替えられる状態にする
  • B. 変更を永続的に別のリポジトリへ保存する
  • C. ステージした変更を commit にまとめる
解答と解説

正解:A. 作業途中の変更を一時的に退避して、他の branch へ切り替えられる状態にする

  • A が正しい。stash はスタック構造で変更を一時保存し、git stash pop で戻せる。
  • B は誤り。stash はローカルの一時保存であり、リモートには関係しない。
  • C は誤り。それは git commit の役割である。

git stash push -m "説明文" でメッセージをつけると、複数の stash があっても区別しやすい。

1-6. .gitignore に記述しても効果がないケースとして最も適切なのはどれか。

  • A. すでに git add されて追跡中のファイル
  • B. まだ一度も作成されていないファイル
  • C. サブディレクトリ内のファイル
解答と解説

正解:A. すでに git add されて追跡中のファイル

  • A が正しい。.gitignore は未追跡ファイルに対してのみ機能する。追跡済みのファイルを除外したい場合は git rm --cached <file> で追跡を外してから commit し直す必要がある。
  • B は誤り。存在しないファイルでもパターン指定しておけば、後で作成されたときに無視される。
  • C は誤り。src/**/*.log のように書けばサブディレクトリ内も無視できる。

.env ファイルを誤って commit してしまうのは典型的なミスである。シークレットが含まれる場合は早めに対処が必要になる。


次の文章の空欄を埋めてください。

  1. 2 つの branch が同じ行を変更して merge したときに発生する状態を (   ) という。
  2. コンフリクト中のファイルで、merge 先(受け取る側)の内容を示す上の区切りマーカーは (   ) である。
  3. commit を書き換えて直線的な履歴にする操作を (   ) という。
  4. 直前 n commit を対話的に整理するコマンドは git rebase -i HEAD~ (   ) の形で指定する。
  5. 作業途中の変更を一時退避するコマンドは (   ) である。
  6. Git の追跡対象から外したいが、ファイル自体はディスクに残したいとき使うコマンドは git rm (   ) である。
解答欄
解答と解説
  1. コンフリクト(conflict)

マージ先のどちらの変更を採用するかを Git が判断できない状態。人間が編集して解消する。

  1. <<<<<<< HEAD

HEAD は現在いる branch(merge 先)を指す。======= より下がマージしようとした branch の内容。

  1. rebase

git rebase <base> で feature の commit を base の先端から始まり直したように書き換える。

  1. n(件数)

HEAD~3 で直前 3 commit を操作する。squash・fixup・reword・drop が代表的なコマンドである。

  1. git stash(または git stash push

退避した内容は git stash list で一覧、git stash pop で最新を戻す。

  1. --cached

git rm --cached <file> でインデックス(追跡)からだけ外す。--cached なしだとファイル自体も消えるので注意。


3-1. コンフリクトが発生した場合に、解消するための手順を順番に説明してください。

解答欄
解答と解説

3-1. 例

  1. git status でコンフリクト中のファイルを確認する(“both modified” と表示される)。
  2. そのファイルをエディタで開き、<<<<<<<=======>>>>>>> のマーカーを見つける。
  3. 残したい内容になるようにファイルを編集し、マーカーを全部削除する。
  4. git add <file> でステージする(解消済みとしてマーク)。
  5. すべてのコンフリクトを解消したら git commit でマージコミットを完成させる。

エディタの Git 統合機能(VS Code など)を使うと、どちらを採用するか GUI で選べるため便利である。

3-2. git rebase を使うとよい場面と、使ってはいけない場面を説明してください。

解答欄
解答と解説

3-2. 例

使うとよい場面:

  • ローカルの feature branch で作業中に、main の最新変更を取り込みたいとき(git rebase main
  • PR を出す前に細かい commit をまとめたり、メッセージを整理したいとき(git rebase -i

使ってはいけない場面:

  • すでに push して他のメンバーが pull している共有 branch。rebase は commit の Hash を書き換えるため、他の人の履歴と食い違いが起き、強制 push(force push)が必要になり混乱を招く。

「ローカルのみの branch なら自由に rebase してよい。共有済みの branch には使わない」が基本ルールである。

3-3. .env ファイルをうっかり commit して push してしまった場合、どのような対処が必要か説明してください。

解答欄
解答と解説

3-3. 例

.env にパスワードや API キーなどのシークレットが含まれている場合、履歴から完全に消す必要がある。

まず最優先でやること:

  1. 漏洩したシークレット(APIキー、パスワード)を直ちに無効化・再発行する
  2. チームリーダーや上長に報告する

その後の対処:

  1. .env.gitignore に追加し、git rm --cached .env で追跡を外して commit・push する
  2. ただし、これだけでは過去の commit に .env の内容が残る。完全な履歴洗浄(git filter-repo 等)は影響範囲が大きいため、メンターや上長の指示のもとで行う

「commit から削除したから安全」ではない。「一度でもリモートに push されたシークレットは漏洩済みとして扱い、まず無効化することを最優先にする」ことが重要である。

3-4. git stash と新しい branch を作ることの違いを説明してください。どちらをどんなときに使うとよいか答えてください。

解答欄
解答と解説

3-4. 例

stash: 変更をスタックに一時退避し、Working Tree をクリーンにする。名前も commit もなく、あくまで「一時保存」。短時間の context switch に向いている。

新しい branch: 変更を commit して branch に名前をつけて保存する。名前がつくので後で見つけやすく、push して共有もできる。「この作業は後で続ける」ような場合に向いている。

迷ったときは branch を作る方が安全である。stash は git stash drop を忘れると積み上がって管理が難しくなる。また、stash は push できないためバックアップにならない。


Git Bash で「コンフリクト作成と解消」「stash と branch 切り替え」「.gitignore 設定」を実際に体験する。

Terminal window
mkdir -p ~/practice/git-advanced
cd ~/practice/git-advanced
git init
git branch -m main
git config user.name "Trainee"
git config user.email "trainee@example.com"

4-A. コンフリクトを作って解消する

Section titled “4-A. コンフリクトを作って解消する”

① ベースとなる commit を作る

Terminal window
echo "price = 1000" > product.txt
git add product.txt
git commit -m "Add product with price 1000"

② 2 つの branch で同じ行を別々に変更する

Terminal window
# feature/price-up で価格を変更
git switch -c feature/price-up
echo "price = 1200" > product.txt
git add product.txt
git commit -m "Increase price to 1200"
# main に戻り、同じ行を別の値に変更
git switch main
echo "price = 900" > product.txt
git add product.txt
git commit -m "Decrease price to 900"

③ merge してコンフリクトを起こす

Terminal window
git merge feature/price-up

次のような表示が出ればコンフリクト発生:

Auto-merging product.txt
CONFLICT (content): Merge conflict in product.txt
Automatic merge failed; fix conflicts and then commit the result.

④ コンフリクトを確認する

Terminal window
git status
cat product.txt

product.txt の中身:

<<<<<<< HEAD
price = 900
=======
price = 1200
>>>>>>> feature/price-up

⑤ コンフリクトを解消して commit する

Terminal window
# エディタで編集するか、echo で上書きする
echo "price = 1200" > product.txt # feature の値を採用
git add product.txt
git status # "All conflicts fixed but you are still merging" と出ることを確認
git commit -m "Merge feature/price-up: adopt 1200"
git --no-pager log --oneline --graph

グラフが分岐・合流している形になれば成功。


4-B. stash を使って作業を中断・再開する

Section titled “4-B. stash を使って作業を中断・再開する”

① 新しい feature branch で作業を始める

Terminal window
git switch -c feature/discount
echo "discount = 10" > discount.txt
git add discount.txt

ここで「急ぎの hotfix が来た」と仮定する。

② stash で退避してから別 branch へ切り替える

Terminal window
git stash push -m "割引機能の実装途中"
git status # Working Tree がクリーンになっていることを確認
git switch main
echo "version = 2" > version.txt
git add version.txt
git commit -m "Bump version to 2"

③ 元の branch に戻って stash を復元する

Terminal window
git switch feature/discount
git stash list # 退避した内容の一覧が見える
git stash pop
git status # discount.txt がステージ済みで戻ってきていることを確認

確認できたら commit して branch を完了する:

Terminal window
git commit -m "Add discount feature"

① .gitignore が効く場合を確認する

Terminal window
# ログファイルと機密ファイルを作る
echo "error log" > app.log
echo "SECRET_KEY=abc123" > .env
git status # app.log と .env が "Untracked files" に出ることを確認

.gitignore を作成する:

Terminal window
cat > .gitignore << 'EOF'
*.log
.env
EOF
git status # app.log と .env が表示から消えていることを確認

② すでに追跡中のファイルを除外する

Terminal window
# tracked 状態のファイルを作って commit する
echo "cached file" > cached.txt
git add cached.txt
git commit -m "Add cached.txt (will be ignored later)"
# .gitignore に追加しても追跡が止まらないことを確認
echo "cached.txt" >> .gitignore
echo "changed" >> cached.txt
git status # まだ "modified: cached.txt" として検出される

git rm --cached で追跡を外す:

Terminal window
git rm --cached cached.txt
# → "deleted: cached.txt" がステージに乗る
# → cached.txt は .gitignore にあるため untracked には表示されない
git status
git commit -m "Stop tracking cached.txt"
# .gitignore の変更(cached.txt 行の追加)と削除を 1 commit にまとめる
echo "changed again" >> cached.txt
git status # cached.txt が表示されなければ成功

③ .gitignore を commit してログを確認する

Terminal window
git --no-pager log --oneline

.gitignoreリポジトリに commit してチームで共有するのが基本である。 ただし .vscode/.DS_Store のような個人環境依存のものはグローバル gitignore に書く方がよい。


4-D. merge と rebase の履歴を比較する

Section titled “4-D. merge と rebase の履歴を比較する”

同じ変更を「merge」と「rebase」でそれぞれ行い、git log --graph でグラフの形を見比べる。

① merge バージョン(分岐・合流グラフ)

Terminal window
mkdir -p ~/practice/git-merge-vs-rebase
cd ~/practice/git-merge-vs-rebase
git init && git branch -m main
git config user.name "Trainee"
git config user.email "trainee@example.com"
echo "v1" > app.txt && git add app.txt && git commit -m "Initial commit"
# feature branch で 1 commit
git switch -c feature/a
echo "feature A" >> app.txt && git add app.txt && git commit -m "Add feature A"
# main でも 1 commit(分岐させる)
git switch main
echo "main update" >> app.txt && git add app.txt && git commit -m "Update main"
# merge する
git merge feature/a -m "Merge feature/a"
git --no-pager log --oneline --graph

グラフが \/ で分岐・合流している形になることを確認する。

② rebase バージョン(直線グラフ)

Terminal window
cd ~/practice
mkdir git-rebase-demo && cd git-rebase-demo
git init && git branch -m main
git config user.name "Trainee"
git config user.email "trainee@example.com"
echo "v1" > app.txt && git add app.txt && git commit -m "Initial commit"
# feature branch で 1 commit
git switch -c feature/b
echo "feature B" >> app.txt && git add app.txt && git commit -m "Add feature B"
# main でも 1 commit(分岐させる)
git switch main
echo "main update" >> app.txt && git add app.txt && git commit -m "Update main"
# rebase で feature/b を main の先端に付け替える
git switch feature/b
git rebase main
git --no-pager log --oneline --graph # ← 直線になっていることを確認
# fast-forward merge で main に取り込む
git switch main
git merge feature/b
git --no-pager log --oneline --graph # ← 完全な直線になる

  • コンフリクトマーカー(<<<<<<</=======/>>>>>>>)を全部削除してから git add しないと merge が完了しないことを確認する
  • git stash pop で退避した変更が正確に戻ってくることを確認する
  • .gitignore に書いてもすでに追跡中のファイルは無視されず、git rm --cached が必要であることを確認する
  • 4-D で merge と rebase の git log --graph の形の違いを目で見て比較する
  • rebase 後は commit の Hash が変わっている(書き換えられた)ことを git log で確認する