Перейти к основному содержимому

Мастер-класс по Git

О работе

В данной работе предстоит:

  1. Познакомиться с основами Git.
  2. Создать репозиторий в GitLab и склонировать.
  3. Выполнить действия по управлению репозиторием.

Часть 0. Установка Git

Установка Git на Windows

Статья основана на "Install Git" и "Download for Windows".

Шаги установки

  1. Загрузите последнюю версию установщика Git для Windows.
  2. После успешного запуска программы установки вы должны увидеть экран мастера настройки Git. Следуйте инструкциям Next и Finish, чтобы завершить установку. Параметры по умолчанию довольно понятны для большинства пользователей.
  3. Настройте имя пользователя и адрес электронной почты в Git, как показано в статье "Настройки Git" из данного руководства.

Установка Git на Linux

Статья основана на "Install Git" и "Download for Linux and Unix".

Возможно, в вашем дистрибутиве git уже установлен. Чтобы узнать, откройте терминал и введите

git --version

Проще всего установить Git в Linux, используя предпочтительный менеджер пакетов вашего дистрибутива Linux. Если вам требуется более свежая версия Git или он по какой-то причине отсутствует в вашей операционной системе, то следуйте указаниям ниже.

Шаги установки

  1. Установите Git согласно инструкциям, указанные в статье "Download for Linux and Unix".
  2. Настройте имя пользователя и адрес электронной почты в Git, как показано в статье "Настройки Git" из данного руководства.

Установка Git на macOS

Статья основана на "Install Git" и "Download for macOS".

Существует несколько способов установки Git на Mac. На самом деле, если вы установили XCode (или его инструменты командной строки), Git, возможно, уже установлен. Чтобы узнать, откройте терминал и введите

git --version

Apple на самом деле поддерживает и поставляет свою собственную версию Git, но она, как правило, отстает от основной версии Git на несколько основных версий. Если вам требуется более свежая версия, то следуйте указаниям ниже.

Шаги установки

  1. Установите Git согласно инструкциям, указанные в статье "Download for macOS".
  2. Настройте имя пользователя и адрес электронной почты в Git, как показано в статье "Настройки Git" из данного руководства.

Часть 1. Основы Git

Управление исходным кодом

Управление исходным кодом (англ. source control management, SCM) используется для отслеживания изменений в репозитории исходного кода. SCM отслеживает текущую историю изменений в базе кода и помогает разрешать конфликты при объединении обновлений от нескольких участников.

Репозиторий — место, где хранятся и поддерживаются какие-либо данные. Чаще всего данные в репозитории хранятся в виде файлов, доступных для дальнейшего распространения по сети. Также под репозиторием понимается каталог файловой системы, в котором могут находиться файлы журналов конфигураций и операций, выполняемых над репозиторием, а также сами контролируемые файлы.

Git-репозиторий (источник: learn.microsoft.com):

Git-репозиторий

По мере роста количества строк кода и количества участников программных проектов растут и затраты на коммуникационные издержки, а также сложность управления. SCM является важнейшим инструментом для облегчения организационной нагрузки, связанной с растущими затратами на разработку.

Важность инструментов управления исходным кодом

Когда несколько разработчиков работают в рамках общей кодовой базы, внесение изменений в общий фрагмент кода является обычным делом. Отдельные разработчики могут работать над, казалось бы, изолированной функцией, однако эта функция может использовать общий модуль кода. Поэтому разработчик №1, работающий над функцией №1, может внести некоторые правки и позже выяснить, что разработчик №2, работающий над функцией №2, имеет конфликтующие правки.

В интересах кого SCM работает (источник atlassian.com/git):

SCM и компания

До принятия SCM это был кошмарный сценарий. Разработчики будут редактировать текстовые файлы напрямую и перемещать их в удаленные места с помощью FTP или других протоколов. Разработчик №1 внесет изменения, а разработчик №2 неосознанно сохранит работу разработчика №1 и уничтожит изменения. Роль SCM как механизма защиты от этого конкретного сценария известна как контроль версий.

SCM внедрила меры контроля версий для предотвращения потери работы из-за перезаписи конфликта. Эти меры предосторожности работают путем отслеживания изменений от каждого отдельного разработчика, выявления конфликтных областей и предотвращения перезаписи. Затем SCM сообщит об этих конфликтных моментах разработчикам, чтобы они могли безопасно просмотреть и устранить.

Взаимодействие в команде разработки (источник atlassian.com/git):

SCM и компания

