Это продолжение статьи ХХ полезных советов для пользователей Git среднего уровня
Про reset, незапланированно снова про альясы, про замечательный filter-branch, про мерджи и разрешение конфликтов с помощью rerere, про rebase (интерактивный и не очень) и, в завершение, про обслуживание своей гитницы.
Если брать самые частые случаи, когда нужно так или иначе отменить последний коммит или изменения после оного, то это будет команды git reset (она же git reset --mixed HEAD) и git reset --hard. Первая удаляет из индекса последний коммит, но не трогает изменения в файлах, вторая же беспощадно приводит и индекс, и файлы к первозданному виду^W^Wсостоянию на момент указанного коммита.
Иными словами, отменить правки, пусть даже и за`stage`нные через git add, можно сделав $ git reset HEAD (помните альяс git unstage?).
А полностью дропнуть, скажем, последний коммит (т.е. отмотать время на момент предпоследнего коммита) — $ git reset --hard HEAD^.
По итогам изучения history|grep " git "| sort -d|uniq, я добавил еще один альяс — $ git config --global alias.amend 'commit --amend -C HEAD' — по команде git amend перезаписывается последний коммит с тем же сообщением.
Возможно, добавлю туда ключ -a (чтобы не делать git add каждый раз).
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 означает все бранчи.
Спасибо 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.
Про ребэйз уже было сказано, что эта команда позволяет «переместить» сделанные изменения наверх, поместив в историю коммиты из мастера, которые «ушли вперед» за время внесения локальных правок.
Есть еще одно интересное применение — 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
Если идёт активная разработка и вы не пушите код на удалённые сервер, то стоит периодически выполнять сборку мусора:
$ git gc
Это подчистит мусор — удалит ни к чему не привязанные объекты и эффективно (пере-|у-)пакует оставшиеся.
Я сказал про удалённые сервера, потому что перед пушем на другой сервер объекты обрабатываются автоматически.
Вот, кажется, и всё, что я хотел бы сказать по поводу гита)
Про 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
Это подчистит мусор — удалит ни к чему не привязанные объекты и эффективно (пере-|у-)пакует оставшиеся.
Я сказал про удалённые сервера, потому что перед пушем на другой сервер объекты обрабатываются автоматически.
Вот, кажется, и всё, что я хотел бы сказать по поводу гита)