Переключение между ветвями, где подкаталог находился в основном репо против подмодулированного

Мы создали подкаталог S в отдельное репо и повторно добавили его в качестве подмодуля в ветки B. Теперь, когда я хочу вернуться к исходной ветки A, где подкаталог был проверен в исходном репо, git жалуется, что материал из подмодуля не отслеживается и сначала должен быть удален. Можем ли мы вернуться к A вообще без ручного перетаскивания S и переместить его обратно для B?

Обновление: приведенные ниже комментарии указывают на то, что это может быть недостатком старой ветки, а git не хочет делать здесь умную вещь. Разве это так, и какая перезагрузка, если таковая имеется, исходной ветки с S в основном репо, может позволить ей сосуществовать и быть в любой момент в зависимости от подмодулированной версии?

Ответ 1

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

---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 всегда казался подмодулем. Это наверняка представит гладкую поверхность для будущих коммутаторов commit, но это осложняется тем фактом, что вам нужно будет убедиться в том, что вы совершили фиксацию в исходном репозитории подмодулей, которые отражают каждое историческое состояние каталога S в исходных коммитах. Если у вас много разных деревьев S (т.е. Вы сделали некоторые локальные изменения в S до преобразования его в подмодуль), то это может быть сложный процесс /script.

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

  • Создайте "пустую ветвь" в подмодуле.

    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 как каталог; Команды 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 -)