усы2

Стой под стрелой

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

Previous Entry Поделиться Next Entry
Принципиальный недостаток Git CLI
усы2
tonsky

Есть простой способ использовать Git: работаем только в ветке, мержим только в мастер. Если все идет хорошо, то выстраивается простой и понятный ритуал: branch, commit, checkout, merge, push, повторить. GitFlow называется, да?

Для такого сценария Git CLI достаточно. Для всего остального — недостаточно. Странно, что люди до GitFlow вообще пытались как-то по-другому с Git работать через CLI. Ну невозможно это.

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

В таких случаях важно понимать, что происходит. Я, например, читаю историю ветки и пытаюсь воссоздать ход мыслей и направление изменений автора, с которым у меня возник конфликт. Чтобы решить конфликт правильно и осмысленно, мне надо пересмотреть всю историю ветки, прочитать диффы, посмотреть что вообще происходило и в какой последовательности. Просто смотреть на бесконтекстный 3-way diff — не вариант.

Простой и красивый вывод легкозапоминающейся команды git log --pretty=format:"%h - %an, %ar : %s" --graph:

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

К сожалению, CLI в этом никак не помогает. Ты находишься в полной темноте. Единственное, что понятно: что-то сломалось. Сообщение совсем неадекватное, или вообще нет сообщения, просто «конфликт» и предлагается выбрать между двумя непонятно откуда взявшимися альтернативами. Я много раз наблюдал, как люди просто впадали в панику и ступор и не понимали, что делать дальше. У таких случаев всегда есть объяснение, и оно обычно совсем несложное, объяснимое, логичное даже, но только если разобраться. Не ситуация сложная, сложно её понять, сидя в CLI. Черт, во всех компаниях, где я работал, я очень быстро становился негласным экспертом по таким вот нестандартным разбирательствам ¯\_(ツ)_/¯

Итак, Git CLI работает только когда четко понимаешь, что происходит. Отсюда нужда в GitFlow: он сильно ограничивает пространство возможных ситуаций, поэтому когда shit hits the fan, у тебя ограниченное количество гипотез о происходящем. Паники меньше, шансов угадать и принять правильное решение больше.

Типичный репозиторий, разрабатываемый по GitFlow

Вообще-то все git-овые best practices оттуда же: не юзать rebase, бояться force push, всегда создавать merge commit, не переписывать историю. Если их нарушать, мир не рухнет, ничего страшного не случится. Но — проблема — коллеги не поймут, что происходит. Если бы они в таких ситуациях видели, что происходит, то разобраться с ними — раз плюнуть. Проблема только в том, чтобы их идентифицировать.

Есть еще одна стратегия поведения в сложных ситуациях. Я называю её «мёржить до последнего». Если ты видишь, что что-то где-то не сходится, мёржи всё со всем, пока не останется одна единственная версия.

Макклейн мёржит крупными мазками

Проблемы:

  1. Возможно излишнее количество мержей (по сравнению с четким пониманием ситуации и точным хирургическим вмешательством).

  2. Никто не возьмется предсказать, что выживет в результате. Мёрж — нетривиальная операция и момент повышенной опасности. Конфликты не на пустом месте возникают: это моменты, когда два предложенных способа решения проблемы не подошли и надо на месте придумать третий. Не просто выбрать из предложенных, а проявить креатив: создать что-то новое, что вобрало бы в себя свойства двух старых версий, сохранило их дух. Поэтому критически важно четко и однозначно понимать, что происходит и как действовать. Если ты не понимаешь, что с чем ты мержишь и почему, шансов принять правильное осмысленное решение очень мало.

Поэтому я так скептически отношусь Git CLI: он устроен так, что пытается «уберечь» вас от сложных ситуаций прямо сейчас, но каждым своим действием усложняет ситуацию на потом. Скажем, git pull по-умолчанию будет мержить, если обнаружит, что ваша ветка разошлась с удаленной. Это «прячет» от вас сложный факт того, что ветки могут расходиться, но мир устроен именно так — они могут и будут расходиться, это нормально. Абстракция, которая пытается это скрыть — дырявая. Зато упрощает ваше существование, потому что у вас якобы всегда будет одна «главная» версия.

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

Фрагмент истории LightTable. Это ещё по-божески

Как тогда жить?

  • Визуальное представление графа коммитов. Многие GUI клиенты неплохо справляются
  • Fetch вместо Pull
  • Не бояться amend, rebase и вообще двигать историю

Про «церковь непереписывания истории». Особого смысла сохранять каждый чих в репозиторий ровно в том виде и тогда, когда он на самом деле произошел, нет. Наоборот: историю надо чистить, пропалывать и приводить в удобоваримый вид. Всякие коммиты типа «поработал», «поработал еще», «баг», «ой бля» в ретроспективе никому и никогда не будут уже нужны.

