git объединить несколько коммитов в один в несвязанной ветви каждый коммит в поддиректории префикса - PullRequest
0 голосов
/ 12 января 2020

Мне нужно объединить более одного коммита из каждой ветки или удаленного репо в один коммит в другой ветке.

input branch#1: o--o- - -o   (C1)
                          \
input branch#2: o--o- - -o | (C2)
    :                     \|
input branch#N: o--o- - -o | (Cn)
                          \|
 output branch:   o--o- - -o (Cm)

Мне нужно сделать это особым образом, где исходное дерево каждый коммит слияния входной ветви является префиксом или подкаталогом в исходном дереве коммита слияния выходной ветви:

<C1>       <C2>       ...  <Cn>
 |          |               |
 +- c1.txt  +- c2.txt       +- cn.txt

<Cm>
 |
 +- C1/c1.txt
 |
 +- C2/c2.txt
 |
 :     :
 |
 +- Cn/cn.txt

Кроме того, мне нужно изменить некоторые параметры коммита слияния, например author date, author email, et c и генерирует сообщение о коммите из сообщений о коммитах всех входных ветвей, оставляя родителей коммита слияния без изменений (включая список родительских коммитов sh в коммите слияния).

Копание в inte rnet Я уже нашел наиболее универсальное решение с минимальным набором команд:

git merge --allow-unrelated-histories --no-edit --no-commit -s ours <input-branches-and-commits>
git read-tree --prefix=C1/ <C1-branch>
git read-tree --prefix=C2/ <C2-branch>
:
git read-tree --prefix=Cn/ <Cn-branch>
cat ... | git commit --no-edit --allow-empty --author="..." --date="..." -F -

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

<Cm>
 |
 +- C1/c1.txt
 |
 +- c1.txt

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

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

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

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

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

1 Ответ

1 голос
/ 12 января 2020

Если вы собираетесь использовать git read-tree, чтобы заполнить индекс для коммита, который вы строите - и да, это простой способ добавить префикс к каждому, так же, как вы делаете - вы уже глубоко во внутренностях Git, так что вы также можете использовать git commit-tree для создания объекта коммита.

Другими словами, вообще не начинайте с git merge. Просто очистите индекс с помощью git read-tree --empty. Затем прочитайте каждый коммит C i , 1 ≤ i ≤ n . Ваш индекс теперь содержит файлы, которые вы намереваетесь поместить в этот коммит слияния C m .

Затем вместо git commit используйте git write-tree, чтобы превратить индекс в дерево объект, за которым следует git commit-tree для встраивания объекта дерева в новый коммит. Так как git commit-tree позволяет вам указать каждого родителя, вы можете напрямую слить N-образного осьминога:

git read-tree --empty
git read-tree --prefix prefix1 C1
git read-tree --prefix prefix2 C2
...
git read-tree --prefix prefixn Cn

tree=$(git write-tree) || die ...
commit=$(cat ... | git commit-tree -p C1 -p C2 -p C3 ... -p Cn) || die ...

Последнее, присоедините новое имя ветви к результирующему коммиту:

git branch the-final-result $commit

и у вас есть коммит C m в этой новой ветке.

Редактировать: очевидно, я немного неправильно понял вопрос, и у вас уже есть один имя существующей ветви B , для которого в данный момент выполняется коммит-коммит C B . Сначала вы должны прочитать это дерево, вместо использования git read-tree --empty, если вы хотите сохранить его файлы, а затем использовать этот коммит в качестве одного из родителей в финальном git commit-tree и просто перенести этот новый коммит в существующую ветку. имя B . Итак:

git read-tree Cm
git read-tree --prefix prefix1 C1
  .
  .
  .
git read-tree --prefix prefixn Cn

tree=$(git write-tree) || die ...
commit=$(cat ... | git commit-tree -p Cm -p C1 -p C2 ... -p Cn) || die ...
git push . $commit:refs/heads/B  # or git branch -f B $commit

Настройка на фактический желаемый результат.

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