Мой офис хочет бесконечные слияния филиалов как политика; какие еще варианты у нас есть?

119

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

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

Теперь, ИМХО, естественный способ справиться с этим - раздавить боковую ветвь в один коммит. masterпродолжает прогрессировать вперед; как и должно быть - мы не задним числом выкидываем месяцы параллельного развития в masterисторию России. И если кому-то нужно лучшее разрешение для истории боковой ветки, ну, конечно, это все еще там - это просто не внутри master, это в боковой ветке.

Вот проблема: я работаю исключительно с командной строкой, но остальная часть моей команды использует GUIS. И я обнаружил, что GUIS не имеет разумной опции для отображения истории из других веток. Так что, если вы достигнете комманда-сквоша со словами «это развитие раздавлено из ветки XYZ», будет очень тяжело увидеть, что там происходит XYZ.

На SourceTree, насколько я могу найти, это огромная головная боль: если вы включены masterи хотите просмотреть историю master+devFeature, вам нужно либо проверить master+devFeature(касаясь каждого отдельного файла), либо прокрутите журнал, отображающий ВСЕ ветви вашего хранилища параллельно, пока не найдете нужное место. И удачи в выяснении, где ты там.

Мои товарищи по команде, совершенно справедливо, не хотят, чтобы история развития была настолько недоступна. Таким образом, они хотят, чтобы эти большие, длинные стороны разработки были объединены, всегда с коммитом слияния. Они не хотят никакой истории, которая не сразу доступна из главной ветки.

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

Есть ли у нас здесь какой-либо вариант, кроме постоянного слияния боковых веток в master с помощью merge-commits? Или есть причина, по которой постоянное использование merge-commits не так плохо, как я боюсь?

Отойди
источник
84
Действительно ли запись слияния как слияния настолько плоха? Я вижу, что вхождение в линейную историю имеет свои преимущества, но я удивлен, что не делать этого противоречит «почти всему, что вы знаете о хорошем управлении филиалами». Какие именно проблемы вы боитесь?
IMSoP
65
Хорошо, но, на мой взгляд, долгожданная ветвь - это именно то место, где вам действительно нужна некоторая наглядность, чтобы обвинения и пополам не просто касались «изменений, внесенных как часть Большого переписывания 2016 года». Я не достаточно уверен, чтобы публиковать в качестве ответа, но мой инстинкт заключается в том, что задачи внутри ветви функций должны быть сокращены, чтобы у вас была краткая история проекта, доступная из основной истории, без необходимости проверить сиротскую ветку.
IMSoP
5
При этом вполне возможно, что вопрос сводится к следующему: «Я пытаюсь использовать git на более высоком уровне, чем мои товарищи по команде; как мне не дать им это испортить». Что, честно говоря, я просто честно не ожидал, git log master+bugfixDec10thчто будет переломным моментом: - /
Отступление
26
«долгосрочные боковые ветви - те, в которых есть несколько человек, работающих на боковых ветках, которые отделяются от мастера, мы развиваемся в течение нескольких месяцев, и когда мы достигаем вехи, мы синхронизируем их». Вы периодически не тянете от мастера в боковую ветку? Выполнение каждого (нескольких) обязательств мастером во многих случаях облегчает жизнь.
TafT
4
Это merge --no-ffто, что вы хотите? На masterсебя у вас есть один коммит , который описывает то , что изменилось в отрасли, но все коммиты все еще существует и основатели к HEAD.
CAD97

Ответы:

243

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

Смысл контроля версий - отслеживать историю всех изменений. Когда что изменилось, почему? Для этого каждый коммит содержит указатели на родительские коммиты, diff и метаданные, такие как сообщение коммита. Каждый коммит описывает состояние исходного кода и полную историю всех изменений, которые привели к этому состоянию. Сборщик мусора может удалять коммиты, которые недоступны.

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

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

[1]: Некоторые серверы Git позволяют защитить ветки от случайного удаления, но я сомневаюсь, что вы хотите сохранить все свои ветки функций на вечность.

