Вчера на Хабре вышла замечательная статья с очень красивым посылом – 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)


  1. Druu
    31.08.2017 03:56
    +11

    У ниндзи серьезные проблемы со скоростью печати, по-этому он использует невнятные однобуквенные сокращения? Особенно весело в этой одной букве ошибиться и сделать gstc вместо gsta.


    1. Lure_of_Chaos
      31.08.2017 07:06
      +1

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


  1. Eldhenn
    31.08.2017 06:32
    +1

    В ответ на статью о базовом использовании гита появилась статья о базовом использовании гита. Зато с zsh альясами!!!


    1. Lure_of_Chaos
      31.08.2017 07:08
      +2

      Действительно. Не в обиду автору, но очень уж сие лицепредставление напоминает фаллометрию.


  1. Talkerbox
    31.08.2017 07:30
    +3

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


  1. 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

    Что тут происходит?


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


  1. shpaker
    31.08.2017 08:26
    +2

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