Подтвердить что ты не робот

Git: Можно ли использовать одну и ту же рабочую копию подмодуля несколькими проектами?

Я новичок в Git. Допустим, у меня есть два репозитория git, которые имеют ту же библиотеку, что и подмодуль:

/home/projects/project1/library_XYZ
/home/projects/project2/library_XYZ

Также можно сказать, что я работаю над проектами и библиотекой одновременно. Когда я внес изменения в библиотеку, скажем, в /home/projects/project1/library_XYZ, мне придется нажать эти изменения, а затем потянуть их в /home/projects/project2/library_XYZ, чтобы сделать их доступными для project2, правильно? Я думаю, что это неудобно по двум причинам:

  • Мне придется строить library_XYZ два раза.
  • У меня есть нежелательная избыточность, которая противоречит фактической организации проекта.

Есть ли способ сделать git клонировать подмодуль library_XYZ в один и тот же локальный каталог, т.е. организовывать файлы таким образом

/home/projects/project1
/home/projects/project2
/home/projects/library_XYZ

пока library_XYZ все еще является подмодулем обоих проектов?

Я думаю, что это может быть связано с этим, что остается без ответа, хотя моя настройка несколько отличается.

4b9b3361

Ответ 1

Настройка общих зависимостей как подмодулей проста. Команда git submodule не делает это автоматически, но подмодуль - это не что иное, как вложенный репозиторий - и git не требует, чтобы какой-либо фактический репозиторий или его рабочая точка находились в каком-либо конкретном месте.

Настройте репозиторий libraryXYZ для использования в качестве общего подмодуля

# a submodule is just a repository. We're going to share this one.
git clone u://r/libraryXYZ 

# and keep its worktree right here:
( cd libraryXYZ; git config core.worktree .. )

Затем, из любого места, клонируйте проект, используя подмодуль, и настройте его для использования общего:

git clone u://r/project1
cd project1
git submodule init
echo gitdir: path/to/shared/libraryXYZ/.git > libraryXYZ/.git

Теперь субмодуль project1 libraryXYZ будет использовать общий репо и т.д. libraryXYZ.

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

# for example to see where all a project submodules' parts are kept
git submodule foreach git rev-parse --git-dir --show-toplevel

# or for just one:
git --git-dir=$project1/libraryXYZ/.git rev-parse --git-dir --show-toplevel