Теперь вы больше не можете искать этот коммит - он просто не существует.

Ссылка на имя ветки в сообщении фиксации еще хуже, поскольку имена ветвей являются локальными для репо. То, что master+devFeatureв вашей местной кассе, может быть doodlediduhв моей. Ветви - это просто перемещающиеся метки, указывающие на некоторый объект коммита

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

То, что masterистория включает в себя полную историю всех ветвей, которые были объединены в нее, - это хорошо, потому что это представляет реальность. [2] Если была параллельная разработка, это должно быть видно в журнале.

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

В командной строке git logстарается упростить отображаемую историю и сохранить актуальность всех отображаемых коммитов. Вы можете настроить упрощение истории в соответствии с вашими потребностями. У вас может возникнуть желание написать собственный инструмент git log, который просматривает граф коммитов, но, как правило, невозможно ответить «был ли этот коммит изначально зафиксирован в той или иной ветке?». Первым родителем коммита слияния является предыдущий HEAD, т. Е. Коммит в ветви, в которую вы сливаете. Но это предполагает, что вы не делали обратного слияния из мастера в ветвь функций, а затем быстро пересылали мастера в слияние.

Лучшее решение для долговременных веток, с которыми я столкнулся, - это предотвращение слияния веток только через пару месяцев. Слияние легче всего, когда изменения недавние и небольшие. В идеале вы будете сливаться хотя бы раз в неделю. Непрерывная интеграция (как в экстремальном программировании, а не как «давайте настроим сервер Jenkins») даже предлагает несколько слияний в день, то есть не для поддержки отдельных ветвей функций, а для совместного использования ветки разработки как команды. Для слияния до того, как функция будет проверена, требуется, чтобы она была скрыта за флагом функции.

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

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

Амон
источник
43
@Standback Мне все равно, что разработчик делает локально ... использовать коммиты "wip", дополнительные коммиты для каждой исправленной опечатки, .... Это хорошо, лучше совершать слишком часто. В идеале, разработчик очищает эти коммиты до того, как они будут отправлены, например, объединяет все коммиты, которые просто исправляют некоторые опечатки. Хорошей идеей будет сохранять отдельные коммиты, если они делают разные вещи, например, один коммит для реальной функциональности и связанных тестов, другой для несвязанных опечаток. Но как только коммиты будут переданы, переписывание или удаление истории - это больше проблем, чем стоит, по крайней мере, по моему опыту.
Амон
11
«Как правило, невозможно ответить« был ли этот коммит изначально зафиксирован в той или иной ветке? »« Я вижу тот факт, что «ветки» являются просто указателями на коммиты без реальной истории как один из самых больших недостатков дизайна в git Я действительно хочу иметь возможность спросить мою систему контроля версий "что было в ветке x на дату y".
Питер Грин
80
Нет ничего более расстраивающего, чем попытка определить причину ошибки в коде, использующей git bisectтолько для того, чтобы он получил uber-коммит из 10 000 строк из боковой ветви.
tpg2114
2
@Standback ИМХО, чем меньше коммит, тем он более читабелен и дружелюбен. Коммит, который касается более чем нескольких пунктов, невозможно понять с первого взгляда, поэтому вы просто берете описание за чистую монету. Это удобочитаемость описания (например, «реализованная функция X»), а не самого коммита (кода). Я предпочел бы иметь 1000 однобуквенных коммитов типа «изменил http на https»
:)
2
cherry-pick не переписывает и не удаляет историю. он просто создает новые коммиты, которые копируют изменения из существующих коммитов
Sarge Borsch
110

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

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

стандартный вид gitk

Да, немного путаницы, но нам это тоже нравится, потому что мы можем точно видеть, кто и что изменил в какое время. Это точно отражает нашу историю развития. Если мы хотим видеть представление более высокого уровня, слияние по одному запросу за раз, мы можем посмотреть на следующее представление, эквивалентное git log --first-parentкоманде:

gitk view с --first-parent

git logимеет много других опций, разработанных, чтобы дать вам именно то, что вы хотите. gitkможет принять любой произвольный git logаргумент для построения графического представления. Я уверен, что другие графические интерфейсы имеют аналогичные возможности. Прочитайте документы и научитесь правильно их использовать, вместо того, чтобы навязывать предпочтительный git logвзгляд на всех во время слияния.

Карл Билефельдт
источник
24
Я не был знаком с этим вариантом! Это огромная помощь для меня, и похоже, что изучение большего количества опций журнала позволит мне с этим легче жить.
Standback
25
+1 за «Вы можете легко упростить историю, просматривая журналы, чтобы удовлетворить ваши потребности, но другие не могут добавить историю, просматривая журналы, чтобы удовлетворить свои потребности». Переписывание истории всегда под вопросом. Если вы думали, что важно записывать во время коммита, то это было важно. Даже если вы обнаружили, что это было неправильно или сделали это позже, это часть истории. Некоторые ошибки имеют смысл только тогда, когда вы видите в вину, что эта строка осталась от более позднего переписывания. Когда ошибки дополняются остальной частью эпоса, вы не можете понять, почему все закончилось так, как есть.
TafT
@Standback Related, одна вещь, которая помогает поддерживать эту структуру для меня, использует merge --no-ff- не используйте ускоренные слияния, вместо этого всегда создавайте коммит слияния, чтобы с ним --first-parentбыло что работать
Izkata
34

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

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

В случае, если вам нужно это сделать, можно объединить git с опцией --no-ff, чтобы истории сохранялись в отдельной ветке. Коммиты по-прежнему будут отображаться в объединенной истории, но их также можно будет увидеть отдельно в ветви функций, чтобы, по крайней мере, можно было определить, в какую линию разработки они входили.

Я должен признать, что когда я впервые начал использовать git, мне показалось немного странным, что коммиты веток появились в той же истории, что и основная ветвь после слияния. Это немного смутило, потому что казалось, что эти коммиты не принадлежали той истории. Но на практике это совсем не больно, если учесть, что ветвь интеграции - это всего лишь ее цель - объединить ветви функций. В нашей команде мы не сквошимся и часто совершаем коммиты слияния. Мы постоянно используем --no-ff, чтобы было легко увидеть точную историю любой функции, если мы захотим ее исследовать.

Брэд Томас
источник
3
Я полностью согласен; все предпочитают держаться поближе к мастеру. Но это другая проблема, которая намного больше и намного меньше под моим собственным скромным влиянием :-P
Standback
2
+1 заgit merge --no-ff
0xcaff
1
Также +1 за git merge --no-ff. Если вы используете Gitflow, это становится по умолчанию.
Дэвид Хаммен
10

Позвольте мне ответить на ваши вопросы прямо и четко:

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

Вы обычно не хотите оставлять свои филиалы несинхронизированными в течение нескольких месяцев.

В вашей ветви функций что-то ответвляется в зависимости от вашего рабочего процесса; давайте просто назовем это masterради простоты. Теперь, когда вы берете на себя обязательство, вы можете и должны git checkout long_running_feature ; git rebase master. Это означает, что ваши ветви, по замыслу, всегда синхронизированы.

git rebaseздесь тоже правильно делать. Это не хак или что-то странное или опасное, но совершенно естественное. Вы теряете один бит информации, который является «днем рождения» ветки функций, но это все. Если кто-то считает, что это важно, это можно сделать, сохранив его где-то еще (в вашей системе тикетов, или, если необходимость велика, в git tag...).

Теперь, ИМХО, естественный способ справиться с этим - раздавить боковую ветвь в один коммит.

Нет, вы абсолютно не хотите этого, вы хотите коммит слияния. Коммит слияния также является «единым коммитом». Каким-то образом он не вставляет все отдельные ветвления в "master". Это один коммит с двумя родителями - masterглавой и главой филиала во время слияния.