Бывает, люди путают git log с личным дневником и записывают свои мысли, настроения, погоду на улице

История должна быть осмысленной, атомарной (по еденицам решаемых проблем), аккуратной. Это всё окупится сто раз, когда с репозиторием придется по-настоящему работать. Git, его история — это не просто safety net, это еще один инструмент, еще одна форма существования кода, часть продукта. Вы же следите за кодом? Вот и за историей надо следить.

А практика непереписывания история имеет смысл только если у вас нет инструмента понять, что происходит вокруг, и вам лень разбираться. Сама по себе она безобидна и в целом работает на упрощение истории.


  • 1
(Анонимно)
Мы у себя выработали практику, при которой все вот эти merge-коммиты - грех. Потому что реального смысла, с позиции развития проекта, эта точка в истории вообще не несет. Используем pull rebase - история получается линейной, по ней можно безопасно ходить туда-сюда.

merge commit нужен только когда вливается результат ветки -- как это делает github.


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

> иначе проблем огребете еще больше

Вот-вот, я про это суеверие. Чего огребете-то? Единственные проблемы, которые это может вызвать — это у людей, которые сидят в CLI, не смотрят графический лог и умеют только pull и merge

без темы (Анонимно) Развернуть
А прелесть CLI в том, что можно хуяк-хуяк и любую тулзу поверх него соорудить.. надо вам просматривать историю изменений файла коммит за коммитом - пара строк и готово!

Нельзя. Git CLI — это интерфейс пользователя, ужасно неудобный для автоматизации. Отсюда libgit2, например

без темы (Анонимно) Развернуть
без темы (Анонимно) Развернуть
без темы (Анонимно) Развернуть
без темы (Анонимно) Развернуть
без темы (Анонимно) Развернуть
без темы (Анонимно) Развернуть
без темы (Анонимно) Развернуть
без темы (Анонимно) Развернуть
без темы (Анонимно) Развернуть
без темы (Анонимно) Развернуть
(Анонимно)
Замечание сугубо по терминологии - если feature бранчи мержатся в мастер то это не гитфлоу. Гитфлоу это более сложная херь со второй "персистентной" веткой develop, куда мержатся feature бранчи и которая в свою очередь при релизах мержится в мастер.

без темы (Анонимно) Развернуть
Проблема с переписыванием истории в том, что начинаешь бояться деструктивных действий.

А-ля «этот пекедж вообще тут неведомо зачем, снесу-ка я его нахер!». А потом, через два месяца: «ой, понадобился метод, я точно помню, что он был в том пекедже — а где же он?»

Если история не переписывалась, найти тот код я смогу тривиально. А если переписывалась, то он засквошился и пропал НАВСЕГДА, понимаете. Форева. Я такого вот не люблю — нафига нужен version control, если там нельзя откатиться точно к произвольно выбранному состоянию?

Edited at 2017-06-23 16:04 (UTC)

Обычно при переписывании схлопываются короткоживущие промежуточные куски, всё-таки. Я ж не предлагаю всегда делать amend, чтобы один коммит в репозитории жил. Ну и плюс переписывание — это всё-таки про приведение истории в линейный порядок/подчищение косяков, а не удаление чего-то навсегда

Edited at 2017-06-23 16:15 (UTC)

без темы (Анонимно) Развернуть
без темы (Анонимно) Развернуть
без темы (Анонимно) Развернуть
без темы (Анонимно) Развернуть
без темы (Анонимно) Развернуть

А как же git reflog?

(Анонимно)
А как же git reflog, по которому всё как раз таки видно и понятно, в читабельном виде!

Re: А как же git reflog?

Что именно по нему видно?

(Анонимно)
По сути же - давно использую практику "в небольших feature ветках перед мержем делаем интерактив ребейз со сквашем большинства комито в в несколько контрольных точек(как правило в один коммит) и с запрещением ребейз где бы то ни было ещё". Работает отлично.

Уже пару лет как живу последовательностью действий, описанной в самом начале. Ну, уходит мастер вперёд и иногда создаёт конфликты локальным изменениям, c'est la vie. Но их всё одно разрешать, т.к. обязательное ревью, и обязательные тесты, а не помойка. Коммиты преимущественно ограничиваются одним исправлением или одной фичей и разделяются на функциональные и наведение красоты без изменения функциональности (чистка комментариев, названий, расстановка книг по полкам кода по файлам в правильном порядке). Большие изменения бьются на независимые куски, ждущие своего часа, венца. Что-то хитрое тоже выкатывается начиная с подготовительных коммитов. Историю в мастере вроде не переписывают. Каких-то диких проблем с merge/rebase/cherry-pick не возникает. Обычные рабочие конфликты - нормуль. Что ещё... Можно посмотреть, что народ в очередь на ревью залил, и ещё до просачивания их кода в мастер понять масштаб бедствия или даже сделать у себя упреждающий локальный rebase с разрешением конфликтов. Читать код приходится полюбас, что с конфликтами, что без. Я не понимаю зачем кто-то что-то усложняет.

