Мой офис пытается выяснить, как мы справляемся с разделением и слиянием ветвей, и мы столкнулись с большой проблемой.
Наша проблема связана с долгосрочными боковыми ветвями - в тех случаях, когда несколько человек работают на боковой ветке, которая отделяется от мастера, мы развиваемся в течение нескольких месяцев, и когда мы достигаем вехи, мы синхронизируем их.
Теперь, ИМХО, естественный способ справиться с этим - раздавить боковую ветвь в один коммит. master
продолжает прогрессировать вперед; как и должно быть - мы не задним числом выкидываем месяцы параллельного развития в master
историю России. И если кому-то нужно лучшее разрешение для истории боковой ветки, ну, конечно, это все еще там - это просто не внутри master
, это в боковой ветке.
Вот проблема: я работаю исключительно с командной строкой, но остальная часть моей команды использует GUIS. И я обнаружил, что GUIS не имеет разумной опции для отображения истории из других веток. Так что, если вы достигнете комманда-сквоша со словами «это развитие раздавлено из ветки XYZ
», будет очень тяжело увидеть, что там происходит XYZ
.
На SourceTree, насколько я могу найти, это огромная головная боль: если вы включены master
и хотите просмотреть историю master+devFeature
, вам нужно либо проверить master+devFeature
(касаясь каждого отдельного файла), либо прокрутите журнал, отображающий ВСЕ ветви вашего хранилища параллельно, пока не найдете нужное место. И удачи в выяснении, где ты там.
Мои товарищи по команде, совершенно справедливо, не хотят, чтобы история развития была настолько недоступна. Таким образом, они хотят, чтобы эти большие, длинные стороны разработки были объединены, всегда с коммитом слияния. Они не хотят никакой истории, которая не сразу доступна из главной ветки.
Я ненавижу эту идею; это означает бесконечный, неуязвимый клубок истории параллельного развития. Но я не вижу, какая у нас альтернатива. И я довольно сбит с толку; кажется, это блокирует почти все, что я знаю о хорошем управлении филиалами, и это будет для меня постоянным разочарованием, если я не смогу найти решение.
Есть ли у нас здесь какой-либо вариант, кроме постоянного слияния боковых веток в master с помощью merge-commits? Или есть причина, по которой постоянное использование merge-commits не так плохо, как я боюсь?
git log master+bugfixDec10th
что будет переломным моментом: - /merge --no-ff
то, что вы хотите? Наmaster
себя у вас есть один коммит , который описывает то , что изменилось в отрасли, но все коммиты все еще существует и основатели к HEAD.Ответы:
Хотя я использую Git в командной строке - я должен согласиться с вашими коллегами. Не имеет смысла объединять большие изменения в один коммит. Таким образом, вы теряете историю, а не просто делаете ее менее заметной.
Смысл контроля версий - отслеживать историю всех изменений. Когда что изменилось, почему? Для этого каждый коммит содержит указатели на родительские коммиты, diff и метаданные, такие как сообщение коммита. Каждый коммит описывает состояние исходного кода и полную историю всех изменений, которые привели к этому состоянию. Сборщик мусора может удалять коммиты, которые недоступны.
Такие действия, как перебазирование, сбор вишни или сжатие, удаляют или переписывают историю. В частности, результирующие коммиты больше не ссылаются на исходные коммиты. Учти это:
[1]: Некоторые серверы Git позволяют защитить ветки от случайного удаления, но я сомневаюсь, что вы хотите сохранить все свои ветки функций на вечность.
Теперь вы больше не можете искать этот коммит - он просто не существует.
Ссылка на имя ветки в сообщении фиксации еще хуже, поскольку имена ветвей являются локальными для репо. То, что
master+devFeature
в вашей местной кассе, может бытьdoodlediduh
в моей. Ветви - это просто перемещающиеся метки, указывающие на некоторый объект коммитаИз всех техник переписывания истории перебазирование является наиболее благоприятным, поскольку оно дублирует полные коммиты со всей их историей и просто заменяет родительский коммит.
То, что
master
история включает в себя полную историю всех ветвей, которые были объединены в нее, - это хорошо, потому что это представляет реальность. [2] Если была параллельная разработка, это должно быть видно в журнале.[2]: По этой причине я также предпочитаю явные коммиты слияния по линеаризованной, но в конечном итоге поддельной истории, возникающей в результате перебазирования.
В командной строке
git log
старается упростить отображаемую историю и сохранить актуальность всех отображаемых коммитов. Вы можете настроить упрощение истории в соответствии с вашими потребностями. У вас может возникнуть желание написать собственный инструмент git log, который просматривает граф коммитов, но, как правило, невозможно ответить «был ли этот коммит изначально зафиксирован в той или иной ветке?». Первым родителем коммита слияния является предыдущийHEAD
, т. Е. Коммит в ветви, в которую вы сливаете. Но это предполагает, что вы не делали обратного слияния из мастера в ветвь функций, а затем быстро пересылали мастера в слияние.Лучшее решение для долговременных веток, с которыми я столкнулся, - это предотвращение слияния веток только через пару месяцев. Слияние легче всего, когда изменения недавние и небольшие. В идеале вы будете сливаться хотя бы раз в неделю. Непрерывная интеграция (как в экстремальном программировании, а не как «давайте настроим сервер Jenkins») даже предлагает несколько слияний в день, то есть не для поддержки отдельных ветвей функций, а для совместного использования ветки разработки как команды. Для слияния до того, как функция будет проверена, требуется, чтобы она была скрыта за флагом функции.
В свою очередь, частая интеграция позволяет обнаруживать потенциальные проблемы намного раньше и помогает поддерживать согласованную архитектуру: возможны далеко идущие изменения, поскольку эти изменения быстро включаются во все ветви. Если какое-либо изменение нарушает какой-либо код, оно нарушает работу только на пару дней, а не на пару месяцев.
Переписывание истории может иметь смысл для действительно огромных проектов, когда существует несколько миллионов строк кода и сотни или тысячи активных разработчиков. Сомнительно, почему такой большой проект должен быть один мерзавец репо вместо того, чтобы быть разделены на отдельные библиотеки, но в таком масштабе это более удобно, если центральный репо содержит только «выпуски» отдельных компонентов. Например, ядро Linux использует сжатие, чтобы поддерживать управляемость основной истории. Некоторые проекты с открытым исходным кодом требуют, чтобы патчи отправлялись по электронной почте вместо слияния на уровне git.
источник
git bisect
только для того, чтобы он получил uber-коммит из 10 000 строк из боковой ветви.Мне нравится ответ Амона , но я чувствовал, что одна небольшая часть требует гораздо большего внимания: вы можете легко упростить историю, просматривая журналы, чтобы удовлетворить ваши потребности, но другие не могут добавить историю, просматривая журналы, чтобы удовлетворить свои потребности. Вот почему сохранение истории в том виде, в котором она произошла, предпочтительнее.
Вот пример из одного из наших репозиториев. Мы используем модель запросов по запросу, поэтому каждая функция выглядит как ваши давно работающие ветки в истории, даже если они обычно работают только неделю или меньше. Отдельные разработчики иногда предпочитают сдавливать свою историю перед слиянием, но мы часто объединяем функции, так что это относительно необычно. Вот несколько верхних коммитов
gitk
, графический интерфейс, который поставляется в комплекте с git:Да, немного путаницы, но нам это тоже нравится, потому что мы можем точно видеть, кто и что изменил в какое время. Это точно отражает нашу историю развития. Если мы хотим видеть представление более высокого уровня, слияние по одному запросу за раз, мы можем посмотреть на следующее представление, эквивалентное
git log --first-parent
команде:git log
имеет много других опций, разработанных, чтобы дать вам именно то, что вы хотите.gitk
может принять любой произвольныйgit log
аргумент для построения графического представления. Я уверен, что другие графические интерфейсы имеют аналогичные возможности. Прочитайте документы и научитесь правильно их использовать, вместо того, чтобы навязывать предпочтительныйgit log
взгляд на всех во время слияния.источник
merge --no-ff
- не используйте ускоренные слияния, вместо этого всегда создавайте коммит слияния, чтобы с ним--first-parent
было что работатьМоя первая мысль - даже не делай этого, если это абсолютно не необходимо. Ваши слияния должны быть сложными иногда. Держите ветви независимыми и как можно более короткими. Это признак того, что вам нужно разбить свои истории на более мелкие куски реализации.
В случае, если вам нужно это сделать, можно объединить git с опцией --no-ff, чтобы истории сохранялись в отдельной ветке. Коммиты по-прежнему будут отображаться в объединенной истории, но их также можно будет увидеть отдельно в ветви функций, чтобы, по крайней мере, можно было определить, в какую линию разработки они входили.
Я должен признать, что когда я впервые начал использовать git, мне показалось немного странным, что коммиты веток появились в той же истории, что и основная ветвь после слияния. Это немного смутило, потому что казалось, что эти коммиты не принадлежали той истории. Но на практике это совсем не больно, если учесть, что ветвь интеграции - это всего лишь ее цель - объединить ветви функций. В нашей команде мы не сквошимся и часто совершаем коммиты слияния. Мы постоянно используем --no-ff, чтобы было легко увидеть точную историю любой функции, если мы захотим ее исследовать.
источник
git merge --no-ff
git merge --no-ff
. Если вы используете Gitflow, это становится по умолчанию.Позвольте мне ответить на ваши вопросы прямо и четко:
Вы обычно не хотите оставлять свои филиалы несинхронизированными в течение нескольких месяцев.
В вашей ветви функций что-то ответвляется в зависимости от вашего рабочего процесса; давайте просто назовем это
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
коммитом слияния .(Примечание: я почти исключительно работаю и с командной строкой (ну, это ложь, я обычно использую emacs magit, но это другая история - если я не в удобном месте с моей индивидуальной настройкой emacs, я предпочитаю команду линии, а). Но, пожалуйста , одолжение и попробовать хотя бы
git gui
один раз. это так гораздо более эффективным для выбора линий, ломти и т.д. для добавления / расстегивать добавляет.)Это потому, что то, что вы пытаетесь сделать, полностью противоречит духу
git
.git
строится из ядра на «ориентированном ациклическом графе», что означает, что много информации находится в родительских-дочерних отношениях коммитов. А для слияний это означает, что истинное слияние совершается с двумя родителями и одним ребенком. Графический интерфейс ваших коллег будет в порядке, как только вы используетеno-ff
коммиты слияния.Да, но это не проблема GUI, а фиксации сквоша. Использование сквоша означает, что вы оставляете головку ветки объекта висящей и создаете совершенно новый коммит
master
. Это нарушает структуру на двух уровнях, создавая большой беспорядок.И они абсолютно правы. Но они не «объединены в », они просто объединяются. Слияние - это действительно сбалансированная вещь, у нее нет предпочтительной стороны, которая сливается «с другой» (
git checkout A ; git merge B
это точно так же, какgit checkout B ; git merge A
за исключением незначительных визуальных различий, таких как ветки, которые меняются местамиgit log
и т. Д.).Что совершенно правильно. В то время, когда нет объединенных функций, у вас будет одна ветвь
master
с богатой историей, инкапсулирующая все строки фиксации объектов, когда-либо существовавшие, возвращаясь кgit init
фиксации с самого начала (обратите внимание, что я специально избегал использовать термин " ветки »в последней части этого абзаца, потому что история в то время уже не была« ветвями », хотя граф коммитов был бы довольно ветвящимся).Тогда вам будет немного больно, так как вы работаете против инструмента, который используете.
git
Подход очень элегантный и мощный, особенно в разветвлениях / сливающуюся области; если вы делаете это правильно (как упоминалось выше, особенно с--no-ff
), это на дрожжах превосходит другие подходы (например, беспорядок подрывной деятельности, имеющий параллельные структуры каталогов для ветвей).Бесконечный, параллельный - да.
Неподвижный, клубок - нет.
Почему бы не работать так же, как изобретатель
git
, ваши коллеги и остальной мир, каждый день?Других вариантов нет; не так плохо
источник
Сокращение долгосрочной боковой ветки заставит вас потерять много информации.
То, что я хотел бы сделать, это попытаться перевести мастер в долгосрочную боковую ветвь перед тем, как объединить боковую ветвь с главной . Таким образом вы сохраняете каждый коммит в master, делая историю коммитов линейной и более легкой для понимания.
Если бы я не мог сделать это легко при каждом коммите, я бы позволил ему быть нелинейным , чтобы сохранить ясность контекста разработки. По моему мнению, если у меня возникнет проблемное слияние во время перехода мастера в боковую ветвь, это означает, что нелинейность имела значение в реальном мире. Это означает, что будет легче понять, что произошло, если мне нужно копаться в истории. Я также получаю немедленную выгоду от того, что мне не нужно делать ребаз.
источник
rebase
. Является ли это лучшим подходом здесь (у меня лично не было большого опыта с этим, хотя я не использовал это много), это определенно, кажется, самая близкая вещь к тому, чего действительно хочет OP - определенно, компромисс между неупорядоченным дампом истории и полным сокрытием этой истории.Лично я предпочитаю делать свою разработку в форке, а затем тянуть запросы на слияние с основным хранилищем.
Это означает, что если я хочу перебазировать свои изменения поверх мастер или раздавить некоторые коммиты WIP, я могу сделать это полностью. Или я могу просто попросить объединить всю мою историю.
Что мне нравится делать, так это заниматься разработкой в ветке, но часто делать ребаз на master / dev. Таким образом я получаю самые последние изменения от мастера, не имея кучу коммитов слияния в моей ветке, или не имея дело с целой нагрузкой конфликтов слияния, когда приходит время слияния с мастером.
Чтобы четко ответить на ваш вопрос:
Да - вы можете объединить их один раз для каждой ветви (когда функция или исправление «завершены») или, если вам не нравится, когда в вашей истории есть коммиты слияния, вы можете просто выполнить ускоренное слияние на мастер после выполнения окончательной перезагрузки.
источник
Ревизионный контроль - мусор на входе.
Проблема в том, что незавершенная работа над веткой функций может содержать много слов «давайте попробуем это ... нет, это не сработало, давайте заменим это на то», и все коммиты, кроме финального «того» просто заканчиваются загрязняя историю бесполезно.
В конечном счете, история должна быть сохранена (некоторые из них могут быть полезны в будущем), но следует объединить только «чистую копию».
В Git это можно сделать, сначала разветвив ветвь функции (чтобы сохранить всю историю), затем (в интерактивном режиме) переставив ветку ветки функции от master, а затем объединив перебазированную ветку.
источник
git
, и они локальные. То, что названоfoo
в одном репо, может быть названоbar
в другом. И они должны быть временными: вы не хотите загромождать репо тысячами ветвей функций, которые необходимо поддерживать, чтобы не потерять историю. И вам нужно будет сохранить эти ветви, чтобы сохранить ссылку на историю, поскольку вgit
конечном итоге удалит любой коммит, который больше не доступен для ветви.