Этот основополагающий механизм предотвращения конфликтов имеет побочный эффект, заключающийся в обеспечении пассивной коммуникации для команды разработчиков. Затем команда может отслеживать и обсуждать текущую работу, которую отслеживает SCM. SCM отслеживает всю историю изменений в базе кода. Это позволяет разработчикам изучать и просматривать правки, которые могли привести к ошибкам или регрессиям.

Преимущества управления исходным кодом

В дополнение к контролю версий SCM предоставляет набор других полезных функций, которые делают совместную разработку кода более удобной для пользователя. Как только SCM начинает отслеживать все изменения в проекте с течением времени, создается подробная историческая запись жизненного цикла проекта. Эта историческая запись затем может быть использована для ‘отмены’ изменений в кодовой базе. SCM может мгновенно вернуть кодовую базу к предыдущему моменту времени. Это чрезвычайно важно для предотвращения регрессий при обновлениях и устранения ошибок.

  1. Архив SCM всех изменений за время существования проекта обеспечивает ценный учет примечаний к выпускной версии проекта. Чистый и поддерживаемый журнал истории SCM можно взаимозаменяемо использовать в качестве примечаний к выпуску. Это обеспечивает понимание и прозрачность хода выполнения проекта, которыми можно поделиться с конечными пользователями или командами, не занимающимися разработкой.
  2. SCM сокращает коммуникационные издержки команды и увеличит скорость выпуска. Без SCM разработка идет медленнее, потому что участникам приходится прилагать дополнительные усилия для планирования непересекающейся последовательности разработки для выпуска. С помощью SCM разработчики могут независимо работать над отдельными разделами разработки функций, в конечном итоге объединяя их вместе.

Коммуникация в команде разработки (источник atlassian.com/git):

SCM и компания

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

Вывод

SCM - бесценный инструмент для современной разработки программного обеспечения. Лучшие команды разработчиков программного обеспечения используют SCM, и ваша команда тоже должна. SCM очень легко настроить в новом проекте, а отдача от инвестиций высока.

Что такое Git и зачем он нужен?

Основано на статье Git для новичков (часть 1) | Habr.

Git - это консольная утилита, для отслеживания и ведения истории изменения файлов, в вашем проекте. Чаще всего его используют для кода, но можно и для других файлов. Например, для картинок - полезно для дизайнеров.

С помощью Git-a вы можете откатить свой проект до более старой версии, сравнивать, анализировать или сливать свои изменения в репозиторий.

Репозиторием называют хранилище вашего кода и историю его изменений. Git работает локально и все ваши репозитории хранятся в определенных папках на жестком диске.

Так же ваши репозитории можно хранить и в интернете. Обычно для этого используют три сервиса:

Каждая точка сохранения вашего проекта носит название коммит (commit). У каждого commit-a есть hash (уникальный id) и комментарий. Из таких commit-ов собирается ветка. Ветка - это история изменений. У каждой ветки есть свое название. Репозиторий может содержать в себе несколько веток, которые создаются из других веток или вливаются в них.

Базовые понятия

Репозиторий – папка проекта, отслеживаемого Git, содержащая дерево изменений проекта в хронологическом порядке. Все файлы истории хранятся в специальной папке .git/ внутри папки проекта.

Индекс – файл, в котором содержатся изменения, подготовленные для добавления в коммит. Вы можете добавлять и убирать файлы из индекса.

Коммит – фиксация изменений, внесенных в индекс. Другими словами, коммит – это единица изменений в вашем проекте. Коммит хранит измененные файлы, имя автора коммита и время, в которое был сделан коммит. Кроме того, каждый коммит имеет уникальный идентификатор, который позволяет в любое время к нему откатиться.

Указатели HEAD, ORIG_HEAD и т. д. – это ссылка на определенный коммит. Ссылка – это некоторая метка, которую использует Git или сам пользователь, чтобы указать на коммит.

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

Рабочая копия – Директория .git/ с её содержимым относится к Git. Все остальные файлы называются рабочей копией и принадлежат пользователю.

Настройки Git

В зависимости от области действия и места хранения в Git существуют 3 типа настроек:

  • Системные. Представляют собой настройки на уровне всей системы, то есть они распространяются на всех пользователей. Файл с этими настройками хранится по следующему пути: C:\Program Files\Git\etc\gitconfig для Windows и /etc/gitconfig для пользователей Linux/MacOS.
  • Глобальные. Эти настройки одинаковы для всех репозиториев, созданных под вашим пользователем. Среди них есть, например, имя ветки по умолчанию. Файл с этими параметрами хранятся по следующему адресу: C:/User/<имя пользователя>/.gitconfig в windows, или ~/.gitconfig в Unix системах.
  • Локальные. Это настройки на уровне репозитория, они не будут применяться к другим вашим проектам. Эти параметры хранятся в каждом вашем репозитории по адресу: .git/config.

