Git (практика, часть 2)
LINEЭто вторая статья практической части про git (первая здесь), в который мы разберем вопросы связанные с удаленным репозиторием и сервисом github в формате: Как сделать что то -> способы это сделать.
Добавить удаленный репозиторий
В первой части мы работали с репозиторием, который находится у вас локально. Но репозиторий может быть размещен удаленно, чтобы к нему имели доступ несколько человек. Для размещения git репозиториев есть несколько веб сервисов. Один из самых известных и популярных - github.
Все что нужно сделать - зарегистрироваться здесь и создать новый репозиторий. Локальный репозиторий особо не отличается от удаленного, кроме того что он удаленный и нужен он в первую очередь для того, чтобы
- Не потерять исходный код своего проекта в случае, если что то случится с жестким диском (или другим источником хранения проекта)
- Дать возможность работать с проектом другим людям
После создания удаленного репозитория (что делается в 2 клика, создать новый репозиторий, дать название и заполнить пару полей) изменения, которые есть в локальном репозитории нужно передать на удаленный. Для этого сначала git нужно сказать, что есть удаленный репозиторий и где он расположен. Для этого используется команда git remote add origin <url>.
Разберем команду подробнее. Git remote означает, что мы будем работать с удаленным репозиторием. Add - мы добавляем новый удаленный репозиторий. Origin - название удаленного репозитория. Название может быть любым словом, origin просто используется как общепринятое. Url нужен, чтобы git понял куда именно отправлять все файлы из локального репозитория. Url будет выглядеть примерно так:
https://github.com/<имя аккаунта>/<название репозитория>.git
Этот url можно скопировать нажав на зеленую кнопку code на главной странице репозитория и нажав на кнопку справа от url адреса

посмотреть удаленные репозитории
Теперь посмотреть удаленные репозитории можно с помощью git remote
Более подробный вывод с помощью git remote --verbose (или -v)

Отправить данные на удаленный репозиторий
Для того, чтобы отправить свои коммиты на удаленный репозиторий, нужно вызвать команду git push origin master. И здесь опять остановимся поподробнее.
В тот момент, когда мы создаем локальный репозиторий, у нас есть история коммитов и ветки, указывающие на определенные коммиты. Пусть локальный репозиторий состоит из 3-х коммитов и одной ветки master

В некоторый момент мы определяем, что хотим отправить изменения в удаленный репозиторий. Для этого мы добавляем удаленный репозиторий (с помощью git remote add). После этого у нас будет репозиторий, но он пустой, так как мы еще не скопировали изменения из локального репозитория.
Вызвав git push мы можем отправить изменения, которые есть в локальном репозитории. В итоге мы получим два репозитория, которые выглядят одинаково.

Удаленных репозиториев может быть несколько, значит git нужно сказать, в какой именно мы хотим сохранить локальные изменения.
Но кроме того, нужно указать еще и ветку. И вот почему.
Скорее всего, начиная работать над некоторой задачей, мы не знаем как ее решить. Мы можем попробовать несколько вариантов, сделать набросок и вообще написать код, который нужен только для того, чтобы протестировать идею реализации в коде. Возможно такой код будет далек от идеала, но он и не должен быть хорошим - это своего рода черновик.
Представим, что находясь на third commit мы создаем ветку temp и пытаемся решить задачу. Мы особо не задумываемся о стиле, clean code, содержательном сообщении коммита и всем прочем. Не задумываемся не потому, что это неважно. Это неважно на текущий момент. Мы пробуем разные варианты, комментируем участки кода а не удаляем их и просто пытаемся решить задачу. Можно делать коммиты с любыми сообщениями и любом удобном виде. В конечном итоге у нас получится что то вроде этого: Ветка temp с плохими сообщениями коммитов, плохим кодом и кучей беспорядка который мы оставили при решении задачи.

