Концепции Git для опытных пользователей

0
534
views

Перевод статьи «Git Concepts I Wish I Knew Years Ago».

Самая используемая разработчиками технология это вовсе не JavaScript.

Это не Python и не HTML.

Эту технологию даже практически не упоминают на собеседованиях или в объявлениях о вакансиях.

Я говорю о Git и системах контроля версий в целом.

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

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

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

Команды Git

Логирование

Что я только что сделал?

git log
git log --oneline # more succinct output
git log --graph # with a visual graph of branches

Просмотреть вашу «undo»-историю

git reflog

Иногда git log не справляется, особенно это касается команд, которые не показываются в истории коммитов. В таких случаях вам поможет reflog.

Команда reflog это ваша страховочная сетка после запуска «страшных» команд, таких как git rebase. С ее помощью вы увидите не только сделанные вами коммиты, но и все действия, которые привели вас к той точке, на которой вы находитесь. Узнать больше об этой команде можно из статьи Atlassian.

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

git status

Хотя git status это одна из базовых команд, которые все мы изучаем в числе первых, она очень важна. Эта команда поможет вам сориентироваться в сложных случаях rebase или merge.

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

git diff --staged # для изменений в стейджинге
git diff # для изменений не в стейджинге

Навигация

Хочу посмотреть, что я делал раньше

git reset <commit-sha>

Эта команда уберет указанные изменения из коммита и стейджинга, но файлы останутся в рабочей директории.

Вернуться к предыдущей ветке

git checkout -

Изменения

Я закопался по уши, давайте-ка начнем сначала

git reset --hard HEAD

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

Я хочу вернуть файл в первоначальное состояние

git checkout -- <bad-filename>

Я хочу отменить последний коммит и переписать историю

git reset --hard HEAD~1

Я хочу вернуться на n коммитов назад

git reset --hard HEAD~n # n это последние n коммитов
git reset --hard <commit-sha> # или вернуться к конкретному коммиту

Между soft, mixed и hard reset есть важные отличия. Если брать в целом,

  1. --soft: убирает изменения из коммита, но сохраняет их в стейджинге.
  2. --mixed (по умолчанию): изменения убираются из коммита и стейджинга, но остаются в рабочей директории.
  3. --hard: изменения убираются из коммита, из стейджинга и из рабочей директории.

Я перезаписал историю и теперь хочу отправить эти изменения в удаленный репозиторий

git push -f

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

Я хочу добавить еще несколько изменений в последний коммит

git commit --amend

Хочу переписать несколько коммитов локально

git rebase -i <commit hash> # где commit hash это коммит перед всеми изменениями, которые вы хотите сделать

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

Я думаю, что rebase это одна из самых запутанных тем при изучении Git. Дальше мы еще к ней вернемся.

Этот rebase ни к чему хорошему не приведет, давайте его прервем

git rebase --abort

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

Я хочу внести коммит из другой ветки

# Запускается из ветки, в которую вы хотите внести коммит 
git cherry-pick <commit-sha>

Я хочу внести конкретный файл из другой ветки

git checkout <имя-ветки> <имя-файла>

Я больше не хочу отслеживать файл в системе контроля версий

git rm --cached <имя-файла>

Мне нужно переключиться между ветками, но мое текущее состояние нарушено

(Прим. ред. Techrocks: подробнее о git stash читайте здесь).

git stash # сохраняет ваши изменения сверху стека stash
git stash save "сообщение, касающееся изменений"
git stash -u # stash untracked files as well

Я хочу посмотреть, что у меня в stash

git stash list

Хочу вернуть что-то из stash

git stash pop # "выталкивает" самый недавний элемент, добавленный в стек stash
git stash apply stash@{stash_index} # применяет указанный элемент в stash (из git stash list)

Я хочу отменить коммит без переписывания истории

git revert HEAD # отменить последний коммит 
git revert <commit hash> # для конкретного коммита

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

Уборка

Photo by Oliver Hale on Unsplash

О боже, почему у меня так много веток?

git branch --no-color --merged | command grep -vE "^(\+|\*|\s*(master|develop|dev)\s*$)" | command xargs -n 1 git branch -d

Таким образом вы удалите все слитые ветки, которые хранятся у вас локально, кроме веток master, developer или dev. Если ваша основная ветка и ветки разработки называются как-то иначе, — отредактируйте регулярное выражение для grep.

Эта команда слишком длинна для запоминания, но вы можете назначить для нее псевдоним (alias):

alias gbda='git branch --no-color --merged | command grep -vE "^(\+|\*|\s*(master|develop|dev)\s*$)" | command xargs -n 1 git branch -d'

Если вы используете Oh My Zosh, все это уже сделано за вас. Подробнее — в разделе про псевдонимы.

Отправляем в мусор старые ветки и лишние коммиты

git fetch --all --prune

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

Псевдонимы

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

Можно сделать еще лучше: установить инструмент вроде Oh My Zosh для Z shell (Zsh), и вы получите набор уже готовых псевдонимов для распространенных команд Git. Я вот слишком ленив, чтобы настраивать свою оболочку в точности по своему вкусу, так что предпочитаю пользоваться инструментами, которые делают это за меня. Кстати, Oh My Zosh и выглядит красиво. Вот несколько моих любимых псевдонимов команд, которыми я пользуюсь ежедневно:

gst - git status 
gc - git commit 
gaa - git add --all 
gco - git checkout 
gp - git push 
gl - git pull 
gcb - git checkout -b 
gm - git merge 
grb - git rebase 
gpsup - git push --set-upstream origin $(current_branch) 
gbda - git branch --no-color --merged | command grep -vE "^(\+|\*|\s*(master|develop|dev)\s*$)" | command xargs -n 1 git branch -d 
gfa - git fetch --all --prune

Если забудете, как выглядит полная команда, для которой создан тот или иной псевдоним, всегда можно подсмотреть:

alias

Или поискать по конкретному псевдониму:

alias grep <имя-псевдонима>

Другие приемы работы с Git

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

Многие файлы не предназначены для контроля версий. К ним относятся директории node_modules, .vscode или файлы других IDE и виртуальных сред Python. Для настройки игнорирования файлов следует использовать global gitignore.

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

Особые файлы

Вы можете пометить отдельные файлы как бинарные, чтобы Git игнорировал их и не продуцировал длинные diff-ы. Для этой цели в Git есть файл .gitattributes. В JavaScript-проекте вы можете захотеть внести в этот файл ваш yarn-lock.json или package-lock.json, чтобы Git не пытался сделать diff каждый раз, как вы вносите изменение.

# .gitattributes
package-lock.json binary

Рабочие процессы Git

Photo by Yancy Min on Unsplash

Rebase vs Merge

В вашей команде может быть принято применять или rebase, или merge. Каждый подход имеет свои достоинства и недостатки, и мне доводилось видеть эффективное использование как одного, так и другого. Чаще всего, пожалуй, наиболее оптимальным будет использование merge. Это если не считать случаев, когда вы действительно знаете, что делаете.

Но даже если вы в основном используете merge, все равно можно пользоваться также и rebase. Наиболее распространенный случай эффективного применения rebase — когда вы работаете над каким-нибудь функционалом, а другой разработчик в это время добавил свою функцию в master. Вы, конечно, можете использовать git merge для внесения своих изменений, но теперь у вас есть один дополнительный коммит с изменениями, внесенными вашим товарищем по команде. Что вам на самом деле нужно сделать, это перенести свои коммиты на верх новой ветки master.

git rebase master

Таким образом у вас будет куда более чистая история коммитов.

Чтобы объяснить разницу, нужна отдельная статья, и я ее обязательно напишу, но пока можете почитать, что на этот счет говорится в документации Atlassian. (Прим. ред. Techrocks: на нашем сайте есть статья о git rebase и статья со сравнением revert, checkout, reset, merge и rebase).

Настройки удаленного репозитория

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

1. Удаление веток при слиянии

Когда все изменения уже слиты, вам больше не нужны отдельные ветки, ведь история должна отражаться в вашей ветке master/dev. Такой подход существенно сокращает количество веток, которыми вам нужно управлять. Также благодаря этому команда git fetch --all --prune становится более эффективной в деле поддержки чистоты вашего локального репозитория.

2. Предотвращение отправки изменений напрямую в master

Без этого вы рискуете допустить весьма распространенную ошибку. Вы можете забыть, что находитесь в master, и выполнить git push, что потенциально способно сломать продакшен-сборку. А это не хорошо.

3. Необходимость получения как минимум одного одобрения перед слиянием

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

4. Необходимость прохождения CI-тестов для слияния

Изменения с дефектами не должны попадать в продакшен. Ревьюеры не всегда способны выловить 100% дефектов, так что стоит автоматизировать проверку.

Названия веток

Для облегчения навигации ваша команда может принять собственное соглашение об именах веток. Мне нравится начинать каждую ветку с первых буква имени и фамилии, за которыми следует косая черта и описание ветки (слова разделяются дефисами).

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

Например, я создаю новую ветку:

git checkout -b gabud/implement-important-feature

Неделю спустя, когда я забуду, как назвал ветку, я смогу начать вводить git checkout gabud (имя автора статьи — Gabriel Abud, — прим. перев.), нажать TAB — и моя Z shell (оболочка) покажет мне все мои локальные ветки. Таким образом я смогу выбрать нужную именно среди своих веток, не путая их с ветками коллег.

Сообщения коммитов

Язык имеет значение. В целом я считаю за лучшее не отправлять в репозиторий ничего сломанного, а каждый коммит сопровождать кратким сообщением о том, какие изменения вносятся. В соответствии с официальными рекомендациями Git, я предпочитаю использовать повелительное наклонение в настоящем времени. Каждое сообщение коммита я формулирую как команду для компьютера/git, которую можно добавить в конец этого предложения:

If this commit were applied, it would…

(Если этот коммит будет применен, он…)

Вот пример хорошего сообщения коммита в настоящем времени и повелительном наклонении:

git commit -m "Add a name field to the checkout form"

Теперь полная фраза будет читаться так: «If this commit were applied, it would add a name field to the checkout form» («Если этот коммит будет применен, он добавит поле имени в форму проверки»). Красиво и понятно.

Итоги

Это, безусловно, далеко не все возможности Git, которые следует изучить. Чтобы узнать больше, я советую заглянуть в официальную документацию и использовать git help. Не бойтесь просить совета у коллег: вы не поверите, сколько из них терзаются теми же вопросами.

ОСТАВЬТЕ ОТВЕТ

Please enter your comment!
Please enter your name here