Области действия настроек Git (источник: smartiqa.ru/courses/git):

Области действия настроек Git

Изменить настройки Git можно двумя способами:

  • Отредактировать файл gitconfig (на уровне системы) или .gitconfig (глобально) или .git/config (на уровне репозитория) напрямую, то есть используя текстовый редактор.
  • Воспользоваться утилитой git config. Кроме того, с помощью этой утилиты можно посмотреть значение соответствующего параметра.

Команда git config устанавливает значение соответствующего параметра в конфигурации Git.

Документация здесь.

Давайте попробуем задать имя пользователя глобально. Воспользуемся утилитой git config с параметром --global:

git config --global user.name "DevOps Student"
git config --global user.email devops@example.com

Посмотреть заданные переменные можно:

git config --global user.name
git config --global user.email

Файл $HOME/.gitconfig с заданным пользователем:

[user]
name = DevOps Student
email = devops@example.com
[filter "lfs"]
clean = git-lfs clean -- %f
smudge = git-lfs smudge -- %f
process = git-lfs filter-process
required = true

Как видно поля user.name и user.email действительно стали такими, какими мы их задали.

Команда git config --list --show-origin показывает все заданные переменные списком (--list) и откуда они наследованы (--show-origin).

Создание репозитория

Как было сказано выше, репозиторий должен обязательно содержать папку .git/ с историей этого репозитория. Создать эту папку можно двумя способами:

  1. Создать новый репозиторий.
  2. Клонировать к себе на компьютер существующий репозиторий.

Второй способ мы рассмотрим позже, а пока займемся первым. Итак, чтобы создать репозиторий, вам понадобится команда git init.

Команда git init создает пустой репозиторий в директории, откуда была вызвана.

Документация здесь.

Создадим папку devops и на ее базе создадим репозиторий Git:

mkdir devops
cd devops/
git init

Эта команда создала папку .git внутри папки вашего проекта. В ней следующее содержимое:

devops/.git
├── HEAD
├── branches
├── config
├── description
├── hooks
├── info
│ └── exclude
├── objects
│ ├── info
│ └── pack
└── refs
├── heads
└── tags

Подробнее о том, что содержится в этой папке и как оно изменяется вы можете изучить в курсе smartiqa.ru/courses/git и в учебнике git-scm.com/book.

Состояния файлов

Давайте более подробно разберемся с тем, в каких состояниях могут быть файлы с точки зрения Git. Каждый файл может находится только в одном из двух состояний:

  1. Отслеживаемым (tracked). Об этих файлах Git знает и отслеживает изменения в них. Отслеживаемые файлы в свою очередь могут находится в следующих состояниях:
    1. Неизмененным (unmodified). То есть с момента последнего коммита в файле не было никаких изменений
    2. Измененным (modified). То есть с последнего коммита в файле были произведены какие-то изменения.
    3. Подготовленным к коммиту (staged). Это значит, что вы внесли изменения в этот файл и затем проиндексировали их, и эти изменения будут добавлены в следующий коммит.
  2. Неотслеживаемым (untracked). О неотслеживаемых файлах Git не знает, поэтому изменения в них не будут добавлены в коммит. Это любые файлы в вашем рабочем каталоге, которые не входили в последний коммит и не подготовлены к текущему коммиту.

Наглядная визуализация состояний и переходов между ними (источник: git-scm.com/book):

Визуализация состояний файлов и переходов между ними

Чтобы посмотреть статус текущих файлов, нам потребуется команда git status.

Команда git status выводит информацию о статусе файлов, находящихся в репозитории.

Документация здесь.

Предположим, вы добавили в свой проект новый файл, простой файл README.

echo 'My Project' > README

Наша папка будет выглядеть следующим образом:

devops/
└── README

Если этого файла раньше не было, и вы выполните git status, вы увидите свой неотслеживаемый файл вот так:

On branch main

No commits yet

Untracked files:
(use "git add <file>..." to include in what will be committed)
README

nothing added to commit but untracked files present (use "git add" to track)

Видно, что на данный момент у нас нет ни одного коммита.

Отслеживание файлов

Чтобы сделать файл отслеживаемым, существует команда git add.

