Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 42 additions & 0 deletions 01-git/Chumakov_Grigorobskiy_Usachev/solution.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
## Для чего Git нужна история?

История для гита является ключевым функционалом, который нужен не просто для того, чтобы хранить архив изменений и в случае чего откатиться на предыдущее, а реализовывать структуру, на которой держится большая часть ключевого функционала. Если рассматривать Git как децентрализованную систему, где каждый разработчик - потенциальный узел в этой системе, то история необходима, чтобы каждый узел сам нес знания о прошлом и мог синхронизировать данные без единой обязательной ноды.
1. **История необходима для автономности каждого узла (разработчика).** Если бы Git хранил только текущее состояние проекта, то каждая копия репозитория была бы зависима от внешнего центра: чтобы понять, что было раньше, пришлось бы постоянно обращаться куда-то ещё. Но в Git почти вся работа делается локально именно потому, что вся история уже лежит на диске у разработчика. Благодаря этому человек может работать даже без сети, т.е без необходимости обращаться в общий сервер, который бы являлся единственным источником данных. С точки зрения децентрализованной системы это крайне важно, потому что узел должен быть способен существовать и функционировать самостоятельно, а не только как клиент внешнего центра.
2. **История нужна для отказоустойчивости, репликации и восстановления.** Если бы Git не хранил историю, то потеря основного сервера с репозиторием являлась бы фатальной, т.к репозиторий является единственным и главным источником знаний. Но поскольку клон содержит полную историю, он фактически является резервной копией (репликой) репозитория. Поэтому история в Git - это не только средство отката, но и механизм обеспечения надежности. Благодаря этому повышается отказоустойчивость, т.к чем больше узлов хранят историю изменений, тем меньше завязка на единую точку отказа. Кроме этого надежность работы обеспечивается возможностью возвращать удалённые данные, восстанавливать потерянные коммиты и находить момент, когда проект перестал работать правильно. Все это обспечивается за счет истории Git.
3. **История позволяет Git обходиться без единственного обязательного центра координации.** Для распределённой системы важно, чтобы управление и синхронизация не были жёстко привязаны к одной центральной ноде. В Git это обеспечивается именно за счёт истории, т.к каждый полный клон содержит не только текущее состояние файлов, но и всю структуру развития проекта. Благодаря этому любой узел может не просто получить изменения, но и сам стать источником этих изменений для других участников. То есть Git не требует единственного источника правды - такой репозиторий может существовать только как организационная договорённость команды. История в данном случае выступает как основа Git в качестве децентрализованной системы: координация строится не вокруг одной обязательной ноды, а вокруг общего графа изменений, который может храниться и распространяться через разные узлы.
4. **История делает возможной синхронизацию и согласование независимого развития кодовой базы.** В распределённой среде мало просто иметь несколько копий файлов и знать какие версии сейчас у какого узла. Нужно понимать, как эти копии соотносятся между собой, где их развитие совпадало, в какой момент оно разошлось и каким образом изменения можно объединить. Git решает эту проблему именно через историю: он не просто синхронизирует текущие состояния, а связанные между собой коммиты. Благодаря этому система может определить общее прошлое двух ветвей, сравнить их развитие и корректно выполнить merge. То есть история в Git нужна не только для хранения прошлого, но и для **согласования разных версий настоящего**, когда независимые узлы некоторое время развивались отдельно.
5. **История лежит в основе параллельной разработки.** Этот пункт вытекает из предыдущего. Обычная линейная модель работы плохо подходит для реальной командной разработки, где несколько человек одновременно делают разные функционал. Git решает эту проблему с помощью веток, но ветки работают именно потому, что у системы есть история. Ветка — это не отдельная папка с файлами, а отдельная линия развития внутри общего графа изменений. Это особенно важно в распределённой системе, где разные узлы (то есть разработчики) могут долго работать независимо друг от друга. История позволяет каждому разработчику свободно развивать свою линию работы локально, а потом встраивать результат в общее развитие проекта. Без истории Git не смог бы поддерживать такую гибкость и параллельность.
6. **История нужна для версионирования релизов и воспроизводимости состояний проекта**. В Git важно не только хранить факт изменений, но и уметь в любой момент точно восстановить конкретное состояние проекта, которое существовало в определённый момент времени. Это позволяет обепечивать высокий уровень надежности, о чем уже говорилось в п.2. Кроме восстановления это еще необходимо для релизов, тегов и поддержки старых версий. Благодаря истории можно однозначно сказать, какая именно версия кода была выложена, протестирована или передана другому узлу. Для Git как распределенной системы это необходимо, потому что участники должны синхронизировать не просто “примерно одинаковый код”, а строго определённые состояния проекта.
7. **История создаёт прозрачность, проверяемость и доверие в совместной работе**. В распределённой системе особенно важно понимать не только то, **что изменилось**, но и **откуда взялось это изменение**. История в Git даёт проекту прослеживаемость: можно увидеть, кто сделал правку, когда она появилась, с каким коммитом она связана и как она встроена в общую структуру разработки. Это важно и технически, и организационно. С технической стороны история помогает находить источник ошибок, анализировать регрессии и проверять происхождение строк кода. С организационной стороны она создаёт прозрачность и доверие между участниками, потому что любое изменение имеет автора, контекст и место в общей истории. Для распределённой системы это особенно важно: координация строится не на слепом доверии одному центру, а на проверяемости происхождения изменений.

