<?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/ko/tags/troubleshooting/</link><description>Recent content in Troubleshooting on kenji.blog</description><generator>Hugo -- gohugo.io</generator><language>ko</language><copyright>kenjinote</copyright><lastBuildDate>Sat, 12 Sep 2026 17:00:00 +0900</lastBuildDate><atom:link href="http://kenji.blog/ko/tags/troubleshooting/index.xml" rel="self" type="application/rss+xml"/><item><title>Git 초보자가 빠지기 쉬운 실수와 해결 명령어 모음 (충돌 해결 등)</title><link>http://kenji.blog/ko/p/git-beginners-mistakes-and-solutions/</link><pubDate>Sat, 12 Sep 2026 17:00:00 +0900</pubDate><guid>http://kenji.blog/ko/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은 &amp;ldquo;무서운 마법의 블랙박스&amp;quot;처럼 느껴질 때가 있습니다. 커밋이 사라지거나, 의도하지 않은 브랜치에 대량의 변경 사항을 밀어넣어 버리거나, 본 적도 없는 충돌 에러 메시지가 화면을 가득 채우거나&amp;hellip; 이런 &amp;ldquo;Git의 덫&amp;quot;에 빠지면 작업 진행이 완전히 멈춰버리고, 최악의 경우 소스 코드를 망가뜨리는 것은 아닐까 하는 공포에 휩싸입니다.&lt;/p>
&lt;p>왜 Git은 이렇게나 어렵고 실수를 유발하기 쉬울까요? 그 가장 큰 이유는 &amp;ldquo;Git 내부에서 무슨 일이 일어나고 있는지 이해하지 못한 채, 표면적인 명령어만 암기해서 사용하고 있기 때문&amp;quot;입니다. Git은 분산 버전 관리 시스템(DVCS)으로서 견고한 설계 사상을 바탕으로 하고 있지만, 그 인터페이스(CLI)가 반드시 직관적인 것은 아닙니다.&lt;/p>
&lt;p>이 글에서는 Git 초보자가 실무에서 자주 겪는 &amp;ldquo;실수&amp;quot;를 여러 케이스로 분류하고, 각각의 구체적인 해결책이 되는 명령어를 제시합니다. 하지만 단순한 명령어의 나열(치트 시트)로 만들지는 않겠습니다. &amp;ldquo;왜 그 실수가 일어나는가&amp;rdquo;, &amp;ldquo;그 명령어를 실행하면 Git 내부에서 데이터가 어떻게 움직이는가&amp;quot;를 &lt;code>.git&lt;/code> 디렉토리의 구조나 백그라운드에서 동작하는 Diff 알고리즘의 수학적 배경, Mermaid를 통한 도해를 곁들여 10,000자가 넘는 분량으로 철저하게 파헤쳐 봅니다.&lt;/p>
&lt;p>이 글을 끝까지 읽었을 때, 당신은 &amp;ldquo;Git이 무섭다&amp;quot;는 감정에서 해방되어 오히려 &amp;ldquo;Git만큼 믿을 수 있는 파트너는 없다&amp;quot;고 확신할 수 있게 될 것입니다. 그러면 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;커밋 (해시: 9f8a)&amp;#34;] --&amp;gt; Tree1[&amp;#34;트리 (해시: 4b82)&amp;#34;]
Tree1 --&amp;gt; Blob1[&amp;#34;블롭 (해시: 8d7e) : index.js&amp;#34;]
Tree1 --&amp;gt; Tree2[&amp;#34;트리 (해시: 3a2c) : src/&amp;#34;]
Tree2 --&amp;gt; Blob2[&amp;#34;블롭 (해시: 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>이것은 &amp;ldquo;현재 상태는 &lt;code>main&lt;/code> 브랜치의 끝에 있다&amp;quot;는 것을 의미합니다. 그리고 &lt;code>.git/refs/heads/main&lt;/code>을 열면 거기에 40자의 SHA-1 해시가 적혀 있으며, 이것이 최신 Commit 객체를 가리키고 있는 것입니다.
Git의 브랜치란, 단순히 특정 커밋을 가리키는 가벼운 포인터(파일)에 불과합니다. 이 사실을 알고 있는 것만으로 &amp;ldquo;브랜치를 삭제하면 파일이 전부 사라지는 거 아냐?&amp;rdquo; 하는 공포가 사라집니다.&lt;/p>
&lt;hr>
&lt;h2 id="3-수학으로-읽어내는-git-diff-알고리즘과-해시-함수">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$로 변환하기 위한 &amp;ldquo;최소의 편집 절차(삽입과 삭제)&amp;ldquo;를 찾는 문제는 그래프 이론에서의 최단 경로 문제로 모델화할 수 있습니다.&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는 &amp;ldquo;양쪽 파일에 1번만 등장하는 유니크한 줄&amp;quot;에 주목하여, 그것들의 최장 공통 부분 수열(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은 해시값을 &amp;ldquo;절대적인 고유 ID&amp;quot;로서 신뢰하며 동작하고 있습니다.&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에서 커밋은 독립된 객체이며, 브랜치는 그저 포인터입니다. 따라서 &amp;ldquo;새로운 브랜치를 만든 다음, 현재 브랜치의 포인터를 되감는&amp;rdquo; 조작으로 순식간에 해결할 수 있습니다.&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;초기 커밋&amp;#34;
commit id: &amp;#34;버그 수정&amp;#34;
commit id: &amp;#34;실수한 커밋&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;실수한 커밋&amp;quot;을 가리키고 있었지만, &lt;code>git branch feature/login&lt;/code>에 의해 거기에 새로운 포인터가 만들어집니다. 그 후 &lt;code>git reset&lt;/code>에 의해 &lt;code>main&lt;/code> 포인터만 &amp;ldquo;버그 수정&amp;quot;의 위치로 돌아가는 것입니다. 객체 자체는 아무것도 삭제되지 않았습니다.&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;strong>&amp;ldquo;실수한 커밋의 변경을 완전히 상쇄하는, 반대의 커밋을 새로 만드는&amp;rdquo;&lt;/strong> 것입니다. 이것이 &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;커밋 A&amp;#34;
commit id: &amp;#34;커밋 B (실수)&amp;#34;
commit id: &amp;#34;커밋 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> 브랜치에서 새로운 기능 구현 중, 소스 코드는 아직 컴파일도 통과하지 않는 어중간한 상태. 갑자기 상사로부터 &amp;ldquo;&lt;code>main&lt;/code> 브랜치의 운영 환경에 긴급 버그가 있으니 지금 당장 고쳐줘!&amp;ldquo;라는 지시가 날아왔다.&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도 결국 &amp;ldquo;이름이 없는 임시 커밋&amp;quot;인 것입니다.&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>가 직접 커밋 객체를 가리키게 됩니다. 이것을 **Detached HEAD(분리된 HEAD)**라고 부릅니다.&lt;/p>
&lt;pre class="mermaid">
graph TD
A[&amp;#34;커밋 A&amp;#34;] --&amp;gt; B[&amp;#34;커밋 B&amp;#34;]
B --&amp;gt; C[&amp;#34;커밋 C&amp;#34;]
C --&amp;gt; D[&amp;#34;커밋 D&amp;#34;]
BranchMain[&amp;#34;브랜치: 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-병합merge과-리베이스rebase에서의-충돌-해결">8. 케이스 스터디 5: 병합(Merge)과 리베이스(Rebase)에서의 충돌 해결
&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>는 &amp;ldquo;충돌을 해결했다는 것을 Git에 알리는&amp;rdquo; 역할을 합니다.
&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>
&amp;ldquo;오타 수정&amp;rdquo;, &amp;ldquo;역시 수정&amp;rdquo;, &amp;ldquo;테스트 추가&amp;rdquo; 등 자잘한 커밋이 대량으로 발생해 버렸다. 이대로 &lt;code>main&lt;/code>에 병합하면 내역이 지저분해진다.&lt;/p>
&lt;h3 id="해결책-인터랙티브-리베이스">해결책: 인터랙티브 리베이스
&lt;/h3>&lt;p>&lt;code>git rebase -i&lt;/code> (interactive)를 사용하면 과거 커밋의 순서를 바꾸거나, 여러 커밋을 하나로 합치거나(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개의 커밋이 하나로 아름답게 통합됩니다.&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이 &amp;ldquo;이 커밋이 첫 번째 Bad 커밋입니다&amp;quot;라고 정확히 알려줍니다. 끝나면 &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에서의 모든 &amp;ldquo;실수&amp;quot;에 대한 최종 오의가 &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에서의 생환, 그리고 충돌 해결. 이 모든 것에서 중요한 것은 &amp;ldquo;Git이 이면에서 어떤 객체와 포인터를 조작하고 있는가&amp;quot;를 이미지화하는 것입니다.&lt;/p>
&lt;p>수식으로 표현되는 엄격한 Diff 알고리즘에 의해 파일의 차이가 계산되고, 암호학적인 해시 함수에 의해 역사의 정합성이 담보되고 있습니다. 이 아름다운 설계 사상을 이해한다면, Git은 결코 &amp;ldquo;정체를 알 수 없는 블랙박스&amp;quot;가 아니라 당신의 소스 코드를 굳건히 지키는 최강의 방패라는 것을 알 수 있을 것입니다.&lt;/p>
&lt;p>다음번에 &amp;ldquo;아차!&amp;rdquo; 하는 생각이 들 때는, 당황해서 터미널을 닫지 말고 심호흡을 한 뒤 &lt;code>git status&lt;/code>를 입력해 보세요. Git은 반드시 복구를 위한 힌트를 당신에게 제시해 줄 것입니다.&lt;/p></description></item></channel></rss>