Принципиальный недостаток Git CLI

Jun 23, 2017 18:20


Есть простой способ использовать 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, это еще один инструмент, еще одна форма существования кода, часть продукта. Вы же следите за кодом? Вот и за историей надо следить.

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

популярные заблуждения, девелопмент, инструментарий, интерфейсы, учу жизни

Previous post Next post
Up