Как сказать Git, что две коммиты на самом деле одинаковы в непересекающейся истории?

У меня есть репозиторий Git, который клонируется из другого репозитория Git, который является клоном из гигантского репо SVN. Мы можем обновить другое репозиторий Git из SVN и передать его на SVN, но с другим пользователем.

Я хочу публиковать свои локальные коммиты в SVN-репо прямо со своим собственным пользователем.

Это то, что я пробовал: я вручную создал ветвь git-svn, добавив ее в .git/config

[branch "master"]
[svn-remote "svn"]
        url = https://url/to/svn/trunk/repo_name
        fetch = :refs/remotes/git-svn

Затем git svn fetch, загруженный 32k совершает.

Но я закончил с двумя непересекающимися историями:

  • Один из Git:
    • Начиная с commit 674c35a, который представляет состояние репо SVN в точке исходного клона.
    • Филиал master всегда синхронизируется с SVN (через другое Git repo).
    • В ветке dev есть наши текущие разработки, которые должны быть объединены с master (обычно раздавлены), а затем переданы обратно в репозиторий SVN.
  • Один для репо SVN:
    • Начинается с правильной начальной фиксации.
    • Он имеет значение 30k +.
    • Конец, который Git изначально был клонирован из SVN [fb1b9c7] (но только с отличием от предыдущих коммитов, а не с полным репо в качестве фиксации начала).
    • Тогда то же самое фиксирует (100+), что должно быть общим для git/origin/master.

Как я могу сказать Git, что начальная фиксация (674c35a) на самом деле совпадает с фиксацией SVN (fb1b9c7)? Таким образом, Git может как-то понять, что коммиты в master, после клонирования, на самом деле такие же, как git-svn?

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

Ответ 1

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

Текущее состояние:

  • Два репозитория git с расходящимися историями, но одно происхождение

Целевое состояние:

  • Один git репо с двумя ветвями, представляющими эти расходящиеся истории

  • Преобразуйте обе истории в ветки git (я думаю, вы уже получили это)
  • Найдите точку расхождения, сравнивая журналы и видя, когда изменяется хеш дерево.
    • Получить список идентификаторов дерева и идентификаторов фиксации в каждой ветки с помощью git log --reverse --format="%T %H" и сохранить это в файле
    • Сравните списки, чтобы увидеть, где хеш дерева расходится (я использую программу diff, которая может делать столбцы, вы можете просто использовать awk и diff, хотя)
  • Отметьте точку расхождения на целевой ветке репо и создайте новую ветку
  • Извлечь все объекты из источника в цель
  • Для каждой ревизии исходного источника создайте новую ревизию для цели, которая
    • Является ли оформление дерева внешней ревизии с помощью git read-tree -um $TREEID
    • Использует исходное сообщение фиксации с помощью git commit --reuse-message $ORIGINAL_COMMIT

Или... cat ваш список идентификатора дерева id + commit-id в этот script.

#!/bin/bash
# graft script
while read TREE COMMIT
do
 git read-tree -um $TREE
 git commit --reuse-message $COMMIT
done

Ответ 2

Ваша история, вероятно, выглядит примерно так:

*--*--*--fb1b9c7--*--*--* [git-svn] (version of master from SVN)

         674c35a--*--*--* [master] (version of master from Git)
                         \
                          *--*--*--* [dev]

Похоже, у вас есть две проблемы: ваша история Git не содержит 30k +, фиксирует полную историю SVN, а dev основана на (неполной) истории Git вместо версии SVN.

Так как fb1b9c7 == 674c35a, вы должны уметь:

git rebase --onto git-svn master dev

Мои эксперименты показывают, что git rebase не заботится о том, чтобы целевая ветвь (git-svn) не имела той же истории, что и базовая ветвь (master). Также не должно иметь значения, что SVN сохраняет только diffs, потому что git -svn преобразует эти diff в real Git, комментирует во время выборки.

Поскольку истории содержат одно и то же содержимое, вы даже можете это сделать:

git checkout dev
git rebase git-svn

После этого вы можете удалить master и позволить этому дереву собирать мусор позже. (Если вам все еще нужна отдельная ветвь master, тогда git checkout -b master git-svn после удаления оригинала master.)

Когда вы сказали:

(коммиты не находятся в верхней части ветки)

Я предполагаю, что вы имеете в виду commits fb1b9c7 и 674c35a. Тем не менее, не имеет значения, что эти коммиты зарыты в истории, потому что мы выберем один (fb1b9c7) для использования, а другой собираем мусор, как упоминалось выше.

Ответ 3

Если вы хотите заменить одно коммит другим фиксатором, просто используйте git replace, например git replace 674c35a fb1b9c7.
Если вы хотите сделать это постоянным, используйте git rebase.