Команда git add добавляет файлы в индекс.

Документация здесь.

Отслеживание новых файлов

Для того чтобы начать отслеживать (добавить под версионный контроль) новый файл, используется команда git add. Чтобы начать отслеживание файла README, вы можете выполнить следующее:

git add README

Если вы снова выполните команду status, то увидите, что файл README теперь отслеживаемый и добавлен в индекс:

On branch main

No commits yet

Changes to be committed:
(use "git rm --cached <file>..." to unstage)
new file: README

Вы можете видеть, что файл проиндексирован, так как он находится в секции Changes to be committed. Если вы выполните коммит в этот момент, то версия файла, существовавшая на момент выполнения вами команды git add, будет добавлена в историю снимков состояния. Как вы помните, когда вы ранее выполнили git init, затем вы выполнили git add (файлы) — это было сделано для того, чтобы добавить файлы в вашем каталоге под версионный контроль. Команда git add принимает параметром путь к файлу или каталогу, если это каталог, команда рекурсивно добавляет все файлы из указанного каталога в индекс.

Индексация изменённых файлов

Давайте модифицируем файл, уже находящийся под версионным контролем.

echo 'My Project 2' > README

Если вы измените отслеживаемый файл README и после этого снова выполните команду git status, то результат будет примерно следующим:

On branch main

No commits yet

Changes to be committed:
(use "git rm --cached <file>..." to unstage)
new file: README

Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: README

Что за чёрт? Теперь README отображается как проиндексированный и непроиндексированный одновременно. Как такое возможно? Такая ситуация наглядно демонстрирует, что Git индексирует файл в точности в том состоянии, в котором он находился, когда вы выполнили команду git add. Если вы выполните коммит сейчас, то файл README попадёт в коммит в том состоянии, в котором он находился, когда вы последний раз выполняли команду git add , а не в том, в котором он находится в вашем рабочем каталоге в момент выполнения git commit. Если вы изменили файл после выполнения git add, вам придётся снова выполнить git add, чтобы проиндексировать последнюю версию файла:

git add README

После этого снова выполните команду git status, то результат будет примерно следующим:

On branch main

No commits yet

Changes to be committed:
(use "git rm --cached <file>..." to unstage)
new file: README

Игнорирование файлов

Зачастую, у вас имеется группа файлов, которые вы не только не хотите автоматически добавлять в репозиторий, но и видеть в списках неотслеживаемых. К таким файлам обычно относятся автоматически генерируемые файлы (различные логи, результаты сборки программ и т. п.). В таком случае, вы можете создать файл .gitignore. с перечислением шаблонов соответствующих таким файлам. Вот пример файла .gitignore:

*.[oa]
*~

Первая строка предписывает Git игнорировать любые файлы заканчивающиеся на .o или .a — объектные и архивные файлы, которые могут появиться во время сборки кода. Вторая строка предписывает игнорировать все файлы заканчивающиеся на тильду (~), которая используется во многих текстовых редакторах, например Microsoft Word, для обозначения временных файлов.

Коллекцию полезных шаблонов файлов .gitignore можно найти в репозитории github.com/github/gitignore.

Подробнее о написании регулярных выражений для игнорирования определенных файлов можно найти в учебнике git-scm.com/book и в статье на tproger.ru.

Запись изменений в репозиторий

Команда git commit - создает новый коммит с файлами из индекса.

Документация здесь.

Основы работы с Git предполагают понимание коммитов. Команда git commit откроет текстовый редактор для ввода сообщения коммита. Также эта команда принимает несколько аргументов:

  • -m позволяет написать сообщение вместе с командой, не открывая редактор. Например git commit -m "Пофиксил баг";
  • -a переносит все отслеживаемые файлы в область подготовленных файлов и включает их в коммит (позволяет пропустить git add перед коммитом);
  • --amend заменяет последний коммит новым изменённым коммитом, что бывает полезно, если вы неправильно набрали сообщение последнего коммита или забыли включить в него какие-то файлы.

Ловушка git commit. По умолчанию команда git commit откроет Vim, из которого выход найти сложно 😈.

Как выйти из Vim можно найти здесь.

