ХХ полезных советов для пользователей Git среднего уровня. Часть 2

:

Это продолжение статьи ХХ полезных советов для пользователей Git среднего уровня

Про reset, незапланированно снова про альясы, про замечательный filter-branch, про мерджи и разрешение конфликтов с помощью rerere, про rebase (интерактивный и не очень) и, в завершение, про обслуживание своей гитницы.

1. Где у Git`a reset

Если брать самые частые случаи, когда нужно так или иначе отменить последний коммит или изменения после оного, то это будет команды git reset (она же git reset --mixed HEAD) и git reset --hard. Первая удаляет из индекса последний коммит, но не трогает изменения в файлах, вторая же беспощадно приводит и индекс, и файлы к первозданному виду^W^Wсостоянию на момент указанного коммита.
Иными словами, отменить правки, пусть даже и за`stage`нные через git add, можно сделав $ git reset HEAD (помните альяс git unstage?).
А полностью дропнуть, скажем, последний коммит (т.е. отмотать время на момент предпоследнего коммита) — $ git reset --hard HEAD^.
2. Еще альясы

По итогам изучения history|grep " git "| sort -d|uniq, я добавил еще один альяс — $ git config --global alias.amend 'commit --amend -C HEAD' — по команде git amend перезаписывается последний коммит с тем же сообщением.
Возможно, добавлю туда ключ -a (чтобы не делать git add каждый раз).
3. filter-branch — спасение для растяпы!

git filter-branch позволяет более чем широкие возможности для манипуляций с историей.
Я познакомился с этой командой, когда заметил, что храню боевые пароли не во внутренней ветке, а в мастере.
Слава Богу, что заметил я это перед пушем в общедоступный репозитарий!
$ git filter-branch --tree-filter «sed -e 's#my_secret_pass#dbpassword#' -e 's#my_db_hostdbhost.tld#' -i settings.py» HEAD
Сим нехитрым действием я переписал пароли в файле настроек в каждом коммите.

Можно сужать область применения, задавая уточняющие параметры --index-filter, --msg-filter, --commit-filter--tag-name-filter и другие.
--all означает все бранчи.

4. И снова про мерджи.

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

Итак, встречайте — git rerere — позволяет записывать решение конфликтов и при повторном мерже использовать их.
Включается так — $ git config --global rerere.enabled 1

Полезно, если есть долгоживущий бранч разработки, который точно конфликтует с другим бранчем (скажем, мастером), то «обучение» гита телодвижениям при мердже происходит в такой последовательности: сначала делаем git pull origin master, устраняем конфликты, коммитим и откатываем тестовое слияние обратно — $ git reset --hard HEAD^. При настоящем слиянии будут автоматически проделаны все те же телодвижения, что мы сделали руками.

Вообще-то git rerere запускается без вмешательства пользователя и без аргументов, но можно подкорректировать ход работы:
Команды: git rerere [clear|diff|status|gc]

clear — Ресет метаданных, используемых rerere, если автоматическое решение конфликта отменяется.
Например, git am [--skip|--abort] или git rebase [--skip|--abort] автоматически использует этот параметр.
diff — Отображение диффа для текущего состояния разрешения конфликта. Полезно для отслеживания что изменилось за время устранения конфликта. дополнительные параметры передаются прямо команде diff.
В отличие от diff, выводит только имена файлов, которые отслеживаются для разрешения конфликта.
gc — Удаляет записи конфликтных слияний, которые имели место быть много времени назад. По умолчанию, неразрешенные конфликты старше 15ти дней и разрешенные старше 60ти дней вычищаются. Эти значения описаны в gc.rerereunresolved и gc.rerereresolved.

5. Про rebase

Про ребэйз уже было сказано, что эта команда позволяет «переместить» сделанные изменения наверх, поместив в историю коммиты из мастера, которые «ушли вперед» за время внесения локальных правок.

Есть еще одно интересное применение — rebase --onto.
Таким образом можно перенести свои коммиты, основываясь на совершенно третьей сторонней ветке.
Например, есть такая картина:
.-x------------master
.   |
.   \ -----server-experiments
.       |
.       \---http-interface

Чтобы смерджить ветку с новым интерфейсом в мастер, оставив игрища с серверной частью, надо сделать
$ git rebase --onto master server-experiments http-interface

После этого можно переключаться в мастер и мерджить ветки (git checkout master; git merge http-interface) — по итогу у нас есть основная ветка, влитый в неё новёхонький интерфейс и совершенно отдельно — оставшаяся ветка с экспериментами над серверной частью.
PROFIT!

Можно ребейзить не всё подряд, а интерактивно и выборочно — для этого нужна опция -i (--interactive).
$ git rebase -i master откроет редактор со списком указанных коммитов вида «command SHA commit_message».
Команды бывают:
p|pick — использовать коммит;
e|edit — использовать коммит, но сделать паузу ради --amend (процесс остановится перед следующим пунктом, чтобы можно было внести какие-то свои особые правки — например, разбить изменение на более мелкие коммиты)
s|squash — использовать коммит, но объединить с предыдущим коммитом. (в процессе будет открыто окно редактора, чтобы можно было соорудить общий commit message. Этакий глобальный commit --amend)

Удаление линии приведёт к удалению коммита.

Продолжить естественный ход вещей можно с помощью $ git rebase --continue

6. Обслуживание

Если идёт активная разработка и вы не пушите код на удалённые сервер, то стоит периодически выполнять сборку мусора:
$ git gc
Это подчистит мусор — удалит ни к чему не привязанные объекты и эффективно (пере-|у-)пакует оставшиеся.
Я сказал про удалённые сервера, потому что перед пушем на другой сервер объекты обрабатываются автоматически.

Вот, кажется, и всё, что я хотел бы сказать по поводу гита)