Для точного понимания механизма ветвлений, необходимо вернуться назад и изучить то, как Git хранит данные.
Как вы можете помнить из раздела Снимки, а не различия, Git не хранит данные в виде последовательности изменений, он использует набор снимков (snapshot).
Когда вы фиксируете изменения, Git сохраняет коммит в виде объекта, который содержит указатель на снимок подготовленных данных. Этот объект так же содержит имя автора и email, сообщение и указатель на коммит или коммиты, непосредственно предшествующие данному (его родителей). У первоначального коммита родитель отсутствует, у обычного коммита есть один родитель, и есть несколько родителей для результатов слияния веток.
Представьте себе каталог, который содержит три файла. Вы подготавливаете их все вместе, а затем сохраняете в виде одного коммита. В процессе подготовки вычисляется контрольная сумма каждого файла (SHA-1 как вы узнали из Целостность Git), хранящая версию файла в репозитории Git (Git ссылается на них). Затем эти контрольные суммы добавляются в область подготовленных файлов.
Когда вы фиксируете изменения, Git вычисляет контрольные суммы каждого подкаталога (в вашем случае это только основной каталог проекта) и сохраняет это в репозитории как объект дерева каталогов. Затем Git создает объект коммита с метаданными и указателем на основное дерево проекта для того, чтобы можно было воссоздать этот снимок в случае необходимости.
Если вы сделаете изменения и создадите еще один коммит (Snapshot B), то он будет содержать указатель на предыдущий коммит.
Ветка в Git это простой перемещаемый указатель на один из этих коммитов. Имя основной ветки в Git по умолчанию — main. На следующем рисунке ветка main указывает на коммит Изменил сообщения.
Как только вы начнете создавать коммиты, ветка main будет всегда указывать на последний коммит. Каждый раз при создании коммита указатель ветки main будет передвигаться на следующий коммит автоматически.
Ветка main в Git — это не какая-то особенная ветка. Она точно такая же, как и все остальные ветки. Она существует почти во всех репозиториях только лишь потому, что ее создает Git, а большинство людей не меняют ее название.
Что же на самом деле происходит, когда вы создаете ветку? Всего лишь создается новый указатель для дальнейшего перемещения.
Допустим, вы хотите создать новую ветку для реализации новой функциональности — issue-20. Для этого в строке репозитория или в строке состояния нажмите на текущую ветку, а затем в палитре команд введите имя новой ветки и нажмите (Создать новую ветвь... / Create new branch...).
В результате создается новый указатель на текущий коммит.
Чтобы увидеть обе ветки, переключите фильтр веток в значение Все.
На схеме это будет выглядеть следующим образом.
Как Git определяет, в какой ветке вы находитесь? Он хранит специальный указатель HEAD. Имейте в виду, что в Git концепция HEAD значительно отличается от других систем контроля версий, которые вы могли использовать раньше (Subversion или CVS). В Git это указатель на локальную ветку, в которой вы находитесь.
Visual Studio Code обозначает HEAD значком Текущий элемент журнала / Current History Item.
В вашем случае вы находитесь в ветке issue-20, на нее указывает HEAD. Вы можете легко это увидеть в командной строке при помощи команды git log, которая покажет вам куда указывают указатели веток. В этом вам поможет параметр --decorate.
> git log --oneline --decorate
eb837ce (HEAD -> issue-20, main) Изменил сообщения
113e571 добавил .gitignore
4c30ca5 Первый коммит
Когда вы нажимаете (Создать новую ветвь... / Create new branch...) Visual Studio Code не только создает новую ветку, но и сразу переключает вас на нее. В результате вы можете сразу начать работу над новой задачей.
Сейчас вы находитесь в ветке issue-20.
На схеме это выглядит следующим образом.
Переключитесь на ветку main. Для этого в строке репозитория или в строке состояния нажмите на текущую ветку, а затем в палитре команд выберите ветку main.
В результате указатель HEAD переместится на ветку main.На схеме это будет выглядеть следующим образом.
Какой в этом смысл? Сделайте то, ради чего вы создавали новую ветку.
Снова переключитесь на ветку issue-20, измените скрипт и зафиксируйте изменения. Как будто вы начали реализовывать задачу issue-20.
Это интересно, потому что указатель на вашу ветку issue-20 переместился вперед, а main все еще указывает на тот коммит, где вы были в момент переключения веток.
Переключитесь назад на ветку main.
На схеме это будет выглядеть следующим образом.
Произошло две вещи. Указатель HEAD переместился назад на ветку main, а файлы в рабочем каталоге вернулись в то состояние, которое было сохранено в снимке, на который указывает ветка. Это также означает, что все изменения, вносимые с этого момента, будут отнесены к старой версии проекта. Другими словами, откатилась вся работа, выполненная в ветке issue-20, а вы можете продолжать в другом направлении.
Предчувствуя это, Visual Studio Code выделил ветку issue-20 другим цветом.
Переключение веток меняет файлы в рабочем каталоге. Важно запомнить, что когда вы переключаете ветки в Git, файлы в рабочем каталоге меняются. Если вы переключаетесь на старую ветку, то рабочий каталог будет выглядеть так же, как выглядел на момент последнего коммита в ту ветку. Если Git по каким-то причинам не может этого сделать, он не позволит вам переключиться.
Теперь сделайте еще одно изменение и зафиксируйте его.
Теперь история вашего проекта разделилась. Вы создали ветку, переключились в нее, поработали, а затем вернулись в основную ветку и поработали в ней.
Эти изменения изолированы друг от друга: вы можете свободно переключаться туда и обратно, а когда будете готовы, можете слить их вместе.
Ветка в Git — это простой файл, содержащий 40 символов контрольной суммы SHA-1 коммита, на который она указывает; поэтому операции с ветками являются дешевыми с точки зрения потребления ресурсов или времени. Создание новой ветки в Git происходит так же быстро и просто как запись 41 байта в файл (40 знаков и перевод строки).
Это принципиально отличает процесс ветвления в Git от более старых систем контроля версий, где все файлы проекта копируются в другой подкаталог. В зависимости от размера проекта, операции ветвления в таких системах могут занимать секунды или даже минуты, когда в Git эти операции мгновенны. Поскольку при фиксации изменений Git сохраняет указатель на родительский коммит, то поиск подходящей базы для слияния веток делается автоматически и, в большинстве случаев, очень прост. Эти возможности побуждают разработчиков чаще создавать и использовать ветки.
Посмотрите, почему и вам имеет смысл делать так же.
По материалам книги Pro Git (авторы Scott Chacon и Ben Straub, издательство Apress). Книга распространяется по лицензии Creative Commons Attribution Non Commercial Share Alike 3.0 license.