Как записать разрешение конфликта слияния при ребазинге в git? - PullRequest
0 голосов
/ 31 октября 2019

Для объединения ветвей объектов в мастер мы используем git rebase ветви объектов на мастер, а затем git merge --no-ff*. Теперь мы хотим записать изменения, необходимые / сделанные для слияния (применяя фиксацию к новому HEAD) в отдельном коммите **.

Обычно git rebase изменения фиксируют изменения из ветви функциитак что они чисто применяются к новому HEAD, объединяющему оба исходных изменения из коммита с разрешением конфликта.

Возможно ли, чтобы оба имели чистую линейную историю, как результат git rebase, за которым следует git mergeи запись (в коммите) изменений, внесенных действием rebase?

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

Мне кажется, я понимаю концепциислияние и перебазировка в git: https://www.atlassian.com/git/tutorials/merging-vs-rebasing и упомянутой статье atlassian (https://www.atlassian.com/git/tutorials/merging-vs-rebasing) не помогают.


*: на самом делемы часто используем git merge --squash;Я предпочитаю ответы, которые используют --no-ff вместо --squash. И, --squash, вероятно, раздавит тщательно разделенную функцию и объединит изменения. И поэтому, не будьте так полезны в этом контексте.

**: Контекст: мой коллега имеет тенденцию создавать большие сдавленные коммиты и включать в себя некоторые из своих не просмотренных кодов и пропустить некоторый код(что мы хотим объединить) других коллег и тем самым сломать сборки;вызывать ошибки;и удалите все функции под названием «просто» перебазирование веток. Поэтому мы ищем способы отслеживать, какой хаос они наносят. Я предполагаю, что общий совет будет заключаться в меньших / атомарных фиксациях, более коротких ветвях, улучшенном автоматизированном тестировании / непрерывной интеграции и избавлении от указанного коллеги. Тем не менее, ни один из них в настоящее время легко достичь в краткосрочной перспективе;мы стремимся улучшить себя в первых трех пунктах.

Ответы [ 2 ]

0 голосов
/ 31 октября 2019

Предположим, у нас есть:

... A - B - C - E - G  (master)
          \
            D - F - H  (feature)

, где F требует ручного разрешения конфликтов слияния.

Обычно git checkout feature && git rebase HEAD~3 --onto master приводит к:

... A - B - C - E - G  (master)
                      \
                        D' - F" - H'  (feature)

, где D'и H' эквивалентны D и H с обновленными родителями. И где F "является либо разумным слиянием, либо разрушительным коммитом.