Обязательно укажите --no-ffопцию, конечно; --no-ffв вашем случае строго запрещается слияние без . К сожалению, --no-ffне по умолчанию; но я верю, что есть вариант, который вы можете установить. Посмотрите, git help mergeчто --no-ffделает (вкратце: это активирует поведение, которое я описал в предыдущем абзаце), это очень важно.

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

Абсолютно нет - вы никогда не вносите что-то «в историю» какой-либо ветки, особенно если вы не делаете коммит слияния.

И если кому-то нужно лучшее разрешение для истории боковой ветки, ну, конечно, это все еще там - это просто не в мастерской, это в боковой ветке.

С коммитом слияния, он все еще там. Не в мастере, а в боковой ветке, отчетливо видимой как один из родителей коммитта слияния, и хранящейся в вечности, как и должно быть.

Видишь, что я наделал? Все, что вы описываете для своего коммита сквоша, тут же с --no-ffкоммитом слияния .

Вот проблема: я работаю исключительно с командной строкой, но остальная часть моей команды использует GUIS.

(Примечание: я почти исключительно работаю и с командной строкой (ну, это ложь, я обычно использую emacs magit, но это другая история - если я не в удобном месте с моей индивидуальной настройкой emacs, я предпочитаю команду линии, а). Но, пожалуйста , одолжение и попробовать хотя бы git guiодин раз. это так гораздо более эффективным для выбора линий, ломти и т.д. для добавления / расстегивать добавляет.)

И я обнаружил, что GUIS не имеет разумной опции для отображения истории из других веток.

Это потому, что то, что вы пытаетесь сделать, полностью противоречит духу git. gitстроится из ядра на «ориентированном ациклическом графе», что означает, что много информации находится в родительских-дочерних отношениях коммитов. А для слияний это означает, что истинное слияние совершается с двумя родителями и одним ребенком. Графический интерфейс ваших коллег будет в порядке, как только вы используете no-ffкоммиты слияния.

Так что, если вы достигнете комманда-сквоша со словами: «Это развитие произошло из ветки XYZ», то увидеть, что находится в XYZ, очень сложно.

Да, но это не проблема GUI, а фиксации сквоша. Использование сквоша означает, что вы оставляете головку ветки объекта висящей и создаете совершенно новый коммит master. Это нарушает структуру на двух уровнях, создавая большой беспорядок.

Таким образом, они хотят, чтобы эти большие, длинные стороны разработки были объединены, всегда с коммитом слияния.

И они абсолютно правы. Но они не «объединены в », они просто объединяются. Слияние - это действительно сбалансированная вещь, у нее нет предпочтительной стороны, которая сливается «с другой» ( git checkout A ; git merge Bэто точно так же, как git checkout B ; git merge Aза исключением незначительных визуальных различий, таких как ветки, которые меняются местами git logи т. Д.).

Они не хотят никакой истории, которая не сразу доступна из главной ветки.