Когда мы поймем, что задача решена - нужно провести рефакторинг, посмотреть что можно улучшить и удалить лишнее. Сейчас все должно выглядеть как должно (что бы это ни значило). Таким образом вся ветка temp была черновиком - мы попробовали различные варианты, прикинули решения и выбрали подходящий вариант. Теперь мы знаем как должно выглядеть решение задачи и у нас есть хороший код для этого.
Теперь мы можем создать еще одну ветку и перенести в нее изменения из temp. Это можно сделать разными способами, и о том как изменения из одной ветки (в том числе частичные) перенести в другую мы еще поговорим.
Основная суть - ветка temp никогда не должна попадать в удаленный репозиторий. О ней не должен знать никто, кроме вас. Это ваша песочница. Но что если вы хотите отправить изменения, которые есть в другой ветка а temp еще в процессе изменения. Если бы git отправлял весь репозиторий, он отправил бы и temp.
Таким образом становится понятным, что возможность указывать git ветки, которые нужно отправить дает большую гибкость и больше возможностей.
Итоговая команда для отправления данных: git push <удаленный репозиторий> <ветка>
Настроить отправку изменений на удаленный репозиторий
Предположим что вы ведете проект и у вас есть удаленный репозиторий. Что будет, если любой человек получит возможность вносить изменения в этот удаленный репозиторий? Очевидно, ничего хорошего, т.к. любой может внести любые изменения.
Чтобы получить возможность отправлять данные в удаленный репозиторий, вы должны пройти аутентификацию, т.к. подтвердить что вы - это вы и имеете право но подобное действие. Стандартным средством для аутентификации является пароль. Вы вводите логин, по которому система узнает кто вы (идентификация), вводите пароль подтверждая что вы это действительно вы (аутентификация) и сервис предоставляем вам различные права - чтение сообщений, создание заметок или запись в репозиторий (авторизация).
Есть 2 основных способа работы с git - personal access token (PAT) и Secure Shell (SSH). Авторизация по паролю больше не работает и ее в некотором смысле заменил PAT.
Personal access token (PAT)
Чтобы пройти аутентификацию по PAT, нужно зайти на страницу github, настройки (settings) -> настройки разработчика (developer settings) -> персональный токен доступа (personal access token)
После чего нажать кнопку сгенерировать новый токен, задать ему имя, срок действия и разрешения выполнять те или иные действия.
Важно! После создания токена, скопируйте его. Вы больше не увидите его, после перезагрузки страницы (Make sure to copy your personal access token now. You won’t be able to see it again!)
PAT в некотором смысле такой же пароль, но лучше. Во-первых, он состоит из 40 символов (а пароли часто бывают qwerty, pasw1234 или похожее). Чем длиннее пароль, тем сложнее его подобрать перебором. Во-вторых, вы можете дать токен своему знакомому и дать ему строго определенные права на строго определенное время. В-третьих, вы можете в любой момент отозвать токен.
Теперь, используя доступ по https, вы можете ввести свой логин и вместо пароля этот PAT после команды git push. Если PAT существует и валидный, (github проверит введенный PAT на соответствие с созданными для репозитория PAT) вы пройдете аутентификацию и сможете отправить изменения на удаленный репозиторий.
Secure Shell (SSH)
Второй популярный способ аутентификации - через ssh. Об этом протоколе можно многое сказать, поэтому расскажу вкратце.
Он основан на асимметричном шифровании. Вы генерируете пару ключей - открытый и закрытый. Открытый ключ нужен для того, чтобы любой человек мог зашифровать им свое сообщение и отправить вам. Закрытый ключ нужен для того, чтобы с его помощью можно было расшифровать сообщение, зашифрованное открытым ключом.
Открытый ключ можно открыто выкладывать и распространять где угодно. Закрытый ключ должен быть только у владельца. Это важно!
Особенность ассиметричного шифрования - то, что для шифрования и дешифрования нужны разные ключи. Т.е. с помощью закрытого ключа можно только расшифровать, а с помощью открытого - только зашифровать. Если нужна связь в двух направлениях, нужно создать 2 пары ключей.
Как это работает с github. Вы генерируете пару открытый - закрытый ключ. После чего сохраняете открытый ключ на github. Зная открытый ключ, github (или любой другой сервис) генерирует проверочную фразу (фраза представляет из себя просто число), которую шифрует публичным ключом. После чего отсылает эту зашифрованную фразу (challenge message) отправителю. Так как ее можно расшифровать только зная приватный ключ, а приватный ключ должен знать только один пользователь (вы сами), если фраза будет расшифрована правильно, то это доказывает что перед нами тот самый пользователь. Клиент отправляет эту фразу (на самом деле не саму фразу, а хэш от этой фразы и еще одного ключа для сессии, но для простоты пусть будет просто фраза). Если фраза, которую отправлял сервер (в зашифрованном виде) и которую получил одинаковы (на самом деле опять же все немного сложнее, сравниваются опять же хэш от ключа сессии и фразы, но для простоты пусть будет так), то аутентификация пройдена. Основная идея - расшифровать фразу может только владелец приватного ключа.
Для начала можете проверить, если ли уже у вас ключи. Для этого введите в bash ls ~/.ssh -al. Именно там хранятся все, что связано с ssh. Если эта папка есть и если там есть файлы вроде id_... и id_....pub, значит у вас уже есть ключи.
Чтобы сгенерировать новые ключи введите команду ssh-keygen -t ed25519 -C "your_email@example.com" (Подробнее здесь). Ssh-keygen - программа для генерации ключей. Флаг -t (type) говорит о типе алгоритма(ed25519), -С (comment) - комментарий. Затем нажмите enter для выбора папки сохранения ключей, введите пароль и подтвердите его или просто нажмите 2 раза enter. Если за компьютером работаете только вы, можете пропустить этот пароль. Дальше вы получите следующий вывод. Key fingerprint - или отпечаток - это что то вроде хэш вашего публичного ключа. Ниже представлен он же, но в виде своеобразного изображения(зеленым цветом). Такой отпечаток просто визуально выглядит нагляднее, чем последовательность символов.

