<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Git on kenji.blog</title><link>http://kenji.blog/ru/categories/git/</link><description>Recent content in Git on kenji.blog</description><generator>Hugo -- gohugo.io</generator><language>ru</language><copyright>kenjinote</copyright><lastBuildDate>Sun, 13 Sep 2026 09:00:00 +0900</lastBuildDate><atom:link href="http://kenji.blog/ru/categories/git/index.xml" rel="self" type="application/rss+xml"/><item><title>【Команды Git】Разница между 'rebase' и 'merge', и их правильное использование на практике</title><link>http://kenji.blog/ru/p/git-rebase-vs-merge-practical-guide/</link><pubDate>Sun, 13 Sep 2026 09:00:00 +0900</pubDate><guid>http://kenji.blog/ru/p/git-rebase-vs-merge-practical-guide/</guid><description>&lt;img src="http://kenji.blog/p/git-rebase-vs-merge-practical-guide/img/eyecatch.jpg" alt="Featured image of post 【Команды Git】Разница между 'rebase' и 'merge', и их правильное использование на практике" />&lt;h1 id="1-введение-почему-выбор-между-merge-и-rebase--это-вечная-проблема">1. Введение: почему выбор между «merge» и «rebase» — это вечная проблема
&lt;/h1>&lt;p>Git является незаменимой системой контроля версий в современной разработке программного обеспечения. Когда несколько разработчиков одновременно вносят изменения в кодовую базу, мощная модель ветвления Git демонстрирует свою эффективность. Однако в командной разработке спор о том, что следует использовать: &lt;code>merge&lt;/code> или &lt;code>rebase&lt;/code>, является одной из тем, которая постоянно беспокоит разработчиков, от новичков до экспертов.&lt;/p>
&lt;p>В этой статье мы подробно рассмотрим различия в механизмах работы &lt;code>git merge&lt;/code> и &lt;code>git rebase&lt;/code>, начиная с внутренней структуры Git — DAG (направленного ациклического графа) и математических свойств хешей коммитов. Затем мы тщательно объясним, как правильно использовать их на практике, сопровождая это конкретными рабочими процессами. Понимая не только сами команды, но и то, какие вычисления Git выполняет «под капотом», вы избавитесь от страха перед конфликтами и сможете создавать чистую, легко отслеживаемую историю.&lt;/p>
&lt;hr>
&lt;h1 id="2-внутренняя-структура-git-хеши-коммитов-и-объектная-модель">2. Внутренняя структура Git: хеши коммитов и объектная модель
&lt;/h1>&lt;p>Чтобы понять, как Git интегрирует историю, сначала нужно узнать, как он хранит данные. Git не просто сохраняет различия (патчи) изменений файлов; он сохраняет снимки всей файловой системы на определенный момент времени.&lt;/p>
&lt;h2 id="21-криптографические-свойства-хешей-коммитов">2.1 Криптографические свойства хешей коммитов
&lt;/h2>&lt;p>Каждый коммит в Git однозначно идентифицируется 40-значным шестнадцатеричным числом, вычисленным на основе его содержимого с помощью хеш-функции SHA-1 (Secure Hash Algorithm 1). Объект коммита состоит из следующих элементов:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Указатель на объект Tree&lt;/strong>: снимок структуры каталогов и файлов (Blob) на тот момент времени.&lt;/li>
&lt;li>&lt;strong>Указатель на родительский коммит&lt;/strong>: хеш-значения одного или нескольких родительских коммитов (первый коммит не имеет родителя, а коммит слияния имеет два или более родителей).&lt;/li>
&lt;li>&lt;strong>Информация об авторе (Author)&lt;/strong>: кто написал код и когда.&lt;/li>
&lt;li>&lt;strong>Информация о коммитере (Committer)&lt;/strong>: кто создал и применил коммит, и когда.&lt;/li>
&lt;li>&lt;strong>Сообщение коммита&lt;/strong>: текст, объясняющий цель изменений.&lt;/li>
&lt;/ol>
&lt;p>В математическом выражении хеш-значение $H(C)$ для объекта коммита $C$ определяется следующим образом:&lt;/p>
$$
H(C) = \text{SHA-1}( \text{tree} \parallel \text{parent} \parallel \text{author} \parallel \text{committer} \parallel \text{message} )
$$&lt;p>Здесь $\parallel$ обозначает конкатенацию данных. Из-за свойств хеш-функции изменение даже одного символа в сообщении коммита или другой родительский коммит приведут к генерации совершенно иного хеш-значения. Это означает, что &lt;strong>коммиты неизменяемы (Immutable)&lt;/strong>. Когда говорят, что &lt;code>rebase&lt;/code> «переписывает историю» (о чем пойдет речь позже), на самом деле это означает создание новых коммитов с похожим содержимым, но с другими хеш-значениями.&lt;/p>
&lt;p>Размер хеш-пространства составляет $2^{160}$, и вероятность коллизии (когда разные коммиты имеют одинаковое хеш-значение) $P$ можно приблизительно вычислить, используя парадокс дней рождения (Birthday Paradox), следующим образом (где $n$ — количество коммитов):&lt;/p>
$$
P(\text{collision}) \approx 1 - \exp\left(-\frac{n^2}{2 \times 2^{160}}\right)
$$&lt;p>Эта вероятность крайне мала, и на практике коллизия хешей коммитов Git практически невозможна.&lt;/p>
&lt;hr>
&lt;h1 id="3-теория-графов-и-dag-математическая-модель-истории-git">3. Теория графов и DAG: математическая модель истории Git
&lt;/h1>&lt;p>История коммитов Git моделируется как «направленный ациклический граф» (Directed Acyclic Graph, DAG) в теории графов.&lt;/p>
&lt;h2 id="31-что-такое-dag-направленный-ациклический-граф">3.1 Что такое DAG (направленный ациклический граф)
&lt;/h2>&lt;p>В графе $G = (V, E)$, $V$ — это множество коммитов (вершин), а $E$ — множество направленных ребер, указывающих на отношения родитель-ребенок между коммитами. В Git ребра направлены от «дочернего коммита к родительскому коммиту». Это связано с тем, что новые коммиты хранят указатели на предыдущие коммиты.&lt;/p>
&lt;pre class="mermaid">
graph BT
A[&amp;#34;Коммит A (Начальный)&amp;#34;]
B[&amp;#34;Коммит B&amp;#34;]
C[&amp;#34;Коммит C (Main)&amp;#34;]
D[&amp;#34;Коммит D (Feature)&amp;#34;]
E[&amp;#34;Коммит E (Merge)&amp;#34;]
B --&amp;gt; A
C --&amp;gt; B
D --&amp;gt; B
E --&amp;gt; C
E --&amp;gt; D
&lt;/pre>
&lt;p>Главной особенностью DAG является «отсутствие циклов». Благодаря этому алгоритмы обхода истории коммитов никогда не попадают в бесконечный цикл и гарантированно достигают конца (начального коммита).&lt;/p>
&lt;h2 id="32-топологическая-сортировка-и-порядок-истории">3.2 Топологическая сортировка и порядок истории
&lt;/h2>&lt;p>При отображении истории, например, с помощью команды &lt;code>git log&lt;/code>, DAG упорядочивается в одномерный список с использованием алгоритма топологической сортировки (Topological Sort). Для любого направленного ребра $u \to v$ в DAG ($u$ является потомком $v$), элементы сортируются так, чтобы $u$ находился перед $v$ в списке.&lt;/p>
&lt;hr>
&lt;h1 id="4-механизмы-и-типы-git-merge">4. Механизмы и типы git merge
&lt;/h1>&lt;p>&lt;code>git merge&lt;/code> — это самая базовая команда для интеграции изменений из веток. Однако, в зависимости от текущего состояния, Git автоматически выбирает разные стратегии слияния.&lt;/p>
&lt;h2 id="41-слияние-fast-forward---ff">4.1 Слияние Fast-Forward (&amp;ndash;ff)
&lt;/h2>&lt;p>Если целевая ветка (например, &lt;code>main&lt;/code>) является прямым предком исходной ветки (например, &lt;code>feature&lt;/code>), Git выполняет слияние «Fast-Forward» (перемотка вперед). Это операция, при которой новые коммиты не создаются, а просто указатель ветки перемещается вперед.&lt;/p>
&lt;pre class="mermaid">
gitGraph
commit id: &amp;#34;A&amp;#34;
commit id: &amp;#34;B&amp;#34;
branch feature
checkout feature
commit id: &amp;#34;C&amp;#34;
commit id: &amp;#34;D&amp;#34;
checkout main
merge feature
&lt;/pre>
&lt;p>Слияние Fast-Forward сохраняет историю линейной, но имеет тот недостаток, что теряется контекст: «какие коммиты были сгруппированы как разработка одной функции (feature)».&lt;/p>
&lt;h2 id="42-слияние-non-fast-forward---no-ff">4.2 Слияние Non-Fast-Forward (&amp;ndash;no-ff)
&lt;/h2>&lt;p>Если явно указать &lt;code>git merge --no-ff&lt;/code>, Git обязательно создаст новый «коммит слияния», даже если возможен Fast-Forward. Коммит слияния — это специальный коммит, имеющий двух родителей.&lt;/p>
&lt;pre class="mermaid">
gitGraph
commit id: &amp;#34;A&amp;#34;
commit id: &amp;#34;B&amp;#34;
branch feature
checkout feature
commit id: &amp;#34;C&amp;#34;
commit id: &amp;#34;D&amp;#34;
checkout main
commit id: &amp;#34;Main Work 1&amp;#34;
merge feature type: NORMAL
&lt;/pre>
&lt;p>Преимущество этого метода в том, что существование и история функциональной ветки четко остаются в DAG. При возникновении проблем можно выполнить &lt;code>git revert -m 1 &amp;lt;хеш коммита слияния&amp;gt;&lt;/code>, чтобы безопасно отменить (revert) всю функцию целиком за один раз.&lt;/p>
&lt;h2 id="43-алгоритм-трехстороннего-слияния-3-way-merge">4.3 Алгоритм трехстороннего слияния (3-Way Merge)
&lt;/h2>&lt;p>Если целевая и исходная ветки имеют свои собственные коммиты, Git выполняет трехстороннее слияние. При этом Git исследует DAG и находит «наименьшего общего предка» (Lowest Common Ancestor, LCA) двух веток.&lt;/p>
&lt;p>Вычислительная сложность алгоритма поиска LCA, $T_{\text{LCA}}$, выполняется за линейное время относительно количества вершин $|V|$ и ребер $|E|$:&lt;/p>
$$
T_{\text{LCA}} = \mathcal{O}(|V| + |E|)
$$&lt;p>Git сравнивает три состояния: «состояние LCA», «состояние текущей ветки» и «состояние другой ветки», и, если изменения не конфликтуют, автоматически генерирует коммит слияния.&lt;/p>
&lt;hr>
&lt;h1 id="5-механизм-git-rebase-и-реконструкция-истории">5. Механизм git rebase и реконструкция истории
&lt;/h1>&lt;p>В то время как &lt;code>git merge&lt;/code> «интегрирует» историю, &lt;code>git rebase&lt;/code> «реконструирует» (перебазирует) её.&lt;/p>
&lt;h2 id="51-что-происходит-за-кулисами-rebase">5.1 Что происходит за кулисами Rebase
&lt;/h2>&lt;p>Внутренняя работа при ребейзе ветки &lt;code>feature&lt;/code> на ветку &lt;code>main&lt;/code> (&lt;code>git rebase main&lt;/code>) выглядит следующим образом:&lt;/p>
&lt;ol>
&lt;li>Найти общего предка (LCA) веток &lt;code>feature&lt;/code> и &lt;code>main&lt;/code>.&lt;/li>
&lt;li>Сохранить разницу коммитов от LCA до конца ветки &lt;code>feature&lt;/code> во временную область.&lt;/li>
&lt;li>Переместить указатель ветки &lt;code>feature&lt;/code> на конец ветки &lt;code>main&lt;/code>.&lt;/li>
&lt;li>Последовательно применить сохраненные изменения (Cherry-Pick) поверх новой базы (конца &lt;code>main&lt;/code>), создавая новые коммиты.&lt;/li>
&lt;/ol>
&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 (Main)&amp;#34;]
B --&amp;gt; D[&amp;#34;Коммит D (Старая ветка Feature)&amp;#34;]
D -.-&amp;gt; E[&amp;#34;Коммит D&amp;#39; (Новая ветка Feature)&amp;#34;]
C --&amp;gt; E
style D stroke-dasharray: 5 5, fill: #f9f9f9, color: #999
&lt;/pre>
&lt;p>Здесь важно отметить, что коммит $D'$, созданный в результате ребейза, имеет &lt;strong>совершенно другой хеш&lt;/strong>, потому что у него другой родительский коммит, в отличие от исходного коммита $D$ (см. определение хеш-функции $H(C)$ выше).&lt;/p>
&lt;h2 id="52-интерактивный-ребейз-interactive-rebase">5.2 Интерактивный ребейз (Interactive Rebase)
&lt;/h2>&lt;p>Использование &lt;code>git rebase -i&lt;/code> (или &lt;code>--interactive&lt;/code>) позволяет вам управлять историей коммитов по своему усмотрению. Это самый мощный инструмент для очистки локальной истории.&lt;/p>
&lt;ul>
&lt;li>&lt;code>pick&lt;/code> : использовать коммит как есть&lt;/li>
&lt;li>&lt;code>reword&lt;/code> : изменить только сообщение коммита&lt;/li>
&lt;li>&lt;code>edit&lt;/code> : приостановить ребейз для изменения содержимого коммита&lt;/li>
&lt;li>&lt;code>squash&lt;/code> : объединить этот коммит с предыдущим, а также объединить их сообщения&lt;/li>
&lt;li>&lt;code>fixup&lt;/code> : то же, что и &lt;code>squash&lt;/code>, но сообщение этого коммита отбрасывается&lt;/li>
&lt;li>&lt;code>drop&lt;/code> : полностью удалить коммит&lt;/li>
&lt;/ul>
&lt;p>С математической точки зрения, если в ветке есть $N$ коммитов, количество возможных вариаций линейной истории (перестановок) $P$, генерируемых путем изменения порядка при ребейзе, выглядит следующим образом:&lt;/p>
$$
P = N!
$$&lt;p>Git предоставляет разработчикам $N!$ степеней свободы, позволяя поддерживать историю в логичном и красивом состоянии.&lt;/p>
&lt;hr>
&lt;h1 id="6-золотое-правило-ребейза-the-golden-rule-of-rebase">6. Золотое правило ребейза (The Golden Rule of Rebase)
&lt;/h1>&lt;p>Команда &lt;code>rebase&lt;/code> очень мощная, но существует одно абсолютное правило.&lt;/p>
&lt;blockquote>
&lt;p>&lt;strong>«Никогда не выполняйте ребейз публичной истории»&lt;/strong>
&lt;em>(Never rebase public history)&lt;/em>&lt;/p>
&lt;/blockquote>
&lt;h2 id="61-почему-нельзя-делать-ребейз-публичной-истории">6.1 Почему нельзя делать ребейз публичной истории?
&lt;/h2>&lt;p>Git — это распределенная система. Коммиты, которые вы запушили в &lt;code>origin/main&lt;/code>, клонируются (копируются) в локальные репозитории других разработчиков. Что произойдет, если вы сделаете ребейз уже запушенных коммитов, перепишете историю и принудительно перезапишете её с помощью &lt;code>git push --force&lt;/code>?&lt;/p>
&lt;p>Локальный DAG у других разработчиков и удаленный DAG кардинально разойдутся. Когда другой разработчик выполнит &lt;code>git pull&lt;/code>, Git попытается принудительно слить группы коммитов с разной историей, что приведет к огромному количеству конфликтов и дублирующихся коммитов (коммитов с одинаковыми изменениями, но разными хешами), повергая репозиторий в состояние паники.&lt;/p>
&lt;p>Железное правило гласит: ребейз следует применять &lt;strong>только к «локальным веткам, которыми вы еще ни с кем не поделились»&lt;/strong>.&lt;/p>
&lt;hr>
&lt;h1 id="7-разрешение-конфликтов-и-git-rebase---continue">7. Разрешение конфликтов и git rebase &amp;ndash;continue
&lt;/h1>&lt;p>Конфликт возникает, когда несколько человек изменяют одно и то же место в одном и том же файле. Процесс разрешения конфликтов в &lt;code>merge&lt;/code> и &lt;code>rebase&lt;/code> отличается.&lt;/p>
&lt;h2 id="71-разрешение-конфликтов-в-merge">7.1 Разрешение конфликтов в Merge
&lt;/h2>&lt;p>В случае с &lt;code>git merge&lt;/code> разрешение конфликтов происходит &lt;strong>только один раз&lt;/strong>. Все конфликтующие участки исправляются за один раз, прямо перед созданием финального коммита слияния.&lt;/p>
&lt;h2 id="72-разрешение-конфликтов-в-rebase">7.2 Разрешение конфликтов в Rebase
&lt;/h2>&lt;p>В случае с &lt;code>git rebase&lt;/code>, из-за природы повторного применения коммитов один за другим, &lt;strong>существует вероятность возникновения конфликта на каждом коммите&lt;/strong>.&lt;/p>
&lt;p>Если во время ребейза возникает конфликт, Git приостанавливает процесс. Порядок разрешения следующий:&lt;/p>
&lt;ol>
&lt;li>Открыть редактор или IDE (например, VS Code) и вручную исправить маркеры конфликтов (&lt;code>&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&lt;/code>, &lt;code>======&lt;/code>, &lt;code>&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&lt;/code>).&lt;/li>
&lt;li>Добавить исправленный файл в индекс:
&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 &amp;lt;исправленный файл&amp;gt;
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;/li>
&lt;li>Не создавая коммит, возобновить процесс ребейза:
&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 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;/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-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">git rebase --abort
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>(※Если разрешение конфликта не требуется, и вы хотите полностью пропустить этот коммит, используйте &lt;code>git rebase --skip&lt;/code>)&lt;/p>
&lt;hr>
&lt;h1 id="8-правильное-использование-на-практике-практика-рабочих-процессов">8. Правильное использование на практике (практика рабочих процессов)
&lt;/h1>&lt;p>Итак, как же правильно выбирать между &lt;code>merge&lt;/code> и &lt;code>rebase&lt;/code> в реальной разработке? Здесь мы представим самый стандартный и безопасный подход.&lt;/p>
&lt;h2 id="81-сценарий-1очистка-локальной-истории-использование-rebase">8.1 【Сценарий 1】Очистка локальной истории (использование Rebase)
&lt;/h2>&lt;p>Предположим, что во время разработки в функциональной ветке накопилось много мелких коммитов (таких как «исправление опечатки», «временное сохранение» и т. д.). Перед созданием Pull Request (PR) вы используете интерактивный ребейз, чтобы организовать их в осмысленные блоки.&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">&lt;span class="c1"># Выполнить, находясь в ветке feature&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">git rebase -i HEAD~5
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># (Откроется редактор, используйте squash и fixup для очистки истории)&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;h2 id="82-сценарий-2следование-за-новейшей-веткой-main-использование-rebase">8.2 【Сценарий 2】Следование за новейшей веткой main (использование Rebase)
&lt;/h2>&lt;p>Если разработка затягивается, и чужие изменения постоянно сливаются в &lt;code>main&lt;/code>, ваша ветка &lt;code>feature&lt;/code> устаревает. В этом случае вы делаете ребейз ветки &lt;code>feature&lt;/code> на самую свежую &lt;code>main&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;/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"># Получить новейшую информацию о main&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">git fetch origin
&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"># Перебазировать ветку feature поверх свежей main&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">git rebase origin/main
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>В результате история становится линейной, что помогает избежать конфликтов при последующих слияниях. Кроме того, это предотвращает создание лишних коммитов слияния (например, &amp;ldquo;Merge branch &amp;lsquo;main&amp;rsquo; into feature&amp;rdquo;).&lt;/p>
&lt;h2 id="83-сценарий-3интеграция-завершенной-функции-использование-merge">8.3 【Сценарий 3】Интеграция завершенной функции (использование Merge)
&lt;/h2>&lt;p>Разработка в ветке &lt;code>feature&lt;/code> завершена, и настал этап интеграции в ветке &lt;code>main&lt;/code>. Здесь используется &lt;strong>&lt;code>git merge --no-ff&lt;/code>&lt;/strong> (это эквивалентно выбору «Create a merge commit» в Pull Request на GitHub и т.д.).&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 checkout main
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">git merge --no-ff feature -m &lt;span class="s2">&amp;#34;Merge feature: Реализация функции входа пользователя&amp;#34;&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;p>Благодаря этому в DAG ветки &lt;code>main&lt;/code> останется исторический узел (коммит слияния), показывающий: «здесь была слита одна функция». При просмотре истории в будущем это облегчит отслеживание кода по функциональным блокам.&lt;/p>
&lt;hr>
&lt;h1 id="9-заключение-итоги">9. Заключение (итоги)
&lt;/h1>&lt;p>В работе с Git крайние подходы, такие как «решать всё только через Merge» или «делать всё линейным только через Rebase», имеют свои плюсы и минусы.&lt;/p>
&lt;p>Лучшая практика в реальной работе — это гибридный подход: &lt;strong>«красиво организовывать локальную приватную историю с помощью rebase, а публичную историю интеграции сохранять с контекстом через merge &amp;ndash;no-ff»&lt;/strong>.&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Локально (личное рабочее пространство)&lt;/strong>: используйте &lt;code>rebase&lt;/code> для устранения лишних коммитов и поддержания линейной истории, следуя за актуальной основной линией.&lt;/li>
&lt;li>&lt;strong>Глобально (общее рабочее пространство)&lt;/strong>: используйте &lt;code>merge --no-ff&lt;/code> для записи существования функциональной ветки в DAG в виде коммита слияния, что облегчает реверт и отслеживание.&lt;/li>
&lt;/ul>
&lt;p>Понимая математические и архитектурные основы, такие как структура DAG и механизмы хеш-функций, команды Git превращаются из простого заучивания в «проектирование истории с определенным намерением». Соблюдая золотое правило ребейза и выбирая оптимальные команды в зависимости от ситуации, давайте создавать чистую, легко читаемую и поддерживаемую историю коммитов для всей команды.&lt;/p></description></item><item><title>Распространенные ошибки новичков в Git и команды для их решения (разрешение конфликтов и др.)</title><link>http://kenji.blog/ru/p/git-beginners-mistakes-and-solutions/</link><pubDate>Sat, 12 Sep 2026 17:00:00 +0900</pubDate><guid>http://kenji.blog/ru/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;hellip; Когда вы попадаете в такие «ловушки Git», процесс работы полностью останавливается, и возникает страх, что в худшем случае вы можете разрушить исходный код.&lt;/p>
&lt;p>Почему Git кажется таким сложным и так часто провоцирует ошибки? Главная причина заключается в том, что «люди заучивают и используют поверхностные команды, не понимая, что происходит внутри Git». Git основан на надежной архитектуре распределенной системы контроля версий (DVCS), но его интерфейс командной строки (CLI) далеко не всегда интуитивно понятен.&lt;/p>
&lt;p>В этой статье мы подробно разберем множество «косяков (типичных ошибок)», с которыми новички часто сталкиваются на практике, классифицируем их и предложим конкретные команды для их решения. Но это не будет просто еще одной шпаргалкой с набором команд. Мы глубоко погрузимся в суть проблемы: «почему возникает эта ошибка» и «как именно перемещаются данные внутри Git при выполнении этой команды». Наш материал объемом более 10 000 символов будет включать анализ структуры директории &lt;code>.git&lt;/code>, математическую основу алгоритма Diff, работающего в фоновом режиме, а также визуализации с помощью Mermaid.&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 символов в шестнадцатеричном формате).&lt;/li>
&lt;li>&lt;strong>Tree (Дерево)&lt;/strong>
Объект, представляющий структуру директорий. Объект Tree содержит указатели (хэши SHA-1) на другие объекты Tree (поддиректории) или объекты Blob (файлы), а также их имена и права доступа. Он выполняет роль, аналогичную директории в 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;Blob-объект (Хэш: 8d7e) : index.js&amp;#34;]
Tree1 --&amp;gt; Tree2[&amp;#34;Дерево (Хэш: 3a2c) : src/&amp;#34;]
Tree2 --&amp;gt; Blob2[&amp;#34;Blob-объект (Хэш: 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>. Это &lt;strong>символическая ссылка (Symbolic Reference)&lt;/strong>, которая указывает на текущую извлеченную (checked out) ветку (или коммит).
Если вы откроете файл &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>, там будет записан хэш SHA-1 из 40 символов, который указывает на последний объект Commit.
Ветка в Git — это просто легковесный указатель (файл), указывающий на конкретный коммит. Зная этот факт, исчезает страх: «А вдруг, удалив ветку, я удалю и все файлы?».&lt;/p>
&lt;hr>
&lt;h2 id="3-читая-git-через-математику-алгоритм-diff-и-хэш-функции">3. Читая Git через математику: Алгоритм Diff и хэш-функции
&lt;/h2>&lt;p>Когда Git обнаруживает конфликты или отображает разницу в файлах, под капотом работают продвинутые алгоритмы.&lt;/p>
&lt;h3 id="31-алгоритм-diff-майерса">3.1 Алгоритм Diff Майерса
&lt;/h3>&lt;p>Алгоритм определения разницы в Git по умолчанию был разработан Юджином В. Майерсом. Если у нас есть два текстовых файла $A$ и $B$, то задачу поиска «минимального количества операций редактирования (вставок и удалений)» для преобразования $A$ в $B$ можно смоделировать как задачу поиска кратчайшего пути в теории графов.&lt;/p>
&lt;p>Пусть длины строк равны $N$ и $M$, а их сумма $V = N + M$. Алгоритм Майерса ищет расстояние редактирования (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>Алгоритм Майерса отличный, но если вы сильно изменили порядок функций или классов, он может сгенерировать неочевидную для человека (лишённую смысла) разницу. Для решения этой проблемы в Git реализованы алгоритмы &lt;code>Patience Diff&lt;/code> и &lt;code>Histogram Diff&lt;/code>.&lt;/p>
&lt;p>Patience Diff фокусируется на «уникальных строках, которые появляются только один раз в обоих файлах», и находит их наибольшую общую подпоследовательность (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), то количество объектов $k$, необходимое для достижения вероятности коллизии $p$ в 50%, будет следующим:&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 на один коммит назад (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>Давайте визуализируем перемещение указателей веток с помощью &lt;code>gitGraph&lt;/code> в Mermaid.&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> указывали на «Ошибочный коммит», но с помощью &lt;code>git branch feature/login&lt;/code> там был создан новый указатель. Затем, с помощью &lt;code>git reset&lt;/code>, только указатель &lt;code>main&lt;/code> возвращается на позицию «Исправление ошибки». Сами объекты при этом не удаляются.&lt;/p>
&lt;hr>
&lt;h2 id="5-пример-из-практики-2-хочу-отменить-уже-отправленный-push-коммит">5. Пример из практики 2: Хочу отменить уже отправленный (push) коммит!
&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>«создать новый коммит, который полностью отменяет изменения ошибочного коммита»&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;Правильная реализация&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> — это безопасный принудительный push, который предотвращает случайную перезапись чужой работы.&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: фича A частично реализована&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 под капотом генерирует два специальных объекта-коммита и сохраняет их в ссылку &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;Коммит 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-разрешение-конфликтов-при-слиянии-и-перебазировании">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>
Выполняет трехстороннее слияние (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>Добавить разрешенный файл в индекс (staging).&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;Разрешен конфликт слияния в 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) вы можете менять порядок прошлых коммитов, объединять несколько коммитов в один (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> есть баг, но месяц назад во время релиза все было нормально. Вы хотите найти коммит, в котором появился баг, но их больше 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"># Состояние месячной давности (например, хэш 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) за определенный период времени. Даже если вы удалили ветку или выполнили неправильный reset, вы можете найти хэш из прошлого в &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: Добавление новой фичи
&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>