Советы для эффективного введения в Git:

  • Коммитьте как можно чаще.
  • Одно изменение — один коммит: не помещайте все не связанные между собой изменения в один коммит, разделите их, чтобы было проще откатиться.
  • Формат сообщений: заголовок должен быть в повелительном наклонении, меньше 50 символов в длину и должен логически дополнять фразу this commit will ___ (this commit will fix bugs — этот коммит исправит баги).
  • Сообщение должно пояснять, почему был сделан коммит, а сам коммит показывает, что изменилось.
  • Если у вас много незначительных изменений, хорошим тоном считается делать небольшие коммиты при разработке, а при добавлении в большой репозиторий объединять их в один коммит (про сжатие коммитов читайте здесь).

Запишем изменения для нашего примера:

git commit -m "Добавил README"

Результат:

[main (root-commit) 02c9b2c] Добавил README
1 file changed, 1 insertion(+)
create mode 100644 README

А так выглядит наш коммит GitKraken:

Первый коммит

Важно не просто коммитить, но и блюсти формат сообщений.

Хорошая древняя статья на тему того КАК нужно писать сообщения к коммитам тут.

История коммитов

HEAD – это указатель, задача которого ссылаться на определенный коммит в репозитории.

Команда git log показывает журналы фиксации.

Документация здесь.

Предположим, что в нашем тестовом репозитории мы 2 раза изменили файл README и записали изменения. Тогда результат выполнения команды git log --oneline будет иметь вид:

fbc8910 (HEAD -> main) Исправил замечание
90f038e Добавил описание о HEAD
02c9b2c Добавил README

Эти коммиты создавались в порядке от самого нижнего (02c9b2c) к самому верхнему (fbc8910). Каждый раз, когда мы записывали новый коммит в репозиторий, HEAD смещался и указывал на него.

После того как вы записали коммит с id = fbc8910, указатель HEAD стал показывать на него, т.е. данный коммит будет родителем для следующего, и когда мы сделаем еще один коммит, HEAD сместится.

Более подробно об указателе HEAD читайте в статье devpractice.ru

Пример истории коммитов (источник: tproger.ru):

История коммитов

Коммиты хранят состояние файловой системы в определённый момент времени и указатели на предыдущие коммиты. Каждый коммит содержит уникальную контрольную сумму — идентификатор, который Git использует, чтобы ссылаться на коммит. Чтобы отслеживать историю, Git хранит указатель HEAD, который указывает на первый коммит (мы следуем по цепочке коммитов в обратном порядке, чтобы попасть к предыдущим коммитам).

Мы можем ссылаться на коммит либо через его контрольную сумму, либо через его позицию относительно HEAD, например HEAD~4 ссылается на коммит, который находится 4 коммитами ранее HEAD.

Более подробную информацию о git log можно найти в статье на tproger.ru.

Удалённый репозиторий

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

Команда git remote управляет набором отслеживаемых удаленных репозиториев.

Документация здесь.

git remote -v выводит список удалённых репозиториев, которые мы отслеживаем, и имена, которые мы им присвоили.

Команда git clone клонирует репозиторий в новый каталог.

Документация здесь.

При использовании команды git clone <url репозитория> мы не только загружаем себе копию репозитория, но и неявно отслеживаем удалённый сервер, который находится по указанному адресу и которому присваивается имя origin.

Наиболее употребляемые команды:

  • git remote add <имя> <url> — добавляет удалённый репозиторий с заданным именем;
  • git remote remove <имя> — удаляет удалённый репозиторий с заданным именем;
  • git remote rename <старое имя> <новое имя> — переименовывает удалённый репозиторий;
  • git remote set-url <имя> <url> — присваивает репозиторию с именем новый адрес;
  • git remote show <имя> — показывает информацию о репозитории.

Команда git fetch загружает объекты и ссылки из другого репозитория.

Документация здесь.

Команда git pull - извлечение и интеграция с другим репозиторием или локальной веткой.

Документация здесь.

Команда git push - обновление удаленных ссылок вместе со связанными объектами.

Документация здесь.

Следующие команды работают с удалёнными ветками:

  • git fetch <имя> <ветка> — получает данные из ветки заданного репозитория, но не сливает изменения;
  • git pull <имя> <ветка> — сливает данные из ветки заданного репозитория;
  • git push <имя> <ветка> — отправляет изменения в ветку заданного репозитория. Если локальная ветка уже отслеживает удалённую, то можно использовать просто git push или git pull.

Иллюстрация git push и git pull (источник: tproger.ru):

git push и git pull

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

Работа с ветками

Ветвление

Ветвление — это возможность работать над разными версиями проекта: вместо одного списка с упорядоченными коммитами история будет расходиться в определённых точках. Каждая ветвь содержит легковесный указатель HEAD на последний коммит, что позволяет без лишних затрат создать много веток. Ветка по умолчанию называется master, но лучше назвать её в соответствии с разрабатываемой в ней функциональностью.