Теперь перейдите на github, в раздел settings (настройки) -> SSH and GPG keys (SSH и GPG ключи). Нажмите кнопку new ssh key (добавить новый ssh ключ), введите любое название ключа и полностью скопируйте публичный ключ из папки ~/.shh. Публичный ключ заканчивается на pub, это важно. Скопировать нужно все, что есть в этом файле и вставить в соответствующее поле на github. Сохраните нажатием Add ssh key.
Теперь добавьте к своему локальному репозиторию удаленный, но обязательно тот, который с ssh (а не по https). Url должен выглядеть примерно так git@github.com:<username>/<repository>.git.
Теперь вы можете отправить изменения по shh. Сделайте коммит и введите git push origin master. После этого вы скорее всего увидите что то вроде этого

Вопрос: как вы можете быть уверены в том, что вы посылаете изменения действительно на сервер github, и эти сообщения никто не перехватывает. Вот пример для демонстрации.
Вы посылаете сообщение на github. Отсылаете свой публичный ключ. Кто-то сумел перехватить ваше сообщение и отправить на github такое же сообщение, но уже со своим публичным ключом (И сохранил ваш публичный ключ). Github отправляем зашифрованную фразу этому злоумышленнику, считая его вами. Он расшифровывает сообщение, зашифровывает его вашим публичным ключом и отправляем вам для расшифровки. Вы расшифровываете, отправляете злоумышленнику сообщение, он отправляет свое сообщение на github и теперь вы думаете, что общаетесь с github, github думает что общается с вами но на самом деле вы общаетесь через посредника, человека посередине.
Когда вы получаете сообщение, вам говорят следующее
the authenticity of host 'github.com (140.82.121.3)' can't be established, т.е. подлинность хоста 'github.com (140.82.121.3)' не может быть установлена. После чего идет некоторый fingerprint. Как проверить подлинность? В двух словах - сравнить sha256, который указан здесь с sha официального сайта (он здесь). Если вам пришло сообщение именно от github с его адреса - fingerpring будет именно таким (nThbg6kXUpJWGl7E1IGOCspRomTxdCARLviKw6E5SY8). Так как человек - посредник должен иметь иной, отличный адрес для отправки/получения данных, хэш данных полученного от такого человека и хэш данных полученных от github будет различным. Так вы можете быть уверены, что общаетесь именно с github напрямую. Это нужно сделать только один раз, после этого данный адрес будет занесет в файл known_hosts и подобное подтверждение не потребуется. Введите yes, если fingerprint совпадает.
На этом все, теперь вы можете отправлять изменения без всяких проблем и подтверждений, ввода пароля и прочего.
Перейти на указанный коммит
Предположим у нас есть история изменений в ветке мастер, состоящая из 3-х коммитов: first -> second -> third. Не так важно, какие изменения в них были (допустим мы добавили по одному файлу с одной строчкой в каждом).
История git выглядит так

