Найти в Дзене
"Мы"-Прогер

Как пользоваться Git (основы)

В прошлой статье (https://dzen.ru/a/aBIJ0yCbeS1gSzFe) мы создали репозиторий Git и подключили его к проекту. Теперь пора научиться его использовать! В статье по-прежнему будет использоваться IntelliJIDEA / Rider / PyCharm / ... Сохранения в гите называются коммитами (commit). Каждый коммит представляет собой набор изменений, которые были сделаны над файлами (на самом деле, это не совсем правда). Продолжая предыдущий учебный проект, изменим что-нибудь в файлах и сделаем пару коммитов. Например, можно создать новый файл, удалить другой файл, а в третьем файле сделать изменения. Названия этих файлов загорятся соответствующим цветом, а напротив строк в изменённом файле будут гореть цветные метки: Эти метки помогут вам быстро находить места, ранее изменённые вами. Также на них можно нажимать и будет открываться менюшка. Чтобы сделать коммит, открываем панель Commit слева вверху и жмём Refresh (обновить). Бывали случаи, когда без Refresh часть изменений не попадала в коммит! Выделим любой фа
Оглавление

В прошлой статье (https://dzen.ru/a/aBIJ0yCbeS1gSzFe) мы создали репозиторий Git и подключили его к проекту. Теперь пора научиться его использовать!

В статье по-прежнему будет использоваться IntelliJIDEA / Rider / PyCharm / ...

Что такое коммиты

Сохранения в гите называются коммитами (commit). Каждый коммит представляет собой набор изменений, которые были сделаны над файлами (на самом деле, это не совсем правда).

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

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

Чтобы сделать коммит, открываем панель Commit слева вверху и жмём Refresh (обновить). Бывали случаи, когда без Refresh часть изменений не попадала в коммит! Выделим любой файл и нажмём Ctrl + D (от слова Difference) - откроется просмотр изменений этого файла. Тут же можно выборочно поставить галочки на нужных изменениях или откатить эти изменения (это удобно, например, если для отладки программы мы внесли тонну изменений в код, а теперь они не нужны):

Выбор изменений из файла (Commit, Ctrl + D)
Выбор изменений из файла (Commit, Ctrl + D)

Также эти галочки полезны, когда делая одну задачу, вы попутно исправили другой баг. Тогда вы можете выборочно залить задачу в один коммит, а попутное исправление - в другой. Разделяйте коммиты по разным тематикам, это важно! Так вашим коллегам будет удобнее смотреть историю, а также вы сможете выбрать отдельный коммит для отката, если внезапно понадобится вернуть что-то на своё место. Бывало, я делал по 3-4 коммита подряд после интенсивной работы над кодом.

Далее вводим краткое описание коммита (можно на русском) и жмём Commit (зафиксировать):

-3

В панели Git Log (внизу) появятся новые коммиты. Если мы нажмём там на любой коммит, в панельке справа откроется список его файлов. Нажав дважды на любой файл, можно просмотреть изменения в нём:

Git Log
Git Log
Закоммиченные изменения в файле
Закоммиченные изменения в файле

Заливка коммитов на сервер (Push)

Если вы не уверены в том, что сделали правильный коммит (например, не отладили свой код как надо, а уже взялись менять что-то ещё), то лучше подождите с залитием на сервер. Коммиты можно отредактировать, только пока они не залиты! (На самом деле, вам дадут их отредактировать и после заливки, но потом окажется, что правильные и неправильные коммиты живут на сервере вместе). Можно сделать несколько коммитов, а затем протестировать код и залить всё сразу.

Для заливки быстро нажимаем Shift-Shift и вводим команду Push:

Push в меню
Push в меню

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

Git Push
Git Push

Что такое ветки в Git

Когда мы делаем коммит, а потом ещё коммит и ещё, образуется цепочка коммитов. Такая цепочка (а точнее, указатель на её конец) называется веткой.

В любом репозитории есть ветка master - основная ветка, куда все коммиты попадают по умолчанию. Каждая ветка имеет свою копию, называющуюся origin (например, "origin/master"). Origin-ветки - это ветки на сервере, соответствующие одноимённым локальным веткам. В окне Git Log можно убедиться, что наша master была запушена на сервер - концы веток master и origin/master совпадают:

-8

Когда ветка запушена не полностью, то origin-ветка заканчивается раньше, чем обычная. Когда ветка не запушена совсем, то origin-ветка не существует.

Чтобы работать в команде, каждый разработчик создаёт ветку для каждой своей задачи. Таким образом, разные разработчики работают в разных ветках и не мешают друг другу. Но, более того, вы можете бросить недоделанную задачу и приступить к более срочной, просто переключившись между ветками, и недоделанные изменения не будут вам мешать. Разработчики ответвляются от одной из основных веток (например, master), а затем их ветки вливаются обратно в основные ветки, где накапливаются сделанные всеми разработчиками изменения:

Ветки в Git Log
Ветки в Git Log

Переключение между ветками и вообще все операции с ними осуществляются в верхнем меню:

Меню веток
Меню веток

Обратите внимание, что хотя ветка master локальная (находится в разделе Local), на ней справа написано "origin/master" - это значит, что она привязана к такой ветке на сервере. Не путайте локальные и удалённые ветки!

Командная работа c Git

Создаём ветку

Итак, предположим, что нам дали какую-то задачу. Идём в веб-интерфейс сервера (тренироваться можно, создав private-репозиторий на https://github.com). На вкладке Issues (задачи) нажимаем New issue, заполняем заголовок задачи и нажимаем кнопку Create:

Создание задачи на Github
Создание задачи на Github

Далее создаём ветку в этой задаче, нажав ссылку Create branch:

Создаём ветку, связанную с задачей
Создаём ветку, связанную с задачей

Обычно в командах ветки называют по номеру задачи, поэтому меняем название ветки на просто "1". Убеждаемся, что ветка начинается от master (на реальных проектах часто бывает несколько основных веток, откуда надо начинать свои ветки):

Настройки новой ветки
Настройки новой ветки

После этого вы увидите всплывающее окно с инструкциями для консольного гита. Но поскольку мы пользуемся обёрткой над ним, нам это не нужно:

Ненужный совет
Ненужный совет

Обновляем список веток и переключаемся на новую

Итак, ветка на сервере создана. Но если мы пойдём в проект, то в меню вверху такой ветки не будет, потому что Git кеширует всё у себя в локальной папке, в том числе весь Git Log. Чтобы загрузить новые данные с сервера, нажмём кнопку Fetch в меню веток:

Как обновить кеш гита
Как обновить кеш гита

Далее найдём нужную ветку с помощью поиска и нажмём Checkout (переключиться):

Как переключиться на нужную ветку в гите
Как переключиться на нужную ветку в гите

На этом меню теперь должен быть заголовок "1", показывающий, что теперь текущая ветка - "1".

Работаем в команде с помощью Git

Изменим какие-нибудь файлы и сделаем Commit & Push. Теперь можно переключиться обратно на master, для чего в меню мастера нажмите Checkout. При этом весь код в проекте примет состояние ветки master, исчезнет то, что мы сделали в ветке "1". А когда сделаем Checkout обратно, то всё вернётся!

Таким образом, ветки позволяют программисту переключиться на более срочную задачу, не доделав текущую (текущая останется в своей ветке, а новая будет в своей).

Но ветки помогают также и работать в команде. Представим, что мы - другой разработчик. Склонируем проект к себе в другую папку (как это сделать, смотрите в https://dzen.ru/a/aBIJ0yCbeS1gSzFe#klonirovanie_repozitoriya). Создадим задачу № 2 и ветку "2" для неё. Обновим список веток и переключимся на "2". Поменяем в коде одну из тех строчек, что мы меняли в "1", но на что-то другое, и сделаем Commit & Push.

Вернёмся обратно в папку первого разработчика. В Git Log мы не увидим то, что сделал второй. Даже кнопка Refresh не поможет. Это потому, что весь Git Log на самом деле закеширован в локальной папке. Чтобы обновить Git Log, нужно нажать ту же самую Fetch в меню веток вверху.

Итак, два разработчика начали свои ветки от master:

Так выглядит командная работа в гите
Так выглядит командная работа в гите

Бейджики на коммитах показывают, где заканчивается каждая ветка. Ветка master заканчивается на коммите "Ещё изменения для master", и от неё отходят две ветки - "1" и "2". В ветке "1" два коммита, в ветке "origin/2" - один.

Объединяем изменения нескольких разработчиков

Идём в веб-интерфейс сервера. Переходим на вкладку Pull Requests (запросы на вытягивание). Жмём кнопку New Pull Request:

-18

Выбираем ветки: слева - куда надо вливать изменения, справа - откуда (стрелочка показывает направление):

-19

Вы увидете коммиты и сравнение кода. Перепроверив всё, жмём Create pull request. В открывшемся окне заполняем заголовок и описание. В описании принято указывать, что именно было сделано в данной ветке и почему вы сделали это так, а не иначе (проверяющие любят это спрашивать). Ещё раз жмём Create pull request.

Теперь проверяющие (то есть, мы сами) могут посмотреть наш запрос: тут есть вкладка с коммитами и вкладка с изменёнными файлами. Проверяющие могут задать нам какие-нибудь вопросы насчёт нашего кода в создавшемся чате. И, конечно, тут же есть большая зелёная кнопка Merge pull request. По нажатию на неё открывается окошко, где можно отредактировать сообщение для нового коммита. Этот новый коммит будет объединять в себе две ветки - "1" и master:

-20

Подтверждая операцию, мы наконец вливаем ветку "1" в master. Итак, в большинстве случаев объединение веток происходит в пару кликов, без необходимости редактировать код вручную.

Обновив Git Log с помощью Fetch, мы увидим, что ветка origin/master на сервере теперь заканчивается на новом коммите, в котором сливаются две ветки, master и "1" (жёлтая и фиолетовая):

Ветка "1" влита в origin/master
Ветка "1" влита в origin/master

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

Как решать конфликты в гите

Однако, у нас не получится проделать то же самое с веткой "2", потому что мы изменили в ней ту же самую строчку, что и в ветке "1". Эти изменения противоречат друг другу, и Git не знает, какое именно из них взять. Это называется конфликт. Мы всё равно можем создать Pull request, но не можем его закончить.

Нужно объединить эти ветки вручную. Мы не можем влить нашу ветку "2" в master, потому что работать напрямую в master обычно запрещено. Поэтому остаётся только сделать наоборот: влить master в нашу ветку. Для этого в IntelliJIDEA / PyCharm / Rider нажимаем быстро Shift-Shift и вводим Pull. Команда Pull вливает выбранную ветку в вашу текущую, причём берёт свежую версию ветки с сервера, так что вам не нужно заботиться об обновлении кеша. Мы должны влить master в нашу ветку "2", поэтому вводим в окне "master". Во время слияния всплывёт окно с конфликтами:

Список файлов с конфликтами
Список файлов с конфликтами

Выберем файл и нажмём кнопку Merge (слияние). Откроектся окно с тремя версиями файла:

-23

Слева - наши локальные изменения, справа - изменения с сервера. По центру - версия, которая должна получиться в итоге. Можно нажимать на стрелочки, забирая изменения, или на крестики, отменяя их. Также можно редактировать итоговую версию вручную.

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

Например, слева мы добавили "Изменения для задачи №2" и "Ещё изменения для задачи №2", а справа с сервера пришли "Изменения для задачи №1" и "Ещё изменения для задачи №1". Конфликтуют между собой (красные) только изменения в последней строчке (которые "Ещё..."). Заберём "Ещё изменения для задачи №1" и припишем туда вручную "и №2". Отменим изменение слева, чтобы показать, что оно нам больше не нужно. А дальше нажмём на кнопку со стрелочками вверху, чтобы все остальные (неконфликтные) изменения применились автоматически. Всплывёт подсказка "All changes have been processed" ("Все изменения были обработаны") со ссылкой "Save changes and finish merging" ("Сохранить изменения и завершить слияние"). Можно нажать на эту ссылку или на кнопку Apply внизу, чтобы завершить слияние файла.

Как решать конфликты в гите
Как решать конфликты в гите

Если вы слили какой-то файл неправильно, то вы можете отменить целиком весь мерж (Abort merge в меню веток) и сделать Pull заново.

После мержа что-то в коде может сломаться, поэтому будьте внимательны! Поэтому принято собрать и протестировать приложение заново.

Получившийся коммит с объединением двух веток нужно запушить (быстро Shift-Shift и "Push"), иначе сервер его не увидит. Дальше можно завершить pull request обычным образом.

Учимся разбираться в Git Log

Разберём, почему каждая из веток заканчивается именно там, где висит бейджик:

После мержа 2 в master
После мержа 2 в master

Локальная ветка master осталась там, где мы сделали последний коммит для неё. Ветки origin/1 (фиолетовая) и origin/2 (красная) заканчиваются на своих последних коммитах. Ветка origin/master (жёлтая) заканчивается коммитом, вливающим в неё ветку 1. Этот коммит принадлежит ветке origin/master (куда вливали), но не 1 (что вливали). Ветка, которую вливали, осталась без изменений. Наконец, мы влили master в 2. Аналогично, этот коммит принадлежит ветке 2, но не master.

Если мы далее завершим pull request ветки 2, то получится вот что:

После мержа 2 в master
После мержа 2 в master

Ветка origin/1 фиолетовая, ветка origin/2 - синяя, origin/master - жёлтая. Читая снизу вверх, мы видим, что сначала ветка origin/1 была влита в origin/master, затем origin/master была влита в origin/2, а затем origin/2 была влита в origin/master. Итого в origin/master есть все изменения от обоих веток.

Заключение

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

В Git есть и много других функций, которые не нужны для повседневного использования. О них я расскажу дальше.