А, ну и revert и revert revert и т.д. честные в истории.

Наверное, хорошо, что я особо не знаю, как другие люди git используют. Так спится спокойнее.

Я-то сам исключительно "git add . && git commit -m A" и "git fetch && git rebase -i origin/master" живу (бранчи редко когда дольше нескольких дней задерживаются). Не помню, когда последний раз merge делал (кроме как merge commit через pull request).

Ну, это такое: что накодил, то и покоммитил. Я часто не коммичу, пока не доделаю фичу до конца, а потом все изменения начинаю группировать логически и коммитить отдельно. У меня git add --patch, git commit --fixup, git rebase -i --autosquash - это стандартный workflow. Иногда бывает, что какой-нть кусочек не в тот коммит включил.. Тогда приходится редактировать историю, разбивать коммит на несколько, эти маленькие кусочки переставлять и сквашить куда надо.

gitup

(Анонимно)
http://gitup.co

Удобная визуализация помогает разбираться.

Вот кстати про Gitup. Как им пользоваться-то вообще? Ветки он рисует, но что от них толку без коммит мессаджей? И чем его визуализация веток лучше любых других Git GUI? Что в нем такого особенного?


Re: gitup (Анонимно) Развернуть
> Типичный репозиторий, разрабатываемый по GitFlow

чтобы такого не было, придумали --topo-order

PS: собственно, на первом текстовом скриншоте оно и есть. Что там непонятного? Практически линейная история.

Edited at 2017-06-24 00:27 (UTC)

Это я очень простое место показал. Вот посложнее (то же самое, что на скриншоте с GitKraken-а):


У git log есть много ключиков, которые компенсируют его ненаглядность. --first-parent, --ancestry-path, --boundary, использование ".." или "...". Как правило, используемые гуи не дотягивают в этом отношении.

А так да, в гуе приятнее.

Ага, вот нехватало еще сидеть и ключики перебирать, мало я детективной работы делаю

> --pretty=format:"...%ar..."

Да, именно от вас этого и следовало ожидать. "10 months ago" - спасибо, очень информативно.

Это я первый попавшийся на SO вариант выложил. Сам я этим не пользуюсь, поэтому не разбирался

В качестве компромисса между GUI/CLI использую tig.
Быстро глянуть на дерево коммитов, посмотреть diff коммита, скопировать его хэш...
Пожалуй, в нём есть всё, что нужно от гуя.
Пару раз в год запускаю Qgit, чтобы посмотреть историю изменений файла в коммитах.


Edited at 2017-06-24 06:30 (UTC)

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

между прочим, "уборка в репозитории" никак не противоречит "history is sacred".

почему ни в одной VCS нельзя выбрать пачку коммитов с названиями вроде "wip wip" и добавить запись "вот эти коммиты показывать как один коммит, с комментом "фиксили баг PROJ-555""?

да и в других местах в самом концепте дофига и больше low-hanging fruit, честно говоря

А накидай еще low-hanging fruits, которые ты видишь? Про неразрушающие перестановки хорошая

Мне по душе следующие правила:

1) одна таска -- один коммит. комент начинается с номера таски. ниже в виде списка перечислены основные изменения.

2) автор вправе делать хоть сто комитов в локальной ветке, но при мердже они свошатся и приводятся к виду из п. 1

3) мердж-комиты запрещены. Лог проекта -- это последовательность тасок. ветки не мерджатся, просто сквош в мастер, ветка таски удаляется с сервера.


это простой и приятный воркфлоу, но я слабо представляю, как его использовать, если надо поддерживать несколько версий параллельно (feature flags не всегда достаточно)

(Анонимно)
Зачем так мучится?! Почему бы не использовать mercurial?

Какие из описанных проблем решает меркуриал?

Фигня какая-то.
Описываются трудности, которые могут возникнуть с любой DVCS, но наезд идёт даже не на конкретную реализацию DVCS, а на CLI-интерфейс.

Всё, что есть гуёвого для работы с git либо тормозит безбожно на нормальных объёмах, либо уёбищно как та же черепаха.

(Анонимно)
Проблема не только в Гите, но и в инструментах для мерджа. В подавляющем большинстве случаев мердж тривиален, потому что изменения не связаны, и мог бы происходить автоматически на уровне AST. Но когда изменения связаны, типа кто-то зарефакторил код, который ты используешь, попадаешь в world of pain, потому что надо мерджить не по одному файлику, а сразу все, и ни один из инструментов так не умеет.

надо так коммитить

(Анонимно)
git commit -m '$(curl -s http://whatthecommit.com/index.txt)'

  • 1
?

Log in

No account? Create an account