(позднее редактирование: примечание @twalberg стоит иметь в виду, это может сделать слишком легким сделать git submodule update из одного проекта, не осознавая вас 've также изменил среду сборки для каждого другого проекта, который разделяет зависимости.)

Ответ 2

У меня была такая же проблема, как и вы: большая универсальная библиотека утилиты в качестве подмодуля и многие проекты, зависящие от нее. Я не хотел создавать отдельную проверку для каждого экземпляра библиотеки утилиты.

Решение, предлагаемое jthill, отлично работает, но оно разрешает только первую половину проблемы, а именно, как сохранить git счастливым.

Что не хватало, так это то, как поддерживать систему сборки счастливой, ожидая, что фактические файлы будут работать, и не заботятся о ссылках на ссылки.

Но если вы объедините его идею с символической ссылкой, вы получите то, что хотите!

Чтобы реализовать это, давайте начнем с проектов из вашего примера

/home/projects/project1
/home/projects/project2
/home/projects/library_XYZ

предполагая, что как project1, так и project2 уже добавлены в библиотеку library_XYZ как подмодуль, и что в настоящее время все три проекта содержат полную проверку библиотеки_XYZ.

Чтобы заменить полные проверки подмодулей библиотеки с помощью общей символической ссылки на проверку библиотеки, сделайте следующее:

sharedproject="/home/projects/library_XYZ"
superproject="/home/projects/project1"
submodule="library_XYZ"
cd "$superproject"
(cd -- "$submodule" && git status) # Verify that no uncommited changes exist!
(cd -- "$submodule" && git push -- "$sharedproject") # Save any local-only commits
git submodule deinit -- "$submodule" # Get rid of submodule check-out
rm -rf .git/modules/"$submodule" # as well as of its local repository
mkdir -p .submods
git mv -- "$submodule" .submods/
echo "gitdir: $sharedproject.git" > ".submods/$submodule/.git"
ln -s -- "$sharedproject" "$submodule"
echo "/$submodule" >> .gitignore

а затем повторите те же шаги для /home/projects/project 2 как $superproject.

И вот объяснение, что было сделано:

Сначала проверка субмодуля удаляется с помощью "git subodule deinit", оставляя в качестве пустой директории library_XYZ. Обязательно сделайте все изменения, прежде чем вы это сделаете, потому что он удалит проверку!

Затем мы сохраняем любые коммиты, локальные для выписки, которые еще не были перенесены в общий проект с помощью "git push" в /home/projects/library _XYZ.

Если это не работает, потому что вы не настроили удаленный или refspec для этого, вы можете сделать это:

(saved_from=$(basename -- "$superproject"); \
 cd -- "$submodule" \
 && git push -- "$sharedproject" \
             "refs/heads/*:refs/remotes/$saved_from/*")

Это сохранит резервные копии всех ветвей локального репозитория подмодуля как удаленные ветки в /home/projects/library _XYZ. Базовое имя каталога $superproject будет использоваться как имя пульта, i. е. project1 или project2 в нашем примере.

Конечно, на самом деле нет этого имени в /home/projects/library _XYZ, но сохраненные ветки будут отображаться так, как если бы они выполнялись там, где git branch -r ".

В качестве защиты refspec в приведенной выше команде не начинается с "+", поэтому "git push" не может случайно перезаписать любую ветвь, которая уже существует, в /home/projects/library _XYZ.

Далее, чтобы сэкономить место, будет удален .git/modules/library_XYZ. Мы можем сделать это, потому что нам больше не нужно использовать "git subodule init" или "git обновление подмодуля". Это так, потому что мы будем делиться как выпиской, так и каталогом .git/home/projects/library_XYZ с подмодулем, избегая локальной копии обоих.

Затем мы дадим git переименовать пустой каталог подмодулей в каталог .submods/library_XYZ, (скрытый), файлы в проектах никогда не будут использоваться напрямую.

Далее мы применим jthill-частное решение проблемы и создадим файл gitlink в .submods/library_XYZ, который делает git см. /home/projects/library _XYZ в качестве рабочего дерева и git репо подмодуля.

И вот новая вещь: мы создаем символическую ссылку с относительным именем "library_XYZ", которая указывает на /home/projects/library _XYZ. Эта символическая ссылка не будет находиться под контролем версий, поэтому мы добавим ее в файл .gitignore.

Все файлы сборки в project1 и project2 будут использовать символическую ссылку library_XYZ, как если бы это был обычный подкаталог, но на самом деле найти файлы из рабочего дерева в /home/projects/library _XYZ.

Никто, кроме git, фактически использует .submods/library_XYZ!

Однако, поскольку symlink./library_XYZ не поддерживается версией, он не будет создан при проверке проекта1 или проекта2. Поэтому нам необходимо позаботиться о том, что он будет создан автоматически при отсутствии.

Это должно быть сделано инфраструктурой сборки проекта1/project2 с командой, эквивалентной следующим командам оболочки:

$ test ! -e library_XYZ && ln -s .submods/library_XYZ

Например, если project1 создается с использованием Makefile и содержит следующее целевое правило для обновления подпроекта

library_XYZ/libsharedutils.a:
        cd library_XYZ && $(MAKE) libsharedutils.a

тогда мы вставляем строку сверху как первую строку действия правила:

library_XYZ/libsharedutils.a:
        test ! -e library_XYZ && ln -s .submods/library_XYZ
        cd library_XYZ && $(MAKE) libsharedutils.a

Если ваш проект использует какую-либо другую систему сборки, вы, как правило, можете сделать то же самое, создав настраиваемое правило для создания подкаталога library_XYZ.

Если ваш проект содержит только скрипты или документы и вообще не использует какую-либо систему сборки, вы можете добавить script, который пользователь может запустить для создания "недостающих каталогов" (на самом деле: символические ссылки) следующим образом:

(n=create_missing_dirs.sh && cat > "$n" << 'EOF' && chmod +x -- "$n")
#! /bin/sh
for dir in .submods/*
do
        sym=${dir#*/}
        if test -d "$dir" && test ! -e "$sym"
        then
                echo "Creating $sym"
                ln -snf -- "$dir" "$sym"
        fi
