Вчера на Хабре вышла замечательная статья с очень красивым посылом – Know your tools. Но тема была практически не раскрыта. А особенно меня поразило то, как статью про костыли и базовые функции Гита, позитивно восприняло сообщество. Плюсы лились. Я же поставил заслуженный минус, пробежался по комментариям и нашёл из сотни всего 2 штуки, упоминающих то, о чём сегодня пойдёт речь.
Я предполагаю, что описанными в предыдущей статье приёмами разработчики и без того пользуются ежедневно. Но когда приходится иметь с чем-то дело так часто, невольно задумываешься об оптимизации. И общепринятой оптимизацией работы с Гитом является использования Zsh с плагинами.
Посплю сегодня поменьше, зато вы узнаете как работать с Гитом со скоростью ниндзя, пользуясь различными хаками, которые многие годы собирало и нарабатывало для нас сообщество.
Подготовка
В среде разработчиков стандартом альтернативы Bash
является Zsh
. А в среде пользователей Zsh
стандартом является использование Oh-my-zsh
. Таким образом, установив этот комплект, мы из коробки получим повышение продуктивности работы с консолью – в нашем случае с Гитом.
Самая большая фишка Zsh
, что он есть и для Linux, и для Mac.
Устанавливаем Zsh
и Oh-my-zsh
по инструкции одной командой:
brew install zsh zsh-completions && sh -c "$(curl -fsSL https://raw.githubusercontent.com/robbyrussell/oh-my-zsh/master/tools/install.sh)"
Поскольку задача – оптимизировать взаимодействие с Гитом, добавим к Zsh
пару плагинов. Откройте файл ~/.zshrc
и добавьте к списку plugins
:
plugins=(git fastgit)
Итого:
git
– набор алиасов и вспомогательных функци;fastgit
– улучшенное автодополнение для Гита.
И последний штрих:
brew install tig
О нём поговорим дальше.
Варианты использования
Разбираться с Гитом лучше всего на примерах решения конкретных задач. Далее рассмотрим задачи из ежедневной практики с вариантами удобного решения на примере некого репозитория с текстовыми файлами.
Создаём коммит
Начнём с самой базовой вещи – добавления коммита. Первым делом воспользуемся самой частой командой, которая поможет понять, что вообще происходит:
gsb # git status -sb
## master
M b.md
?? d.md
Ага, мы находимся в ветке master
, изменили файл b.md
и создали файл, который ещё не добавлен в индекс Гита. Коротко и ясно.
Добавляем все изменения в индекс:
gaa # git add --all
Проверяем, что в индекс попало именно то, что нам нужно:
diff --git a/b.md b/b.md
index 698d533..cf20072 100644
--- a/b.md
+++ b/b.md
@@ -1,3 +1,3 @@
# Beta
-Next step.
+Next step really hard.
diff --git a/d.md b/d.md
new file mode 100644
index 0000000..9e3752e
--- /dev/null
+++ b/d.md
@@ -0,0 +1,3 @@
+# Delta
+
+Body of article.
Хм, в один коммит должны попадать изменения, решающие единственную задачу. Здесь же изменения обоих файлов никак не связаны между собой. Давайте пока исключим файл d.md
из индекса:
gru d.md # git reset -- d.md
И создадим коммит:
gc
Пишем название коммита и сохраняем. А следом создаём ещё один коммит для файла d.md
:
gaa
gcmsg "Add new file"
Здесь мы воспользовались алиасом для создания коммита вместе с названием в одну строчку.
А ещё мы можем...
… коммитить изменённые файлы из индекса одной командой:
gcam "Add changes" # git commit -a -m "Add changes"
… смотреть изменения по словам вместо строк (очень полезно при работе с текстом):
gdw # git diff --word-diff
… добавлять файлы по частям:
gapa # git add --patch
… добавлять в индекс только файлы, уже находящиеся под наблюдением Гита:
gau # git add --update
Исправляем коммит
Название последнего коммита не объясняет сделанных нами изменений. Давайте переформулируем:
gc! # git commit -v --amend
И в открывшемся текстовом редакторе назовём его более понятно: "Add Delta article"
.
А ещё мы можем...
… более гибко пользоваться исправлением:
gcn! # git commit -v --no-edit --amend
gca! # git commit -v -a --amend
gcan! # git commit -v -a --no-edit --amend
gcans! # git commit -v -a -s --no-edit --amend
Начинаем работать над новой фичей
Создаём новую ветку от текущей:
gcb erlang
Хотя нет, лучше напишем статью про Elixir:
gb -m elixir # git branch -m elixir
Вносим изменения в репозиторий и создаём коммит как уже умеем:
echo "# Elixir\nWunsh.ru – русскоязычное сообщество Эликсир-разработчиков." > e.md
gaa && gcmsg "Add article about Elixir and Wunsh.ru"
Сливаем изменения
Теперь добавляем нашу новую статью об Эликсире в master
. Сначала переключимся на основную ветку:
gcm # git checkout master
И мерджим изменения:
gm elixir # git merge elixir
Упс, а в master
кто-то уже успел внести свои изменения. И вместо красивой линейной истории, которая принята у нас в проекте, создался ненавистный мердж-коммит.
Удаляем последний коммит
Ничего страшного! Нужно просто удалить последний коммит и попробовать слить изменения ещё раз.
git reset --hard HEAD~
А вот и повод для контрибьюта в Zsh: никакого удобного сокращения для удаления последнего коммита не предусмотрено.
Решаем конфликты
Стандартная последовательность действий checkout – rebase – merge
для подготовки линейной истории изменений выполняется следующими алиасами:
gco elixir # git checkout elixir
grb master # git rebase master
gcm # git checkout master
gm elixir # git merge elixir
И не забывайте, что можно дополнять названия веток клавишей Tab
.
Отправка изменений на сервер
Сначала добавляем origin
, а затем отправляем изменения напрямую в текущую ветку:
gra origin git@gitlab.com/...
ggpush
Вы также можете...
… отправить изменения на сервер с установкой upstream
:
gpsup # git push --set-upstream origin $(git_current_branch)
Получаем изменения с сервера
Работа кипит. Мы успели добавить новую статью f.md
в master
, а наши коллеги изменить статью a.md
и отправить это изменение на сервер. Эта ситуация тоже решается очень просто:
gup # git pull --rebase
После чего можно спокойно отправлять изменения на сервер и никто никогда не узнает о нашем маленьком ребейзе.
Удаляем слитые ветки
Итак, мы успешно влили в master
несколько веток, в том числе и ветку elixir
из предшествующего примера. Они нам больше не нужны. Можно удалять:
gbda # git branch --no-color --merged | command grep -vE "^(\*|\s*(master|develop|dev)\s*$)" | command xargs -n 1 git branch -d
Очень красивая и хитрая команда. Обычно я забываю очищать потерявшие актуальность ветки и эта изящная команда – настоящее спасение.
Создаём временный коммит
Работа над новой статьёй h.md
про Haskell идёт полным ходом. Написана половина и нужно получить отзыв от коллеги. Недолго думая набираем:
gwip # git add -A; git rm $(git ls-files --deleted) 2> /dev/null; git commit --no-verify -m "--wip-- [skip ci]"
И тут же создаётся коммит с названием Work in Progress, пропускающим CI и удаляющим "лишние" файлы.
Затем этот коммит можно отменить и вернуть файлы в исходное состояние:
gunwip # git log -n 1 | grep -q -c "\-\-wip\-\-" && git reset HEAD~1
А проверить, если в вашей ветке WIP
-коммиты можно командой:
work_in_progress
Команда gwip
– довольно надёжный аналог stash
, когда нужно переключиться на соседнюю ветку. Но в Zsh
есть много алиасов и для самого stash
.
Прячем изменения
С этой командой нужно быть осторожным. Файлы можно спрятать, а затем неаккуратным действием удалить насовсем, благо есть reflog
, в котором можно попытаться найти потерянные наработки.
Давайте спрячем файлы, над которыми работаем:
gsta # git stash save
А затем вернём их обратно:
gstp # git stash pop
Или более безопасным методом:
gstaa # git stash apply
Вы также можете ...
… посмотреть, что конкретно мы припрятали:
gsts # git stash show --text
… воспользоваться сокращениями для связанных команд:
gstc # git stash clear
gstd # git stash drop
gstl # git stash list
Ищем баг
Инструмент git-bisect
, который неоднократно спасал мне жизнь, тоже имеет свои алиасы. Начинаем с запуска процедуры "двоичного поиска ошибки" и сообщения Гиту о "плохом" коммите:
gbss && gbsb
Затем прыгаем на столько шагов, сколько будут гарантировать нам рабочее состояние приложения:
gco HEAD~20
gbsg
А теперь остаётся продолжать отвечать на вопросы Гита фразами gbsb
или gbsg
, а после нахождения виновника сбросить процедуру:
gbsr
Ищем зачинщика беспредела
Даже с высоким процентом покрытия кода тестами, никто не застрахован от ситуации, когда приложение падает и любезно указывает на конкретную строчку с ошибкой. Или, например, в нашем случае мы хотим узнать, кто допустил ошибку во второй строчке файла a.md
. Для этого выполните команду:
gbl a.md -L 2
Сегодня к вечеру добавлю ещё несколько интересных алиасов и приёмов, которые пригодятся в ежедневном использовании Гита. Следите за обновлениями статьи.
Заключение
Не обязательно запоминать все описанные в статье команды. Запомните десяток основных. Уже их хватит, чтобы значительно оптимизировать процесс работы с Гитом. Остальные изучите ради интереса. Некоторые алиасы сделаны действительно очень нетривиально, но оказываются очень полезными на практике.
Также всегда полезно изучить профессионально подобранные команды Гита с ключами, которые не принято использовать при обычной работе с Гитом, но которые сильно упрощают работу и позволяют решить задачу точнее.
Теперь со спокойной совестью можно сказать: Know your tools. Конечно, если завтра кто-то не решит описать в третьей части свою версию эффективного использования Гита. А было бы интересно :)
Комментарии (7)
Eldhenn
31.08.2017 06:32+1В ответ на статью о базовом использовании гита появилась статья о базовом использовании гита. Зато с zsh альясами!!!
Lure_of_Chaos
31.08.2017 07:08+2Действительно. Не в обиду автору, но очень уж сие лицепредставление напоминает фаллометрию.
Talkerbox
31.08.2017 07:30+3Кажется, статья описывает, как обмазать гит алиасами. И лишь немного дополняет тему самого Гита из предыдущей статьи.
shybovycha
31.08.2017 07:58+2… более гибко пользоваться исправлением:
gcn! # git commit -v --no-edit --amend gca! # git commit -v -a --amend gcan! # git commit -v -a --no-edit --amend gcans! # git commit -v -a -s --no-edit --amend
Что тут происходит?
Вот сижу на работе, думаю, "дай посмотрю, может какие-то действительно интересные трюки найдутся", а тут — какой-то набор непонятных сокращений без внятных комментариев.
shpaker
31.08.2017 08:26+2Какой-то ад буквенный. Не надо так. Возможно так и удобно пользоваться, но для туториала это как-то слишком за гранью.
Druu
У ниндзи серьезные проблемы со скоростью печати, по-этому он использует невнятные однобуквенные сокращения? Особенно весело в этой одной букве ошибиться и сделать gstc вместо gsta.
Lure_of_Chaos
Но для начала придется хорошенько выучить эти алясы
и при этом не призвать демона.Не говоря уж о том, что в качестве подготовки нужно это все хозяйство доустанавливать. Это не проблема на домашнем компе,
имея черный пояспользуясь так н-ный год. Но вот если потребуется подшаманить где-нибудь через схху, вспомнит ли мозг,покалеченныйразбалованный алясами и прочими удобствами стандартные пути?