Мы можем перейти на конкретный коммит, т.е. восстановить проект к виду, который сохранен в данном коммите. Для этого нужно указать hash коммита (или ветку, или тэг). Например, чтобы перейти на коммит first commit нужно выполнить checkout <hash first commit>
Git выдаст на это следующее

Git отвечает на переход большим предупреждением, и говорит следующее:
Вы находитесь в состоянии "отсоединенной головы" (detached HEAD). Вы можете оглядеться, поэкспериментировать, сделать коммит отсюда, и можете отменить любые коммиты сделанные в данном состоянии просто переключившись обратно на ветку.
Для создания новой ветки и сохранения сделанных коммитов, выполните git switch -c <new-branch-name>.
Отменить эту операцию можно с помощью git switch - .
Выключить это сообщение подсказку можно настроив переменную advice.detachedHead в false.
Чтобы понять, что хочет сказать нам git, выполним коммит из данного места - добавим случайный файл, заполним его случайным содержимым и выполним коммит (файл "temp.txt", содержимое "temp message")

Теперь по порядку, что происходит. В самом начале мы имели репозиторий вида

Когда мы сделали checkout <hash commit>, мы изменили указатель HEAD на первый коммит. Но указатель ветки master остался на своем месте. Теперь репозиторий выглядит так

Остальные коммиты сохранены, но мы их не видим т.к. попросили восстановить прошлую версию репозитория. Теперь мы добавили новый коммит, что привело к следующему

HEAD теперь указывает на новый коммит temp. Теперь мы можем переключиться обратно на мастер с помощью git checkout master. Но вопрос - как вернуться обратно на коммит "add temp"? Это можно сделать только одним способом - зная hash этого комита.
Заметьте, что вернуться на коммит third commit легко - нужно просто помнить название ветки. Даже если вы не помните название ветки, с помощью git branch вы можете увидеть все ветки, которые есть в проекте.
С другой стороны, запомнить hash, т.е. неупорядоченный набор букв и цифр довольно сложно. Именно поэтому git предпочитает, чтобы указатель HEAD указывал на ветку, а ветка указывала на конкретный коммит (как на первом изображении).
Вот что git пытался сказать, когда мы выполнили команду git checkout <commit hash>. Detached head означает, что HEAD указывает не на ветку, а на коммит. Если мы делаем коммиты с этого места, то они сохранятся но для возвращения к ним нужно помнить hash.
В частности в такой ситуации полезна команда git reflog - это история всех изменения ссылок при работы с git.

С ее помощью мы сможем отыскать hash коммита, если мы его забыли.
Git подсказывает, что нужно сделать, чтобы не потерять коммиты: git switch --create (или -c) <new-branch-name>. Т.е. создать новую ветку, которая будет указывать на коммит, на котором мы сейчас находимся.

Заметьте, что после команды создания ветки git log выводит последний сделанный коммит и в скобках (HEAD -> temp_branch), т.е. теперь head указывает на ветку, которая указывает на коммит. Все изменения сохранятся и будут доступны по этой ветке, поэтому мы их не потеряем и не забудем.
Перейти на указанную ветку
Для того, чтобы перейти на ветку использовалась команда git checkout <branch>. Но checkout работает и с ветками, и с файлами что вносит некоторую неопределенность. Поэтому впоследствии были добавлена команда switch для веток и restore для файлов. Сейчас команды git checkout <branch name> и git switch <branch name> работают одинаково.
Создать новую ветку
Для создания ветки служит команда git branch <branch name>. Есть также сокращения для создания и переключения на ветку. Это можно сделать в 2 этапа: создать ветку (git branch <branch name>) и перейти на нее с помощью git checkout или git switch. Сокращенные команды выглядят так git checkout -b <branch name> или git switch -c <branch name>, т.е. с флагом -b или -c для checkout и switch соответственно (сокращения branch и create).
Удалить ветку
Для удаления коммита служит команда git branch -d <branch name>
Предположим у нас есть ветка master, и в некоторый момент вы хотите сделать новую ветку feature_one. Вы делаете несколько коммитов и сейчас ситуация выгляди так

Если теперь вы удалите ветку feature_one, то вы потеряете важный указатель на коммит. Поэтому, при попытке удаления git скажет следующее

Т.е. git предупреждает о последствиях и говорит, если ты хочешь действительно удалить эту ветку, введи команду git branch -D feature_one.
После того, как вы закончили делать feature_one, вы можете слить изменения с некоторой другой веткой (в нашем случае с master). После этого ситуация будет следующей