Таким образом, гиту нужна история не просто для того, чтобы помнить прошлое проекта. В Git история является частью самого механизма работы его как распределенной системы. Она делает каждый клон автономным, обеспечивает отказоустойчивость и восстановление, позволяет синхронизировать и согласовывать независимо развивавшиеся копии, поддерживает параллельную разработку и создаёт прозрачность происхождения изменений. Поэтому история в Git - это не просто журнал старых версий, а фундамент, на котором вообще строится вся система.

## Как история технически используется в Git?

История в Git хранится не как абстрактный журнал изменений, а поверх content-addressed object store, то есть хранилища объектов, где идентификатор объекта определяется его содержимым. В основе Git лежит key-value model: в репозиторий помещаются данные, а Git вычисляет для них хеш и использует его как ключ для последующего обращения к этим данным. Поэтому Git хранит набор объектов, адресация по которым работает исходя из содержимого самого объекта.

Есть 3 основных типа объекта в Git:
* blob - это содержимое конкретного файла, без информации о том, в какой папке он лежит и как он называется
* tree — это объект каталога: он хранит список имён файлов и подпапок и для каждого элемента указывает, на какой объект тот ссылается
* commit - указатель на корневой tree плюс родители, автор, время и сообщение коммита

Коммит в Git ссылается не только на родительский коммит, но и на snapshot состояния проекта через tree-объект, то есть коммит не хранит внутри себя все файлы напрямую, а указывает на объект, который описывает структуру проекта на уровне каталогов.

Поскольку все эти объекты являются объектами, адресуемыми по содержимому (то есть их идентификатор вычисляется на основе данных), изменение даже небольшой части данных приводит к появлению нового объекта с новым идентификатором. Из-за этого история в Git по своей природе неизменяема на уровне объектов: старый commit не редактируется на месте, а при изменениях создаются новые объекты и новые связи между ними. Именно поэтому Git может надёжно строить граф истории, сравнивать состояния проекта и точно определять, какие данные уже существуют, а какие являются новыми.

### Как это используется на практике

1. **Через историю работают ветки.**
Ветка в Git - это по сути указатель на определённый commit. Когда создаётся новый commit, указатель ветки просто передвигается дальше. Поэтому создание веток в Git быстрое и лёгкое, т.к не нужно копировать весь проект, достаточно создать новый указатель внутри уже существующей истории.

2. **Через историю работает merge.**
Когда Git объединяет ветки, он смотрит не просто на два набора файлов, а на их положение в истории. Git решает, можно ли просто передвинуть указатель ветки вперёд или нужен полноценный merge, он анализирует отношение ребенок-родитель между коммитами. Если новый commit является потомком старого, возможен `fast-forward`, если нет, Git должен создать merge-коммит, который свяжет две линии развития и будет иметь несколько родителей.

3. **История используется при push и pull.**
При `push` Git смотрит на **историю коммитов** локальной и удалённой ветки и проверяет можно ли безопасно передвинуть удалённый указатель ветки. По правилам для обычных веток разрешен только `fast-forward`, т.е удаленный коммит должен быть предком того, который мы отправляем. В ином случае обычный `push` отклоняется и нужен `merge`. При `pull` Git тоже опирается на историю: сначала он скачивает все недостающие объекты по типу коммитов, тэгов, чтобы дополнить локальную историю (`fetch`). Далее Git встраивает полученную историю в текущую ветку (то есть по аналогии с п.2).

4. **История лежит в основе отладки и диагностики.**
Например, команда `git bisect` использует бинарный поиск по истории коммитов, чтобы найти, после какого именно коммита в проекте появилась ошибка. А `git blame` позволяет понять, кто последним изменил конкретную строку кода.

5. **История нужна для тэгов и релизов**
Релиз в Git технически фиксируется не как рандомная версия, а как ссылка на конкретную точку истории, обычно на конкретный commit. Из этого следует, что **релиз можно точно воспроизвести**. Поскольку тег указывает на конкретный commit, Git может в любой момент восстановить именно то состояние проекта, которое было у релиза. Кроме этого через историю можно сравнивать релизы между собой. Например, когда есть тэг `v1.0` и `v1.1`, Git может пройти по графу коммитов и посмотреть, какие коммиты достижимы из одного тега и недостижимы из другого, благодаря этому можно технически сравнить, что изменилось между двумя релизами.