Как я могу объединить ветку из другого репо без добавления полной истории коммитов? - PullRequest
1 голос
/ 30 апреля 2019

Вопрос:

Есть ли способ объединить ветку из другого репо, но только самая последняя фиксация в этой ветке добавляется в нашу историю фиксации?

Справочная информация:

Мы работаем над проектом UE4. Мы пытаемся обновить до последней версии движка, когда Epic выпускает обновление. Наш рабочий процесс выглядит примерно так:

dev:          a - b -- c -- d -- e -- f -- g - h - i
             /             /              /
upstream:  A (4.19) - B - C (4.20) - D - E (4.21) - F - G

Обратите внимание, что b и c представляют пару сотен коммитов, а B обычно представляет несколько тысяч коммитов. Когда мы объединяем C с нашим репо, мы «получаем» все коммиты, представленные B. Эти дополнительные коммиты добавляют наворот к нашему репо и обнаруживаются с чередованием с нашими собственными коммитами в представлении истории в BitBucket и в базовом git log выход.

В последний раз, когда я выполнял слияние, скажем, C на приведенном выше графике, я делал это как слияние в виде сквоша, которое дало мне все изменения, но сделал только один коммит.

К сожалению, после того, как я все еще изучаю git, я понял, что это эффективно разорвало связь с коммитами вверх по течению. Таким образом, когда я пошел на слияние E, общий базовый коммит был A вместо C. Что касается git, я независимо выполнил работу, которая была в B и C в нашей ветви. Я столкнулся с десятками тысяч конфликтов слияния из файлов, которые были изменены в B & C, а затем изменены в D & E.

К счастью, мне удалось довольно легко восстановиться путем повторного слияния C, сохранения истории, а затем слияния E.

Однако я вернулся к своему первоначальному вопросу. Я хотел бы иметь возможность объединить все изменения, ведущие к C, в наше репо, но, по сути, только C появляется в истории коммитов нашего репо (для использования в качестве общей базы, когда я иду к слиянию E). Есть ли хороший способ сделать это?

Спасибо за ваше время!

Ответы [ 3 ]

2 голосов
/ 30 апреля 2019

Короткий ответ: нет, вы не можете этого сделать. Вы можете сделать то, что может достаточно.

История - только коммит. Коммиты являются историей.

Каждый коммит имеет свой уникальный хэш-идентификатор. Этот хэш-идентификатор является фиксацией в очень реальном смысле, хотя технически это криптографическая контрольная сумма содержимого этой фиксации. Содержимое включает в себя идентификатор хэша сохраненного исходного снимка и идентификатор хэша непосредственно предыдущей фиксации. Это то, что позволяет Git начинать с последнего коммита и работать в обратном направлении, по одному коммиту за раз, через цепочку коммитов: commit Z имеет родительский хеш Y, поэтому Git может найти Y и посмотрите, что у него есть родительский хэш X и т. д.

Коммиты слияния являются особенными только одним способом: они имеют более одного родительского хэша. (Обычно их ровно два; более двух - это слияние осьминога , и они на самом деле не достигают ничего, чего нельзя было бы сделать несколькими отдельными слияниями, хотя они полезны для демонстрации того, что цель объединение состояло в том, чтобы связать кучу ревизий одновременно, и, конечно, для демонстрации своего Git-fu. :-)) При коммите слияния Git будет следовать обе истории , если вы не скажете это не (см. ниже).

Как вы видели, git merge работает, следуя истории - обратной цепочке коммитов - обратно к общему коммиту. У вас либо есть коммиты, и они являются общими; или у вас их нет, так что делать нечего. Затем, для обычного git merge, он делает коммит слияния, помня обоих непосредственных предшественников, что делает возможным будущие слияния. Использование git merge --squash обрезает дополнительного родителя, что - по крайней мере потенциально, а часто и практически - делает гораздо более трудным слияние в будущем, поскольку вы получаете древнего предка вместо желаемого современного.

Что вы можете делать

Обычно git log следует за историей - за все истории - путем обхода графика коммитов, один шаг за раз, назад:

...--o--o--o--o   <-- branch (HEAD)

Когда история линейная (без слияний), это работает нормально, но когда слияние имеет:

          o---------o-------o
         /                   \
...--o--o                     *--o--o   <-- branch (HEAD)
         \                   /
          o--o--o--o--o--o--o

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

git log --first-parent

Эта опция --first-parent сообщает Git, что когда он встречает коммит слияния, такой как * выше, он должен смотреть только на первого родителя слияния.

Какой родитель является первым родителем? Ответ таков: первым родителем слияния является коммит, который был текущий коммит, когда вы сделали слияние. Так что в этом случае у нас было:

          o---------o-------o   <-- branch (HEAD)
         /
...--o--o
         \
          o--o--o--o--o--o--o   <-- other

до вы бежали git merge. Вы пробежали git checkout branch, чтобы войти в это состояние. Затем вы запустили git merge other, чтобы сделать коммит слияния *. Таким образом, первый родительский коммит * - это коммит в верхнем ряду, который вы использовали при запуске git merge.

Таким образом, git log --first-parent вообще не будет показывать нижний ряд коммитов. Они по-прежнему будут присутствовать, часть истории, позволяя будущим слияниям работать хорошо и, конечно, также увеличивать ваш репозиторий, но вы не будете видеть их.

Огромное количество git log аргументов о невидимости конкретных коммитов: отсеивание деревьев, чтобы вы могли видеть лес. Например, git log --simplify-by-decoration пропускает показ любых коммитов, которые не имеют название ветви или тега. Используя git log [--follow] -- <path>, вы говорите Git не показывать коммиты, что не изменяет данный файл или поддерево. Существуют и другие варианты, влияющие на то, как работает «упрощение истории», и они становятся довольно сложными. Вы можете изучить справочную страницу git log в течение нескольких дней. Но начните с --first-parent.

1 голос
/ 30 апреля 2019

Есть ли способ объединить ветку из другого репо, но только самая последняя фиксация в этой ветке добавляется в нашу историю фиксации?

Есть много способов сделать это, но не в полном смысле «имей свой торт и прячь его». Это будет звучать неловко, извините, но я не могу найти более простой способ выразить это: объединение историй обязательно объединяет истории, которые вы объединили.

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

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

git clone --bare . --single-branch --branch upstream `mktemp -d`
cd $_

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

Чтобы разделить эту историю до самых ярких моментов, вы можете

git filter-branch --tag-name-filter 's,^,sliced-,' -- upstream --simplify-by-decoration
git clone --no-hardlinks --bare . --single-branch --branch upstream `mktemp -d`
cd $_
du -sh

и посмотрите, сколько места на репо спасает вас. Я запустил это на ветке Git, 703 коммитов, ~ 55K коммитов. Это сэкономило 100 МБ дискового пространства. Мой каталог скриншотов занимает больше, чем это. Проверка Git занимает в три раза больше времени.

Если важно просто снять помехи с ваших git log дисплеев, вам не нужно ничего делать. В вашем репо сделайте

mkdir .git/info
git rev-list upstream --parents --simplify-by-decoration >.git/info/grafts

и это все, что вам нужно.

0 голосов
/ 30 апреля 2019

Скажем, ваша ветка upstream называется upstream, и вы хотите объединить ее с веткой разработки:

git checkout development
git merge --squash upstream
git commit

Это примет все коммиты из ветки upstream, раздавит их в 1 коммит и объединит егос вашей веткой разработки.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...