Предположим, что мы можем разделить F" на F "(эквивалентно F с обновленным родителем и коммиту слияния M, который разрешает конфликты, вызванныеприменение F к мастеру, это приведет к:

... A - B - C - E - G  (master)
                      \
                        D' - - - M - H'  (feature)
                           \   /
                             F'

Теперь каждый может видеть, что произошло в M. Большой недостаток в том, что F 'не компилируется.

Какие команды git дляпроблема, когда git rebase делает паузу после D, чтобы разработчик разрешил конфликт слияния? (в процессе разработки: я надеюсь, что смогу предоставить важные детали, когда смогу поэкспериментировать с git)

  • (возможно) git reset --hard
    для очистки рабочего каталога
    (проверьте, не мешает ли это выполнению перебазирования)
  • git checkout --theirs && git commit
    или git cherry-pick F
    для записи F '
  • git reset --hard HEAD~1
    для возврата к D'
  • git merge F' с разрешениемконфликты слияния. Для записи M
  • каким-то образом приведите рабочий каталог в такое состояние, чтобы git rebase cпродолжение
    Полагаю, git rebase ожидает некоторые промежуточные файлы, из которых он создает перебазированный коммит, а не чистую промежуточную область. Возможно git rebase --skip работает.
    (расследовать)
0 голосов
/ 31 октября 2019

Возможно ли иметь как чистую линейную историю, как результат git rebase с последующим git merge, так и запись (в коммите) изменений, внесенных действием rebase?

Не совсем, нет.

То, что есть в Git - это коммиты. Это означает, что в большинстве случаев все Git являются коммитами. Я сейчас произнесу слово , в основном .

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

Имя ветви, например master или dev, или удаленное отслеживаниеимя типа origin/master или origin/dev, содержит хэш-идентификатор одного коммита.

Каждый коммит содержит хэш-идентификатор некоторого числа непосредственных предшествующих коммитов: родителей этого коммита. Большинство коммитов содержат один родительский хэш-идентификатор. Коммиты слияния удерживают два или более, что делает их коммитами слияния. По крайней мере, один коммит - самый первый, который кто-либо сделал в этом хранилище - обязательно не имеет родителей. Этот коммит (обычно "the") root commit. Git использует родительский хеш tip коммита ветки, чтобы найти предыдущий коммит в ветке, затем продолжает обратно, по одному коммиту за раз, чтобы добраться до корня: в этом процессе пройденные коммиты, - это коммиты, которые находятся в ветви.

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

Каждый коммит хранит полный и полный снимок всех файлов, которые составляют этот коммит. (Внутренние элементы здесь на самом деле не имеют отношения к вашей проблеме; факт, что коммит хранит, косвенно, только один снимок.) Он также хранит метаданные для этого коммита: кто его сделал (автор и коммиттер, каждый из которых состоит изимя + электронная почта + дата и время), родительские хэш-идентификаторы и сообщение журнала. Вот и все! Это все, что есть. Git находит изменения , сравнивая смежные коммиты. Что бы ни отличалось в двух снимках, это должно быть то, что изменилось .

Когда вы используете git rebase, вы действительно говорите Git: копировать некоторые коммиты. оригинальная цепочка коммитов может выглядеть так:

          I--J--K   <-- feature
         /
...--G--H--L   <-- master

Ветвь feature состоит из коммита K (коммит-подсказка, найденная по имени feature, в которой хранится фактический хэш-идентификатор, который мы находимсявызов K здесь), перед которым J предшествует I, перед которым H, и так далее. Ветвь master состоит из коммита L, затем H, затем G и т. Д., Таким же образом.

Такая структура потребует истинного слияния для повторного объединения feature в master, поэтому ваша группа использует rebase. Rebase означает: Найти коммиты, уникальные для feature: те, которые не могут быть найдены, начиная с кончика master и работая в обратном направлении. Затем скопируйте большинство или все из них, создав новые копии, превратив каждый коммит в изменения, и применив эти изменения к новой начальной точке. В этом случае Git должен скопировать коммиты I, JK, чтобы новые копии появлялись после L.

Каждый шаг копирования по сути совпадает с git cherry-pick, а некоторые команды git rebase действительно буквально используют git cherry-pick для выполнения копирования,Вы можете думать о каждой копии как о вишне. Мы будем обозначать скопированные коммиты простой меткой, например, I':

          I--J--K   <-- feature
         /
...--G--H--L   <-- master
            \
             I'-J'-K'  <-- [new feature, being built]

Теперь, когда перебазирование завершено, Git просто вытаскивает имя feature из старого совета и делает егоуказать на новый совет:

          I--J--K   [abandoned]
         /
...--G--H--L   <-- master
            \
             I'-J'-K'  <-- feature

WhЕсли вы посмотрите на этот репозиторий позже, не зная, какими были исходные три хеш-идентификатора, вы не сможете найти исходные три. Вы можете даже не знать, что оригинальные три коммита когда-либо существовали. Если тот, кто делал копирование, не передавал оригиналов , а только когда-либо передавал копий , они будут всем, что вы когда-либо увидите. И, поскольку оригиналы больше не доступны для поиска, Git в конечном итоге удалит их полностью («соберите мусор» с помощью git gc). С таким же успехом они могли бы никогда не существовать. Только если вы сохраните где-нибудь хэш-идентификаторы, чтобы сам Git не собирал оригиналы GC, вы сможете их увидеть и увидеть. А так как Git хранит только снимков , вам нужно оригиналов, чтобы увидеть что произошло между оригиналами и их копиями.

Обращаясь к "в основном "выше" и другие вафельные биты

Git имеет:

  • Теги (аннотированные теги): они хранят данные. Данные тегов, которые вы можете хранить здесь, являются произвольными и зависят от вас. Если бы вы могли заставить своих пользователей помещать важные данные в аннотированный тег, а также прикреплять и вставлять этот тег, это сохранит его. Но для этого необходимо, чтобы они поместили важные данные куда-то.

  • Примечания: git notes позволяет пользователям записывать произвольные дополнительные данные и свободно связывать эти данные с фиксацией (по хэш-идентификатору фиксации). Если бы вы могли заставить своих пользователей помещать важные данные в заметки и прикреплять эти заметки к скопированным коммитам, это сохранит их.

  • Фиксация сообщений. Мы уже упоминали об этом, но они позволяют пользователям хранить произвольные данные. Если бы вы могли заставить своих пользователей помещать важные данные в сообщения коммита перебазированных коммитов, это сохраняло бы их.

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

Я уже упоминал выше, что ребаз копирует "большинство или все" коммиты. Rebase пропускает:

  • все коммиты слияния (по умолчанию) и
  • любой коммит, который Git считает уже существующим в восходящем потоке. 1

По крайней мере, он делает это по умолчанию: -p, а новые опции --rebase-merges позволяют «копировать» слияния (действительно, заново выполняя слияние).


1 Git делает это, сравнивая git patch-id, что приводит к симметричной разности перебазируемой ветви и переброски вверх по течению. Технически это означает, что Git использует эквивалент git rev-list --left-right HEAD...upstream (обратите внимание на трехточечный синтаксис) для генерации списков коммитов. Затем он запускает эквивалент git patch-id для всех из них. Затем он сравнивает левый и правый идентификаторы патчей коммитов. Коммиты со стороны HEAD, то есть слева, которые соответствуют коммиту в правой / восходящей стороне, выбиваются. Идея здесь состоит в том, чтобы исключить вишневый коммит, который уже был вишнево выбран в восходящем потоке.


Заключение

То, к чему это все сводится, это:

  • Не позволяйте пользователям, которые регулярно неправильно разрешают конфликты, перебазировать ветки.

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

Git вполне способен справиться с запутанной историей:

          I--J--K
         /       \
...--G--H--L------M--Q--R   <-- master
            \       /
             N--O--P

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

...