Итак, есть общий указатель HEAD и HEAD для каждой ветки. Переключение между ветками предполагает только перемещение HEAD в HEAD соответствующей ветки.

Введение в ветвление

Команда git branch - получить список, создание или удаление ветвей.

Документация здесь.

Команды:

git branch <имя ветки> — создаёт новую ветку с HEAD, указывающим на HEAD. Если не передать аргумент <имя ветки>, то команда выведет список всех локальных веток; git checkout <имя ветки> — переключается на эту ветку. Можно передать опцию -b, чтобы создать новую ветку перед переключением; git branch -d <имя ветки> — удаляет ветку.

Пример выполнения этих команд:

Пример 1

Локальный и удалённый репозитории могут иметь немало ветвей, поэтому когда вы отслеживаете удалённый репозиторий — отслеживается удалённая ветка (git clone привязывает вашу ветку main или master к ветке origin/main или origin/master удалённого репозитория).

Привязка к удалённой ветке:

  • git branch -u <имя удалённого репозитория>/<удалённая ветка> — привязывает текущую ветку к указанной удалённой ветке;
  • git checkout --track <имя удалённого репозитория>/<удалённая ветка> — аналог предыдущей команды;
  • git checkout -b <ветка> <имя удалённого репозитория>/<удалённая ветка> — создаёт новую локальную ветку и начинает отслеживать удалённую;
  • git branch --vv — показывает локальные и отслеживаемые удалённые ветки;
  • git checkout <удалённая ветка> — создаёт локальную ветку с таким же именем, как у удалённой, и начинает её отслеживать.

В общем, git checkout связан с изменением места, на которое указывает HEAD ветки, что похоже на то, как git reset перемещает общий HEAD.

Слияние

Дадим определения:

  • Сливаемая ветка – та ветка, с которой мы берем изменения, чтобы влить их в целевую.
  • Целевая ветка – та ветка, в которую мы сливаем наши изменения.
  • Слияние веток – это перенос изменений с одной ветки на другую. При этом слияние не затрагивает сливаемую ветку, то есть она остается в том же состоянии, что позволяет нам потом продолжить работу с ней.

Слияние включает в себя создание нового коммита, который основан на общем коммите-предке двух ветвей и указывает на оба HEAD в качестве предыдущих коммитов.

Пример слияния (источник: tproger.ru):

Пример слияния

Команда git merge сливает изменения с переданной ветки в текущую.

Документация здесь.

Для слияния мы переходим на основную ветку и используем команду git merge <тематическая ветка>.

Если обе ветви меняют одну и ту же часть файла, то возникает конфликт слияния — ситуация, в которой Git не знает, какую версию файла сохранить, поэтому разрешать конфликт нужно собственноручно. Чтобы увидеть конфликтующие файлы, используйте git status.

Пример с двумя ветками, в которых изменен один и тот же файл:

Пример конфликта 1

Переходим в ветку main через git checkout main и делаем git merge new-branch.

Статус репозитория:

Пример конфликта 2

git status показывает:

On branch main
You have unmerged paths.
(fix conflicts and run "git commit")
(use "git merge --abort" to abort the merge)

Unmerged paths:
(use "git add <file>..." to mark resolution)
both modified: README

no changes added to commit (use "git add" and/or "git commit -a")

После открытия таких файлов вы увидите похожие маркеры разрешения конфликта:

Пример конфликта 3

Замените в этом блоке всё на версию, которую вы хотите оставить, и подготовьте файл. После разрешения всех конфликтов можно использовать git add и git commit для завершения слияния.

Часть 2. Создание репозитория

Вам предстоит работать автономном самоуправляемом экземпляре GitLab (GitLab Self managed).

После регистрации в Gitlab, создайте приватный пустой проект в пространстве имен вашего профиля (Your work/Projects/New project/Create blank project):

Создание нового проекта

Часть 3. Клонирование репозитория

Созданный удаленно репозиторий необходимо склонировать на рабочую станцию.

Это можно сделать с помощью:

  • команды git clone в терминале;
  • через графический интерфейс git (см. пример ниже);
  • через Visual Studio Code.

В данном руководстве мы будем делать через графический интерфейс git через токены авторизации.

В повседневной практике рекомендуется работу через SSH с удаленными репозиториями. О том, как это сделать, см. в статье SSH и удалённые git-репозитории.

