Улучшаем процесс работы с Git

Photo by Quang Nguyen Vinh from Pexels

Представьте: вас вызвали, чтобы разобраться с инцидентом в продакшене. Покопавшись, вы установили, какой коммит сломал код. Вы решаете откатить эти изменения:

$ git revert 1a2b3c

К сожалению, в результате появляется новый баг! Оказывается, в этом старом «сломанном» коммите был припрятан код, от которого зависела другая часть кодовой базы. И после отката изменений сайт все равно остался в нерабочем состоянии.

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

От редакции Techrocks. Что касается отката изменений, рекомендуем статьи:

Обычный процесс работы с Git

Давайте рассмотрим, как чаще всего происходит работа с Git при создании новой функции:

  1. Вы создаете новую ветку из main.
  2. Сохраняя свою работу и попутно исправляя встреченные баги, вы делаете коммиты.
  3. Когда с функцией покончено, делаете пул-реквест.
  4. Когда пул-реквест одобрен, делаете мерж ветки в main.

Эта процедура наверняка вам знакома. Многих из нас учили работать с Git именно таким образом. Но такой подход имеет два слабых места.

Во-первых, если вы делаете коммиты по ходу дела, некоторые из них могут содержать незаконченные варианты работы. Это делает откат довольно рискованным.

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

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

А насколько было бы лучше, если бы каждый коммит касался только одного изменения! Но этого трудно достичь, когда вы в процессе разработки. Постоянное виляние в стороны и переписывание отдельных частей — неотъемлемая часть процесса. Наша работа редко бывает линейной, и это отражается на наших коммитах.

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

От редакции Techrocks. О другом подходе к организации работы с Git можно почитать в статьях «Что такое Trunk Based Development (TBD)?» и «Мнение: пушить сразу в мастер — хорошо. Обсуждаем Trunk Based Development».

Улучшенный процесс работы с Git

В основе следующего подхода лежит рабочий процесс моего коллеги, Дэна Вендорфа. Его работа с Git построена на ключевом принципе: сначала делаешь работу, а коммиты подчищаешь потом.

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

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

Улучшенный рабочий процесс можно разбить на три шага.

Шаг 1. Вносим изменения

Здесь, в принципе, все остается, как при обычном рабочем процессе. Начинаем с создания новой ветки и вносим в нее нужные изменения.

На этом этапе не стоит уделять слишком много внимания написанию содержательных сообщений коммитов. Они все равно не войдут в итоговый пул-реквест. Пока что будет достаточно простого «work in progress» («WIP»), или любого другого сообщения. Например: «WIP: Started building new model» («Работа ведется: начато создание новой модели»).

Назначение этих коммитов — не потеряться в проделанной работе и очертить ее общие направления.

$ git checkout -b my-feature-branch

...make changes...

$ git commit -m"WIP"

...make more changes...

$ git commit -m"WIP"

...make even more changes...

$ git commit -m"WIP"

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

Шаг 2. Сброс

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

$ git reset origin/main

Без дополнительных аргументов git reset не изменяет рабочее дерево, так что ваш код будет в сохранности. Но поскольку вы сбрасываете все до более давнего коммита, git status будет показывать все изменения, которые вы внесли с начала работы над функцией. Это как будто вы сделали всю нужную работу с кодом, но не делали никаких «WIP»-коммитов.

$ git reset origin/main
Unstaged changes after reset:
M       src/components/Footer/Footer.tsx
M       src/components/Nav/Nav.css
M       src/components/Nav/Nav.tsx
M       src/components/Posts/Post.tsx
M       src/components/Posts/PostList.tsx

$ git status
On branch feature-branch
Your branch is behind 'origin/feature-branch' by 3 commits, and can be fast-forwarded.
  (use "git pull" to update your local branch)

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:   src/components/Footer/Footer.tsx
        modified:   src/components/Nav/Nav.css
        modified:   src/components/Nav/Nav.tsx
        modified:   src/components/Posts/Post.tsx
        modified:   src/components/Posts/PostList.tsx

Не бойтесь сбрасывать коммиты! Вы всегда можете их вернуть. Каждый сделанный вами коммит остается в папке .git, даже после применения reset. Это только кажется, что они исчезают, на самом деле они на месте, просто спрятаны.

Если вы хотите вернуться к коммиту, на котором все работало, git reflog покажет вам таймлайн всех коммитов, на которые вы ссылались в своем локальном репозитории, даже между ветками. Запустите git reflog, чтобы найти коммит, к которому вы хотите вернуться, а затем git reset <commit-sha>. Эта команда переведет HEAD текущей ветки на этот коммит.

Теперь мы готовы делать новые коммиты.

Шаг 3. Делаем новые, логически сгруппированные коммиты

Теперь просмотрите все файлы, которые вы изменили. Можно ли что-нибудь логически сгруппировать? Например, все обновления зависимостей или изменения, относящиеся к конкретной модели. Здесь нет какого-то единственно правильного метода группировки, вы делаете это на свой вкус. Добавьте выбранные файлы в стейджинг и сделайте коммит с описанием изменений.

$ git add src/components/Nav/Nav.css
$ git add src/components/Nav/Nav.tsx
$ git commit -m"Added new styles to navigation"

$ git add src/components/Posts/Post.tsx
$ git add src/components/Posts/PostList.tsx
$ git commit -m"Updated author images on posts"

$ git add src/components/Footer/Footer.tsx
$ git commit -m"Fixed responsive bug in footer"

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

Что, если в одном файле есть ряд изменений, которые должны входить в разные группы? Вы можете поместить в стейджинг часть файла, используя git add --patch (или git add -p). Некоторые редакторы кода также позволяют помещать в стейджинг диапазон изменений, а не целый файл.

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

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

Если окажется, что в коммит нужно включить еще какой-то файл, выполните git stash pop, чтобы вернуть спрятанные изменения, затем добавьте в стейджинг пропущенный файл (git add) и выполните git commit --amend . Последний коммит будет заменен новым, с тем же описанием. Он будет включать и старый коммит, и изменение, которое вы только что внесли.

Итоговый результат

Разделив свою работу на логически сгруппированные коммиты, вы будете готовы сделать пул-реквест! Итоговый результат — набор изменений, которые ваш коллега сможет просмотреть коммит за коммитом.

Преимущество такой работы с Git в том, что учитывается обычная текучка задач и вместе с тем есть возможность поддерживать порядок в истории репозитория.

Перевод статьи «Git Organized: A Better Git Flow».

[customscript]techrocks_custom_after_post_html[/customscript]

[customscript]techrocks_custom_script[/customscript]

Оставьте комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Прокрутить вверх