Теперь все изменения из feature_one зафиксированы в master, или ветка feature_one "смерджена". Об этом говорил git чуть выше в фразе error: The branch 'feature_one' is not fully merged. Теперь мы можем спокойно удалить ветку feature_one, т.к. этот указатель нам не нужен.

Посмотреть локальные ветки
Для просмотра всех локальных веток используется команда git branch

Посмотреть последний коммит каждой из локальных веток
Просмотреть последний коммит каждой ветки можно с помощью git branch --verbose (или -v)

Посмотреть все ветки
Посмотреть все ветки (и локальные, и удаленные) можно с помощью git branch --all (или -a).
В тот момент, когда мы создаем удаленный репозиторий и отправляем в него наш локальный репозиторий, у нас имеется 2 одинаковых репозитория. Но одинаковых только на этот момент. В дальнейшем они могут (и будут) расходиться. Например, если у нас есть репозитория вида 3 коммита (first commit -> second commit -> third commit), дальше мы отправляем изменения на удаленный репозиторий и делаем в локальный репозитория еще 2 коммита, то репозитории будут выглядеть так

Git branch выведет только master, но у нас есть еще одна ветка и она называется origin/master. Т.е. это ветка master в удаленном репозитории. именно это делает флаг -a - показывает в том числе удаленные ветки (и HEAD)

Посмотреть последний коммит каждой из веток
Команда аналогична команде для локальных веток: git branch -v -a

Склонировать репозиторий к себе
Дать возможность любому писать в удаленный репозиторий довольно опасно, но из того что кто то читает из удаленного (публичного) репозитория ничего не произойдет. Часто можно найти специальные репозитории, созданные для учебных целей и направлены на то, чтобы их склонировали к себе.
Для этого нужно выбрать любую папку в файловой системе, куда вы хотите сохранить репозиторий и выполнить команду git clone <url>. Можно склонировать репозиторий используя протокол https или shh. Можно даже скачать репозиторий в виде zip архива (нажмите зеленую кнопку code на главной странице репозитория и выбурите нужный вариант)

Перейти на ветку удаленного репозитория
Как только вы склонируете репозиторий к себе и выполните команду git branch, вы увидите только ветку master (главную ветку, возможно она имеет другое название), даже если веток в репозитории намного больше. Дело в том, что все эти ветки пока представлены как удаленные. Добавьте флаг -v к выводу веток и вы увидите, что все ветки начинаются с remotes/origin.

Однако вы можете перейти на любую ветку используя switch, после чего у вас появится как локальная ветка, так и удаленная. На примере удаленной ветки new_branch:

Последняя фраза выглядит не совсем понятно. Что означает "Ветка 'new_branch' установлена на отслеживание ветки 'new_branch' из 'origin' ".
Как мы уже заметили, ветки в удаленном и локальном репозитории могут обозначать одно и тоже, но они не связаны между собой. Т.е. ветка new_branch и origin/new_branch не следят друг за другом и не знают друг о друге. Но мы можем установить локальную ветку как отслеживаемую ветку удаленного репозитория. Что это нам дает.
Во первых, теперь в выводе status мы увидим различия между ветками. После того, как мы сделали switch удаленной ветки, локальная ветка стала отслеживать удаленную автоматически (последняя фраза из команды switch). Теперь добавим любые изменения, сделаем коммит и выполним команду git status -v, мы увидим следующее

Мы сделали новый коммит в ветку и теперь локальная ветка как бы опережает удаленную на 1 коммит. Об этом можно узнать по фразе [ahead 1], т.е. таким образом мы может оценить от каких веток мы отстаем, а какие опережаем.
Вторым важным моментом является то, что при отправке на удаленный репозиторий нам больше не нужно указывать ветку и сам репозиторий. Достаточно просто git push, т.к. отслеживаемая ветка связанна с конкретной веткой конкретного репозитория и здесь не будет неясностей. Аналогична ситуация с pull.
Чтобы посмотреть как настроены ветки слежения, выполните команду git branch с флагом -vv. так вы увидите за какой конкретно веткой следит локальная ветка

