переключение между ветвями, где подкаталог был в основном репо по сравнению с субмодульным - PullRequest
12 голосов
/ 02 июля 2011

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

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

1 Ответ

11 голосов
/ 02 июля 2011

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

---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 -)
    
...