3-3. 演習問題
問題1:選択問題
Section titled “問題1:選択問題”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 rebase と git 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 してしまうのは典型的なミスである。シークレットが含まれる場合は早めに対処が必要になる。
問題2:穴埋め問題
Section titled “問題2:穴埋め問題”次の文章の空欄を埋めてください。
- 2 つの branch が同じ行を変更して merge したときに発生する状態を ( ) という。
- コンフリクト中のファイルで、merge 先(受け取る側)の内容を示す上の区切りマーカーは ( ) である。
- commit を書き換えて直線的な履歴にする操作を ( ) という。
- 直前 n commit を対話的に整理するコマンドは
git rebase -i HEAD~( ) の形で指定する。 - 作業途中の変更を一時退避するコマンドは ( ) である。
- Git の追跡対象から外したいが、ファイル自体はディスクに残したいとき使うコマンドは
git rm( ) である。
解答と解説
- コンフリクト(conflict)
マージ先のどちらの変更を採用するかを Git が判断できない状態。人間が編集して解消する。
<<<<<<< HEAD
HEAD は現在いる branch(merge 先)を指す。======= より下がマージしようとした branch の内容。
- rebase
git rebase <base> で feature の commit を base の先端から始まり直したように書き換える。
n(件数)
HEAD~3 で直前 3 commit を操作する。squash・fixup・reword・drop が代表的なコマンドである。
git stash(またはgit stash push)
退避した内容は git stash list で一覧、git stash pop で最新を戻す。
--cached
git rm --cached <file> でインデックス(追跡)からだけ外す。--cached なしだとファイル自体も消えるので注意。
問題3:記述問題
Section titled “問題3:記述問題”3-1. コンフリクトが発生した場合に、解消するための手順を順番に説明してください。
解答と解説
3-1. 例
git statusでコンフリクト中のファイルを確認する(“both modified” と表示される)。- そのファイルをエディタで開き、
<<<<<<<・=======・>>>>>>>のマーカーを見つける。 - 残したい内容になるようにファイルを編集し、マーカーを全部削除する。
git add <file>でステージする(解消済みとしてマーク)。- すべてのコンフリクトを解消したら
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 キーなどのシークレットが含まれている場合、履歴から完全に消す必要がある。
まず最優先でやること:
- 漏洩したシークレット(APIキー、パスワード)を直ちに無効化・再発行する
- チームリーダーや上長に報告する
その後の対処:
.envを.gitignoreに追加し、git rm --cached .envで追跡を外して commit・push する- ただし、これだけでは過去の 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 できないためバックアップにならない。
問題4:ハンズオン
Section titled “問題4:ハンズオン”Git Bash で「コンフリクト作成と解消」「stash と branch 切り替え」「.gitignore 設定」を実際に体験する。
mkdir -p ~/practice/git-advancedcd ~/practice/git-advancedgit initgit branch -m maingit config user.name "Trainee"git config user.email "trainee@example.com"4-A. コンフリクトを作って解消する
Section titled “4-A. コンフリクトを作って解消する”① ベースとなる commit を作る
echo "price = 1000" > product.txtgit add product.txtgit commit -m "Add product with price 1000"② 2 つの branch で同じ行を別々に変更する
# feature/price-up で価格を変更git switch -c feature/price-upecho "price = 1200" > product.txtgit add product.txtgit commit -m "Increase price to 1200"
# main に戻り、同じ行を別の値に変更git switch mainecho "price = 900" > product.txtgit add product.txtgit commit -m "Decrease price to 900"③ merge してコンフリクトを起こす
git merge feature/price-up次のような表示が出ればコンフリクト発生:
Auto-merging product.txtCONFLICT (content): Merge conflict in product.txtAutomatic merge failed; fix conflicts and then commit the result.④ コンフリクトを確認する
git statuscat product.txtproduct.txt の中身:
<<<<<<< HEADprice = 900=======price = 1200>>>>>>> feature/price-up⑤ コンフリクトを解消して commit する
# エディタで編集するか、echo で上書きするecho "price = 1200" > product.txt # feature の値を採用
git add product.txtgit 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 で作業を始める
git switch -c feature/discountecho "discount = 10" > discount.txtgit add discount.txtここで「急ぎの hotfix が来た」と仮定する。
② stash で退避してから別 branch へ切り替える
git stash push -m "割引機能の実装途中"git status # Working Tree がクリーンになっていることを確認
git switch mainecho "version = 2" > version.txtgit add version.txtgit commit -m "Bump version to 2"③ 元の branch に戻って stash を復元する
git switch feature/discountgit stash list # 退避した内容の一覧が見えるgit stash popgit status # discount.txt がステージ済みで戻ってきていることを確認確認できたら commit して branch を完了する:
git commit -m "Add discount feature"4-C. .gitignore を設定する
Section titled “4-C. .gitignore を設定する”① .gitignore が効く場合を確認する
# ログファイルと機密ファイルを作るecho "error log" > app.logecho "SECRET_KEY=abc123" > .env
git status # app.log と .env が "Untracked files" に出ることを確認.gitignore を作成する:
cat > .gitignore << 'EOF'*.log.envEOF
git status # app.log と .env が表示から消えていることを確認② すでに追跡中のファイルを除外する
# tracked 状態のファイルを作って commit するecho "cached file" > cached.txtgit add cached.txtgit commit -m "Add cached.txt (will be ignored later)"
# .gitignore に追加しても追跡が止まらないことを確認echo "cached.txt" >> .gitignoreecho "changed" >> cached.txtgit status # まだ "modified: cached.txt" として検出されるgit rm --cached で追跡を外す:
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.txtgit status # cached.txt が表示されなければ成功③ .gitignore を commit してログを確認する
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 バージョン(分岐・合流グラフ)
mkdir -p ~/practice/git-merge-vs-rebasecd ~/practice/git-merge-vs-rebasegit init && git branch -m maingit 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 commitgit switch -c feature/aecho "feature A" >> app.txt && git add app.txt && git commit -m "Add feature A"
# main でも 1 commit(分岐させる)git switch mainecho "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 バージョン(直線グラフ)
cd ~/practicemkdir git-rebase-demo && cd git-rebase-demogit init && git branch -m maingit 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 commitgit switch -c feature/becho "feature B" >> app.txt && git add app.txt && git commit -m "Add feature B"
# main でも 1 commit(分岐させる)git switch mainecho "main update" >> app.txt && git add app.txt && git commit -m "Update main"
# rebase で feature/b を main の先端に付け替えるgit switch feature/bgit rebase maingit --no-pager log --oneline --graph # ← 直線になっていることを確認
# fast-forward merge で main に取り込むgit switch maingit merge feature/bgit --no-pager log --oneline --graph # ← 完全な直線になる確認したいポイント
Section titled “確認したいポイント”- コンフリクトマーカー(
<<<<<<</=======/>>>>>>>)を全部削除してからgit addしないと merge が完了しないことを確認する git stash popで退避した変更が正確に戻ってくることを確認する.gitignoreに書いてもすでに追跡中のファイルは無視されず、git rm --cachedが必要であることを確認する- 4-D で merge と rebase の
git log --graphの形の違いを目で見て比較する - rebase 後は commit の Hash が変わっている(書き換えられた)ことを
git logで確認する