company_banner

Почему нужно перестать использовать Git rebase

https://medium.com/bekk/why-you-should-stop-using-git-rebase-5552bee4fed1
  • Перевод


После нескольких лет работы с Git я обнаружил, что постепенно стал переходить на всё более сложные Git-команды в рабочем процессе. Вскоре после того как я открыл для себя Git rebase, я тоже быстро внедрил эту команду в повседневные задачи. Те, кто знаком с этой процедурой, знают, насколько это мощный инструмент и какой это соблазн — постоянно им пользоваться. Но вскоре оказалось, что rebase влечёт за собой ряд неочевидных на первый взгляд трудностей. Но прежде чем обсудить их, хочу быстро рассмотреть различия между merge и rebase.


Возьмём простой пример с интегрированием ветки feature в ветку master. При слиянии мы создаём новый коммит g, который представляет слияние между двумя ветками. График коммита ясно показывает, что произошло, и хорошо видны контуры графика «железнодорожных путей», знакомого нам по более крупным Git-репозиториям.



Пример слияния


Также перед слиянием можно выполнить rebase. Коммиты будут убраны, а ветка feature — сброшена в master, после чего все коммиты будут снова применены поверх feature. Диффы этих переприменённых коммитов обычно идентичны оригинальным, но у них будут другие родительские коммиты, а значит, и другие ключи SHA-1.



Пример rebase


Теперь мы изменили базовый коммит feature с b на c, то есть перебазировали. Слияние feature с master теперь выполняется ускоренно (fast-forward merge), потому что все коммиты feature — это прямые потомки master.



Пример ускоренного слияния


По сравнению с обычным слиянием мы получаем линейную историю изменений без ответвлений. Я предпочитал использовать rebase перед слиянием из-за улучшения читабельности и думал, что другие руководствуются той же причиной.


Но, как я упоминал, этот подход влечёт за собой ряд неочевидных сложностей.


Допустим, мы удалили из master зависимость, которая всё ещё используется в feature. Когда feature перебазируется в master, первый переприменённый коммит сломает вашу сборку, но если не будет конфликтов слияния, то процесс rebase продолжится. Ошибка из первого коммита останется во всех последующих, положив начало цепочке битых коммитов.


Эта ошибка проявится только после завершения процесса rebase, и обычно она исправляется с помощью нового bugfix-коммита g, применённого сверху.



Пример неудачного rebase


Но если при rebase возникают конфликты, Git поставит конфликтный коммит на паузу, позволив вам исправить проблему перед продолжением. Решение конфликтов посреди rebase длинной цепочки коммитов часто превращается в непростую задачу: с ней трудно справиться, не наделав новых ошибок.


Новые ошибки во время rebase — это очень большая проблема. Они возникают, когда вы переписываете историю, и могут скрыть от вас подлинные баги, которые были при первом переписывании истории. В частности, это усложнит использование Git bisect, одного из самых мощных инструментов отладки в инструментарии Git. Посмотрите на эту ветку фичи. Допустим, ближе к концу у нас появился баг.



Ветка с багом ближе к концу


Вы можете не знать о баге в течение недель после слияния ветки с master. Для обнаружения коммита, с которым был внесён этот баг, вам придётся перелопатить десятки или сотни коммитов. Процесс автоматизируется, если написать скрипт для тестирования на наличие бага и автоматически запускать его во время Git bisect с помощью команды git bisect run <yourtest.sh>.


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



Пример успешного выполнения Git bisect


С другой стороны, если мы при rebase внесли другие битые коммиты (в нашем примере это d и e), то у bisect будут трудности. Можно понадеяться, что Git идентифицирует коммит f как сбойный, но вместо этого он ошибочно выбирает d, потому что тот содержит какие-то другие ошибки, ломающие тест.



Пример сбойного Git bisect


Эта проблема гораздо важнее, чем может показаться.


Почему мы вообще используем Git? Потому что это наш самый важный инструмент для отслеживания источника багов в коде. Он наша страховочная сеть. При rebase мы снижаем важность этой роли Git ради желания получить линейную историю коммитов.


Некоторое время назад я применял bisect к нескольким сотням коммитов, чтобы найти баг в системе. Битый коммит находился в середине длинной цепочки коммитов. Она не компилировалась из-за сбойного rebase, выполненного моим коллегой. Этой ошибки можно было легко избежать, и я потратил почти день на её поиск.


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


Другой подход: вынудить Git становиться на паузу на каждом этапе процесса rebase, тестировать и немедленно исправлять баги, прежде чем продолжать дальше. Это неудобный вариант, чреватый внесением новых ошибок, так что прибегать к нему стоит лишь тогда, когда вам нужна линейная история. Есть более простой и надёжный способ?


Есть — Git merge. Простой процесс в один этап, при котором все конфликты разрешаются в единственном коммите. Получившийся коммит ясно обозначает точку интегрирования двух веток, а наша история отразит не только что произошло, но и когда.


Не нужно недооценивать важность сохранения достоверности истории. Rebase — это обман самого себя и своей команды. Вы притворяетесь, что коммиты были написаны сегодня, хотя по факту они написаны вчера на основе другого коммита. Вы вытащили коммиты из исходного контекста, завуалировав то, что произошло в действительности. Вы уверены, что код соберётся? Вы уверены, что сообщения коммитов всё ещё имеют смысл? Вы можете верить в то, что чистите и проясняете историю, но в результате добьётесь прямо противоположного.


Нельзя предсказать, какие ошибки и трудности появятся в вашей кодовой базе в будущем. Но будьте уверены — достоверная история полезнее переписанной (или фальшивой).


Что заставляет людей переносить ветки?


Думаю, тщеславие. Rebase — чисто эстетическая операция. Чистенькая история приятна нам как разработчикам, но это не может быть оправдано ни с технической точки зрения, ни с точки зрения функциональности.



Пример нелинейной истории


Графики нелинейной истории, «железнодорожные пути», могут выглядеть пугающе. Но нет никаких причин бояться их. По сути, это инструменты на основе GUI и CLI, позволяющие анализировать и визуализировать сложную Git-историю. Эти графики содержат ценную информацию о том, что и когда происходило, и мы ничего не получаем от превращения их в линейные.


Git создан для сохранения нелинейной истории, он это поощряет. Если вас это смущает, то лучше используйте более простой VCS, поддерживающий только линейную историю.


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

Я…

Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.

