Ваша история выглядит примерно так:
---X A
\
Y B
git ls-tree A
показывает (например):
040000 tree 48770cdc854cc14fecc71029180be7a979f4baa1 S
100644 blob beac6e189b7c69b249271b52cd2db5e418c05a50 file
git ls-tree A:S
показывает (например):
100644 blob 6357df9903460c9e8b43311aff3c7a6fd7fe6aa1 somefile
git ls-tree B
показывает (например):
100644 blob 1f8556335163a2bcbcc366a17d08d1f8e0540e6f .gitmodules
160000 commit 234871cd6f0c1f9109e483383d7712dd8a1986e5 S
100644 blob beac6e189b7c69b249271b52cd2db5e418c05a50 file
(cd S; git ls-tree HEAD)
показывает (например):
100644 blob abccc3958be33be4b93f56efae1b60820545aad2 somefile
Вы хотите перейти от коммита Y (или позже)) совершить X (или ранее) или наоборот.
Если ваша активная ветвь - B, то git checkout A
говорит (например):
error: The following untracked working tree files would be overwritten by checkout:
S/somefile
Please move or remove them before you can switch branches.
Aborting
Git очень старается, чтобы никогда не проигратьданные, если вы не скажете это сделать (например, с опциями «force»).Проблема, которую Git находит и сообщает здесь, состоит в том, что ветвь A имеет другое содержимое для S/somefile
, чем рабочее дерево.Поскольку S/somefile
не отслеживается (с точки зрения суперпроекта), Git отказывается заменить файл и, таким образом, отказывается переключать ветки / коммиты.
Git может быть умнее в этом (заметив, что файлотслеживается в подмодуле, поэтому его не следует считать неотслеживаемым при переключении ветвей в суперпроекте), но это ограничение текущей реализации.Существует проект Google Summer of Code 2011 для Git, цель которого - решить некоторые области поддержки подмодулей , но мне не ясно, будет ли решена эта конкретная проблема.
Вы можете, как вы предлагаете, переписать свою историю так, чтобы S
всегда казался субмодулем.Это, безусловно, представило бы самую гладкую поверхность для будущих переключателей фиксации, но это усложняется тем фактом, что вам необходимо убедиться, что у вас есть коммиты в исходном репозитории подмодуля, которые отражают каждое историческое состояние каталога S
в оригинальных коммитах.Если у вас есть много разных деревьев S
(то есть вы сделали некоторые локальные изменения в S
перед преобразованием его в подмодуль), то это может быть сложный процесс / сценарий.
Более простой обходной путь может бытьвременно извлекать «пустую ветвь» в подмодуле перед переключением на коммит, который имеет его в качестве каталога.
Создать «пустую ветвь» в подмодуле.
git checkout --orphan empty
git rm -r --cached .
git commit --allow-empty -mempty
Вы можете опубликовать эту «ветку» в исходном репозитории подмодуля, чтобы никому больше не понадобилось воссоздавать ее из себя.
Когда вам нужно переключиться с коммита, гдеS
- это подмодуль для коммита, где S
- это каталог, сначала извлеките «пустую ветку» в подмодуле:
(cd S && git checkout empty)
git checkout A
Вы увидите это предупреждение, потому что Git оставит позади S/.git
:
warning: unable to rmdir S: Directory not empty
Поскольку S/.git
все еще присутствует, вы должны быть осторожны, выполняя команды Git только за пределами S
, когда работаете над коммитом, для которого S
является указаниемctory;Команды Git, введенные в S
, будут работать на S/.git
(в этом состоянии это просто «под-репозиторий», а не полный подмодуль) вместо верхнего уровня .git
.
Когда вам нужно переключиться с коммита, где S
- каталог, на коммит, где S
- подмодуль, вам нужно будет проверить соответствующую ветку / коммит в подмодуле после переключения коммита суперпроекта.
Вы можете использовать git submodule update
для восстановления фиксации, записанной в суперпроекте.Или, если вы работали над веткой в подмодуле, просто проверьте ее.
git checkout B
# THEN
git submodule update
# OR checkout a specific branch
(cd S && git checkout master)
# OR checkout previous branch
(cd S && git checkout -)