В конечном итоге 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