Mail.Ru Group 773,39
Строим Интернет
Поделиться публикацией
Похожие публикации
Комментарии 374
  • +44
    Я считаю, что лучше сохранять достоверность истории.

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


    Но нет никаких причин бояться их.

    Trunk-based development?

    • +8
      то есть вся проблема решается запретом на пуш с форсом в мастер, нет?

      Нет, это вообще не решает проблему. Допустим на локальном компьютере 10 коммитов. Программист делаем rebase на мастере и 5 из этих коммитов становятся поломаны. В 11 коммите он чинит поломку и делает push. force при этом пуше не нужен, всё пройдёт хорошо и битые коммиты окажутся в условно главном репозитории.


      если в ремоут все будет как если бы мы этим самым ребейзом никогда не пользовались?

      Не будет коммитов, которые явно ломают сборку?


      Trunk-based development?

      Это вы к чему? Так выглядит одна ветка, если в неё мёржили другие. Это не обязательно master.

      • +10
        Допустим на локальном компьютере 10 коммитов. Программист делаем rebase на мастере и 5 из этих коммитов становятся поломаны

        А с какого перепугу 5 стали поломаны?


        В 11 коммите он чинит поломку и делает push. force при этом пуше не нужен

        Если из 10 коммитов хотя бы один был на сервере, то нужен


        битые коммиты окажутся в условно главном репозитории.

        Но в локальной ветке. Потом программист сквошит коммиты и вливает одним merge чистенький и исправленный коммит.


        One feature — one commit


        Не будет коммитов, которые явно ломают сборку?

        squash/fixup

        • +6
          А с какого перепугу 5 стали поломаны?

          Этому вопросу посвящена немаленькая часть статьи :). Чтобы не быть голословным цитирую кусочек.


          Допустим, мы удалили из master зависимость, которая всё ещё используется в feature. Когда feature перебазируется в master, первый переприменённый коммит сломает вашу сборку, но если не будет конфликтов слияния, то процесс rebase продолжится. Ошибка из первого коммита останется во всех последующих, положив начало цепочке битых коммитов.

          Вот


          Если из 10 коммитов хотя бы один был на сервере, то нужен

          Ну вроде очевидно, что я говорю о ситуации, в которой на сервере нет ни одного из этих коммитов.


          One feature — one commit

          И таким образом программист прибьёт историю и понять какой код для чего коммитили будет непросто я против такого подхода.

          • +4
            Вот

            А без rebase после merge ошибка переедет в master — это просто восхитительно. Если же имеется в виду, что мы делаем git merge master, то ситуация никак не меняется, потому что после обратного merge в мастер у нас будет 10 битых коммитов и один merge commit с исправлениями. Вопрос: зачем нужны в истории 10 коммитов с ошибкой? Чтобы увлекательнее было лазить по коду с git bisect?


            И таким образом программист прибьёт историю и понять какой код для чего коммитили будет непросто я против такого подхода

            https://habrahabr.ru/company/mailru/blog/340558/.com[perevod]-pochemu-nuzhno-perestat-ispolz#comment_10486872


            С такой историей лучше уж никакой истории. Она только дичайше засоряет git log

            • +6
              Если же имеется в виду, что мы делаем git merge master, то ситуация никак не меняется, потому что после обратного merge в мастер у нас будет 10 битых коммитов и один merge commit с исправлениями.

              Нет, у нас будет 10 нормальных коммитов и, возможно один плохой сразу после мёржа.


              Вопрос: зачем нужны в истории 10 коммитов с ошибкой?

              10 коммитов с ошибкой могут появиться из-за rebase. Они, конечно, никому не нужны и поэтому rebase лучше избегать.


              С такой историей лучше уж никакой истории. Она только дичайше засоряет git log

              Я редко смотрю историю, чтобы получить понимание, как менялся код коммит за коммитом. Мне интересно увидеть что происходило в отдельном конкретном коммите. Для этого коммиты должны быть простыми и понятными.

              • –6
                Нет, у нас будет 10 нормальных коммитов и, возможно один плохой сразу после мёржа.

                Лол. Ну как так-то? Зависимость в мастере убрана ДО мерджа фичи. Фикс делался в самом конце. Следовательно все коммиты до мерджа с мастером будут нерабочими.


                10 коммитов с ошибкой могут появиться из-за rebase. Они, конечно, никому не нужны и поэтому rebase лучше избегать.

                Даже если все 10 коммитов будут сбойными, то rebase для того и создан, чтобы потом сделать fixup и получить на выходе один правильный коммит.


                Я редко смотрю историю, чтобы получить понимание, как менялся код коммит за коммитом. Мне интересно увидеть что происходило в отдельном конкретном коммите. Для этого коммиты должны быть простыми и понятными.

                Это нужно один единственный раз: во время code review. После этого все коммиты должны быть засквошены и смерджены.

                • +6
                  Лол. Ну как так-то? Зависимость в мастере убрана ДО мерджа фичи. Фикс делался в самом конце.

                  После мёржа в мастер коммиты какими были в ветке, такими и остались. Вы думаете код и мастера в них как-то проникнет? :)


                  Следовательно все коммиты до мерджа с мастером будут нерабочими.

                  Они не изменятся.


                  Это нужно один единственный раз: во время code review. После этого все коммиты должны быть засквошены и смерджены.

                  Это приводит к проблемам с пониманием что и зачем комитили.

                  • 0
                    После мёржа в мастер коммиты какими были в ветке, такими и остались. Вы думаете код и мастера в них как-то проникнет? :)

                    Это шутка? Какая мне разница, что сам по себе коммит в вакууме рабочий? Если он накладывается на мастер, где уже убрана нужная зависимость и значит все, что было, начиная отсюда — не работает. до тех пор, пока не был сделан 11-й коммит, который исправлял положение, внесенное мастером.


                    Это приводит к проблемам с пониманием что и зачем комитили.

                    Никаких проблем с пониманием, если в мастер приезжает один единственный коммит с хорошим commit message и ссылкой на таск.


                    В отличие, кстати, от ситуации, когда в мастер приезжает 11 коммитов с непонятно какими сообщениями (вы не можете быть уверены, что за все время работы над таском разработчик ни разу не закоммитился с ничего не значащим сообщением типа "fixed")

                    • +4
                      да вы издеваетесь что-ли? на сервере у вас так и будет родительский коммит у начала листа из 10 будет с фичей если вы будете сделаете git checkout sha1-от-того коммита то сборка у вас нормально соберется со старым мастером.
                      • +3
                        Если он накладывается на мастер, где уже убрана нужная зависимость и значит все, что было, начиная отсюда — не работает. до тех пор, пока не был сделан 11-й коммит, который исправлял положение, внесенное мастером.

                        Думаю, тут непонимание того, что именно означает "удалили зависимость". В статье этот момент действительно крайне непонятен, все домысливают по-своему.
                        Я бы понял это (предельно конструктивно по отношению к автору) так: есть некоторое API, реализуется подключенной библиотекой; в мастере убрали использование этой библиотеки и одновременно, или вслед за этим, убрали подключение библиотеки. Если разработка feature опиралась на эту библиотеку, то коммиты после rebase будут несобираемыми. Вот после этого, согласно автору статьи, делают правку типа "а теперь уберём вызовы того, чего уже нет".


                        Так вот — неправ тут именно автор, потому что если он требует рабочей истории для всяких bisect, то уже в коммите dʹ надо было требовать собираемости и тестируемости. Да, это требует чуть большей работы. Требуется добавить коммит, условно говоря, dʹfix1, перебазировать его по истории сразу после dʹ и слить с ним (squash/fixup), получив некий dʺ. Аналогично со следующими промежуточными. И уже такая история приемлема для отправки "наверх".


                        Когда я работал через Gerrit, то это было основным моим вариантом. Он допускает и построение коммита на основе достаточно старых версий (что неизбежно приводило к merge), но вариант с rebase требовал работы всех промежуточных стадий.


                        Никаких проблем с пониманием, если в мастер приезжает один единственный коммит с хорошим commit message и ссылкой на таск.

                        А вот это очень часто диверсия и делать так не надо. Потому что создание новой фичи может означать, например,


                        • несколько подготовительных рефакторингов
                        • исправление багов, найденных при рефакторинге или просто вычитке кода
                        • наконец, добавление функциональности для фичи

                        и потом склеивать это в один коммит означает получить дурнопахнущую нечитаемую кашу.


                        что за все время работы над таском разработчик ни разу не закоммитился с ничего не значащим сообщением типа "fixed")

                        Да он может хоть 100500 раз так делать. Но на экспорт другим он должен дать цепочку простых и понятных действий с чётким описанием, как для посторонних. Именно цепочку, а не один коммит.

                      • 0
                        После мёржа в мастер коммиты какими были в ветке, такими и остались. Вы думаете код и мастера в них как-то проникнет? :)

                        На мой взгляд, вы не понимаете фундаментальное понятие «ветка в git». Ветка — это всего лишь ссылка на какой-то коммит. Алиас, указатель, ссылка — не принципиально. Главное, что у ветки нет коммитов. Удалите ветку, но коммиты останутся.
                        Что значит «код мастера»? Мастер — это ветка, у нее нет кода. У цепочки коммитов, начинающейся с коммита, на который ссылается мастер будет только один коммит, содержащий патчи, которые разработчик в своей ветке делал. Это будет мердж коммит. Он будет содержать проблему, и у него будет «второй родительский» коммит, который со всеми своими предками содержит проблему. И фиксить в мастере вы будете мердж коммит, а не ветку разработчика.
                        • +5

                          Нет, это вы не понимаете о чем вам говорят.


                          Допустим, есть вот такая двойная цепочка коммитов (более старые коммиты ниже):


                            H
                           / \
                          F   G
                          |   |
                          D   E
                          |   |
                          B   C
                           \ /
                            A
                          

                          D и E скрыто конфликтуют, поэтому H содержит баг. Ни один другой коммит этого бага не содержит, потому что ни в каком коммите кроме H не присутствуют изменения внесенные D и E одновременно.


                          Именно это и говорит poxvuibr. А его оппонент, Yeah почему-то утверждает что после появления коммита H коммиты E и G тоже стали нерабочими. Как это так случилось — не спрашивайте, я тоже не понимаю.

                          • –2

                            А по каким коммитам и в какой последовательности пойдет git bisect в вашем примере?

                            • +1
                              Какая разница? Тут обсуждается ваша идея что merge оставит более одного коммита с багом а не другие аргументы.
                              • –1

                                Моя идея не в том, что будет более одного коммита с багом, а в том, что будет более одного нерабочего коммита. Постарайтесь понять разницу

                                • +3

                                  Какие из приведенных мною коммитов — нерабочие и почему?

                            • 0
                              Нет, это вы не понимаете о чем вам говорят.

                              Вы оспариваете только мою последнюю фразу, при этом заявляете, что я не прав в целом. Все что я сказал про бранчи — верно и «код мастера» — это чушь. А если Вы не согласны с утверждением про наследование проблемы, код коммитов D, E в студию, плиз.
                              • 0

                                Нет, я не оспариваю вашу последнюю фразу, я поясняю вам о чем тут вообще был спор.


                                В данном случае словами "мастер" и "фича" были обозначены те коммиты, которые писались в рамках этой ветки. Можно хоть 10 раз ветку удалить — коммиты от этого не пропадут, тут вы правильно заметили. Вот только разговор именно про коммиты и шел.


                                Процитированная вами фраза "После мёржа в мастер коммиты какими были в ветке, такими и остались" означает что после появления коммита H коммиты C, E, G не изменились, а потому если они ранее были классифицированы как "рабочие", они не могли оказаться нерабочими после выполнения merge.

                                • 0
                                  «код мастера» — это чушь.

                                  Под кодом мастера я имел в виду те коммиты, которые были в мастере до мёржа. Под кодом ветки — коммиты, которые были в ветке.


                                  А если Вы не согласны с утверждением про наследование проблемы, код коммитов D, E в студию, плиз.

                                  После мёржа коммиты, которые были в ветке не изменятся. Их можно будет чекаутить и спокойно собирать код. Ни один из них поломан не будет. Будет поломан мёрж коммит, но его можно поправить перед тем, как пушить.


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

                                  • –1
                                    чтобы в апстриме не было битых коммитов возможно каждый из них придётся поправить руками.

                                    Т.е. переписать историю? Вы же вроде против этого.

                                    Пример в студию.
                                    • 0
                                      Т.е. переписать историю? Вы же вроде против этого.

                                      Но это придётся сделать, чтобы все коммиты были нормальными. Я как раз предлагаю так не делать :).


                                      Правда, я не против переписывания истории, если коммиты не успели попасть в апстрим. Я против огромных коммитов. И ещё я против возни с проверкой коммитов, которые можно не проверять, если делаешь мёрж.


                                      Пример в студию.

                                      Сложно это :). Но давайте попробуем


                                      В коммите H есть функция mult, которая принимает int и умножает на 2


                                      int mult(int number) {
                                         return number*2;
                                      }

                                      В коммите G мы её используем в одном месте


                                      return mult(res);

                                      В коммитах E и С используем в двух других местах, не связанных с местом в G.


                                      А в коммите D в мастере стало понятно, что нужно добавлять множитель в параметры.


                                      - int mult(int number) {
                                      -  return number*2;
                                      +int mult(int number, int multiplier) {
                                      +  return number*multiplier;
                                      }

                                      Потом сделали ребейс ветки на B


                                      И получили несобираемый код. Причём не собирается ни один коммит в ветке. Нужно каждый изменить.


                                      Если делать мёрж из мастера, то можно изменить только мёрж коммит перед тем, как мёржить ветку в мастер.

                                      • 0
                                        В коммите H есть функция mult, которая принимает int и умножает на 2
                                        Замените «умножает на 2» (ну кому, в самом деле, такая функция нужна) на «умножает float'ы» и добавьте в вашем изменении в аргументы FPSCR (или MXCSR) — и вот у вас уже жизненный пример для эмулятора ARM'а или X86.

                                        Как видим от искусственного примера до реального — даже не шаг, а полшага…
                                        • –4
                                          > И получили несобираемый код. Причём не собирается ни один коммит в ветке. Нужно каждый изменить.

                                          Но количество изменений по сути — одинаковое в случае rebase и merge.
                                          При этом, в случае rebase они аккуратно оказались именно там, где вводится использование mult(), а в случае merge — они вообще непонятно где — их можно отловить только косвенно диффом между мастером и результатом мержа, в каше с другими 100500 изменениями.
                            • 0
                              Следовательно все коммиты до мерджа с мастером будут нерабочими.

                              Вот тут у вас логическая ошибка. При мерже локальной ветки в master, она должна быть заребейзена на мастер и пройти тесты.
                              • 0

                                У меня??? Вот это номер! Да я же тут за это и топлю! А вот автор статьи жестко ставит вопрос: rebase применять нельзя

                                • +1
                                  Ну уж не серчайте. Ошибся просто комментом, хотел на уровень выше ответить.
                        • +1
                          И таким образом программист прибьёт историю и понять какой код для чего коммитили будет непросто я против такого подхода.

                          это является проблемой только если у вас фичи не дробятся достаточно. Если у вас один коммит = одна фича = изменение парочки файлов не вижу проблемы разобраться.

                          • +1
                            это является проблемой только если у вас фичи не дробятся достаточно.

                            Если получается хорошо дробить фичи — не вижу проблем с тем, чтобы сквашить коммиты. Но у меня размер фич обычно больше, чем парочка файлов. Что, конечно, не может не печалить.

                            • 0

                              А самостоятельно дробить на подзадачи? Интересует как у других принято, скажем, вам же никто не запрещает дробить фичи на более мелкие фича брэнчи? Или жесткая привязка к тикетам в джире? Если так — можно ли самостоятельно дробить на подтаски? Или в таком случае затык может случиться на других этапах жизни таски?

                              • +1
                                Интересует как у других принято, скажем, вам же никто не запрещает дробить фичи на более мелкие фича брэнчи?

                                Часто в мастер всё равно фичу можно вливать только одним куском. Мелкие фича бренчи делай, но в мастер попадёт только та, что соответствует большой фиче.

                              • +2
                                Выглядит как перенос технических средств в организационные. Вместо 3 понятных последовательных коммитов 3 задачи, которые надо обязательно писать, и развесистое дерево веток.
                                • 0

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


                                  Я это к чему. Инструменты и практики призваны решать проблемы. Но зачастую проблемы как таковой и нет, и инструмент/практика выбираются по принципу "ну вроде так большинство делает".


                                  В этом отношении больше всего меня бесят любители всяких там scrum of scrum. Такое ощущение что кроме скрама мир ничего не придумал.

                                  • 0
                                    Тут игра слов. СКВ — это технические средства управления разработкой, задачи — организационные средства управления разработкой. Можно сказать «средства организации кода», для задач первое слово относится к работникам, поэтому тавтологии нет. Как-то так я это представляю.

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

                                      За счет чего? А как же быть с тем фактом. что поскольку у нас есть несколько версий кода которые работают, нам всеравно придется делать повторный регрес при сливании веток ибо "оно может внезапно перестать работать".


                                      Да и "одна ветка" — это целая куча подходов которые невилируют недостаток изоляции функционала. Начиная от feature toggle и заканчивая branch by abstraction.


                                      А разбивать одну задачу на три потому что «мы хотим один коммит на задачу»

                                      Я вопрос задавал не вам, меня конкретно интересовало насколько дают свободу разработчикам делать на их уровне дополнительную декомпозицию. Речь идет не о "один коммит на фичу" а "как сделать так что бы все фича брэнчи жили менее пары дней". Ибо если это возможно сделать, то можно дальше общаться на тему других подходов.

                                • 0
                                  Основной затык на этапах ревью и тестирования — отдавать нужно таску целиком.
                                  • +1

                                    Вопрос. Сколько времени в среднем в день уходит на ревью? Сколько времени проходит в среднем от создания мердж реквеста до вливания ветки в основную? Как часто происходят конфликты? Что вы делаете если задача в целом протестирована но кто-то успел влить в основную ветку какие-то другие изменения и теперь у вас конфликты? происходит ли ретест после разруливания конфликтов? Происходит ли после этого повторное ревью (как никак то что раньше имело смысл может потерять его в связи с новыми изменениями).


                                    p.s. из моего опыта — код ревью на этапе мердж реквеста показывает плохую эффективность, особенно в ситуациях когда команда может меняться (типичный аутсорс). Из того что пробовал — код ревью пост фактум + парное программирование с людьми которые запушили что-то не то намного больше выхлопа дает. Но это если есть такая возможность конечно.

                          • +1
                            Но в локальной ветке. Потом программист сквошит коммиты и вливает одним merge чистенький и исправленный коммит

                            Но что делать с ситуацией, когда спустя неделю требования поменялись и он понял, что изменения из 6,7,8 коммита уже не актуальны. Или, допустим, QA нашёл баг, который оказался именно в тех изменениях. В вашем случае ему будет сложнее откатывать(исправлять) изменения. А это, на самом деле, не такой редкий случай.
                          • +2
                            В 11 коммите он чинит поломку и делает push

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

                            Не будет коммитов, которые явно ломают сборку?

                            если будут, это неплохой повод обсудить внутри команды вопрос координации. Они у вас так и так будут, просто реакция на это все будет разной.


                            Это вы к чему?

                            Нет мерджей — нет проблем с мерджами. А изолировать изменения можно не только фича брэнчами. Что вы скажем думаете о подходах с CI/CD?

                            • +3
                              коммиты должны быть атомарны

                              Атомарность не нарушается.


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

                              Чего ради мучаться, если можно сделать мёрж?

                              • –1
                                Чего ради мучаться, если можно сделать мёрж?

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


                                В результате было принято решение всем работать в develop ветке и делать код фриз путем вмердживания (да да, merge --no-ff) в master для полноценного регрешен теста.


                                Тут сразу обусловлюсь что поскольку мы пишем чисто API и у нас есть мобилки, у меня по любому после каждого коммита должна быть соблюдена обратная совместимость. Так что с фича брэнчами или нет — мой флоу работы не сильно меняется с точки зрения разработки. Но это позволяет проще координировать действия между разработчиками (особенно когда их хотя бы больше 3-х и они не все сидят рядом), а в силу того что с одним и тем же кодом работает теперь больше людей (у нас увы покрытие кода тестами не такое большое как хотелось бы) — регрессии стали находить чаще и быстрее теми же силами.


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

                            • 0
                              В 11 коммите он чинит поломку и делает push.

                              Автор не осилил rebase interactive.

                              • 0
                                Автор не осилил rebase interactive.

                                Автору не хочется вносить правки в каждый коммит при ребейзе.

                            • +13
                              вот это дело. Как раз как у нас. В бранчах делай ребейз от мастера сколько душе угодно, а в мастер фича идет через git merge --squash. И никаких конфликтов, т.к. ветка с фичей постоянно ребейзилась с мастером.
                              • +2

                                А bisect потом работает? git может понять, что все коммиты из фича ветки есть в мастере и ветках, отбранченных от мастера?

                                • +8
                                  может навлеку на себя гнев, но я ни разу не пользовался bisect. Даже не в курсе, что это.
                                  git может понять, что все коммиты из фича ветки есть в мастере
                                  А ему это не надо. фича мержится в мастер с параметром --squash, это значит, что будет все одним коммитом. А бисект вы можете посмотреть в ветке, если нужны конкретные коммиты фичи.
                                  • +5
                                    А ему это не надо. фича мержится в мастер с параметром --squash, это значит, что будет все одним коммитом.

                                    Таким образом номальной истории у нас не будет и нормального bisect тоже. Я от этого отказываться не готов.

                                    • –4
                                      что такое нормальная история? Для меня нормальная история в мастере — линейная. А в бранче пусть зоопарк будет, я не против.
                                      • +2

                                        Нормальная история это такая история, с помощью которой можно понять что сделано в каждом коммите.

                                        • +14

                                          Разработчик работает над веткой. Тут он так писал, тут эдак, тут вообще рыбу заворачивал, тут новый фреймворк добавил, тут убрал. Зачем это в мастере?

                                          • 0
                                            Разработчик работает над веткой. Тут он так писал, тут эдак, тут вообще рыбу заворачивал, тут новый фреймворк добавил, тут убрал.

                                            Коммиты лучше бы делать осмысленно.

                                            • +1

                                              Сильно много "лучше бы". И коммиты подавай вам осмысленные и сообщения о коммитах пиши красивые. Но реальная разработка — она не такая. Часто приходится что-то пробовать, что-то откатывать. К исходу дня в локальной ветке может быть 15-20 коммитов, некоторые из которых прямо противоположны предыдущим. Что тогда?

                                              • +5
                                                И коммиты подавай вам осмысленные и сообщения о коммитах пиши красивые.

                                                И ещё хорошо бы код покрытый юнит тестами.


                                                Часто приходится что-то пробовать, что-то откатывать.

                                                Можно не комитить результаты проб и ошибок. Если закомитили — можно поправить историю.

                                                • +2

                                                  А можно вообще не коммитить, пока таск не сделаешь. Зачем нужен git? Будем коммитить один раз, когда все уже точно готово. :)

                                                  • –10
                                                    Ну так в том и смысл.
                                                    • +7
                                                      Зачем нужен git?
                                                      А вот на этот вопрос можно ответить совершенно точно: для того, чтобы можно было нарисовать красивую историю.

                                                      С небольшими, правильно выверенными, коммитами. С пониманием того, что, где и для чего делали. И с убранными «метаниями в поисках решения задачи».

                                                      Написание истории при работе с Git'ом (да даже и с SVN'ом) — такая же работа, как и написание кода.

                                                      Никто даже смотреть не будет на изменение в 1000 строк, которое вы предложите залить в Git (а разработка Git'а — это как бы эталонное применение Git'а, правильно?).

                                                      Его попросят разбить на части. Да-да, руками.
                                                      • 0
                                                        Мы используем геррит, коммит состоит из нескольких пач-сетов. Во время пуша коммиты амендятся (т.е. все коммиты идут в один и тот же ченьдж и являются пач-сетами), таким образом, в геррит-ревью получается несколько патч-сетов. Каждый патч-сет проходит через CI. В рамках одного такого ченьджа можно делать любые эксперименты, накапливая пач-сеты. На бранч сабмитится самый последний пач-сет, который ничего не ломает, содержит определенную, законченную работу. История коммитов в этом случае чистая и ясная. Рекомендую попробовать.
                                                        • 0
                                                          > Зачем нужен git? Будем коммитить один раз, когда все уже точно готово. :)

                                                          Чтобы не потерять результаты работы, раз. Чтобы было много чекпоинтов для собственного удобства и экспериментов, два.

                                                          Без git программировать — все равно что в хардкорные компьютерные игры без сохранений и чекпоинтов играть. Шаг влево, шаг вправо — расстрел и потеря времени и нервов.
                                                          • +1
                                                            Я поддерживаю: я не коммичу, пока не доделаю всю фичу целиком, а потом все изменения коммичу отдельными коммитами. Но даже при таком подходе, есть же еще код ревью, где Вам тоже накомментят и будете исправлять. И эти исправления надо делать fixup'ами (git commit --fixup) для соответствующих коммитов, а потом делать git rebase -i --autosquash (ну или всегда держать autosquash включенным, что я и делаю).

                                                            Считаю верхом безолаберности слать в мастер 100500 коммитов, где исправляются опечатки, форматирование, туда-сюда меняется реализация, «wip» коммиты. Никому не нужна вся эта драма.

                                                            Одна фича — один коммит — это тоже сказка и очень зависит от структуры проекта и того, как формулируются и детализируются фичи. Если, например, фичи высокоуровневые, их реализация может подразумевать изменения во многих подсистемах и логично оформлять изменения в разных подсистемах как отдельные коммиты.
                                                          • 0
                                                            Некоторые флоу предполагают обязательный коммит даже для интеграционного тестирования. И даже полный прогон юнит-тестов локально не всегда возможен и(или) разумен.
                                                          • 0
                                                            Можно пробовать и откатывать в отдельной ветке, а локальную вести так чтобы при слиянии в мастер не было стыдно за коммиты.
                                                            • +5

                                                              Ох жесть какая. То есть от нежелания делать rebase я должен заводить столько веток, сколько раз я захотел поэкспериментировать в локальной ветке?

                                                              • 0
                                                                не нравятся ветки — используй несколько сташей )
                                                            • +9
                                                              А тогда мы запускаем интерактивный rebase, выкидываем лишние коммиты, если надо, какие-то коммиты сквошим друг с другом и получаем нормальную историю с атомарными коммитами.
                                                              Это же классика работы с гитом. У себя в локальной ветке можно делать что угодно. Но наружу надо отдавать красивую цепочку атомарных коммитов.
                                                              • 0

                                                                И тогда вы используете interactive rebase, объединяя коммиты в атомарные и красиво оформленные. И потом это мерджите/ребейзите в мастер.
                                                                Я буду читать предыдущие комменты до конца.

                                                              • +2

                                                                Вот для их осмысления и существует rebase. Или вы к модели водопада предлагаете вернуться?

                                                                • +2
                                                                  Вот для их осмысления и существует rebase.

                                                                  Тут речь шла про squash. Использовать rebase для генерации осмысленной истории можно, но только тогда, когда rebase делается для того, чтобы переставить коммиты или удалить какие-то. rebase на мастер осмысленности коммитам не добавляет. Squash тем более.


                                                                  Или вы к модели водопада предлагаете вернуться?

                                                                  Какое отношение модель водопада имеет к проблемам с rebase?

                                                                  • –2

                                                                    И squash и fixup вполне могут добавить осмысленности, Вы просто в каких-то розовых облаках витаете, где каждый коммит в рабочей ветке идеально продуман, логически выверен и не имеет ни единой опечатки. Отсюда и связь с водопадом, потому что на практике такое корпение над каждым коммитом в рабочей ветке — это пустая трата времени.
                                                                    Объединение всех коммитов фичи в 1 — иногда оправдано, иногда — нет, но в целом если в ветке после rebase осталось больше 7 коммитов, то у вас что-то не так с определением границ фич.

                                                                    • 0
                                                                      «А ты чё говно-код написал? Это не говно-код, это жизнь» ©
                                                                      • +3

                                                                        Все какают, просто обычно делают это в специализированных местах и за закрытыми дверьми.

                                                                • 0
                                                                  К сожалению, не всегда получается осмысленно не забыть добавить файл в коммит. Не знаю, можно ли после пуша делать «git ci --amend» в понедельник утром, но точно не нужно. Сюда же идут всякие хотфиксы, когда очевидный баг не поймался тестами. По-моему, если в ветке 1-3 дня работы в сумме, то этому действительно не место в мастере.
                                                                  • +2
                                                                    Коммиты лучше бы делать осмысленно.

                                                                    Так это одна из причин, почему стоит использовать rebase. Пишите код, не задумываясь о истории, делайте коммиты с сообщениями "tmp", "Foo", "debug", "watafak". А когда созреете — git rebase -i, и причесываете историю.

                                                                  • 0
                                                                    Есть мнение, что все это важная информация и должно попадать в мастер. По крайней мере автор который это писал привел аргументы, которые меня убедили. Смысла делать «красивую историю» нет, потому что это вранье. Как все было на самом деле — так пусть и будет. Сделали коммит, который исправляет грамматическую ошибку в названии класса? Почему бы и нет. Коммит который не компилится? Да ради бога.

                                                                    Покуда мастер билдится в каждый момент времени всё хорошо. То есть нерабочая ветка быть может где-то в середине, но не перед мержем в мастер. И да, merge покрывает 100% задач и не вижу никакой причины почему бы пользоваться чем-то другим. Разрезолвить все конфликты за раз лично мне например намного проще, чем покоммитно.
                                                                    • 0
                                                                      Смысла делать «красивую историю» нет, потому что это вранье.
                                                                      Может это и «враньё», но это единственный способ потом разобраться в том, что происходит.

                                                                      Все успешные проекты рано или поздно переходят в состояние, когда 90% кода в них написано людьми, которые больше над проектом не работают (хотя бы потому, что люди смертны). Соотвественно спросить разработчиков о чём-либо нельзя. Что, в свою очередь, делает жизненно важным описание коммитов и понятность изменений, которые в этих коммитах были произведены. Ну а далее — всё просто: может у вас какие-нибудь супермены работают, я не знаю — но у нас не получается сразу «набело» все изменения делать, что при чтении промежуточных вариантов у читателя вопросов не возникало.
                                                                      • 0
                                                                        Лично мне часто жизненно необходимо посмотреть ВСЕ изменения файла, включая «рыбу заворачивали». Мб у меня просто такая маленькая кодовая база, что просмотреть все коммиты не проблема, но ни на одной из 4 работ где я участвовал это не нужно было.
                                                                      • 0

                                                                        Но есть и альтернативное мнение (которого я придерживаюсь), которое состоит в том, что коммиты в feature-ветке — это поток мыслей разработчика пока он работал над веткой. На эти мысли многое может оказывать влияние: погода, настроение, успехи/неуспехи в личной жизни, разработчик может ставить эксперименты и попросту хулиганить. И я вполне допускаю, что он имеет на все это право. Но в конечном итоге мне нужна готовая фича, а не его поток мыслей. Мне нужна квинтэссенция таска, выраженная в коде. И поэтому это должен быть один красивый и рабочий (желательно) коммит с красивым описанием и ссылкой на баг-трекер. А поток мыслей пусть останется с разработчиком как часть его личности, отраженная в его личностных воспоминаниях. В гите это все не нужно. ИМХО

                                                                        • 0
                                                                          И поэтому это должен быть один красивый и рабочий (желательно) коммит с красивым описанием и ссылкой на баг-трекер.
                                                                          И что вы будете делать с этим коммитом на 10000 строк, если он вам что-нибудь поломает?

                                                                          Я исхожу из того, что разработчики ядра используют git, в некотором смысле, эталонным образом. И вот у них одна фича почти никогда не бывает одним commit'ом. Обычно её просят разбить на лёгкие для понимания преобразования кода. Вот тут — мы добавили новый аргумент класс. Пустой пока. Тут — реализовали новую функциональность. Тут — исправили одного клиента. Там — второго. И в коммите номер 25 — удалили старую функциональность.

                                                                          Тогда в случае необходимости — можно сделать git bisect до небольшого, обозримого изменения. А не ковыряться в здоровенном изменении, которое в некоторых файлах может менять до 60% строк.
                                                                          • 0

                                                                            Неважно, сколько строк затронула фича. Важно какой процент от codebase это составило. Если 10000 строк — это 1% от всего проекта, значит это небольшая фича, которая отлично просматривается в истории. И да, при таком объеме кода эта фича должна быть полностью покрыта тестами, которые делают крайне маловероятной ситуацию "все поломалось". А если 10000 строк — это 50%+ вашего проекта, то это значит, что кто-то не умеет дробить фичи на части. Это уже не фича, а переписывание половины проекта и такого коммита, конечно же быть не должно. Но вторая ситуация встречается только у не слишком умных или дисциплинированных разработчиков, которые привыкли всю разработку вести в одной ветке, временами мерджа ее в мастер и продолжая дальше. Излишне говорить, что это пагубная практика и от нее нужно избавляться весьма решительным образом.

                                                                            • 0
                                                                              Очень часто правилным вариантом бывает вариант, который был где-то в середине истории, но был удален в итоговом коммите. Если бы сквашили коммиты, то эту информацию бы потеряли.
                                                                              • 0
                                                                                Если он правильный — то почему после него что-то ещё делали?

                                                                                На практике самое ужасное, что происходит — исчезновение «строительных лесов». То есть, грубо говоря, если вы переходите с BLAS на Eigen — то вы врял ди сделаете это за один день. Скорее всего вы сначала сделаете прослойку, которая позволит вам выбирать между BLAS и Eigen'ом (пусть даже она приведёт к повышенному расходу памяти и замедлению работы), потом, постепенно, переведёте всё с одной библиотеки на другую — ну и в самом конце «прослойку совместимости» (которая более не нужна и теперь только мешает) уберёте.

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

                                                                                Ну и какой тогда смысл в хранении истории, если ключевые моменты в ней прописаны недостаточно подробно для поиска ошибок?
                                                                                • 0
                                                                                  Разработчик мог решить «оптимизировать» и переписать медленный правильный вариант на быстрый неправильный и т.п. случаются странные вещи, когда время на таску еще не вышло, а беклог еще пустует. Кто-то говорит ПМу «у меня кончились задачи» и начинает читать хабр, а кто-то тратит время на допилку решения, во время которой может что-то сломать (и не протестировать по той или иной причине). Не очень надуманная причина, хотя и не самая редкая. Лично мне удобнее, когда коммит файла меняет 10-20 строк, а не его весь.
                                                                              • +4
                                                                                Неважно, сколько строк затронула фича. Важно какой процент от codebase это составило. Если 10000 строк — это 1% от всего проекта, значит это небольшая фича, которая отлично просматривается в истории.
                                                                                Гениально! Вы всерьёз считаете, что вот этот вот patch на 90 мегабайт — можно разбить на десяток «небольших фич, которые будут отлично просматриваться в истории»? Там изменено всего-то 5% проекта. Какие-то жалкие 59806 файлов изменены. Подумаешь. Мелочь какая. Даёшь change'и на 100000 строк каждый!

                                                                                Ну бред же! Размеры коммитов и фич должны разумно соотноситься с человеческими возможностями, а не с размерами проекта!

                                                                                И да, при таком объеме кода эта фича должна быть полностью покрыта тестами, которые делают крайне маловероятной ситуацию «все поломалось».
                                                                                Как показывает практике «полное покрытие тестами» ни разу не гарантирует, что у вас всё будет работать правильно — при условии, что у вас задача нетривиальна и в ней много взаимозавимимостей.

                                                                                Это уже не фича, а переписывание половины проекта и такого коммита, конечно же быть не должно.
                                                                                Опять-таки? С чего вы взяли? Если у вас был проект, помогающий вам «пасти котов» и там, через строчку, были классы «Cat», «CatBehavior» и тому подобные — а вы решили поддержать ещё и собак, то у вас легко может измениться половина строк в проекте из-за появления классов (интерфейсов в Java) «Animal», «AnimalBehavior» и тому подобное.

                                                                                На этом фоне жалкая горстка изменений, вводящих класс Dog- просто «утонут». И понять — где случился косяк и почему у нас собаки всё ещё имеют втягивающиеся когти будет не проще с историей, чем без неё, просто исследую код. Нафига такая история кому нужна???

                                                                                Но вторая ситуация встречается только у не слишком умных или дисциплинированных разработчиков, которые привыкли всю разработку вести в одной ветке, временами мерджа ее в мастер и продолжая дальше.
                                                                                Нет, такая ситуация встречается у нормальных разработчиков тоже. Переписывание половины проекта в рамках реализации одной фичи — всречается редко. А вот переписывание какого-нибудь файла целиком (или почти целиком) — очень даже. И если бы Alex Deucher (добавивший в вышеупомянутую «маленькую фичу на миллион строк добрые 300'000 оных строк) не разделял механические коммиты и „реальные“, то разобраться в этой каше не смог бы никто и никогда…
                                                                                • –4
                                                                                  Гениально! Вы всерьёз считаете, что вот этот вот patch на 90 мегабайт — можно разбить на десяток «небольших фич, которые будут отлично просматриваться в истории»? Там изменено всего-то 5% проекта. Какие-то жалкие 59806 файлов изменены. Подумаешь. Мелочь какая. Даёшь change'и на 100000 строк каждый!

                                                                                  Что, вот правда не видим разницы между тэгом версии и коммитом фичи/фикса? Если нет, то гуглим linux 4.12 changelog и внимательно считаем количество фиксов и фич в этом релизе. Вот именно столько должно было быть коммитов, а не один, десяток или 42. One feature — one commit


                                                                                  Как показывает практике «полное покрытие тестами» ни разу не гарантирует, что у вас всё будет работать правильно — при условии, что у вас задача нетривиальна и в ней много взаимозавимимостей.

                                                                                  А типа merge вместо rebase гарантирует?


                                                                                  Опять-таки? С чего вы взяли? Если у вас был проект, помогающий вам «пасти котов» и там, через строчку, были классы «Cat», «CatBehavior» и тому подобные — а вы решили поддержать ещё и собак, то у вас легко может измениться половина строк в проекте из-за появления классов (интерфейсов в Java) «Animal», «AnimalBehavior» и тому подобное.

                                                                                  Да, и эта функциональность должна быть разбита на несколько фич. Как минимум вынесение общей логики в абстрактные классы — отдельной фичей.


                                                                                  И если бы Alex Deucher (добавивший в вышеупомянутую «маленькую фичу на миллион строк добрые 300'000 оных строк) не разделял механические коммиты и „реальные“, то разобраться в этой каше не смог бы никто и никогда…

                                                                                  Смотрим выше, чем отличается версия от фичи.

                                                                                  • +2
                                                                                    А типа merge вместо rebase гарантирует?
                                                                                    Merge без squash гарантирует, что у вас останется достаточно истории, для того, чтобы найти проблему.

                                                                                    Если нет, то гуглим linux 4.12 changelog и внимательно считаем количество фиксов и фич в этом релизе.
                                                                                    Я правильно вас понял: для того, чтобы понять что должно быть фиксов или коммитом — нужно купить машину времени, заглянуть в будущее и посмотреть на то, что попало в trunk? А без машины времени — никак? Linux 4.12 changelog — это же просто список всех коммитов в ядро, не больше, не меньше.

                                                                                    Да, и эта функциональность должна быть разбита на несколько фич. Как минимум вынесение общей логики в абстрактные классы — отдельной фичей.
                                                                                    Идея понятна. Вот только в списке фич какого-нибудь ядра 4.12 фичи «рефакторинг интерфейса DRM» вы не найдёте. Драйвер для Radeon RX Vega — да. А вот какой-нибудь «drop definitions of removed ION_IOC_{FREE,SHARE} ioctls» вы там в качестве отдельной фичи вы вряд ли увидите. Это — часть исправлений в драйвере ION'а, на отдельную фичу, оно, я извиняюсь, «не тянет».

                                                                                    Смотрим выше, чем отличается версия от фичи.
                                                                                    Смотрим, смотрим. Пока совет выглядит так: чтобы узнать что есть фича, а что — нет, слетайте-ка в будущее да посмотрите на список коммитов.

                                                                                    Отличный совет, только на практике он слабо применим.
                                                                      • 0
                                                                        Что было сделано — указано в commit message, а фича может состоять из нескольких коммитов, сделанных разными разработчиками в разных ветках. Дробить фичи так, чтобы каждый коммит создавал цельный рабочий функционал и оставался единственным коммитом — это будет сложно.
                                                                        • 0

                                                                          все упирается в понятие "цельный рабочий функционал". Вы же как-то смогли разбить работу на несколько коммитов. Скорее всего даже в каждом коммите присутствовал какой-то функционал.

                                                                          • +1
                                                                            Ну смотрите. Для нового функционала нужно изменить бэкэнд, фронтэнд и что-то в структуре базы. Все это пишется разными людьми, и коммиты от них в отдельности даже не будут работать отдельно.
                                                                            Но все разработчики коммитят в ветку, которая после того, как все завершили — должна собраться и функционал должен заработать.
                                                                            И уже после этого идет мерж в мастер или куда-то еще.

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

                                                                            Единственное правило — в одном коммите идет работа только по одному тикету, commit message соответственно номер только одного тикета — тогда при кроссчеке все отслеживается.
                                                                            • 0
                                                                              нужно изменить бэкэнд, фронтэнд и что-то в структуре базы.

                                                                              изменения в базе сделать с учетом обратной совместимости в большинстве случаев возможно (особенно если речь идет о фичах которые делаются хотя бы за рабочую неделю).


                                                                              бэкэнд так же можно пилить спокойно и вливать в мастер отдельно, так как если это что-то новое (новый эндпоинт, новое поле и т.д.) мы точно знаем что мы ничего не сломали.


                                                                              Фронтэнд — тут сложнее. Лично мне нравятся фичатоглы, как минимум потому что можно помимо простой изоляции функционала делать еще и a/b тестирование, а так же иметь возможность быстренько отключить свежий функционал. Ну или возможность держать бета тестеров на продакшене.


                                                                              Но да, все это будет работать только при определенном покрытии кода тестами, сильно зависит от уровня команды и в целом есть проекты где не так уж сильно нужна высокая скорость доставки функционала.

                                                                              • 0
                                                                                Делать изменения в структуре базы с учетом обратной совместимости? В подавляющем большинстве случаев это не нужно.
                                                                                Просто делается скрипт миграции данных из старой схемы в новую, или из старой структуры в новую, и все.

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

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

                                                                                P.S. Держать бета-тестеров на продакшене можно далеко не для всех приложений

                                                                                • 0
                                                                                  Делать изменения в структуре базы с учетом обратной совместимости? В подавляющем большинстве случаев это не нужно.
                                                                                  В аду для таких разработчиков есть отдельный котёл. Хотя это и часто применяемая практика. Что вы будете делать, если после миграции какая-то функциональность отвалилась? Что будут делать условные «тётеньки из бухгалтерии» я и так знаю — считать всё, что ваша база перестала считать на, условно, «счётах» — и желать вашей смерти. И они правы.
                                                                                  • 0

                                                                                    Грамотные up и down миграции в большинстве случаев покрывают случай "что-то отвалилось". Изменения в структуре с уч'том обратной совместимости нужны, прежде всего, имхо, для нулевого даунтайма при деплое.

                                                                                    • 0
                                                                                      Грамотные up и down миграции в большинстве случаев покрывают случай «что-то отвалилось».
                                                                                      Случаи, которые этим покрываются — это случаи, когда все данные из новой версии могут быть безболезненно, без потери данных, помещены в старую. В этом случае миграции не должно было быть вообще — то есть в 90% случаев это просто «блажь».

                                                                                      В случае же если новая версия хранит что-то, чего старая версия хранить не умеет — down миграция приведёт к потере данных, что, в большинстве случаев, недопустимо.

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

                                                                                      Но в любом случае — это должно быть исключительным, редким явлением, а не «нормой жизни»…
                                                                                      • 0
                                                                                        Случаи, которые этим покрываются — это случаи, когда все данные из новой версии могут быть безболезненно, без потери данных, помещены в старую. В этом случае миграции не должно было быть вообще — то есть в 90% случаев это просто «блажь».

                                                                                        Переносы колонок из одной таблицы в другую, денормализация путём слияния таблиц и так далее. Тут данные из новой версии могут без проблем быть перенесены в старую, но миграция всё же нужна. Правда, тут откат обычно совсем не проблема.


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

                                                                                        • 0

                                                                                          Зачастую потеря данных вполне допустима при срочном откате. Грубо, добавили в профиль пользователя новое поле, создали миграцию, но что-то пошло не так на, например, фронте для старых пользователей, у которых этого поля нет, хотя данные пишутся для новых, и решили откатить мерж-коммит по этой фиче, в который и миграция входила. Что кто-то успел заполнить это поле — невелика потеря с точки зрения бизнеса, если подавляющее большинство пользователя с незаполненным ничего сделать не могут. Хотя, на доун миграции можно предусмотреть сохранение этих данных в отдельную таблицу.


                                                                                          В тяжёлых, запущенных случаях

                                                                                          Это обычная практика для проектов, где zero-downtime одно из основных требований. На первом этапе запуска фичи переходим на структуру где есть новая, но старая не удалена, старые клиенты пишут в старую, новые в обе, какие-то воркеры/триггеры дописывают в новую из старых. Когда убедились, что новые работают нормально, а старых не осталось, то тогда обновляем новых клиентов на версию, где пишут только в новую и когда обновлятся удаляем старую структуру.

                                                                          • 0
                                                                            Нормальная история это такая история, с помощью которой можно понять что сделано в каждом коммите.

                                                                            Ага, удачи вам потом с cherry-pick'ом…
                                                                            • 0
                                                                              Ага, удачи вам потом с cherry-pick'ом…
                                                                              А какая с ним проблема? Git merge корректно разруливает ситуацию когда в обоих ветках было сделано одно и то же изменение (в частности если в одну из них был за-cherry-pick'ан CL из другой ветки). Вот если после этого делатьь git rebase — тогда может быть беда…
                                                                              • 0
                                                                                Я отвечал на то что нужно комитить каждый чих, и не делать squash.
                                                                                А потом чери-пикать это все в другую ветку.
                                                                                • 0
                                                                                  А потом чери-пикать это все в другую ветку.
                                                                                  Ну да… и?

                                                                                  Я отвечал на то что нужно комитить каждый чих, и не делать squash.
                                                                                  Вот как раз если деть squash, то потом непонятно как cherry-pick'ать. Потому что у вас будут как вещи, которые нужны (скажем расширения в API для поддержки новой функциональности), так и вещи, которые не нужны (собственно сама новая функциональность с тестами и прочим) в одном огромном CL'е.

                                                                                  А как раз если CL'ей много и они небольшие, то проблемы сделать git blame (ну или git log если ситуация совсем тяжёлая) я не вижу. Вернее проблемы могут быть, конечно, но по сравнению с попытками выцепить из CL'я на несколько тысяч строк, и производящего рефакторинг и меняющемго API и добавляющего новую фичу и тут же подключающую её к frontend'у (а там, в свою очередь, могут и другие фичи потребоваться, чтобы можно было это всё использовать) маленький кусочек добавлющий в функцию новый аргумент — это не проблемы.
                                                                                  • 0
                                                                                    Вот как раз если деть squash, то потом непонятно как cherry-pick'ать.

                                                                                    Просто у вас примеры какие-то слишком «крайние». Я лично обычно придерживаюсь подхода одна фича — одна ветка.
                                                                                    Предположим нам в текущий релиз нужно добавить какую-то одну фичу.
                                                                                    Берем и чери-пикаем конкретно ее, а не 100500 мелких комитов (ситуация в которой накосячить гораздо проще)

                                                                                    меняющемго API и добавляющего новую фичу и тут же подключающую её к frontend'у

                                                                                    У вас АПИ и фронтэнд в одном репозитории лежат?
                                                                                    • 0
                                                                                      Предположим нам в текущий релиз нужно добавить какую-то одну фичу.
                                                                                      Тут, я боюсь, мы с вами расходимся насчёт трактивки понятия «фича» и «добавить».

                                                                                      У вас АПИ и фронтэнд в одном репозитории лежат?
                                                                                      Если честно подобные вопросы меня поначалу при обсуждении этой статьи просто удивляли до глубины души, но раз это регулярно повторяется, то… нет не все API и не весь фронтэнд лежат в одном репозитории — у нас есть кой-какие компоненты, которые лежат отдельно. Это ужасно неудобно и мы пытаемся с этим бороться с переменным успехом. До идеала таки далеко — в частности потому, что Git для этого несколько не приспособлен…

                                                                                      Но в общем и целом — да, конечно. Соотвественно для меня «добавление фичи» — это серия CL'ей, которые «протаскивают» фичу снизу доверху. А как иначе мы вообще можем говорить о «добавлении» какой-нибудь фичи, если её пользователь не видит?
                                                                    • 0

                                                                      По мне, достаточно того, что bisect найдёт "сломали, когда вмержили ветку". Дальше проверяем уже её (что, как правило, не понадобится, если ветка была на одну задачу).

                                                                    • +14

                                                                      Вот-вот… Сначала что-то монструозное пилят в отдельной ветке полгода, без единого ребейза на master… А потом rebase виноват в том, что надо несколько часов потратить на него для сохранения нескольких сотен коммитов :-)

                                                                      • –1
                                                                        Сначала что-то монструозное пилят в отдельной ветке полгода, без единого ребейза на master…

                                                                        Да, мастер в ветку мёржат.


                                                                        А потом rebase виноват в том, что надо несколько часов потратить на него для сохранения нескольких сотен коммитов :-)

                                                                        rebase виноват в том, что коммиты выходят битыми. Такое впечатление, что вы не читали статью.

                                                                        • +4

                                                                          Статью то я читал… только она о каких-то вымышленных проблемах, которые мне за 8 лет использования rebase ни разу не встретились… Потому что по факту после rebase у вас остаётся всё такая же отдельная ветка, на которой вы всё так же запускаете тесты, и если что-то поломалось, то сначала выясняете в чём проблема, исправляете её, делаете ещё раз ребейз (начиная со второго раза это всегда легко), прогоняете тесты ещё раз, и убедившись, что всё работает, вливаете эту ветку в master.
                                                                          Таким образом, битые коммиты после ребейз теоретически возможны, если прокралась какая-то ошибка, которая: 1) не мешает компиляции и запуску проекта; 2) не отлавливается тестами. Впрочем, сюрприз, такую ошибку вы и после merge ещё не скоро заметите. Только с линейной историей отследить и исправить её будет гораздо легче, вот и вся разница.


                                                                          P.S. Ребейз всегда делается интерактивный, ума ни приложу, кому и зачем может захотететься запустить его в неинтерактивном режиме.

                                                                          • 0
                                                                            отдельная ветка, на которой вы всё так же запускаете тесты, и если что-то поломалось, то сначала выясняете в чём проблема, исправляете её

                                                                            Еще один довод поднять staging со сборкой на каждый PR. У нас такого пока нет, страдаем.
                                                                            • +2

                                                                              Ну, можно для начала CI настроить с прогоном тестов на каждый коммит (в любую ветку).

                                                                              • 0
                                                                                Так и сделали. Но не хватает возможности полноценный проект собрать и функционально/вручную прогнать.
                                                                                Хотя полный проект может и не надо собирать по PR, достаточно сделать такую сборку по кнопке и собирать только релизы.

                                                                                По flow: делаем rebase в ветках PR, а в GitHub есть возможность «Squash and Merge» — классная штука, github.com/blog/2141-squash-your-commits.
                                                                                • 0

                                                                                  Да, деплой на staging лучше всё-таки с ручным управлением оставить. Чтобы тестировщики сами могли определять, какую ветку они в текущий момент тестируют и она не изменилась случайно, только из-за того, что новый PR пришёл.

                                                                                  • 0
                                                                                    Как раз думаю вскоре написать такую штуку чтобы и за PR смотрела, и с кнопками была. (Хотя может и есть где-то такое уже… У меня стейджинге на docker swarm mode, соотвественно разворот/сворот веток немного сложнее — за всем надо следить)
                                                                    • +16

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


                                                                      git pull --rebase
                                                                      • 0
                                                                        Допустим, мы удалили из master зависимость, которая всё ещё используется в feature. Когда feature перебазируется в master, первый переприменённый коммит сломает вашу сборку, но если не будет конфликтов слияния, то процесс rebase продолжится.

                                                                        Что-то я не понял чем вам здесь merge поможет?

                                                                        • +4

                                                                          Автор, думаю, имеет в виду, что если мёржить master в feature при необходимости (вместо rebase), то не будет лишних сломанных коммитов в истории. То есть при rebase может внезапно появиться коммит из серии "… а тут мы исправляем баг, из-за которого десять предыдущих коммитов даже не компилируются, потому что был rebase, но его тут не видно". Если же делать merge, то в явном виде будет сломанный merge commit и фикс сразу за ним (хотя история будет более кучерявая, это да).

                                                                          • 0
                                                                            Самим фактом своего существования. Можно будет восстановить логически обе ветки и понять что и где пошло не так.

                                                                            Другое дело, не представляю, как работает по смерженным веткам bisect.
                                                                            • +3

                                                                              Я думаю, что эта проблема как раз не стоит, поскольку даже после rebase всё равно можно сделать merge --no-ff. Автор как-то всё вместе подаёт, но в целом это независимые вещи, можно делать rebase и иметь явные слияния.

                                                                              • 0
                                                                                После rebase коммиты feature ветки внезапно могут стать сломанными, и именно от этого предостерегает автор статьи.
                                                                                • 0

                                                                                  Это уже другой вопрос, я в ответе повыше об этом написал. Автор до кучи ещё добавляет проблему линейной истории (и MonkAlex как раз об этом пишет, так что тут я отвечаю как раз на эту проблему — которая в общем-то не относится к проблеме сломанных коммитов).

                                                                                  • +6

                                                                                    Что значит "могут быть"? Конфликты будут ровно теми же самыми. Разница лишь в том, что при rebase конфликты разрешаются по-коммитно и соответственно — проще (практика "разделяй и властвуй"), а при merge — всё кучей. Как по мне, то при merge можно наделать не меньше, а даже больше ошибок.

                                                                                    • +1

                                                                                      Там речь не про конфликты слияния. Речь про то, что в мастере могли сделать изменения, которые конфликты слияния не вызывают, но код ломают. То есть, допустим, отключили библиотеку, которая была нужна. В результате rebase пройдёт нормально, коммиты накатит заново на обновлённый master, но компилироваться они не будут.

                                                                                      • +5

                                                                                        … я стесняюсь спросить, а в случае с мержом ситуация чем будет отличаться? Сделаем мерж, конфликта не будет, но компилироваться тоже не будет.

                                                                                        • +3

                                                                                          Тем, все коммиты, кроме коммита с мёржем — будут компилироваться.

                                                                                          • +3

                                                                                            … кроме коммита с мержом и всех последующих, вы хотели сказать? И чем это отличается от "будут компилироваться все коммиты до середины ребейза"?


                                                                                            (и это мы еще не затронули сценария, когда ребейз делается в фиче-ветке, а мерж делается с --no-ff, и тогда вообще не понятно, в чем разница)

                                                                                            • +2
                                                                                              кроме коммита с мержом и всех последующих, вы хотели сказать?

                                                                                              После мёржа запустим тесты и увидим, что код сломан и поправим.


                                                                                              это мы еще не затронули сценария, когда ребейз делается в фиче-ветке, а мерж делается с --no-ff, и тогда вообще не понятно, в чем разница

                                                                                              Я честно говоря не понял, что вы имеете в виду.

                                                                                              • +5
                                                                                                После мёржа запустим тесты и увидим, что код сломан и поправим.

                                                                                                … после ребейза запустим тесты, увидим, что код сломан, и поправим в том коммите, где сломалось. Ребейз надо делать не один раз, а столько, сколько надо для достижения результата.


                                                                                                Я честно говоря не понял, что вы имеете в виду

                                                                                                git checkout feature
                                                                                                git rebase master
                                                                                                git checkout master
                                                                                                git merge --no-ff feature

                                                                                                Но я уже понял, что мы с вами говорили про мержи в разные стороны.

                                                                                                • +4
                                                                                                  после ребейза запустим тесты, увидим, что код сломан, и поправим в том коммите, где сломалось. Ребейз надо делать не один раз, а столько, сколько надо для достижения результата.

                                                                                                  Вот, кстати, да. Статья написана так, как-будто после rebase ветка сразу объединяется с master, хотя по факту (при адекватной разработке) слияние будет только после прохождения тестов в ребейзнутой ветке.

                                                                                                  • 0
                                                                                                    Многие начинающие разработчики делают rebase только при неудачном пуше. Таким, может быть, и правда лучше merge использовать.
                                                                                            • 0

                                                                                              Как? Если не было конфликтов, как я вообще узнаю, что изменения в мастере поломали мою ветку???

                                                                                              • 0
                                                                                                Если не было конфликтов, как я вообще узнаю, что изменения в мастере поломали мою ветку???

                                                                                                Узнаете после прогона тестов или после того, как не скомпилируется код.

                                                                                                • +3

                                                                                                  Другими словами, в случае rebase это обнаружится до вливания ветки в master, а в случае с merge — после. Вот и приехали.

                                                                                                  • 0

                                                                                                    В случае с rebase надо будет делать rebase много раз. В случае с мёржем — не придётся.

                                                                                                    • +7

                                                                                                      Повторный rebase — это тривиальнейшая процедура, уже без конфликтов в 99% случаев. Зато в master не будет ни одного битого (с т.з. тестов) коммита, а в случае с merge их так просто не избежать.
                                                                                                      Главный аргумент статьи провалился из-за банального непонимания автора как правильно делать rebase. Из "бонусов" merge остались только уродливые merge-коммиты из master в ветку и обратно, которые типо интересно кому-то будет смотреть.

                                                                                                      • 0
                                                                                                        Повторный rebase — это тривиальнейшая процедура, уже без конфликтов в 99% случаев.

                                                                                                        Зачем делать эту тривиальнейшую процедуру, если её можно и не делать?


                                                                                                        Зато в master не будет ни одного битого (с т.з. тестов) коммита, а в случае с merge их так просто не избежать.

                                                                                                        Откуда им взяться в случае c merge?

                                                                                                        • 0
                                                                                                          Зачем делать эту тривиальнейшую процедуру, если её можно и не делать?

                                                                                                          Чтобы не было битых коммитов и чтобы была удобная в работе история коммитов.


                                                                                                          Откуда им взяться в случае c merge?

                                                                                                          Оттуда что сначала идёт merge, а потом уже проверка. И, как тут уже писали, придётся доп.коммит с исправлениями после merge делать.

                                                                                                          • 0
                                                                                                            Оттуда что сначала идёт merge, а потом уже проверка. И, как тут уже писали, придётся доп.коммит с исправлениями после merge делать.

                                                                                                            Нет, не придётся. Нужно смёржить мастер в ветку, прогнать тесты, поправить код, сделать git commit --amend, а потом уже мёржить ветку в мастер. И не будет битых коммитов.


                                                                                                            Чтобы не было битых коммитов и чтобы была удобная в работе история коммитов.

                                                                                                            Вы вроде не выступаете за то, чтобы делать squash при мёрже? Видимо под удобной в работе историей вы имеете в виду линейную. Чем она удобна? В чём преимущество перед нелинейной?

                                                                                                            • +1
                                                                                                              Нужно смёржить мастер в ветку, прогнать тесты, поправить код, сделать git commit --amend, а потом уже мёржить ветку в мастер.

                                                                                                              Ну вариант, да. Хотя лично для меня все эти мерджи master в ветку выглядят как какое-то дикое извращение.


                                                                                                              Вы вроде не выступаете за то, чтобы делать squash при мёрже?

                                                                                                              У меня нет строгого правила, что должен остаться обязательно 1 коммит, т.к. иногда удобнее сделать 3-5 коммитов в одной ветке, чем дробить это на 3-5 минифич. Но ветки по 100 коммитов, или как тут писали на 10000 значимо измененных строк, я не одобряю.


                                                                                                              Видимо под удобной в работе историей вы имеете в виду линейную. Чем она удобна? В чём преимущество перед нелинейной?

                                                                                                              Ну тут имхо очевидно. Что может быть проще прямой линии? Всё красиво и откатывать при необходимости можно фичи целиком, а не по 100 коммитов. Зачем засорять себе восприятие какими-то коммитами, которых по факту никогда не было в master? Чтобы найдя к-н опечатку вооружиться git bisect в поисках коммита, в котором она была сделана? Чтобы что? Чтобы revert сделать? А если там ещё изменения есть кроме той опечатки… И эти все пляски с бубном вместо того, чтобы за пару секунд исправить опечатку и закоммитить исправление? Или это так важно найти кто виноват и оштрафовать его на ползарплаты?