Когда подпроект является слиянием поддерева, как мне отменить коммит, полученный с удаленного поддерева? - PullRequest
9 голосов
/ 28 апреля 2011

У меня есть сценарий оболочки для теста .Я хочу понизить подпроект , но не могу понять, как.Вот история:

Я создал два репозитория: " подпроект " и " суперпроект ".Я поместил несколько версий некоторых файлов в подпроект, присвоив им теги «v0», «v1», «v2» ... и т. Д. В суперпроекте я добавил подпроект в качестве репозитория и объединил его в поддерево:

git remote add subproject ../subproject
git fetch --tags subproject
git merge -s ours --no-commit v1
git read-tree --prefix=subproject/ -u v1
git commit -m "added subproject at v1"

Затем я обновляю подпроект до версии v3, и все идет хорошо:

git merge -s subtree -X subtree=subproject v3

Но потом я понимаю, что действительно хочу v2.Я думаю, что мне здесь нужен ребаз, но я не уверен.Вот что я попробовал:

git checkout -b downgrade-subproject-to-v2
git rebase -s subtree -X subtree=subproject --onto v2 downgrade-subproject-to-v2

Но я вижу, как содержимое подпроекта выскользнуло по всему основному каталогу моего суперпроекта!

Так что я немного почитал и нашел некоторые другие идеи.Этот не работал:

git revert --no-edit v3

Он также обнаружил файлы в моем суперпроекте и жаловался на конфликты.Это также не сработало:

git merge -s subtree -X subtree=subproject v2

Очевидно, v3 уже объединен, поэтому объединение v2 (предка v3) не даст никакого эффекта.

Единственное, что я нашелэто приводит к понижению рейтинга так:

git diff v3 v2 --src-prefix=subproject/ --dst-prefix=subproject/ | patch -p0

Но мне это не нравится, потому что на самом деле это не показывает правильное происхождение в коммите DAG.Это просто переписывание дерева грубой силой.Кроме того, для «слияния» используется программа-патч, что не так умно, как слияние с помощью git.Если суперпроект внес какие-либо изменения в подпроект до перехода на более раннюю версию, исправление не будет так же вероятно, чтобы устранить ошибки слияния, как было бы слияние git.

Есть ли канонический способвернуть версию подпроекта с помощью слияний поддеревьев?Если нет, то есть ли хоть что-то лучше чем diff | patch ?

Ответы [ 2 ]

13 голосов
/ 29 апреля 2011

В конечном итоге git revert v3 в superproject завершается неудачей, поскольку не учитывает тот факт, что v3 находится «за» слиянием поддерева (таким образом, записанные пути отличаются в N и v3).

Вернуть поверх superproject история

С Git 1.7.5 вы можете использовать

git revert --strategy subtree -X subtree=subproject v3

вместо вашего git diff | patch; commit метода. Это потенциально позволит лучше использовать механизм слияния. Однако при любом из этих методов родительский объект нового коммита будет построен на истории superproject (что может быть не тем, что вам действительно нужно).

Ваша история subproject выглядит так:

     (v1) (v2) (v3)
     /    /    /
----o----o----o

Ваша superproject история выглядит так:

     (v1) (v2) (v3)
     /    /    /
----o----o----o
     \         \
  ----M---------N

Оба метода создают коммит O с этой историей:

     (v1) (v2) (v3)
     /    /    /
----o----o----o
     \         \
  ----M---------N----O

Вернуть поверх subproject история

Возможно, вы будете счастливее, если коммит будет построен прямо над v3. Вы можете добавить поддерево такого коммита в историю superproject, как вы делаете это с любым новым subproject коммитом.

  • В subproject: оформить заказ v3, вернуть его (R) и пометить результат (revert-v3)

    git checkout v3
    git revert HEAD
    git tag revert-v3
    git checkout -       # go back to wherever you originally were
    

    Итоговая история:

         (v1) (v2) (v3) (revert-v3)
         /    /    /    /
    ----o----o----o----R
    
  • В superproject: получить результат и использовать объединение поддеревьев (P), чтобы включить его

    git fetch --tags subproject
    git merge -s subtree -X subtree=subproject revert-v3
    

    Итоговая история:

         (v1) (v2) (v3) (revert-v3)
         /    /    /    /
    ----o----o----o----R
         \         \    \
      ----M---------N----P
    

Если вы не хотите иметь revert-v3 в subproject (например, потому что v3 подходит в контексте subproject, но просто не подходит для использования в superproject), тогда вы можете Выполните работу полностью в superproject за счет изменения вашего рабочего дерева (переключение туда и обратно между «несвязанными» коммитами эффективно удалит и восстановит все ваши файлы рабочего дерева, поэтому значения mtimes, inode и т. д. будут изменены):

  • В superproject: оформить заказ v3, вернуть его обратно, пометить его, вернуться к предыдущей проверке, поддерево объединить новый коммит

    git status        # make sure to start with a clean index and working tree
    git checkout v3
    git revert HEAD
    git tag revert-v3
    git checkout -
    git merge -s subtree -X subtree=subproject revert-v3
    

Основное отличие состоит в том, что последний тег revert-v3 присутствует только в superproject во втором варианте. Полученная история имеет ту же структуру и содержание, что и первая вариация.

Если вы не можете потерять рабочее дерево subproject или superproject и не хотите revert-v3 в subproject, то вы можете использовать временный клон subproject (в клоне: отменить коммит, пометить его; в superproject: извлечь метку из клона, поддерево объединить, а затем удалить временный клон).


git subtree

Возможно, вы захотите изучить команду git subtree от apenwarr. Поддержка слияний поддеревьев немного более упорядочена (например, git subtree pull -P prefix repository refspec). Кроме того, ее команда git subtree split может представлять особый интерес, поскольку она позволит вам преобразовать коммит, сделанный из результата вашего git diff new old | patch (или git revert --strategy), в коммит, который, кажется, был сделан непосредственно поверх истории поддерева.

git subtree split --prefix=subproject/ --onto v1 1 будет принимать «возврат на вершину superproject» истории:

     (v1) (v2) (v3)
     /    /    /
----o----o----o
     \         \
  ----M---------N----R

и извлеките subproject -только историю, подобную этой:

     (v1) (v2) (v3)
     /    /    /
----o----o----o-----R'

где все до R' - это оригинальная subproject история, а R' - это версия R только с содержимым из subproject/.

Эта способность «извлекать по факту» означает, что вы можете просто работать с самим контентом в superproject, не беспокоясь о том, могут ли коммиты, которые касаются subproject/, быть кандидатами для отправки «восходящего потока» на subproject хранилище.

1 Вы можете отключить --onto v1, если вы использовали git subtree add для первоначального добавления поддерева 2 . Он помещает специальный текст в сообщения фиксации, которые он генерирует, чтобы он мог идентифицировать «поддерево» битов истории.

2 Или вы можете «конвертировать в git subtree» что-то вроде

git rm -rf subproject &&
git commit -m 'converting to subproject/ to "git subtree"' &&
git subtree add --prefix=subproject most-recent-subproject-commit
1 голос
/ 29 апреля 2011

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

Надеюсь, это поможет.

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