done
EOF

Это создаст символические ссылки на все подмодульные проверки в .submods, но только если они еще не существуют или если они сломаны.

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

Как только у вас уже установлен этот макет, посмотрите где-нибудь суперпроект, перейдите в его каталог верхнего уровня и выполните следующие действия, чтобы разрешить совместное использование:

sharedproject="/home/projects/library_XYZ"
submodule="library_XYZ"
ln -sn -- "$sharedproject" "$submodule"
echo "gitdir: $sharedproject.git" > ".submods/$submodule/.git"

Надеюсь, вы поняли: подкаталог library_XYZ, используемый project1 и project2, является неверсированной символической ссылкой, а не соответствующей пути подмодуля, как определено в ".gitmodules".

Симлинковая ссылка будет создана автоматически самой инфраструктурой построения и затем укажет на .submods/library_XYZ, но только, и это важно, если символическая ссылка еще не существует.

Это позволяет создать символическую ссылку вручную, вместо того, чтобы позволить системе сборки создать ее, поэтому ее также можно указать на единую общую проверку, а не на .submods/library_XYZ.

Таким образом, вы можете использовать общую выписку на своем компьютере, если хотите.

Но если другой человек ничего не делает специально и просто проверяет проект1 и выполняет обычное обновление подмодуля git --init library_XYZ ", все будет работать одинаково без общей выписки.

Никаких изменений в извлеченных файлах сборки, необходимых в любом случае!

Другими словами, проверка проекта 1 и проекта2 будет работать из коробки, как обычно, никакие специальные инструкции не должны сопровождаться другими людьми, использующими ваше репо.

Но вручную создавая файл gitlink и символическую ссылку library_XYZ до того, как система сборки сможет создать символическую ссылку, вы можете локально "переопределить" символическую ссылку и обеспечить совместную проверку библиотеки.

И есть еще одно преимущество: как оказалось, вам вообще не нужно возиться с "git subodule init" или "git обновлением подмодуля", если вы используете вышеупомянутое решение: оно просто работает без!

Это связано с тем, что "git subodule init" необходим только в качестве подготовки к "обновлению подмодуля git". Но вам это не понадобится, потому что библиотека уже выгружена где-то в другом месте, а также уже имеет свой собственный .git-каталог. Таким образом, нечего делать для "обновления подмодуля git", и нам это не нужно.

В качестве побочного эффекта, который больше не использует обновление подмодуля "git", также не требуется подкаталог .git/module. Также нет необходимости устанавливать альтернативы (опция -ограничение) для подмодулей.

Кроме того, вам больше не нужны пульты для push/pulling/home/projects/library_XYZ в /home/projects/project 1 и/home/projects/project2. Таким образом, вы можете удалить удаленный доступ для доступа к библиотеке_XYZ из project1 и project2.

Беспроигрышная ситуация!

Единственным очевидным недостатком этого решения является то, что для его работы требуются символические ссылки.

Это означает, что невозможно проверить проект1, скажем, в файловой системе VFAT.

Но тогда кто это делает?

И даже при этом проекты, такие как http://sourceforge.net/projects/posixovl, могут по-прежнему работать над любым ограничением символической файловой системы.

Наконец, некоторые советы для пользователей Windows здесь:

Символьные ссылки доступны с VISTA через команду mklink, но для этого требуются особые привилегии.

Но при использовании команды "соединения" из sysinternals символические ссылки на каталоги могут быть созданы даже в Windows XP раз.

Кроме того, у вас есть возможность использовать CygWin, который может (AFAIK) эмулировать символические ссылки даже без поддержки ОС.

Ответ 3

Вы можете сделать это, так как git 2.5:

  • Удалить /home/projects/project 2/library_XYZ
  • Удалить /home/projects/project 2/. git/modules/library_XYZ
  • cd /home/projects/project1/library_XYZ
  • создать проект ветки2 в /home/projects/project 1/library_XYZ
  • run git worktree add ../../project2/library_XYZ project2

Теперь /home/projects/project1/.git/modules/library_XYZ делится между двумя проектами.