Нам требуется ссылка, которая указана в разделе "Клонировать с помощью HTTPS".

Ссылку на репозиторий можно узнать непосредственно на страничке созданного вами проекта:

Ссылки на репозиторий

Получение токена

При доступа к приватному репозиторию необходим токен доступа. В данном руководстве используется персональный токен доступа.

Его можно получить в профиле пользователя:

Получение токена шаг 1

Выпуск токенов находится во вкладке редактирования профиля:

Получение токена шаг 2

Для создаваемого токена необходимо указать имя, дата истечения и сферы применения (read_repository и write_repository):

Получение токена шаг 3

Клонирование репозитория через GUI

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

Интерфейс Sourcetree для macOS и Windows отличается, но подход в работе схож. Для демонстрации используется Sourcetree для Windows.

В интерфейсе выбираем клонирование репозитория по Url:

Клонирование репозитория по Url

Вводим данные для авторизации (включая ранее выпущенный токен):

Ввод данных для авторизации

Указываем целевой путь и название:

Клонирование репозитория

В результате откроется окно управления репозиторием:

Результат клонирования

Результат клонирования

Часть 4. Создание проекта

В данной статье мы создадим наш тестовый проект. Наш проект будет на Python. Для иллюстрации в данном репозитории используется Visual Studio Code. Как использовать Python и Visual Studio Code показано в статье.

При работе над проектом не забудьте использовать изолированную среду Python. С помощью Virtualenv создадим ее в папке проекта:

python3 -m venv env

Если мы создали ее в папке проекта, то не забываем исключить папку со средой из управления системой контроля версий. Для этого в корне репозитория создается файл .gitignore с необходимым паттерном для игнорирования:

*env

Если вы используете IDE, то не забывайте добавить в игнорируемые служебные объекты. Например, Visual Studio Code:

*env
.vscode
__pycache__

В корне репозитория создадим файл hypotenuse.py:

import math

def get_hypotenuse(a, b):
return math.sqrt(math.pow(a, 3) + math.pow(b, 3))

if __name__ == "__main__":
a = 1
b = 2
print(get_hypotenuse(a,b))

Ошибка в коде допущена специально.

В результате в репозитории получиться следующая структура файлов и папок:

my-awesome-project
├── hypotenuse.py
├── README.md
└── .gitignore

Часть 5. Первая часть изменений

Материал статьи основан на "Laboratory work II"

В этой части работы предлагается:

  1. Зафиксировать изменения в репозитории с осмысленным сообщением.

  2. Измените исходный код так, чтобы программа через стандартный поток ввода запрашивалось ввод чисел a и b. А в стандартный поток вывода печатались какие-нибудь информационные сообщения. Например:

    import math

    def get_hypotenuse(a, b):
    return math.sqrt(math.pow(a, 3) + math.pow(b, 3))

    if __name__ == "__main__":
    print("Введите a:")
    a = int(input())
    print("Введите b:")
    b = int(input())
    print("c =", get_hypotenuse(a,b))
  3. Зафиксируйте новую версию программы.

  4. Отправьте изменения в удалённый репозиторий.

  5. Проверьте, что история коммитов доступна в удаленном репозитории.

Результат фиксации изменений в локальном репозитории

Результат фиксации изменений в удаленном репозитории

Часть 6. Вторая часть изменений

Материал статьи основан на "Laboratory work II"

В этой части работы предлагается:

  1. В локальной копии репозитория создайте локальную ветку feature1.

  2. Внесите изменения в ветке feature1. Например, добавьте расчет площади прямоугольного треугольника. Сделайте в этой функциональности осознанную ошибку.

  3. Зафиксируйте и отправьте изменения в удалённый репозиторий.

  4. Так как мы добавили новую функциональность, то нужно переименовать исходный наш файл в calculator.py.

  5. Зафиксируйте и отправьте изменения в удалённый репозиторий.

    По итогу в репозитории должна получиться следующая ситуация:

    my-awesome-project
    ├── calculator.py
    ├── README.md
    └── .gitignore

    А код calculator.py выглядит следующим образом:

    import math

    def get_area(a, b):
    return a * b

    def get_hypotenuse(a, b):
    return math.sqrt(math.pow(a, 3) + math.pow(b, 3))

    if __name__ == "__main__":
    print("Введите a:")
    a = int(input())
    print("Введите b:")
    b = int(input())
    print("c =", get_hypotenuse(a,b))
    print("S =", get_area(a,b))
  6. Проверьте, что ветка feature1 доступна в удаленном репозитории. Например, результат может выглядеть так:

    После выполнения второй части изменений

