Gitで青い画面が出たら? 対応図解備忘録 マージコミットの書き方(新人エンジニア向け)



merge branch hogehoge を実行したときに出るメッセージ入力プロンプト(please enter a commit to ecplain why this merge is necessary ...)の意味と、「何を書けば良いか」「なぜ重要か」「実例」 を分かりやすく
初めてのマージでも迷わないよう、テンプレート・良い例・悪い例・運用ルールまでカバーします。
1. なぜマージコミットに意味のあるメッセージが必要か?
他の人(将来の自分含む)が変更の目的を短時間で理解できる。
CI/リリース履歴やバグ調査で「いつ・なぜ」その変更が入ったか追える。
特に 「上流(upstream)の更新をトピックブランチに取り込む」 場合は、競合解決や再現手順の説明が重要。
2. Git のプロンプトに書かれている意味(日本語で簡単に)
please enter a commit to explain why this merge is necessary
especially if merges an updated upstream into a topic branch.
Line starting with '#' will be ignored, and an empty message aborts the commit
「このマージが必要な理由を書く」:何を取り込んだのか・なぜ取り込んだのかを説明してね、という指示です。
「特に upstream の更新を topic ブランチにマージする場合は詳細に」:上流の変更が原因で挙動が変わりうるので、何が更新されたかを書くと親切。
# で始まる行はコメントとして無視される(テンプレートや説明行)。
空のメッセージにするとコミットは中止される(誤って空コミットするのを防ぐ)。
3. マージコミットの基本フォーマット(推奨)
短い要約(50文字以内)
何をしたか/なぜしたか(理由、背景、影響)
- 何をマージしたか(例: origin/main を hogehoge に取り込んだ)
- 目的(例: 依存ライブラリの脆弱性修正を取り込むため)
- コンフリクトの有無と、解決方法(あれば簡潔に)
- テスト結果(ローカルでの確認、CI通過など)
関連チケット/PR: #123 , PR: https://...
ポイント
1行目は「タイトル」:何をしたかが一目で分かる短文に。
2行目は空行で区切る。
本文では「why(なぜ)」「what(何を)」「how(どうやって)」「verify(確認方法)」を書く。
可能なら関連チケットやPR番号を入れる。
4. 具体例(よくあるパターン)
A. upstream(main)を topic ブランチにマージする場合
Merge branch 'origin/main' into hogehoge
取り急ぎ upstream の main を取り込み、依存パッケージの更新とセキュリティ修正を反映しました。
- 取り込んだ内容: main のコミット abc123..def456(依存: lib-xyz 1.2.3 -> 1.2.4)
- 目的: CI のテストエラー解消およびセキュリティパッチ適用
- コンフリクト: src/config.yml の設定差分を main 側に合わせる形で解決
- 確認: ローカルで unit tests 全通(`npm test`), lint OK
関連: PR #45 , JIRA-789
B. トピックブランチ同士をマージ(機能統合)
Merge branch 'feature/login' into develop
feature/login を develop に統合。ログインフローの UI/UX を統合するため。
- 主な変更: OAuth2 ログイン実装、セッション周りの修正
- コンフリクト: session-manager.js の競合を feature/login の実装優先で解決
- テスト: e2e テスト(ログイン成功/失敗)を確認済み
関連 PR: #88
C. 悪い例(NG)
Merge branch 'hogehoge'
→ 何を取り込んだか・なぜか全く分からない。将来の調査で困る。
5. コンフリクトがあった場合は必ず書く
コンフリクトを手で直したら、どこをどう直したかは必ず本文に書きます。
例:「app/config.yml の DB 接続設定は main のものを採用」「utils/parse.js はトピック側のロジックを残した」など。
理由:後から見たときに「なぜその差分になっているのか」が分かるからです。
6. 実務運用のTips(チームルール例)
マージは --no-ff(明示的なマージコミット)で残す:履歴が分かりやすくなる。
git merge --no-ff feature/foo -m "Merge branch 'feature/foo' into develop: <summary>"小さな変更であれば squash merge も可。ただし squash の場合はマージ時に PR の説明をちゃんと残す。
メッセージはテンプレ化しておく(CI の自動生成やレビューで使いやすくなる)。
重要なマージ(アップデート・セキュリティ)には必ずレビューを通す。
7. エディタでの操作メモ(補足)
Git がエディタを開いたときは、上部のテンプレート(# 始まり)を無視して本文を書きます。
保存してエディタを閉じるとコミットが実行されます。
空行にして保存・終了するとコミットは中止されます(abort)。
コマンドラインで直接メッセージを付けるなら:
git merge --no-ff hogehoge -m "Merge branch 'hogehoge' into develop hogehoge の変更を統合。foo のバグ修正を取り込み、bar の挙動を調整。コンフリクト箇所: src/a.js(hogehoge を採用)"
8. まとめチェックリスト(コミット前に確認)
1行目で何をしたかが分かるか(50字以内)
なぜこのマージが必要かを書いたか(理由)
コンフリクトがあれば解決方法を記載したか
テストや確認方法を書いたか(CI やローカルの確認)
関連チケット/PR を記載したか
良いですね 👍
以下は、新人エンジニアがマージ時にそのままエディタに貼って使える Gitマージコミットメッセージテンプレート(Qiita風・実務向け) です。
Visual Studio Code や Vim、Git の commit.template 設定にもそのまま利用できます。
🧩 Git Merge Commit Template(マージコミットテンプレート)
# ==========================================================
# 🧩 Merge Commit Template
# このマージが「何を」「なぜ」行うのかを明確に書く!
# (# から始まる行はコメントとして無視されます)
# ==========================================================
Merge branch '<ブランチ名>' into <統合先ブランチ>
<1行で要約(50文字以内): このマージの目的を簡潔に説明>
<空行>
【目的・背景】
- 例:上流ブランチ(main)の更新を取り込み、依存パッケージの脆弱性修正を反映するため。
- 例:feature/login の実装を develop に統合し、ログイン機能をテスト可能にするため。
【取り込んだ内容】
- 対象コミット: <コミットID範囲やPR番号>
- 主な変更点:
- <ポイント1>
- <ポイント2>
【コンフリクト対応】
- 発生箇所: <ファイル名1>, <ファイル名2>
- 対応方針:
- <どちらのブランチの内容を採用したか>
- <理由>
【動作確認・テスト】
- [ ] ローカルでビルド成功
- [ ] 単体テスト通過
- [ ] CI/CD パイプライン成功
- [ ] 主要機能(例: ログイン/注文)手動確認済み
【備考・関連リンク】
- 関連PR/Issue: #<番号>
- 関連チケット: <JIRA/Trello等のID>
- メモ: <補足があれば>
# ----------------------------------------------------------
# Tips:
# - 1行目は短く簡潔に(タイトルとして表示される)
# - 背景・理由を具体的に書くとレビューや将来の解析に役立つ
# - 空のメッセージで保存するとマージが中止されます
# ----------------------------------------------------------
🛠️ 設定方法(任意)
このテンプレートを .gitmessage-merge.txt などの名前でプロジェクト直下に保存し、
Git にデフォルトテンプレートとして登録できます👇
git config --global commit.template ~/.gitmessage-merge.txt
以後、マージ時に自動でこのテンプレートが開かれます。
HTML版
# Phase 8: push(GitHubリポジトリ作成後)<br>
git remote add origin https://github.com/ユーザー名/git-3min-practice.git<br>
git push -u origin main<br>
git push --all<br><br>
# 履歴確認<br>
git log --graph --oneline --all
</div>
<button class="copy-btn" onclick="copyCommand(this, 'mkdir git-3min-practice && cd git-3min-practice\ngit init\necho \"# 3分Git実践\" > README.md\ngit add README.md\ngit commit -m \"Initial commit\"\ngit checkout -b featureA\necho \"機能A - パート1\" > featureA.txt\ngit add featureA.txt\ngit commit -m \"featureA: パート1を追加\"\necho \"機能A - パート2\" >> featureA.txt\ngit add featureA.txt\ngit commit -m \"featureA: パート2を追加\"\ngit checkout main\necho \"メイン作業 - その1\" > main-work.txt\ngit add main-work.txt\ngit commit -m \"main: 作業1を追加\"\necho \"メイン作業 - その2\" >> main-work.txt\ngit add main-work.txt\ngit commit -m \"main: 作業2を追加\"\ngit checkout -b featureB\necho \"機能B - 開始\" > featureB.txt\ngit add featureB.txt\ngit commit -m \"featureB: 開始\"\necho \"機能B - 完了\" >> featureB.txt\ngit add featureB.txt\ngit commit -m \"featureB: 完了\"\ngit checkout featureA\ngit rebase main\ngit checkout featureB\ngit revert HEAD --no-edit\ngit checkout main\ngit merge featureA\ngit merge featureB\ngit log --graph --oneline --all')">コピー</button>
</div>
</div>
<div class="phase">
<div class="phase-header">
<span><strong>🔥 緊急対応:</strong> マージコミットメッセージエディタが開いた時</span>
</div>
<div class="warning">
<h3 style="color:#721c24; margin-bottom:10px;">❓ こんな画面が出ましたか?</h3>
<div class="output-box">
Merge branch 'hogehoge'<br>
<br>
# Please enter a commit message to explain why this merge is necessary,<br>
# especially if it merges an updated upstream into a topic branch.<br>
#<br>
# Lines starting with '#' will be ignored, and an empty message<br>
# aborts the commit.
</div>
</div>
<div class="note">
<h3 style="margin-bottom:10px;">💡 これは何?</h3>
<p>Git が自動的にエディタ(Vi/Vim)を開いて、マージの理由を書くように求めています。</p>
</div>
<div class="success">
<h3 style="margin-bottom:15px;">✅ 対処法:3つの方法</h3>
<div style="background:white; padding:15px; border-radius:8px; margin:15px 0;">
<h4 style="color:#155724; margin-bottom:10px;">方法1: そのまま保存して終了(デフォルトのメッセージを使用)</h4>
<div class="command-container">
<div class="command-box">
# Viエディタの場合<br>
:wq<br>
<span style="color:#ffc107;"># または</span><br>
:x
</div>
<button class="copy-btn" onclick="copyCommand(this, ':wq')">コピー</button>
</div>
<p style="margin-top:10px; color:#666;">
<strong>手順:</strong><br>
1. キーボードで <code>:</code> を押す(コロン)<br>
2. <code>wq</code> と入力<br>
3. <kbd>Enter</kbd> を押す
</p>
</div>
<div style="background:white; padding:15px; border-radius:8px; margin:15px 0;">
<h4 style="color:#155724; margin-bottom:10px;">方法2: メッセージを編集してから保存</h4>
<div class="command-container">
<div class="command-box">
# 1. 'i' を押して編集モードに入る<br>
i<br><br>
# 2. メッセージを入力(例)<br>
Merge branch 'featureB' into main<br>
<br>
featureBの新機能をmainに統合<br>
- 機能Bの実装完了<br>
- テスト済み<br><br>
# 3. ESCキーを押して編集モード終了<br>
<span style="color:#ffc107;">[ESC]</span><br><br>
# 4. 保存して終了<br>
:wq
</div>
<button class="copy-btn" onclick="copyCommand(this, 'i\n[メッセージ入力]\n[ESC]\n:wq')">コピー</button>
</div>
</div>
<div style="background:white; padding:15px; border-radius:8px; margin:15px 0;">
<h4 style="color:#155724; margin-bottom:10px;">方法3: キャンセルして中止</h4>
<div class="command-container">
<div class="command-box">
# マージを中止する<br>
:q!
</div>
<button class="copy-btn" onclick="copyCommand(this, ':q!')">コピー</button>
</div>
<p style="margin-top:10px; color:#666;">
<strong>注意:</strong> マージ自体がキャンセルされます
</p>
</div>
</div>
<div class="warning">
<h3 style="color:#721c24; margin-bottom:10px;">⚠️ Viエディタが苦手な場合</h3>
<p style="margin-bottom:10px;">今後エディタが開かないようにする方法:</p>
<div style="background:white; padding:15px; border-radius:8px; margin:10px 0;">
<strong>オプション1: mergeコマンドで直接メッセージを指定</strong>
<div class="command-container">
<div class="command-box">
git merge featureB -m "featureBをmainに統合"
</div>
<button class="copy-btn" onclick="copyCommand(this, 'git merge featureB -m \"featureBをmainに統合\"')">コピー</button>
</div>
</div>
<div style="background:white; padding:15px; border-radius:8px; margin:10px 0;">
<strong>オプション2: デフォルトエディタを変更</strong>
<div class="command-container">
<div class="command-box">
# VS Codeに変更<br>
git config --global core.editor "code --wait"<br><br>
# または、メモ帳に変更(Windows)<br>
git config --global core.editor "notepad"
</div>
<button class="copy-btn" onclick="copyCommand(this, 'git config --global core.editor \"code --wait\"')">コピー</button>
</div>
</div>
<div style="background:white; padding:15px; border-radius:8px; margin:10px 0;">
<strong>オプション3: --no-editオプション(デフォルトメッセージ使用)</strong>
<div class="command-container">
<div class="command-box">
git merge featureB --no-edit
</div>
<button class="copy-btn" onclick="copyCommand(this, 'git merge featureB --no-edit')">コピー</button>
</div>
</div>
</div>
</div>
<div class="phase">
<div class="phase-header">
<span><strong>📚 参考:</strong> Viエディタ基本コマンド</span>
</div>
<table>
<thead>
<tr>
<th>コマンド</th>
<th>説明</th>
<th>使用場面</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>i</code></td>
<td>編集モードに入る(Insert)</td>
<td>テキストを入力する前</td>
</tr>
<tr>
<td><code>ESC</code></td>
<td>編集モードを終了</td>
<td>入力が終わった後</td>
</tr>
<tr>
<td><code>:wq</code></td>
<td>保存して終了(Write & Quit)</td>
<td>編集が完了した時</td>
</tr>
<tr>
<td><code>:x</code></td>
<td>保存して終了(:wq と同じ)</td>
<td>編集が完了した時</td>
</tr>
<tr>
<td><code>:q!</code></td>
<td>保存せずに終了(強制)</td>
<td>変更を破棄したい時</td>
</tr>
<tr>
<td><code>:w</code></td>
<td>保存(終了しない)</td>
<td>途中で保存したい時</td>
</tr>
<tr>
<td><code>dd</code></td>
<td>1行削除</td>
<td>不要な行を消す時</td>
</tr>
<tr>
<td><code>u</code></td>
<td>元に戻す(Undo)</td>
<td>間違えた時</td>
</tr>
<tr>
<td><code>yy</code></td>
<td>1行コピー</td>
<td>行をコピーしたい時</td>
</tr>
<tr>
<td><code>p</code></td>
<td>貼り付け</td>
<td>コピーした内容を貼る時</td>
</tr>
</tbody>
</table>
<div class="note" style="margin-top:20px;">
<h3 style="margin-bottom:10px;">💡 覚えるべき最重要コマンド</h3>
<ol style="margin-left:20px;">
<li style="margin:8px 0;"><strong>i</strong> → 編集開始</li>
<li style="margin:8px 0;"><strong>ESC</strong> → 編集終了</li>
<li style="margin:8px 0;"><strong>:wq</strong> → 保存して終了</li>
<li style="margin:8px 0;"><strong>:q!</strong> → 保存せず終了</li>
</ol>
<p style="margin-top:10px;">この4つだけ覚えれば大丈夫!</p>
</div>
</div>
<div class="phase">
<div class="phase-header">
<span><strong>📝 まとめ:</strong> マージコミットメッセージのベストプラクティス</span>
</div>
<div class="note">
<h3 style="margin-bottom:10px;">良いマージコミットメッセージの例</h3>
<div class="command-container">
<div class="command-box" style="color:#e2e8f0;">
Merge branch 'feature/user-authentication' into main<br>
<br>
ユーザー認証機能をmainブランチに統合<br>
<br>
## 変更内容<br>
- ログイン機能の実装<br>
- パスワードリセット機能<br>
- セッション管理<br>
<br>
## テスト<br>
- 単体テスト: 全て通過<br>
- 統合テスト: 正常系・異常系確認済み<br>
<br>
## レビュー<br>
Reviewed-by: Tanaka Taro<br>
Closes #123
</div>
<button class="copy-btn" onclick="copyCommand(this, 'Merge branch \'feature/user-authentication\' into main\n\nユーザー認証機能をmainブランチに統合\n\n## 変更内容\n- ログイン機能の実装\n- パスワードリセット機能\n- セッション管理\n\n## テスト\n- 単体テスト: 全て通過\n- 統合テスト: 正常系・異常系確認済み\n\n## レビュー\nReviewed-by: Tanaka Taro\nCloses #123 ')">コピー</button>
</div>
</div>
<div class="note">
<h3 style="margin-bottom:10px;">シンプルな例(小規模な変更)</h3>
<div class="command-container">
<div class="command-box" style="color:#e2e8f0;">
Merge branch 'featureB' into main<br>
<br>
機能Bを追加
</div>
<button class="copy-btn" onclick="copyCommand(this, 'Merge branch \'featureB\' into main\n\n機能Bを追加')">コピー</button>
</div>
</div>
</div>
<div class="phase">
<div class="phase-header">
<span><strong>🎯 完全版:</strong> 実行コマンド全体フロー</span>
</div>
<div class="command-container">
<div class="command-box" style="max-height:500px; overflow-y:auto; line-height:1.8;">
<span style="color:#ffc107;"># ===== Phase 0: 準備 =====</span><br>
mkdir git-3min-practice<br>
cd git-3min-practice<br>
git init<br>
echo "# 3分Git実践" > README.md<br>
git add README.md<br>
git commit -m "Initial commit"<br>
<br>
<span style="color:#ffc107;"># ===== Phase 1: featureAブランチで2回コミット =====</span><br>
git checkout -b featureA<br>
echo "機能A - パート1" > featureA.txt<br>
git add featureA.txt<br>
git commit -m "featureA: パート1を追加"<br>
echo "機能A - パート2" >> featureA.txt<br>
git add featureA.txt<br>
git commit -m "featureA: パート2を追加"<br>
<br>
<span style="color:#ffc107;"># ===== Phase 2: mainブランチで2回コミット =====</span><br>
git checkout main<br>
echo "メイン作業 - その1" > main-work.txt<br>
git add main-work.txt<br>
git commit -m "main: 作業1を追加"<br>
echo "メイン作業 - その2" >> main-work.txt<br>
git add main-work.txt<br>
git commit -m "main: 作業2を追加"<br>
<br>
<span style="color:#ffc107;"># ===== Phase 3: featureBブランチで2回コミット =====</span><br>
git checkout -b featureB<br>
echo "機能B - 開始" > featureB.txt<br>
git add featureB.txt<br>
git commit -m "featureB: 開始"<br>
echo "機能B - 完了" >> featureB.txt<br>
git add featureB.txt<br>
git commit -m "featureB: 完了"<br>
<br>
<span style="color:#ffc107;"># ===== 履歴確認 =====</span><br>
git log --graph --oneline --all<br>
<br>
<span style="color:#ffc107;"># ===== Phase 4: rebase(featureAを一直線に) =====</span><br>
git checkout featureA<br>
git rebase main<br>
<br>
<span style="color:#ffc107;"># ===== Phase 5: revert(featureBのコミット取り消し) =====</span><br>
git checkout featureB<br>
git revert HEAD --no-edit<br>
<br>
<span style="color:#ffc107;"># ===== Phase 6: merge(featureAをmainに統合) =====</span><br>
git checkout main<br>
git merge featureA <span style="color:#68d391;"># Fast-forward マージ</span><br>
<br>
<span style="color:#ffc107;"># ===== Phase 7: merge(featureBをmainに統合) =====</span><br>
<span style="color:#68d391;"># エディタが開いた場合は :wq で保存終了</span><br>
git merge featureB -m "featureBをmainに統合" <span style="color:#68d391;"># エディタを開かない</span><br>
<span style="color:#68d391;"># または</span><br>
git merge featureB --no-edit <span style="color:#68d391;"># デフォルトメッセージ使用</span><br>
<br>
<span style="color:#ffc107;"># ===== Phase 8: push(GitHubに送信) =====</span><br>
<span style="color:#68d391;"># GitHubで新しいリポジトリを作成してから実行</span><br>
git remote add origin https://github.com/ユーザー名/git-3min-practice.git<br>
git push -u origin main<br>
git push --all <span style="color:#68d391;"># 全ブランチをpush</span><br>
<br>
<span style="color:#ffc107;"># ===== 最終確認 =====</span><br>
git log --graph --oneline --all<br>
git branch -a <span style="color:#68d391;"># 全ブランチ確認</span><br>
git status <span style="color:#68d391;"># 状態確認</span>
</div>
<button class="copy-btn" onclick="copyCommand(this, 'mkdir git-3min-practice\ncd git-3min-practice\ngit init\necho \"# 3分Git実践\" > README.md\ngit add README.md\ngit commit -m \"Initial commit\"\ngit checkout -b featureA\necho \"機能A - パート1\" > featureA.txt\ngit add featureA.txt\ngit commit -m \"featureA: パート1を追加\"\necho \"機能A - パート2\" >> featureA.txt\ngit add featureA.txt\ngit commit -m \"featureA: パート2を追加\"\ngit checkout main\necho \"メイン作業 - その1\" > main-work.txt\ngit add main-work.txt\ngit commit -m \"main: 作業1を追加\"\necho \"メイン作業 - その2\" >> main-work.txt\ngit add main-work.txt\ngit commit -m \"main: 作業2を追加\"\ngit checkout -b featureB\necho \"機能B - 開始\" > featureB.txt\ngit add featureB.txt\ngit commit -m \"featureB: 開始\"\necho \"機能B - 完了\" >> featureB.txt\ngit add featureB.txt\ngit commit -m \"featureB: 完了\"\ngit log --graph --oneline --all\ngit checkout featureA\ngit rebase main\ngit checkout featureB\ngit revert HEAD --no-edit\ngit checkout main\ngit merge featureA\ngit merge featureB -m \"featureBをmainに統合\"\ngit log --graph --oneline --all')">コピー</button>
</div>
</div>
</div>
</div>
<script>
function copyCommand(button, command) {
navigator.clipboard.writeText(command).then(() => {
const originalText = button.textContent;
button.textContent = 'コピー完了!';
button.classList.add('copied');
setTimeout(() => {
button.textContent = originalText;
button.classList.remove('copied');
}, 2000);
});
}
function downloadExcel() {
const wb = XLSX.utils.book_new();
// シート1: 3分実践フロー
const flowData = [
['Phase', '時間', 'タスク', 'コマンド'],
['0', '20秒', '準備: リポジトリ作成', 'mkdir git-3min-practice && cd git-3min-practice\ngit init\necho "# 3分Git実践" > README.md\ngit add README.md\ngit commit -m "Initial commit"'],
['1', '40秒', 'featureAで2回コミット', 'git checkout -b featureA\necho "機能A - パート1" > featureA.txt\ngit add featureA.txt\ngit commit -m "featureA: パート1を追加"\necho "機能A - パート2" >> featureA.txt\ngit add featureA.txt\ngit commit -m "featureA: パート2を追加"'],
['2', '20秒', 'mainで2回コミット', 'git checkout main\necho "メイン作業 - その1" > main-work.txt\ngit add main-work.txt\ngit commit -m "main: 作業1を追加"\necho "メイン作業 - その2" >> main-work.txt\ngit add main-work.txt\ngit commit -m "main: 作業2を追加"'],
['3', '30秒', 'featureBで2回コミット', 'git checkout -b featureB\necho "機能B - 開始" > featureB.txt\ngit add featureB.txt\ngit commit -m "featureB: 開始"\necho "機能B - 完了" >> featureB.txt\ngit add featureB.txt\ngit commit -m "featureB: 完了"'],
['4', '20秒', 'rebase: featureAを一直線に', 'git checkout featureA\ngit rebase main'],
['5', '15秒', 'revert: コミット取り消し', 'git checkout featureB\ngit revert HEAD --no-edit'],
['6', '20秒', 'merge: featureA統合', 'git checkout main\ngit merge featureA'],
['7', '15秒', 'merge: featureB統合', 'git merge featureB -m "featureBをmainに統合"'],
['8', '20秒', 'push: GitHubに送信', 'git remote add origin <URL>\ngit push -u origin main\ngit push --all']
];
const ws1 = XLSX.utils.aoa_to_sheet(flowData);
ws1['!cols'] = [{wch: 8}, {wch: 10}, {wch: 30}, {wch: 70}];
XLSX.utils.book_append_sheet(wb, ws1, '3分実践フロー');
// シート2: Viエディタ基本コマンド
const viData = [
['コマンド', '説明', '使用場面', '優先度'],
['i', '編集モードに入る(Insert)', 'テキストを入力する前', '★★★★★'],
['ESC', '編集モードを終了', '入力が終わった後', '★★★★★'],
[':wq', '保存して終了(Write & Quit)', '編集が完了した時', '★★★★★'],
[':x', '保存して終了(:wq と同じ)', '編集が完了した時', '★★★★☆'],
[':q!', '保存せずに終了(強制)', '変更を破棄したい時', '★★★★★'],
[':w', '保存(終了しない)', '途中で保存したい時', '★★★☆☆'],
['dd', '1行削除', '不要な行を消す時', '★★★☆☆'],
['u', '元に戻す(Undo)', '間違えた時', '★★★☆☆'],
['yy', '1行コピー', '行をコピーしたい時', '★★☆☆☆'],
['p', '貼り付け', 'コピーした内容を貼る時', '★★☆☆☆']
];
const ws2 = XLSX.utils.aoa_to_sheet(viData);
ws2['!cols'] = [{wch: 15}, {wch: 35}, {wch: 30}, {wch: 12}];
XLSX.utils.book_append_sheet(wb, ws2, 'Viエディタコマンド');
// シート3: マージコミットメッセージ対処法
const mergeMessageData = [
['方法', 'コマンド', '説明', 'おすすめ度'],
['方法1: デフォルトメッセージ使用', ':wq', 'そのまま保存して終了', '★★★★★'],
['方法2: メッセージ編集', 'i → [編集] → ESC → :wq', 'メッセージをカスタマイズ', '★★★★☆'],
['方法3: キャンセル', ':q!', 'マージを中止', '★★★☆☆'],
['予防1: コマンドでメッセージ指定', 'git merge branch -m "メッセージ"', 'エディタを開かない', '★★★★★'],
['予防2: --no-editオプション', 'git merge branch --no-edit', 'デフォルトメッセージで即実行', '★★★★★'],
['予防3: エディタ変更', 'git config --global core.editor "code --wait"', 'VS Codeなど使いやすいエディタに変更', '★★★★☆']
];
const ws3 = XLSX.utils.aoa_to_sheet(mergeMessageData);
ws3['!cols'] = [{wch: 25}, {wch: 45}, {wch: 35}, {wch: 12}];
XLSX.utils.book_append_sheet(wb, ws3, 'マージメッセージ対処法');
// シート4: コマンド比較(rebase/revert/merge)
const compareData = [
['操作', 'コマンド', '効果', '履歴への影響', '使用場面'],
['rebase', 'git rebase main', 'ブランチのコミットを付け替える', '履歴が書き換わる(一直線)', '自分のブランチを最新化'],
['revert', 'git revert HEAD', 'コミットを打ち消す新しいコミット作成', '履歴は残る(安全)', '公開済みコミットの取り消し'],
['merge', 'git merge feature', 'ブランチを統合', 'マージコミットが作成される', 'ブランチをmainに統合'],
['reset', 'git reset --hard HEAD^', 'コミットを削除', '履歴が消える(危険)', 'ローカルのみの取り消し']
];
const ws4 = XLSX.utils.aoa_to_sheet(compareData);
ws4['!cols'] = [{wch: 15}, {wch: 25}, {wch: 40}, {wch: 30}, {wch: 30}];
XLSX.utils.book_append_sheet(wb, ws4, 'コマンド比較');
// シート5: トラブルシューティング
const troubleData = [
['トラブル', '原因', '対処法', 'コマンド'],
['エディタが開いて困った', 'mergeでメッセージ入力を求められた', ':wq で保存終了', ':wq'],
['エ<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>3分Git実践ガイド</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/xlsx/0.18.5/xlsx.full.min.js"></script>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Segoe UI', Meiryo, sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
padding: 20px;
}
.container {
max-width: 1400px;
margin: 0 auto;
background: white;
border-radius: 20px;
box-shadow: 0 20px 60px rgba(0,0,0,0.3);
overflow: hidden;
}
.header {
background: linear-gradient(135deg, #ff6b6b 0%, #ee5a6f 100%);
color: white;
padding: 40px;
text-align: center;
}
.header h1 {
font-size: 2.5em;
margin-bottom: 10px;
text-shadow: 2px 2px 4px rgba(0,0,0,0.2);
}
.timer {
background: white;
color: #ff6b6b ;
font-size: 3em;
font-weight: bold;
padding: 20px;
border-radius: 15px;
margin: 20px 40px;
text-align: center;
box-shadow: 0 4px 15px rgba(0,0,0,0.2);
}
.content {
padding: 40px;
}
.phase {
margin-bottom: 30px;
border-left: 5px solid #667eea ;
padding-left: 20px;
}
.phase-header {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
padding: 15px 25px;
border-radius: 10px;
margin: 20px 0;
display: flex;
justify-content: space-between;
align-items: center;
}
.phase-timer {
background: rgba(255,255,255,0.3);
padding: 5px 15px;
border-radius: 20px;
font-size: 0.9em;
}
.command-container {
position: relative;
margin: 15px 0;
}
.command-box {
background: #2d3748 ;
color: #68d391 ;
padding: 15px;
border-radius: 8px;
font-family: 'Courier New', monospace;
font-size: 0.95em;
overflow-x: auto;
line-height: 1.8;
}
.copy-btn {
position: absolute;
top: 10px;
right: 10px;
background: #667eea ;
color: white;
border: none;
padding: 8px 15px;
border-radius: 5px;
cursor: pointer;
font-size: 0.9em;
transition: all 0.3s;
z-index: 10;
}
.copy-btn:hover {
background: #5568d3 ;
}
.copy-btn.copied {
background: #28a745 ;
}
.output-box {
background: #1a202c ;
color: #e2e8f0 ;
padding: 15px;
border-radius: 8px;
font-family: 'Courier New', monospace;
font-size: 0.9em;
margin-top: 10px;
overflow-x: auto;
line-height: 1.6;
}
.visual {
background: #f8f9fa ;
padding: 20px;
border-radius: 10px;
margin: 15px 0;
font-family: 'Courier New', monospace;
font-size: 0.9em;
}
.note {
background: #fff3cd ;
border-left: 4px solid #ffc107 ;
padding: 15px;
margin: 15px 0;
border-radius: 5px;
}
.success {
background: #d4edda ;
border-left: 4px solid #28a745 ;
padding: 15px;
margin: 15px 0;
border-radius: 5px;
}
.warning {
background: #ffe5e5 ;
border-left: 4px solid #f44336 ;
padding: 15px;
margin: 15px 0;
border-radius: 5px;
}
.highlight {
background: #fff9c4 ;
padding: 2px 6px;
border-radius: 3px;
font-weight: bold;
}
table {
width: 100%;
border-collapse: collapse;
margin-top: 15px;
background: white;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
border-radius: 10px;
overflow: hidden;
}
th {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
padding: 15px;
text-align: left;
font-weight: 600;
}
td {
padding: 12px 15px;
border-bottom: 1px solid #e9ecef ;
}
.download-section {
padding: 30px 40px;
background: #f8f9fa ;
border-bottom: 3px solid #e9ecef ;
text-align: center;
}
.btn-download {
background: linear-gradient(135deg, #ff6b6b 0%, #ee5a6f 100%);
color: white;
border: none;
padding: 15px 40px;
font-size: 1.1em;
border-radius: 50px;
cursor: pointer;
transition: all 0.3s;
box-shadow: 0 4px 15px rgba(255, 107, 107, 0.4);
}
.btn-download:hover {
transform: translateY(-2px);
box-shadow: 0 6px 20px rgba(255, 107, 107, 0.6);
}
.step-indicator {
background: linear-gradient(135deg, #a8edea 0%, #fed6e3 100%);
padding: 10px 20px;
border-radius: 20px;
display: inline-block;
font-weight: bold;
margin-bottom: 10px;
}
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>⚡ 3分完結!Git実践マスター</h1>
<p>2ブランチ切り替え → コミット → rebase/revert/merge/push</p>
</div>
<div class="timer">
⏱️ 3:00
</div>
<div class="download-section">
<button class="btn-download" onclick="downloadExcel()">
📥 全コマンドをExcelでダウンロード
</button>
</div>
<div class="content">
<div class="note">
<h3 style="margin-bottom:10px;">🎯 この3分で学べること</h3>
<ul style="margin-left:20px;">
<li>✅ ブランチ切り替えの実践</li>
<li>✅ 複数コミットの作成</li>
<li>✅ rebase(履歴を一直線に)</li>
<li>✅ revert(コミットの取り消し)</li>
<li>✅ merge(ブランチ統合)</li>
<li>✅ push(GitHubへ送信)</li>
</ul>
</div>
<div class="phase">
<div class="phase-header">
<span><strong>Phase 0:</strong> 準備(初回のみ)</span>
<span class="phase-timer">20秒</span>
</div>
<div class="step-indicator">Step 0-1: 練習用リポジトリ作成</div>
<div class="command-container">
<div class="command-box">
# 練習用ディレクトリ作成<br>
mkdir git-3min-practice<br>
cd git-3min-practice<br>
git init<br><br>
# 最初のコミット<br>
echo "# 3分Git実践" > README.md<br>
git add README.md<br>
git commit -m "Initial commit"
</div>
<button class="copy-btn" onclick="copyCommand(this, 'mkdir git-3min-practice\ncd git-3min-practice\ngit init\necho \"# 3分Git実践\" > README.md\ngit add README.md\ngit commit -m \"Initial commit\"')">コピー</button>
</div>
<div class="success">
<strong>✅ 準備完了!</strong> mainブランチに最初のコミットができました
</div>
</div>
<div class="phase">
<div class="phase-header">
<span><strong>Phase 1:</strong> featureAブランチで2回コミット</span>
<span class="phase-timer">40秒</span>
</div>
<div class="step-indicator">Step 1-1: featureAブランチ作成・切り替え</div>
<div class="command-container">
<div class="command-box">
git checkout -b featureA
</div>
<button class="copy-btn" onclick="copyCommand(this, 'git checkout -b featureA')">コピー</button>
</div>
<div class="step-indicator">Step 1-2: 1回目のコミット</div>
<div class="command-container">
<div class="command-box">
echo "機能A - パート1" > featureA.txt<br>
git add featureA.txt<br>
git commit -m "featureA: パート1を追加"
</div>
<button class="copy-btn" onclick="copyCommand(this, 'echo \"機能A - パート1\" > featureA.txt\ngit add featureA.txt\ngit commit -m \"featureA: パート1を追加\"')">コピー</button>
</div>
<div class="step-indicator">Step 1-3: 2回目のコミット</div>
<div class="command-container">
<div class="command-box">
echo "機能A - パート2" >> featureA.txt<br>
git add featureA.txt<br>
git commit -m "featureA: パート2を追加"
</div>
<button class="copy-btn" onclick="copyCommand(this, 'echo \"機能A - パート2\" >> featureA.txt\ngit add featureA.txt\ngit commit -m \"featureA: パート2を追加\"')">コピー</button>
</div>
<div class="visual">
<strong>現在の状態:</strong>
* 2b5e846 (HEAD -> featureA) featureA: パート2を追加
* 1a4c923 featureA: パート1を追加
* c3d7a12 (main) Initial commit
</div>
</div>
<div class="phase">
<div class="phase-header">
<span><strong>Phase 2:</strong> mainブランチで作業</span>
<span class="phase-timer">20秒</span>
</div>
<div class="step-indicator">Step 2-1: mainブランチに切り替え</div>
<div class="command-container">
<div class="command-box">
git checkout main
</div>
<button class="copy-btn" onclick="copyCommand(this, 'git checkout main')">コピー</button>
</div>
<div class="note">
<strong>💡 確認:</strong> featureA.txt が消えています!これが正常です。
</div>
<div class="step-indicator">Step 2-2: mainで2回コミット</div>
<div class="command-container">
<div class="command-box">
# 1回目<br>
echo "メイン作業 - その1" > main-work.txt<br>
git add main-work.txt<br>
git commit -m "main: 作業1を追加"<br><br>
# 2回目<br>
echo "メイン作業 - その2" >> main-work.txt<br>
git add main-work.txt<br>
git commit -m "main: 作業2を追加"
</div>
<button class="copy-btn" onclick="copyCommand(this, 'echo \"メイン作業 - その1\" > main-work.txt\ngit add main-work.txt\ngit commit -m \"main: 作業1を追加\"\necho \"メイン作業 - その2\" >> main-work.txt\ngit add main-work.txt\ngit commit -m \"main: 作業2を追加\"')">コピー</button>
</div>
<div class="visual">
<strong>現在の状態(分岐している):</strong>
* 4d8f156 (HEAD -> main) main: 作業2を追加
* 3c7e245 main: 作業1を追加
| * 2b5e846 (featureA) featureA: パート2を追加
| * 1a4c923 featureA: パート1を追加
|/
* c3d7a12 Initial commit
</div>
</div>
<div class="phase">
<div class="phase-header">
<span><strong>Phase 3:</strong> featureBブランチで作業</span>
<span class="phase-timer">30秒</span>
</div>
<div class="step-indicator">Step 3-1: featureBブランチ作成</div>
<div class="command-container">
<div class="command-box">
git checkout -b featureB
</div>
<button class="copy-btn" onclick="copyCommand(this, 'git checkout -b featureB')">コピー</button>
</div>
<div class="step-indicator">Step 3-2: featureBで2回コミット</div>
<div class="command-container">
<div class="command-box">
# 1回目<br>
echo "機能B - 開始" > featureB.txt<br>
git add featureB.txt<br>
git commit -m "featureB: 開始"<br><br>
# 2回目<br>
echo "機能B - 完了" >> featureB.txt<br>
git add featureB.txt<br>
git commit -m "featureB: 完了"
</div>
<button class="copy-btn" onclick="copyCommand(this, 'echo \"機能B - 開始\" > featureB.txt\ngit add featureB.txt\ngit commit -m \"featureB: 開始\"\necho \"機能B - 完了\" >> featureB.txt\ngit add featureB.txt\ngit commit -m \"featureB: 完了\"')">コピー</button>
</div>
<div class="step-indicator">Step 3-3: 全体の履歴を確認</div>
<div class="command-container">
<div class="command-box">
git log --graph --oneline --all
</div>
<button class="copy-btn" onclick="copyCommand(this, 'git log --graph --oneline --all')">コピー</button>
</div>
<div class="visual">
<strong>現在の状態(3つのブランチ):</strong>
* 6f9a234 (HEAD -> featureB) featureB: 完了
* 5e8b123 featureB: 開始
* 4d8f156 (main) main: 作業2を追加
* 3c7e245 main: 作業1を追加
| * 2b5e846 (featureA) featureA: パート2を追加
| * 1a4c923 featureA: パート1を追加
|/
* c3d7a12 Initial commit
</div>
</div>
<div class="phase">
<div class="phase-header">
<span><strong>Phase 4:</strong> rebase(featureAを一直線に)</span>
<span class="phase-timer">20秒</span>
</div>
<div class="step-indicator">Step 4-1: featureAに切り替えてrebase</div>
<div class="command-container">
<div class="command-box">
git checkout featureA<br>
git rebase main
</div>
<button class="copy-btn" onclick="copyCommand(this, 'git checkout featureA\ngit rebase main')">コピー</button>
</div>
<div class="output-box">
Successfully rebased and updated refs/heads/featureA.
</div>
<div class="visual">
<strong>rebase後(featureAが一直線に):</strong>
* 7g1b345 (HEAD -> featureA) featureA: パート2を追加
* 6h2c456 featureA: パート1を追加
| * 6f9a234 (featureB) featureB: 完了
| * 5e8b123 featureB: 開始
|/
* 4d8f156 (main) main: 作業2を追加
* 3c7e245 main: 作業1を追加
* c3d7a12 Initial commit
</div>
<div class="note">
<strong>💡 rebase の効果:</strong> featureAのコミットがmainの最新の上に移動。一直線の履歴になりました!
</div>
</div>
<div class="phase">
<div class="phase-header">
<span><strong>Phase 5:</strong> revert(コミットの取り消し)</span>
<span class="phase-timer">15秒</span>
</div>
<div class="step-indicator">Step 5-1: featureBの最新コミットを取り消す</div>
<div class="command-container">
<div class="command-box">
git checkout featureB<br>
git revert HEAD --no-edit
</div>
<button class="copy-btn" onclick="copyCommand(this, 'git checkout featureB\ngit revert HEAD --no-edit')">コピー</button>
</div>
<div class="output-box">
[featureB 8i3d567] Revert "featureB: 完了"
1 file changed, 1 deletion(-)
</div>
<div class="visual">
<strong>revert後(取り消しコミットが追加):</strong>
* 8i3d567 (HEAD -> featureB) Revert "featureB: 完了"
* 6f9a234 featureB: 完了
* 5e8b123 featureB: 開始
* 4d8f156 (main) main: 作業2を追加
...
</div>
<div class="warning">
<strong>⚠️ revert vs reset の違い:</strong>
<ul style="margin-left:20px; margin-top:10px;">
<li><strong>revert:</strong> 取り消しを新しいコミットとして記録(履歴が残る・安全)</li>
<li><strong>reset:</strong> コミット自体を削除(履歴が消える・危険)</li>
</ul>
</div>
</div>
<div class="phase">
<div class="phase-header">
<span><strong>Phase 6:</strong> merge(featureAをmainに統合)</span>
<span class="phase-timer">20秒</span>
</div>
<div class="step-indicator">Step 6-1: mainに切り替えてmerge</div>
<div class="command-container">
<div class="command-box">
git checkout main<br>
git merge featureA
</div>
<button class="copy-btn" onclick="copyCommand(this, 'git checkout main\ngit merge featureA')">コピー</button>
</div>
<div class="output-box">
Updating 4d8f156..7g1b345
Fast-forward
featureA.txt | 2 ++
1 file changed, 2 insertions(+)
create mode 100644 featureA.txt
</div>
<div class="note">
<strong>💡 Fast-forward とは?</strong> rebaseしたので、一直線の履歴。単純に進めるだけでマージ完了!
</div>
<div class="visual">
<strong>merge後(featureAの変更がmainに統合):</strong>
* 7g1b345 (HEAD -> main, featureA) featureA: パート2を追加
* 6h2c456 featureA: パート1を追加
| * 8i3d567 (featureB) Revert "featureB: 完了"
| * 6f9a234 featureB: 完了
| * 5e8b123 featureB: 開始
|/
* 4d8f156 main: 作業2を追加
* 3c7e245 main: 作業1を追加
* c3d7a12 Initial commit
</div>
</div>
<div class="phase">
<div class="phase-header">
<span><strong>Phase 7:</strong> featureBもmerge(通常のマージ)</span>
<span class="phase-timer">15�span>
</div>
<div class="step-indicator">Step 7-1: featureBをmerge</div>
<div class="command-container">
<div class="command-box">
git merge featureB
</div>
<button class="copy-btn" onclick="copyCommand(this, 'git merge featureB')">コピー</button>
</div>
<div class="output-box">
Merge made by the 'recursive' strategy.
featureB.txt | 2 ++
1 file changed, 2 insertions(+)
create mode 100644 featureB.txt
</div>
<div class="visual">
<strong>merge後(マージコミット作成):</strong>
* 9j4e678 (HEAD -> main) Merge branch 'featureB'
|\
| * 8i3d567 (featureB) Revert "featureB: 完了"
| * 6f9a234 featureB: 完了"
| * 5e8b123 featureB: 開始
* | 7g1b345 (featureA) featureA: パート2を追加
* | 6h2c456 featureA: パート1を追加
|/
* 4d8f156 main: 作業2を追加
* 3c7e245 main: 作業1を追加
* c3d7a12 Initial commit
</div>
<div class="note">
<strong>💡 2種類のmerge:</strong>
<ul style="margin-left:20px; margin-top:10px;">
<li><strong>Fast-forward:</strong> rebase後のmerge。一直線</li>
<li><strong>Recursive:</strong> 通常のmerge。マージコミット作成</li>
</ul>
</div>
</div>
<div class="phase">
<div class="phase-header">
<span><strong>Phase 8:</strong> push(GitHubに送信)</span>
<span class="phase-timer">20秒</span>
</div>
<div class="step-indicator">Step 8-1: GitHubリポジトリと接続(初回のみ)</div>
<div class="command-container">
<div class="command-box">
# GitHubで新しいリポジトリを作成してから<br>
git remote add origin https://github.com/あなたのユーザー名/git-3min-practice.git
</div>
<button class="copy-btn" onclick="copyCommand(this, 'git remote add origin https://github.com/ユーザー名/git-3min-practice.git')">コピー</button>
</div>
<div class="step-indicator">Step 8-2: mainブランチをpush</div>
<div class="command-container">
<div class="command-box">
git push -u origin main
</div>
<button class="copy-btn" onclick="copyCommand(this, 'git push -u origin main')">コピー</button>
</div>
<div class="step-indicator">Step 8-3: 全ブランチをpush</div>
<div class="command-container">
<div class="command-box">
git push --all
</div>
<button class="copy-btn" onclick="copyCommand(this, 'git push --all')">コピー</button>
</div>
<div class="success">
<h3 style="margin-bottom:10px;">🎉 完了!</h3>
<ul style="margin-left:20px;">
<li>✅ 2つのブランチ(featureA, featureB)作成</li>
<li>✅ 各ブランチで2回以上コミット</li>
<li>✅ rebase でfeatureAを一直線に</li>
<li>✅ revert でfeatureBのコミットを取り消し</li>
<li>✅ merge で両ブランチをmainに統合</li>
<li>✅ push でGitHubに送信</li>
</ul>
</div>
</div>
<div class="phase">
<div class="phase-header">
<span><strong>おまけ:</strong> 全コマンドを一気にコピー</span>
<span class="phase-timer">0秒</span>
</div>
<div class="command-container">
<div class="command-box" style="max-height:400px; overflow-y:auto;">
# Phase 0: 準備<br>
mkdir git-3min-practice && cd git-3min-practice<br>
git init<br>
echo "# 3分Git実践" > README.md<br>
git add README.md<br>
git commit -m "Initial commit"<br><br>
# Phase 1: featureAブランチ<br>
git checkout -b featureA<br>
echo "機能A - パート1" > featureA.txt<br>
git add featureA.txt<br>
git commit -m "featureA: パート1を追加"<br>
echo "機能A - パート2" >> featureA.txt<br>
git add featureA.txt<br>
git commit -m "featureA: パート2を追加"<br><br>
# Phase 2: mainブランチ<br>
git checkout main<br>
echo "メイン作業 - その1" > main-work.txt<br>
git add main-work.txt<br>
git commit -m "main: 作業1を追加"<br>
echo "メイン作業 - その2" >> main-work.txt<br>
git add main-work.txt<br>
git commit -m "main: 作業2を追加"<br><br>
# Phase 3: featureBブランチ<br>
git checkout -b featureB<br>
echo "機能B - 開始" > featureB.txt<br>
git add featureB.txt<br>
git commit -m "featureB: 開始"<br>
echo "機能B - 完了" >> featureB.txt<br>
git add featureB.txt<br>
git commit -m "featureB: 完了"<br><br>
# Phase 4: rebase<br>
git checkout featureA<br>
git rebase main<br><br>
# Phase 5: revert<br>
git checkout featureB<br>
git revert HEAD --no-edit<br><br>
# Phase 6-7: merge<br>
git checkout main<br>
git merge featureA<br>
git merge featureB<br><br>
# Phase 8: push(GitHubリポジトリ作成後