Что совершенно правильно. В то время, когда нет объединенных функций, у вас будет одна ветвь masterс богатой историей, инкапсулирующая все строки фиксации объектов, когда-либо существовавшие, возвращаясь к git initфиксации с самого начала (обратите внимание, что я специально избегал использовать термин " ветки »в последней части этого абзаца, потому что история в то время уже не была« ветвями », хотя граф коммитов был бы довольно ветвящимся).

Я ненавижу эту идею;

Тогда вам будет немного больно, так как вы работаете против инструмента, который используете. gitПодход очень элегантный и мощный, особенно в разветвлениях / сливающуюся области; если вы делаете это правильно (как упоминалось выше, особенно с --no-ff), это на дрожжах превосходит другие подходы (например, беспорядок подрывной деятельности, имеющий параллельные структуры каталогов для ветвей).

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

Бесконечный, параллельный - да.

Неподвижный, клубок - нет.

Но я не вижу, какая у нас альтернатива.

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

Есть ли у нас здесь какой-либо вариант, кроме постоянного слияния боковых веток в master с помощью merge-commits? Или есть причина, по которой постоянное использование merge-commits не так плохо, как я боюсь?

Других вариантов нет; не так плохо

Anoe
источник
10

Сокращение долгосрочной боковой ветки заставит вас потерять много информации.

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

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

Pierre.Sassoulas
источник
: + за упоминание rebase. Является ли это лучшим подходом здесь (у меня лично не было большого опыта с этим, хотя я не использовал это много), это определенно, кажется, самая близкая вещь к тому, чего действительно хочет OP - определенно, компромисс между неупорядоченным дампом истории и полным сокрытием этой истории.
Кайл Стрэнд,
1

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

Это означает, что если я хочу перебазировать свои изменения поверх мастер или раздавить некоторые коммиты WIP, я могу сделать это полностью. Или я могу просто попросить объединить всю мою историю.

Что мне нравится делать, так это заниматься разработкой в ​​ветке, но часто делать ребаз на master / dev. Таким образом я получаю самые последние изменения от мастера, не имея кучу коммитов слияния в моей ветке, или не имея дело с целой нагрузкой конфликтов слияния, когда приходит время слияния с мастером.

Чтобы четко ответить на ваш вопрос:

Есть ли у нас здесь какой-либо вариант, кроме постоянного слияния боковых веток в master с помощью merge-commits?

Да - вы можете объединить их один раз для каждой ветви (когда функция или исправление «завершены») или, если вам не нравится, когда в вашей истории есть коммиты слияния, вы можете просто выполнить ускоренное слияние на мастер после выполнения окончательной перезагрузки.

Уэйн Вернер
источник
3
Извините - я делаю то же самое, но не понимаю, как это отвечает на вопрос: - /
Отступление
2
Добавил четкий ответ на поставленный вопрос.
Уэйн Вернер
Так что это хорошо для моих коммитов, но я (и моя команда) буду заниматься работой каждого . Сохранять мою собственную историю в чистоте легко; работа в грязной истории разработчиков - вот проблема.
Standback
Ты имеешь в виду, что ты хочешь требовать от других разработчиков ... что? Не ребазировать, а просто сквошить при слиянии? Или вы только что опубликовали этот вопрос, чтобы рассказать о недостатке мерзости в отношении ваших коллег?
Уэйн Вернер
1
Регулярный перебазирование бесполезно, если над этой веткой функционируют несколько разработчиков.
Пажло Эберманн
-1

Ревизионный контроль - мусор на входе.

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

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

В Git это можно сделать, сначала разветвив ветвь функции (чтобы сохранить всю историю), затем (в интерактивном режиме) переставив ветку ветки функции от master, а затем объединив перебазированную ветку.

DepressedDaniel
источник
1
Просто чтобы уточнить: вы говорите о том, чтобы переписать историю (копию) ветви функций, чтобы у нее была короткая, чистая история - устраняя все взад и вперед первоначальной разработки. А затем объединить переписанную ветку в master проще и понятнее. Я вас правильно понял?
Резерв
1
Да. И когда вы объединяетесь с мастером, это будет просто ускоренная перемотка вперед (при условии, что вы недавно сделали ребазинг перед слиянием).
ДепрессияДаниэль
Но как эта история сохраняется? Вы позволяете оригинальной ветви функций лежать вокруг?
Paŭlo Ebermann
@ PaŭloEbermann Bingo.
ДепрессияДаниэль
Ну, как кто-то сказал в комментарии: ветви - это просто указатели на граф истории git, и они локальные. То, что названо fooв одном репо, может быть названо barв другом. И они должны быть временными: вы не хотите загромождать репо тысячами ветвей функций, которые необходимо поддерживать, чтобы не потерять историю. И вам нужно будет сохранить эти ветви, чтобы сохранить ссылку на историю, поскольку в gitконечном итоге удалит любой коммит, который больше не доступен для ветви.
Мастер