Часть 7. Запрос на слияние

Материал статьи основан на "Creating merge requests"

Создание запроса на слияние

Далее нам предстоит создать запрос на слияние. Сделать это можно из списка запросов на слияние в интерфейсе управления удаленным репозиторием. Для этого:

  1. Перейдите на главную страницу созданного ранее проекта.

  2. В меню слева выберите Merge requests:

    Список запросов на слияние

  3. В правом верхнем углу выберите New merge request.

  4. Выберите исходную (feature1) и целевую (master) ветви, а затем сравните ветви и продолжите.

    Сравнение изменений

  5. Заполните поля и выберите Create merge request.

    Описание запроса на слияние

Запросы на слияние разрабатываются на основе взаимно однозначных (1:1) отношений ветвления. Одновременно с заданной целевой веткой может быть связан только один открытый запрос на слияние.

Принятие запроса на слияние

Осуществите проверку запроса на слияние:

Проверка запроса на слияние

В случае отсутствия замечаний и проблем, подтвердите запрос на слияние (Approve), активируйте флаги Delete source branch и Squash commits, и нажмите на Merge.

Проверьте результат слияния в удаленном репозитории:

Результат слияния

Работа с локальным репозиторием

В локальном репозитории выполните следующие шаги:

  1. Перейдите в ветку master с помощью команды git checkout.
  2. Вытяните все изменения в локальном репозитории из удаленного через git pull.
  3. С помощью команды git log просмотрите историю в локальной версии ветки master.
  4. Удалите локальную ветку feature1.

Часть 8. Третья часть изменений

Материал статьи основан на "Laboratory work II"

В этой части работы предлагается:

  1. Создать новую локальную ветку patch1.

  2. В этой ветке добавить комментарии к коду в файле calculator.py. Например, так:

    import math

    # Получить площадь прямоугольного треугольника
    def get_area(a, b):
    return a * b

    # Рассчитать гипотенузу
    def get_hypotenuse(a, b):
    return math.sqrt(math.pow(a, 3) + math.pow(b, 3))

    if __name__ == "__main__":
    print("Введите a:")
    a = int(input())
    print("Введите b:")
    b = int(input())
    print("c =", get_hypotenuse(a, b))
    print("S =", get_area(a, b))

  3. Зафиксируйте и отправьте изменения в удалённый репозиторий.

  4. Перейдите в локальную ветку master. В этой ветке измените порядок функций calculator.py. Например, так:

    import math

    def get_hypotenuse(a, b):
    return math.sqrt(math.pow(a, 3) + math.pow(b, 3))

    def get_area(a, b):
    return a * b

    if __name__ == "__main__":
    print("Введите a:")
    a = int(input())
    print("Введите b:")
    b = int(input())
    print("c =", get_hypotenuse(a, b))
    print("S =", get_area(a, b))

  5. Зафиксируйте и отправьте изменения в удалённый репозиторий.

  6. Создайте запрос на слияние.

  7. Убедитесь в наличии конфликта:

    Конфликт слияния

  8. Проверьте, просмотрите и объедините локально. Подсказку о том, что делать, можно посмотреть в GitLab, нажав на кнопку Merge locally:

    Слияние локально с конфликтом

  9. Зафиксируйте и отправьте изменения в удалённый репозиторий.

  10. Убедиться, что открытый ранее запрос на слияния осуществлен:

    Слияние веток

  11. Просмотреть текущий граф в удаленном репозитории:

    Граф репозитория после слияния

Итоговый файл calculator.py, который получился для демонстрации:

import math


# Рассчитать гипотенузу
def get_hypotenuse(a, b):
return math.sqrt(math.pow(a, 3) + math.pow(b, 3))

# Получить площадь прямоугольного треугольника
def get_area(a, b):
return a * b


if __name__ == "__main__":
print("Введите a:")
a = int(input())
print("Введите b:")
b = int(input())
print("c =", get_hypotenuse(a, b))
print("S =", get_area(a, b))

Для дальнейшего изучения

Книги

  1. Фишерман Л. В. Практическое руководство. Управление и контроль версий в разработке программного обеспечения

Курсы

  1. GitKraken: Learn Git
  2. Smartiqa: Работа с Git

Статьи

  1. Введение в Git: от установки до основных команд
  2. Git за полчаса: руководство для начинающих
  3. Что такое GIT простым языком? Как работает, основные команды GIT

Игры

  1. LearnGitBranching