<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Troubleshooting on kenji.blog</title><link>http://kenji.blog/tags/troubleshooting/</link><description>Recent content in Troubleshooting on kenji.blog</description><generator>Hugo -- gohugo.io</generator><language>ja</language><copyright>kenjinote</copyright><lastBuildDate>Sat, 12 Sep 2026 17:00:00 +0900</lastBuildDate><atom:link href="http://kenji.blog/tags/troubleshooting/index.xml" rel="self" type="application/rss+xml"/><item><title>Git初心者が陥りやすいミスと解決コマンド集（コンフリクト解消など）</title><link>http://kenji.blog/p/git-beginners-mistakes-and-solutions/</link><pubDate>Sat, 12 Sep 2026 17:00:00 +0900</pubDate><guid>http://kenji.blog/p/git-beginners-mistakes-and-solutions/</guid><description>&lt;img src="http://kenji.blog/p/git-beginners-mistakes-and-solutions/img/eyecatch.jpg" alt="Featured image of post Git初心者が陥りやすいミスと解決コマンド集（コンフリクト解消など）" />&lt;h1 id="git初心者が陥りやすいミスと解決コマンド集コンフリクト解消など">Git初心者が陥りやすいミスと解決コマンド集（コンフリクト解消など）
&lt;/h1>&lt;h2 id="1-はじめになぜgitでミスをしてしまうのか">1. はじめに：なぜGitでミスをしてしまうのか？
&lt;/h2>&lt;p>ソフトウェア開発において、Gitは空気や水のように不可欠な存在となっています。しかし、多くの初心者（そして時として熟練者でさえ）にとって、Gitは「恐ろしい魔法のブラックボックス」のように感じられることがあります。コミットが消えたり、意図しないブランチに大量の変更を押し込んでしまったり、見たこともないコンフリクトのエラーメッセージが画面を埋め尽くしたり……。こうした「Gitの罠」にハマると、作業の進行が完全にストップしてしまい、最悪の場合はソースコードを破壊してしまうのではないかという恐怖に駆られます。&lt;/p>
&lt;p>なぜGitはこれほどまでに難しく、ミスを誘発しやすいのでしょうか？その最大の理由は、「Gitの内部で何が起こっているのかを理解せずに、表面的なコマンドだけを暗記して使っているから」です。Gitは、分散型バージョン管理システム（DVCS）としての堅牢な設計思想に基づいていますが、そのインターフェース（CLI）は必ずしも直感的ではありません。&lt;/p>
&lt;p>この記事では、Git初心者が現場で頻繁に遭遇する「やらかし（よくあるミス）」を多数のケースに分類し、それぞれの具体的な解決策となるコマンドを提示します。しかし、単なるコマンドの羅列（チートシート）にはしません。「なぜそのミスが起きるのか」「そのコマンドを実行するとGitの内部でデータがどのように動くのか」を、&lt;code>.git&lt;/code> ディレクトリの構造や、背後で動いているDiffアルゴリズムの数学的背景、Mermaidによる図解を交えて、10,000文字を超えるボリュームで徹底的に深掘りします。&lt;/p>
&lt;p>この記事を最後まで読み終えたとき、あなたは「Gitが怖い」という感情から解放され、むしろ「Gitほど信頼できる相棒はいない」と確信できるようになるはずです。それでは、Gitの深淵なる世界へ飛び込んでいきましょう。&lt;/p>
&lt;hr>
&lt;h2 id="2-gitの深淵git-ディレクトリの内部構造を理解する">2. Gitの深淵：&lt;code>.git&lt;/code> ディレクトリの内部構造を理解する
&lt;/h2>&lt;p>多くのトラブルシューティングを容易にするための第一歩は、Gitがデータをどのように保存しているかを知ることです。あなたのプロジェクトのルートディレクトリに存在する隠しフォルダ &lt;code>.git&lt;/code>、これこそがGitの心臓部です。Gitは、単なるファイルの差分（パッチ）を順に記録していくシステムではなく、&lt;strong>スナップショットのストリーム&lt;/strong>としてデータを管理しています。&lt;/p>
&lt;h3 id="21-オブジェクトモデルblob-tree-commit">2.1 オブジェクトモデル：Blob, Tree, Commit
&lt;/h3>&lt;p>Gitは主に3つのオブジェクトを使用してリポジトリの状態を表現します。これらのオブジェクトは &lt;code>.git/objects&lt;/code> に保存されます。&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Blob (Binary Large Object)&lt;/strong>
ファイルの内容そのものを保存するオブジェクトです。ファイル名や権限の情報はここには含まれません。純粋なバイト列がzlibで圧縮され、SHA-1ハッシュ値（40文字の16進数）によって識別されます。&lt;/li>
&lt;li>&lt;strong>Tree&lt;/strong>
ディレクトリの構造を表すオブジェクトです。Treeオブジェクトは、他のTreeオブジェクト（サブディレクトリ）やBlobオブジェクト（ファイル）へのポインタ（SHA-1ハッシュ値）、およびそれらのファイル名、アクセス権限を含みます。UNIXのディレクトリのような役割を果たします。&lt;/li>
&lt;li>&lt;strong>Commit&lt;/strong>
ある時点でのリポジトリ全体のトップレベルのTreeオブジェクトへのポインタと、メタデータ（作成者、コミット日時、コミットメッセージ）、そして直前のコミット（親コミット）へのポインタを保持します。&lt;/li>
&lt;/ol>
&lt;pre class="mermaid">
graph TD
Commit1[&amp;#34;Commit (Hash: 9f8a)&amp;#34;] --&amp;gt; Tree1[&amp;#34;Tree (Hash: 4b82)&amp;#34;]
Tree1 --&amp;gt; Blob1[&amp;#34;Blob (Hash: 8d7e) : index.js&amp;#34;]
Tree1 --&amp;gt; Tree2[&amp;#34;Tree (Hash: 3a2c) : src/&amp;#34;]
Tree2 --&amp;gt; Blob2[&amp;#34;Blob (Hash: 5f1b) : app.js&amp;#34;]
&lt;/pre>
&lt;h3 id="22-headと参照refsの正体">2.2 HEADと参照（Refs）の正体
&lt;/h3>&lt;p>Gitで作業していると頻繁に目にする &lt;code>HEAD&lt;/code> という単語。これは現在チェックアウトしているブランチ（またはコミット）を指し示す**シンボリック参照（Symbolic Reference）**です。
&lt;code>.git/HEAD&lt;/code> というファイルをテキストエディタで開いてみると、次のような文字列が書かれています。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">ref: refs/heads/main
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>これは、「現在の状態は &lt;code>main&lt;/code> ブランチの先端にある」ということを意味します。そして、&lt;code>.git/refs/heads/main&lt;/code> を開くと、そこに40文字のSHA-1ハッシュが書かれており、これが最新のCommitオブジェクトを指し示しているのです。
Gitのブランチとは、単に特定のコミットを指し示す軽量なポインタ（ファイル）に過ぎません。この事実を知っているだけで、「ブランチを削除したらファイルが全部消えるのでは？」という恐怖がなくなります。&lt;/p>
&lt;hr>
&lt;h2 id="3-数学で読み解くgitdiffアルゴリズムとハッシュ関数">3. 数学で読み解くGit：Diffアルゴリズムとハッシュ関数
&lt;/h2>&lt;p>Gitがコンフリクトを検知したり、ファイルの差分を表示したりする際、内部では高度なアルゴリズムが動作しています。&lt;/p>
&lt;h3 id="31-myersのdiffアルゴリズム">3.1 MyersのDiffアルゴリズム
&lt;/h3>&lt;p>Gitのデフォルトの差分検出アルゴリズムは、Eugene W. Myersによって考案されたアルゴリズムです。2つのテキストファイル $A$ と $B$ があるとき、$A$ を $B$ に変換するための「最小の編集手順（挿入と削除）」を見つける問題は、グラフ理論における最短経路問題としてモデル化できます。&lt;/p>
&lt;p>文字列の長さをそれぞれ $N, M$ とし、合計を $V = N + M$ とします。Myersのアルゴリズムでは、編集距離（Edit Distance） $D$ を探索します。このアルゴリズムの時間計算量は以下の式で表されます。&lt;/p>
$$ \mathcal{O}(V \cdot D) $$&lt;p>ここで、ファイル間の差分が小さい（つまり $D$ が小さい）場合、アルゴリズムは非常に高速 $\mathcal{O}(V)$ で動作します。しかし、ファイルが全く異なる場合、$D \approx V$ となり、最悪計算量は $\mathcal{O}(V^2)$ となります。&lt;/p>
&lt;h3 id="32-patience-diff-と-histogram-diff">3.2 Patience Diff と Histogram Diff
&lt;/h3>&lt;p>Myersのアルゴリズムは優秀ですが、関数やクラスの順序を大きく入れ替えた場合などに、人間にとって直感的ではない（意味の通らない）差分を生成することがあります。これを解決するためにGitは &lt;code>Patience Diff&lt;/code> と &lt;code>Histogram Diff&lt;/code> を実装しています。&lt;/p>
&lt;p>Patience Diffは、「両方のファイルに1度だけ登場するユニークな行」に着目し、それらの最長共通部分列（Longest Common Subsequence: LCS）を見つけます。ユニークな要素の数を $U$ とすると、LCSの計算は以下の計算量で解くことができます。&lt;/p>
$$ \mathcal{O}(U \log U) $$&lt;p>コンフリクト解消が難しいと感じた時は、&lt;code>git diff --histogram&lt;/code> や、マージ戦略にこのアルゴリズムを指定する（&lt;code>git merge -s recursive -X histogram&lt;/code>）のが一つの手です。&lt;/p>
&lt;h3 id="33-sha-1と衝突確率">3.3 SHA-1と衝突確率
&lt;/h3>&lt;p>Gitは全てのオブジェクトをSHA-1ハッシュ値で管理します。ハッシュ空間の大きさは $2^{160}$ です。ハッシュの衝突（異なるコンテンツが同じハッシュ値を持つこと）が起きる確率について、誕生日のパラドックス（Birthday Paradox）を用いて近似すると、衝突確率 $p$ が50%になるために必要なオブジェクト数 $k$ は以下のようになります。&lt;/p>
$$ k \approx \sqrt{2 \ln(2)} \cdot 2^{80} \approx 1.2 \times 2^{80} $$&lt;p>これは天文学的な数字であり、通常のソフトウェア開発において意図せず衝突が発生する確率は実質的にゼロです。そのため、Gitはハッシュ値を「絶対的な一意のID」として信頼して動作しています。&lt;/p>
&lt;hr>
&lt;h2 id="4-ケーススタディ1間違ったブランチにコミットしてしまった">4. ケーススタディ1：間違ったブランチにコミットしてしまった！
&lt;/h2>&lt;p>&lt;strong>【状況】&lt;/strong>
&lt;code>main&lt;/code> ブランチで作業していることに気づかず、新機能のコードをバリバリ書いてしまい、あろうことか &lt;code>git commit&lt;/code> までしてしまった。本来は &lt;code>feature/login&lt;/code> というブランチを作ってそこで作業するはずだったのに！&lt;/p>
&lt;h3 id="解決方法git-reset-とブランチの作成">解決方法：&lt;code>git reset&lt;/code> とブランチの作成
&lt;/h3>&lt;p>Gitでは、コミットは独立したオブジェクトであり、ブランチはただのポインタです。したがって、「新しいブランチを作ってから、現在のブランチのポインタを巻き戻す」という操作で瞬時に解決できます。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;span class="lnt">8
&lt;/span>&lt;span class="lnt">9
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 1. 現在のコミット（間違えて作ったコミット）を指す新しいブランチを作成する&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">$ git branch feature/login
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 2. mainブランチのポインタを1つ前のコミット（HEAD~1）に巻き戻す&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># --keep を使うと、作業ディレクトリの未コミットの変更を維持しつつ安全にリセットできます。&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">$ git reset --keep HEAD~1
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 3. 正しいブランチに切り替える&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">$ git checkout feature/login
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="図解内部で何が起きたのか">図解：内部で何が起きたのか？
&lt;/h3>&lt;p>Mermaidの &lt;code>gitGraph&lt;/code> を用いて、この時のブランチポインタの移動を可視化してみましょう。&lt;/p>
&lt;pre class="mermaid">
gitGraph
commit id: &amp;#34;Initial commit&amp;#34;
commit id: &amp;#34;Bugfix&amp;#34;
commit id: &amp;#34;Mistaken Commit&amp;#34; type: HIGHLIGHT
branch feature/login
checkout feature/login
checkout main
&lt;/pre>
&lt;p>最初は &lt;code>main&lt;/code> と &lt;code>HEAD&lt;/code> が &amp;ldquo;Mistaken Commit&amp;rdquo; を指していましたが、&lt;code>git branch feature/login&lt;/code> により、そこに新しいポインタが作られます。その後 &lt;code>git reset&lt;/code> によって &lt;code>main&lt;/code> ポインタだけが &amp;ldquo;Bugfix&amp;rdquo; の位置に戻るのです。オブジェクト自体は何も削除されていません。&lt;/p>
&lt;hr>
&lt;h2 id="5-ケーススタディ2プッシュ済みのコミットを取り消したい">5. ケーススタディ2：プッシュ済みのコミットを取り消したい！
&lt;/h2>&lt;p>&lt;strong>【状況】&lt;/strong>
深夜のテンションで書いたバグだらけのコードをコミットし、さらに &lt;code>git push origin main&lt;/code> でリモートリポジトリに公開してしまった。重大なバグに気づき青ざめる。&lt;/p>
&lt;h3 id="解決策1歴史を打ち消す-git-revert-推奨安全">解決策1：歴史を打ち消す &lt;code>git revert&lt;/code> （推奨・安全）
&lt;/h3>&lt;p>チーム開発において、すでにプッシュされたコミットの履歴を &lt;code>git reset&lt;/code> などで改ざんすることは厳禁です。他の開発者のローカルリポジトリと整合性が取れなくなります。正しいアプローチは、**「間違えたコミットの変更を完全に打ち消す、逆のコミットを新しく作る」**ことです。これが &lt;code>git revert&lt;/code> です。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 最新のコミットを打ち消すコミットを作成&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">$ git revert HEAD
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">[&lt;/span>main 7f3a8b2&lt;span class="o">]&lt;/span> Revert &lt;span class="s2">&amp;#34;間違えたコミットのメッセージ&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="m">1&lt;/span> file changed, &lt;span class="m">1&lt;/span> insertion&lt;span class="o">(&lt;/span>+&lt;span class="o">)&lt;/span>, &lt;span class="m">10&lt;/span> deletions&lt;span class="o">(&lt;/span>-&lt;span class="o">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># リモートへプッシュ&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">$ git push origin main
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;pre class="mermaid">
gitGraph
commit id: &amp;#34;Commit A&amp;#34;
commit id: &amp;#34;Commit B (Mistake)&amp;#34;
commit id: &amp;#34;Revert Commit B&amp;#34; type: REVERSE
&lt;/pre>
&lt;p>歴史は進み続け、コードの状態だけが元に戻ります。&lt;/p>
&lt;h3 id="解決策2歴史を改ざんする-git-push---force-with-lease">解決策2：歴史を改ざんする &lt;code>git push --force-with-lease&lt;/code>
&lt;/h3>&lt;p>もしあなたが自分一人しか使っていないブランチにプッシュした直後であれば、歴史を書き換えることも容認されます。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 手元でコミットをリセットして修正&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">$ git reset --hard HEAD~1
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">$ git add .
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">$ git commit -m &lt;span class="s2">&amp;#34;Correct implementation&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># リモートの歴史を強制上書き&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">$ git push origin feature/login --force-with-lease
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>&lt;code>--force-with-lease&lt;/code> は、他人の作業を誤って上書きしてしまう事故を防ぐための安全な強制プッシュです。&lt;/p>
&lt;hr>
&lt;h2 id="6-ケーススタディ3作業途中で別のブランチに切り替えたいstashの魔法">6. ケーススタディ3：作業途中で別のブランチに切り替えたい（Stashの魔法）
&lt;/h2>&lt;p>&lt;strong>【状況】&lt;/strong>
&lt;code>feature/A&lt;/code> ブランチで新機能の実装中、ソースコードはまだコンパイルも通らない中途半端な状態。突然、上司から「&lt;code>main&lt;/code> ブランチの本番環境に緊急のバグがあるから、今すぐ直してくれ！」と指示が飛んできた。&lt;/p>
&lt;h3 id="解決方法git-stash-による退避">解決方法：&lt;code>git stash&lt;/code> による退避
&lt;/h3>&lt;p>&lt;code>git stash&lt;/code> は、未コミットの変更を一時的な領域に退避させるコマンドです。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 1. 作業中の変更を退避&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">$ git stash push -m &lt;span class="s2">&amp;#34;WIP: feature A partially implemented&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 2. mainブランチに切り替え可能に&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">$ git checkout main
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># ...（緊急バグ修正作業を行い、コミット・プッシュ）...&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 3. 作業が終わったら元のブランチに戻る&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">$ git checkout feature/A
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 4. 退避していた変更を復元&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">$ git stash pop
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>&lt;code>git stash&lt;/code> を実行すると、Gitは内部的に2つの特殊なコミットオブジェクトを生成し、&lt;code>refs/stash&lt;/code> という参照に保存します。つまり、Stashも結局のところ「名前のつかない一時的なコミット」なのです。&lt;/p>
&lt;hr>
&lt;h2 id="7-ケーススタディ4恐怖の-detached-head-状態">7. ケーススタディ4：恐怖の &amp;ldquo;Detached HEAD&amp;rdquo; 状態
&lt;/h2>&lt;p>&lt;strong>【状況】&lt;/strong>
過去の特定の時点のコードを確認したくて &lt;code>git checkout 9f8a7b6&lt;/code> と実行した。すると &lt;code>You are in 'detached HEAD' state.&lt;/code> と表示された。そのままコミットしたが、ブランチを切り替えたらコミットが消えてしまった！&lt;/p>
&lt;h3 id="detached-headのメカニズム">Detached HEADのメカニズム
&lt;/h3>&lt;p>通常 &lt;code>HEAD&lt;/code> は &lt;code>refs/heads/main&lt;/code> などのブランチを指し示しています。しかし、特定のコミットを直接チェックアウトすると、&lt;code>HEAD&lt;/code> が直接コミットオブジェクトを指すようになります。これを &lt;strong>Detached HEAD（分離したHEAD）&lt;/strong> と呼びます。&lt;/p>
&lt;pre class="mermaid">
graph TD
A[&amp;#34;Commit A&amp;#34;] --&amp;gt; B[&amp;#34;Commit B&amp;#34;]
B --&amp;gt; C[&amp;#34;Commit C&amp;#34;]
C --&amp;gt; D[&amp;#34;Commit D&amp;#34;]
BranchMain[&amp;#34;Branch: main&amp;#34;] --&amp;gt; D
HEAD[&amp;#34;HEAD&amp;#34;] --&amp;gt; B
style HEAD fill:#f9f,stroke:#333,stroke-width:4px
&lt;/pre>
&lt;p>この状態でコミットを重ねても、どのブランチもその新しいコミットを追跡してくれません。他のブランチに切り替えた瞬間、新しいコミットは迷子になります。&lt;/p>
&lt;h3 id="解決策新しいブランチとして保存する">解決策：新しいブランチとして保存する
&lt;/h3>&lt;p>現在いる場所に新しいブランチを作成すれば解決します。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 現在のHEADの位置に新しいブランチを作り、そこに切り替える&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">$ git checkout -b feature/recovered-work
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;hr>
&lt;h2 id="8-ケーススタディ5マージとリベースでのコンフリクト解消">8. ケーススタディ5：マージとリベースでのコンフリクト解消
&lt;/h2>&lt;p>&lt;strong>【状況】&lt;/strong>
&lt;code>git merge&lt;/code> や &lt;code>git rebase&lt;/code> を実行したところ、&lt;code>CONFLICT (content)&lt;/code> と表示され、プロセスが中断された。&lt;/p>
&lt;h3 id="マージmergeとリベースrebaseの違い">マージ（Merge）とリベース（Rebase）の違い
&lt;/h3>&lt;ol>
&lt;li>&lt;strong>Merge（マージ）&lt;/strong>
2つのブランチの最新コミットと共通の祖先を用いた3-way mergeを行い、マージコミットを作成します。&lt;/li>
&lt;li>&lt;strong>Rebase（リベース）&lt;/strong>
現在のブランチのコミットを一時保存し、ターゲットの先端に適用し直します。歴史が一直線になります。&lt;/li>
&lt;/ol>
&lt;pre class="mermaid">
gitGraph
commit id: &amp;#34;M1&amp;#34;
commit id: &amp;#34;M2&amp;#34;
branch feature
checkout feature
commit id: &amp;#34;F1&amp;#34;
commit id: &amp;#34;F2&amp;#34;
checkout main
commit id: &amp;#34;M3&amp;#34;
merge feature
&lt;/pre>
&lt;h3 id="コンフリクトの解消法">コンフリクトの解消法
&lt;/h3>&lt;p>コンフリクトが発生したファイルには、以下のようなマーカーが挿入されています。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="o">&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&lt;/span> &lt;span class="nx">HEAD&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">apiUrl&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;https://api.production.example.com&amp;#34;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">=======&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">apiUrl&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;https://api.staging.example.com&amp;#34;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span> &lt;span class="nx">feature&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="k">new&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="nx">api&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>解決手順は極めてシンプルです。&lt;/p>
&lt;ol>
&lt;li>&lt;strong>マーカーを削除し、正しいコードに修正する。&lt;/strong>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">apiUrl&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">process&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">env&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">NODE_ENV&lt;/span> &lt;span class="o">===&lt;/span> &lt;span class="s1">&amp;#39;production&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">?&lt;/span> &lt;span class="s2">&amp;#34;https://api.production.example.com&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">:&lt;/span> &lt;span class="s2">&amp;#34;https://api.staging.example.com&amp;#34;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;/li>
&lt;li>&lt;strong>解決したファイルをステージングに追加する。&lt;/strong>
&lt;code>git add&lt;/code> は「コンフリクトを解決したことをGitに伝える」役割を持っています。
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">$ git add index.js
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;/li>
&lt;li>&lt;strong>プロセスを完了させる。&lt;/strong>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># マージの場合&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">$ git commit -m &lt;span class="s2">&amp;#34;Resolve merge conflict in index.js&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># リベースの場合&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">$ git rebase --continue
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;/li>
&lt;/ol>
&lt;p>パニックになったら &lt;code>$ git merge --abort&lt;/code> または &lt;code>$ git rebase --abort&lt;/code> でいつでも中止できます。&lt;/p>
&lt;hr>
&lt;h2 id="9-ケーススタディ6コミット履歴がぐちゃぐちゃ-git-rebase--i">9. ケーススタディ6：コミット履歴がぐちゃぐちゃ！ &lt;code>git rebase -i&lt;/code>
&lt;/h2>&lt;p>&lt;strong>【状況】&lt;/strong>
「タイポ修正」「やっぱり修正」「テスト追加」など、細かいコミットが大量に発生してしまった。このまま &lt;code>main&lt;/code> にマージすると履歴が汚くなる。&lt;/p>
&lt;h3 id="解決策インタラクティブリベース">解決策：インタラクティブリベース
&lt;/h3>&lt;p>&lt;code>git rebase -i&lt;/code>（interactive）を使うと、過去のコミットの順番を入れ替えたり、複数のコミットを1つにまとめたり（squash）、コミットメッセージを修正したりできます。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 直近3つのコミットを整理する&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">$ git rebase -i HEAD~3
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>エディタが開き、以下のように表示されます。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">pick 1a2b3c4 タイポ修正
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">pick 2b3c4d5 やっぱり修正
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">pick 3c4d5e6 テスト追加
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>これを以下のように書き換えます。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">pick 1a2b3c4 機能Xの実装
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">squash 2b3c4d5 やっぱり修正
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">squash 3c4d5e6 テスト追加
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>保存して閉じると、これら3つのコミットが1つに美しく統合されます。&lt;/p>
&lt;hr>
&lt;h2 id="10-ケーススタディ7バグがいつ混入したかわからない-git-bisect">10. ケーススタディ7：バグがいつ混入したかわからない！ &lt;code>git bisect&lt;/code>
&lt;/h2>&lt;p>&lt;strong>【状況】&lt;/strong>
現在の &lt;code>main&lt;/code> ブランチにはバグがあるが、1ヶ月前のリリース時は正常だった。どのコミットでバグが混入したのか特定したいが、コミットが100個以上あって手作業では無理！&lt;/p>
&lt;h3 id="解決策二分探索によるバグ特定">解決策：二分探索によるバグ特定
&lt;/h3>&lt;p>Gitには、バグが混入したコミットを数学的な二分探索（Binary Search）で見つけ出すツールが組み込まれています。計算量は $\mathcal{O}(\log N)$ なので、1000個のコミットがあっても約10回のテストで特定できます。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 探索を開始&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">$ git bisect start
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 現在のコミットにはバグがある（bad）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">$ git bisect bad
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 1ヶ月前（例えばハッシュが a1b2c3d）は正常だった（good）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">$ git bisect good a1b2c3d
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Gitが自動的に中間のコミットをチェックアウトするので、テストを実行する&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># テストが成功したら：&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">$ git bisect good
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># テストが失敗したら：&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">$ git bisect bad
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>これを繰り返すだけで、Gitが「このコミットが最初のBadコミットです」と正確に教えてくれます。終わったら &lt;code>$ git bisect reset&lt;/code> で元の状態に戻ります。&lt;/p>
&lt;hr>
&lt;h2 id="11-究極のセーフティネットgit-reflog">11. 究極のセーフティネット：&lt;code>git reflog&lt;/code>
&lt;/h2>&lt;p>Gitでのあらゆる「やらかし」に対する最終奥義が &lt;code>git reflog&lt;/code> です。Gitはローカルでの操作履歴（HEADの移動履歴）を一定期間すべて記録しています。ブランチを削除してしまったり、誤ったリセットをしてしまったりしても、&lt;code>git reflog&lt;/code> で過去のハッシュを見つけ出し、そこに &lt;code>git reset --hard&lt;/code> するだけで復旧可能です。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">$ git reflog
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">9f8a7b6 &lt;span class="o">(&lt;/span>HEAD -&amp;gt; main&lt;span class="o">)&lt;/span> HEAD@&lt;span class="o">{&lt;/span>0&lt;span class="o">}&lt;/span>: commit: Add new feature
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">1a2b3c4 HEAD@&lt;span class="o">{&lt;/span>1&lt;span class="o">}&lt;/span>: reset: moving to HEAD~1
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 id="12-おわりに">12. おわりに
&lt;/h2>&lt;p>Git初心者が陥りやすいミスと、その背後にあるGitの仕組み、そして解決方法について非常に詳細に解説してきました。間違ったブランチへのコミット、プッシュ済みコミットの打ち消し、Stashの活用、Detached HEADからの生還、そしてコンフリクトの解消。これらすべてにおいて重要なのは、「Gitが背後でどのようなオブジェクトとポインタを操作しているのか」をイメージすることです。&lt;/p>
&lt;p>数式で表されるような厳格なDiffアルゴリズムによってファイルの差分が計算され、暗号学的なハッシュ関数によって歴史の整合性が担保されている。この美しい設計思想を理解すれば、Gitは決して「得体の知れないブラックボックス」ではなく、あなたのソースコードを強固に守る最強の盾であることがわかるはずです。&lt;/p>
&lt;p>次回「やっちまった！」と思ったときは、慌ててターミナルを閉じるのではなく、深呼吸して &lt;code>git status&lt;/code> を打ち込んでみてください。Gitは必ず、復旧のためのヒントをあなたに提示してくれます。&lt;/p></description></item></channel></rss>