Важное замечание - вывод команды -vv, который говорит о том, на сколько вы опережаете/отстаете от ветки слежения не обновляется автоматически, а только при обращении к удаленному репозиторию. Т.е. информация будет актуально на последнее обращение к удаленному репозиторию. Чтобы получить актуальную информацию, выполните сначала git fetch --all. Этак команда просто получит все изменения с удаленного репозитория.
Ветку можно установить как отслеживаемую с помощью флага при отправке изменений -u (upstream): git push -u origin master.
Получить изменения с удаленного репозитория
Представим себе удаленный репозиторий, состоящий из 3-х коммитов на основе локального репозитория

После чего кто то другой добавляет в удаленный репозиторий 2 новых коммита. У вас этих коммитов еще нет.

Вы можете получить те коммиты, которые есть в удаленном репозитории, но нет у вас в локальном с помощью git fetch origin. Важно то, что fetch просто получает изменения. В результате локальный репозиторий будет выглядеть так

Т.е. изменения появились, но как бы в отдельной ветке (origin/master). Теперь вам нужно слить ветки, например с помощью merge. В данном случае master прямой предок origin/master, так что указатель master просто перейдет на origin/master.
Чтобы ветка master в локальном репозитории стала такой же, как в удаленном мы сделали 2 действия
- скачали все изменения в удаленном репозитории с помощью fetch
- слили удаленную ветку в origin/master в локальную master с помощью merge
Теперь обе ветки одинаковы. Есть другая команда, которая делает тоже, но за 1 действие (точнее команда одна, действия 2). Это git pull origin.
Теперь представим ситуацию, что в тот момент когда мы захотели получить изменения, но уже сделали коммит/коммиты в локальном репозитории в ту же ветку. Выглядит это так

В удаленном репозитории нет красного коммита, а в локальном нет двух зеленых.
Теперь вам нужно привести оба репозитория в одно состояние. Вы можете выполнить git pull, и вот что вы получите. Сначала git нужно скачать два удаленных (зеленых) коммита в локальный репозиторий. Это будет выглядеть так

После чего слить удаленную ветку в локальную, используя merge

Теперь можно отправить изменения в удаленный сервер. В итоге история будет следующей: first commit -> seecond commit -> third commit -> fourth commit -> fifth commit -> merge commit. Все изменения из красного коммита теперь содержатся в самом последнем коммите.
Но есть другой способ сделать это слияние - используя rebase. И наверно это способ лучше, но какой из них использовать зависит от личных предпочтений и принятых в команде правил. Для использования этого варианта слияния нужно выполнить git pull --rebase. После этого git также скачать все коммиты с удаленного репозитория, но дальше выполнит перебазирования ветки локальный master поверх удаленного master. Это будет выглядеть так

Такой способ не оставляет в истории коммитов слияния и вся историю в будущем выглядит как линейная, так что она визуально нагляднее и приятнее (хотя кому как). Но есть важное правило по поводу rebase. Красный коммит есть только у вас в локальном репозитории, так что вы вправе перебазировать его как хотите. Как только вы отправили изменения в публичный репозиторий (т.е. доступ к этой ветке и коммитам из этой ветки есть у кого-то, кроме вас), вы не имеете право применять перебазирование. Это так называемое золотое правило перебазирования. Проблема перебазирования публичной ветки в том, что никто, кроме вас, не знает, что ветка теперь находится в другом месте и все продолжают работать со старой версией. Этот момент приведет к множеству недопониманий в истории, которые будут со временем только увеличиваться. И для разрешения подобной ситуации придется использовать merge двух веток, которые представляют из себя почти одно и тоже, что только еще запутает истории проекта.
Но что, если мы решили что изменения в локальном репозитории верные, а в удаленном - нет. Т.е. удаленный репозиторий должен быть приведен в состояние локального. Мы можем попробовать отправить изменения с помощью git push origin, но git не позволит нам это сделать.
В двух словах, git знает что в удаленном репозитории есть коммиты, которых нет у нас и не знает теперь делать. Он просит сначала скачать все обновления, которых у вас нет, а затем отправлять свои изменения. Но можно принудительно переписать историю удаленного репозитория, вызвав git push --force origin. Флаг --force говорит о том, что нужно игнорировать все ошибки и сделать ветку как у меня. Это довольно опасная операция и стоит несколько раз подумать, прежде чем ее использовать.
В заключительной статье мы подробнее рассмотрим операции слияния веток, отбора коммитов, интерактивное перебазирование и некоторые другие темы.