<p class="wp-block-paragraph"><strong>Большинство разработчиков используют Git каждый день</strong>, но мало кто задумывается, что происходит внутри, когда мы пишем <code>git checkout -b feature</code> или <code>git merge main</code>. С первого взгляда кажется, что ветки — это просто копии папок с кодом. На деле Git устроен гораздо элегантнее, и понимание его внутреннего механизма делает вас увереннее при работе с конфликтами, rebase и историей.</p>
<span id="more-12"></span>
<h2 class="wp-block-heading">Объекты Git: всё — хеш</h2>
<p class="wp-block-paragraph">В основе Git лежит простая идея: <strong>все данные хранятся как объекты, адресуемые по SHA-1 хешу</strong>. Когда вы делаете коммит, Git создаёт не один, а три типа объектов:</p>
<ul class="wp-block-list">
<li><strong>Blob</strong> — сжатое содержимое файла. Имя файла не хранится здесь, только его данные.</li>
<li><strong>Tree</strong> — аналог директории. Содержит список имён файлов, прав доступа и ссылки на blob-объекты.</li>
<li><strong>Commit</strong> — точка во времени. Содержит автора, дату, сообщение, ссылку на tree и ссылку на родительский коммит (или несколько, в случае merge).</li>
</ul>
<p class="wp-block-paragraph">Ключевой момент: <strong>если вы изменили один символ в файле, Git создаёт новый blob</strong>, но все остальные файлы остаются без изменений. Это делает хранилище компактным и позволяет мгновенно переключаться между версиями.</p>
<h2 class="wp-block-heading">Ветка — это просто файл с 40 символами</h2>
<p class="wp-block-paragraph">Вот где начинается магия. Когда вы создаёте ветку <code>feature</code>, Git не копирует ни одного файла проекта. Вместо этого он создаёт <strong>маленький текстовый файл</strong> в папке <code>.git/refs/heads/feature</code>, содержащий всего лишь SHA-1 хеш последнего коммита.</p>
<p class="wp-block-paragraph">Переключение ветки (<code>git checkout</code> или <code>git switch</code>) означает: «возьми хеш из файла ветки, найди соответствующий tree-объект и восстанови файлы в рабочей директории». Это происходит почти мгновенно, потому что Git работает не с дельтами, а со снимками (snapshots).</p>
<h2 class="wp-block-heading">Почему merge быстрый, а rebase — хирургия</h2>
<p class="wp-block-paragraph"><strong>Fast-forward merge</strong> — это вообще не «слияние» в привычном смысле. Git просто перемещает указатель ветки на новый коммит. Никаких новых объектов не создаётся.</p>
<p class="wp-block-paragraph"><strong>Трёхсторонний merge</strong> находит общего предка (base), сравнивает две ветки с ним и объединяет изменения. Если в одном и том же месте файла меняли обе ветки — возникает конфликт.</p>
<p class="wp-block-paragraph"><strong>Rebase</strong> же переписывает историю. Git берёт ваши коммиты, вычисляет diff по отношению к base, «откладывает» их во временную область, перематывает ветку на новое основание и по очереди применяет патчи. Каждый применённый патч становится <em>новым</em> коммитом с новым хешем. Именно поэтому rebase опасен для публичных веток — вы меняете историю, с которой уже могли работать коллеги.</p>
<h2 class="wp-block-heading">HEAD и detached HEAD: что это значит</h2>
<p class="wp-block-paragraph">Файл <code>.git/HEAD</code> обычно содержит что-то вроде <code>ref: refs/heads/main</code>. Это указатель на текущую ветку. Но если вы перейдёте на конкретный коммит по хешу (<code>git checkout a1b2c3d</code>), HEAD будет содержать сам хеш. Такое состояние называется <strong>detached HEAD</strong> — вы «плаваете» в истории без привязки к ветке. Коммиты, сделанные в этом состоянии, легко потерять при переключении, если не создать ветку.</p>
<h2 class="wp-block-heading">Практические выводы</h2>
<ul class="wp-block-list">
<li>Ветки дёшевы — создавайте их для каждой задачи без страха.</li>
<li>Не бойтесь конфликтов: Git просто просит вас решить, чьи изменения оставить в пересекающихся участках.</li>
<li>Используйте rebase для локальных веток, чтобы история была линейной и читаемой.</li>
<li>Никогда не делайте rebase веток, которые уже запушены и используются командой.</li>
</ul>
<p class="wp-block-paragraph">Понимание внутреннего устройства Git превращает его из «магической чёрной коробки» в предсказуемый и мощный инструмент. А следующий раз, когда кто-то скажет «Git сломался», вы будете знать: Git не ломается — просто нужно понять, на какой коммит указывает HEAD.</p>
<p class="wp-block-paragraph"><em>А вы когда-нибудь теряли коммиты в detached HEAD? Расскажите в комментариях, как спасали ситуацию.</em></p>
Большинство разработчиков используют Git каждый день, но мало кто задумывается, что происходит внутри, когда мы пишем git checkout -b feature или git merge main. С первого взгляда кажется, что ветки — это просто копии папок с кодом. На деле Git устроен гораздо элегантнее, и понимание его внутреннего механизма делает вас увереннее при работе с конфликтами, rebase и историей.
Объекты Git: всё — хеш
В основе Git лежит простая идея: все данные хранятся как объекты, адресуемые по SHA-1 хешу. Когда вы делаете коммит, Git создаёт не один, а три типа объектов:
- Blob — сжатое содержимое файла. Имя файла не хранится здесь, только его данные.
- Tree — аналог директории. Содержит список имён файлов, прав доступа и ссылки на blob-объекты.
- Commit — точка во времени. Содержит автора, дату, сообщение, ссылку на tree и ссылку на родительский коммит (или несколько, в случае merge).
Ключевой момент: если вы изменили один символ в файле, Git создаёт новый blob, но все остальные файлы остаются без изменений. Это делает хранилище компактным и позволяет мгновенно переключаться между версиями.
Ветка — это просто файл с 40 символами
Вот где начинается магия. Когда вы создаёте ветку feature, Git не копирует ни одного файла проекта. Вместо этого он создаёт маленький текстовый файл в папке .git/refs/heads/feature, содержащий всего лишь SHA-1 хеш последнего коммита.
Переключение ветки (git checkout или git switch) означает: «возьми хеш из файла ветки, найди соответствующий tree-объект и восстанови файлы в рабочей директории». Это происходит почти мгновенно, потому что Git работает не с дельтами, а со снимками (snapshots).
Почему merge быстрый, а rebase — хирургия
Fast-forward merge — это вообще не «слияние» в привычном смысле. Git просто перемещает указатель ветки на новый коммит. Никаких новых объектов не создаётся.
Трёхсторонний merge находит общего предка (base), сравнивает две ветки с ним и объединяет изменения. Если в одном и том же месте файла меняли обе ветки — возникает конфликт.
Rebase же переписывает историю. Git берёт ваши коммиты, вычисляет diff по отношению к base, «откладывает» их во временную область, перематывает ветку на новое основание и по очереди применяет патчи. Каждый применённый патч становится новым коммитом с новым хешем. Именно поэтому rebase опасен для публичных веток — вы меняете историю, с которой уже могли работать коллеги.
HEAD и detached HEAD: что это значит
Файл .git/HEAD обычно содержит что-то вроде ref: refs/heads/main. Это указатель на текущую ветку. Но если вы перейдёте на конкретный коммит по хешу (git checkout a1b2c3d), HEAD будет содержать сам хеш. Такое состояние называется detached HEAD — вы «плаваете» в истории без привязки к ветке. Коммиты, сделанные в этом состоянии, легко потерять при переключении, если не создать ветку.
Практические выводы
- Ветки дёшевы — создавайте их для каждой задачи без страха.
- Не бойтесь конфликтов: Git просто просит вас решить, чьи изменения оставить в пересекающихся участках.
- Используйте rebase для локальных веток, чтобы история была линейной и читаемой.
- Никогда не делайте rebase веток, которые уже запушены и используются командой.
Понимание внутреннего устройства Git превращает его из «магической чёрной коробки» в предсказуемый и мощный инструмент. А следующий раз, когда кто-то скажет «Git сломался», вы будете знать: Git не ломается — просто нужно понять, на какой коммит указывает HEAD.
А вы когда-нибудь теряли коммиты в detached HEAD? Расскажите в комментариях, как спасали ситуацию.