un-submodule git submodule
Как снять субмодуль с подмодуля git (вернуть весь код обратно в ядро)?
Как в "как" я, как в "Лучшая процедура" ...
13 ответов
Если все, что вам нужно, это поместить свой код субмодуля в основной репозиторий, вам просто нужно удалить субмодуль и повторно добавить файлы в основной репозиторий:
git rm --cached submodule_path # delete reference to submodule HEAD (no trailing slash)
git rm .gitmodules # if you have more than one submodules,
# you need to edit this file instead of deleting!
rm -rf submodule_path/.git # make sure you have backup!!
git add submodule_path # will add files instead of commit reference
git commit -m "remove submodule"
Если вы также хотите сохранить историю подмодуля, вы можете сделать небольшую хитрость: "объединить" подмодуль с основным репозиторием так, чтобы результат был таким же, как был раньше, за исключением того, что файлы подмодуля теперь находятся в основной репозиторий.
В основном модуле вам нужно будет сделать следующее:
# Fetch the submodule commits into the main repository
git remote add submodule_origin git://url/to/submodule/origin
git fetch submodule_origin
# Start a fake merge (won't change any files, won't commit anything)
git merge -s ours --no-commit submodule_origin/master
# Do the same as in the first solution
git rm --cached submodule_path # delete reference to submodule HEAD
git rm .gitmodules # if you have more than one submodules,
# you need to edit this file instead of deleting!
rm -rf submodule_path/.git # make sure you have backup!!
git add submodule_path # will add files instead of commit reference
# Commit and cleanup
git commit -m "removed submodule"
git remote rm submodule_origin
Результирующий репозиторий будет выглядеть немного странно: будет более одного начального коммита. Но это не вызовет никаких проблем для мерзавца.
В этом втором решении вы получите большое преимущество, заключающееся в том, что вы по-прежнему можете запускать git blame или git log для файлов, которые изначально были в подмодулях. Фактически, вы сделали это для переименования множества файлов в одном репозитории, и git должен автоматически определить это. Если у вас все еще есть проблемы с git log, попробуйте некоторые опции (--follow, -M, -C), которые лучше обнаруживают переименование / копирование.
Я создал скрипт, который будет переводить подмодуль в простой каталог, сохраняя всю историю файлов. Это не страдает от git log --follow <file>
проблемы, от которых страдают другие решения. Это также очень простой однострочный вызов, который сделает всю работу за вас. G'luck.
Он основан на превосходной работе Лукаса Йенса, описанной в его посте в блоге " Интеграция подмодуля в родительский репозиторий", но автоматизирует весь процесс и разбирает несколько других угловых случаев.
Последний код будет поддерживаться с исправлениями ошибок на github по адресу https://github.com/jeremysears/scripts/blob/master/bin/git-submodule-rewrite, но для правильного протокола ответа stackru я включил Решение во всей полноте ниже.
Использование:
$ git-submodule-rewrite <submodule-name>
ГИТ-подмодуль-переписан:
#!/usr/bin/env bash
# This script builds on the excellent work by Lucas Jenß, described in his blog
# post "Integrating a submodule into the parent repository", but automates the
# entire process and cleans up a few other corner cases.
# https://x3ro.de/2013/09/01/Integrating-a-submodule-into-the-parent-repository.html
function usage(){
echo "Merge a submodule into a repo, retaining file history."
echo "Usage: $0 <submodule-name>"
echo ""
echo "options:"
echo " -h, --help Print this message"
echo " -v, --verbose Display verbose output"
}
function abort {
echo "$(tput setaf 1)$1$(tput sgr0)"
exit 1
}
function request_confirmation {
read -p "$(tput setaf 4)$1 (y/n) $(tput sgr0)"
[ "$REPLY" == "y" ] || abort "Aborted!"
}
function warn() {
cat << EOF
This script will convert your "${sub}" git submodule into
a simple subdirectory in the parent repository while retaining all
contents and file history.
The script will:
* delete the ${sub} submodule configuration from .gitmodules and
.git/config and commit it.
* rewrite the entire history of the ${sub} submodule so that all
paths are prefixed by ${path}.
This ensures that git log will correctly follow the original file
history.
* merge the submodule into its parent repository and commit it.
NOTE: This script might completely garble your repository, so PLEASE apply
this only to a fresh clone of the repository where it does not matter if
the repo is destroyed. It would be wise to keep a backup clone of your
repository, so that you can reconstitute it if need be. You have been
warned. Use at your own risk.
EOF
request_confirmation "Do you want to proceed?"
}
function git_version_lte() {
OP_VERSION=$(printf "%03d%03d%03d%03d" $(echo "$1" | tr '.' '\n' | head -n 4))
GIT_VERSION=$(git version)
GIT_VERSION=$(printf "%03d%03d%03d%03d" $(echo "${GIT_VERSION#git version}" | tr '.' '\n' | head -n 4))
echo -e "${GIT_VERSION}\n${OP_VERSION}" | sort | head -n1
[ ${OP_VERSION} -le ${GIT_VERSION} ]
}
function main() {
warn
if [ "${verbose}" == "true" ]; then
set -x
fi
# Remove submodule and commit
git config -f .gitmodules --remove-section "submodule.${sub}"
if git config -f .git/config --get "submodule.${sub}.url"; then
git config -f .git/config --remove-section "submodule.${sub}"
fi
rm -rf "${path}"
git add -A .
git commit -m "Remove submodule ${sub}"
rm -rf ".git/modules/${sub}"
# Rewrite submodule history
local tmpdir="$(mktemp -d -t submodule-rewrite-XXXXXX)"
git clone "${url}" "${tmpdir}"
pushd "${tmpdir}"
local tab="$(printf '\t')"
local filter="git ls-files -s | sed \"s/${tab}/${tab}${path}\//\" | GIT_INDEX_FILE=\${GIT_INDEX_FILE}.new git update-index --index-info && mv \${GIT_INDEX_FILE}.new \${GIT_INDEX_FILE}"
git filter-branch --index-filter "${filter}" HEAD
popd
# Merge in rewritten submodule history
git remote add "${sub}" "${tmpdir}"
git fetch "${sub}"
if git_version_lte 2.8.4
then
# Previous to git 2.9.0 the parameter would yield an error
ALLOW_UNRELATED_HISTORIES=""
else
# From git 2.9.0 this parameter is required
ALLOW_UNRELATED_HISTORIES="--allow-unrelated-histories"
fi
git merge -s ours --no-commit ${ALLOW_UNRELATED_HISTORIES} "${sub}/master"
rm -rf tmpdir
# Add submodule content
git clone "${url}" "${path}"
rm -rf "${path}/.git"
git add "${path}"
git commit -m "Merge submodule contents for ${sub}"
git config -f .git/config --remove-section "remote.${sub}"
set +x
echo "$(tput setaf 2)Submodule merge complete. Push changes after review.$(tput sgr0)"
}
set -euo pipefail
declare verbose=false
while [ $# -gt 0 ]; do
case "$1" in
(-h|--help)
usage
exit 0
;;
(-v|--verbose)
verbose=true
;;
(*)
break
;;
esac
shift
done
declare sub="${1:-}"
if [ -z "${sub}" ]; then
>&2 echo "Error: No submodule specified"
usage
exit 1
fi
shift
if [ -n "${1:-}" ]; then
>&2 echo "Error: Unknown option: ${1:-}"
usage
exit 1
fi
if ! [ -d ".git" ]; then
>&2 echo "Error: No git repository found. Must be run from the root of a git repository"
usage
exit 1
fi
declare path="$(git config -f .gitmodules --get "submodule.${sub}.path")"
declare url="$(git config -f .gitmodules --get "submodule.${sub}.url")"
if [ -z "${path}" ]; then
>&2 echo "Error: Submodule not found: ${sub}"
usage
exit 1
fi
if ! [ -d "${path}" ]; then
>&2 echo "Error: Submodule path not found: ${path}"
usage
exit 1
fi
main
Начиная с git 1.8.5 (ноябрь 2013) (без учета истории подмодуля):
mv yoursubmodule yoursubmodule_tmp
git submodule deinit yourSubmodule
git rm yourSubmodule
mv yoursubmodule_tmp yoursubmodule
git add yoursubmodule
Что будет:
- отменить регистрацию и выгрузить (т.е. удалить содержимое) субмодуль (
deinit
, следовательноmv
первый), - очистить
.gitmodules
для тебя (rm
), - и удалите специальную запись, представляющую этот подмодуль SHA1 в индексе родительского репо (
rm
).
Как только удаление субмодуля завершено (deinit
а также git rm
), вы можете переименовать папку обратно в исходное имя и добавить ее в git repo как обычную папку.
Примечание: если субмодуль был создан старым Git (< 1.8), вам может понадобиться удалить вложенный .git
папка внутри самого подмодуля, как прокомментировал Simon East
Если вам нужно сохранить историю подмодуля, смотрите jsears, который использует git filter-branch
,
git rm --cached the_submodule_path
- удалить раздел субмодулей из
.gitmodules
файл или, если это единственный подмодуль, удалите файл. - сделать коммит "удаленный подмодуль xyz"
git add the_submodule_path
- другой коммит "добавлена кодовая база xyz"
Я не нашел более простой способ. Вы можете сжать 3-5 в один шаг через git commit -a
- дело вкуса.
Здесь много ответов, но все они кажутся слишком сложными и, вероятно, не делают то, что вы хотите. Я уверен, что большинство людей хотят сохранить свою историю.
Для этого примера основной репо будет git@site.com:main/main.git
и субмодуль репо будет git@site.com:main/child.git
, Это предполагает, что подмодуль находится в корневом каталоге родительского репо. Отрегулируйте инструкции по мере необходимости.
Начните с клонирования родительского репо и удаления старого субмодуля.
git clone git@site.com:main/main.git
git submodule deinit child
git rm child
git add --all
git commit -m "remove child submodule"
Теперь мы добавим дочерние репозитории в основной репозиторий.
git remote add upstream git@site.com:main/child.git
git fetch upstream
git checkout -b merge-prep upstream/master
На следующем шаге предполагается, что вы хотите переместить файлы в ветви подготовки к слиянию в то же место, что и субмодуль, который был выше, хотя вы можете легко изменить местоположение, изменив путь к файлу.
mkdir child
переместите все папки и файлы, кроме папки.git, в дочернюю папку.
git add --all
git commit -m "merge prep"
Теперь вы можете просто объединить ваши файлы обратно в главную ветку.
git checkout master
git merge merge-prep # --allow-unrelated-histories merge-prep flag may be required
Посмотрите вокруг и убедитесь, что все выглядит хорошо, прежде чем бежать git push
Единственное, что вы должны помнить сейчас, это то, что git log по умолчанию не отслеживает перемещенные файлы git log --follow filename
Вы можете увидеть полную историю ваших файлов.
С нами случилось, что мы создали 2 репозитория для 2 проектов, которые были настолько связаны, что не было никакого смысла их разделять, поэтому мы объединили их.
Сначала я покажу, как объединить основные ветви в каждой, а затем объясню, как вы можете расширить это на каждую полученную ветку, надеюсь, это поможет вам.
Если у вас работает подмодуль, и вы хотите преобразовать его в каталог на месте, вы можете сделать:
git clone project_uri project_name
Здесь мы делаем чистый клон на работу. Для этого процесса вам не нужно инициализировать или обновлять подмодули, поэтому просто пропустите его.
cd project_name
vim .gitmodules
редактировать .gitmodules
с вашим любимым редактором (или Vim), чтобы удалить подмодуль, который вы планируете заменить. Строки, которые нужно удалить, должны выглядеть примерно так:
[submodule "lib/asi-http-request"]
path = lib/asi-http-request
url = https://github.com/pokeb/asi-http-request.git
После сохранения файла
git rm --cached directory_of_submodule
git commit -am "Removed submodule_name as submodule"
rm -rf directory_of_submodule
Здесь мы полностью удаляем отношение подмодулей, чтобы создать новое репо в проекте на месте.
git remote add -f submodule_origin submodule_uri
git fetch submodel_origin/master
Здесь мы выбираем хранилище субмодулей для слияния.
git merge -s ours --no-commit submodule_origin/master
Здесь мы начинаем операцию слияния двух репозиториев, но останавливаемся перед фиксацией.
git read-tree --prefix=directory_of_submodule/ -u submodule_origin/master
Здесь мы отправляем содержимое мастера в подмодуле в каталог, где он был до префикса имени каталога
git commit -am "submodule_name is now part of main project"
Здесь мы завершаем процедуру, делая фиксацию изменений в слиянии.
После этого вы можете нажать и начать заново с любой другой ветки для слияния, просто извлеките ветку в вашем репозитории, которая получит изменения, и измените ветку, которую вы вносите в операциях слияния и чтения.
Вот немного улучшенная версия (IMHO) текущего топ-ответа:
В отдельном каталоге (чтобы было легче исправить ошибки и повторить попытку) проверьте и верхний репо, и субрепо.
git clone ../main_repo main.tmp
git clone ../main_repo/sub_repo sub.tmp
Сначала отредактируйте вложенный репозиторий, чтобы переместить все файлы в нужный подкаталог.
cd sub.tmp
mkdir sub_repo_path
git mv `ls | grep -v sub_repo_path` sub_repo_path/
git commit -m "Moved entire subrepo into sub_repo_path"
Запишите ГОЛОВУ
SUBREPO_HEAD=`git reflog | awk '{ print $1; exit; }'`
Теперь удалите подпункт из основного репо
cd ../main.tmp
rmdir sub_repo_path
vi .gitmodules # remove config for submodule
git add -A
git commit -m "Removed submodule sub_repo_path in preparation for merge"
И, наконец, просто объединить их
git fetch ../sub.tmp
git merge $SUBREPO_HEAD
И сделано! Безопасно и без всякой магии.
Лучший ответ на этот вопрос я нашел здесь:
http://x3ro.de/2013/09/01/Integrating-a-submodule-into-the-parent-repository.html
Эта статья объясняет процедуру очень хорошо.
Основываясь на ответе VonC, я создал простой скрипт bash, который делает это. add
в конце должен использовать подстановочные знаки, иначе он отменит предыдущий rm
для самого подмодуля. Важно добавить содержимое каталога подмодулей, а не называть сам каталог в add
команда.
В файле с именем git-integrate-submodule
:
#!/usr/bin/env bash
mv "$1" "${1}_"
git submodule deinit "$1"
git rm "$1"
mv "${1}_" "$1"
git add "$1/**"
Когда
git rm [-r] --cached submodule_path
возвращается
fatal: pathspec 'emr/normalizers/' did not match any files
Контекст: я сделал rm -r .git*
в моих папках субмодулей, прежде чем понять, что их нужно было отменить в основном проекте, в который я только что добавил их. Я получил вышеупомянутую ошибку при отмене субмодулирования некоторых, но не всех из них. Во всяком случае, я исправил их, запустив, (после, конечно, rm -r .git*
)
mv submodule_path submodule_path.temp
git add -A .
git commit -m "De-submodulization phase 1/2"
mv submodule_path.temp submodule_path
git add -A .
git commit -m "De-submodulization phase 2/2"
Обратите внимание, что это не сохраняет историю.
В основном репо
- git rm --cached [submodules_repo]
- git commit -m "Подмодули удалены".
- git push источник [мастер]
В репозитории подмодулей
- рм -рф .git
Снова основное репо
- git добавить [submodules_repo]
- git добавить .
- git commit -m «Репозиторий подмодулей добавлен в основной».
- git push источник [мастер]
Вот что я нашел самым лучшим и простым.
В репо подмодуля из HEAD вы хотите объединить с основным репо:
git checkout -b "mergeMe"
mkdir "foo/bar/myLib/"
(путь идентичен тому, где вы хотите разместить файлы в основном репо)git mv * "foo/bar/myLib/"
(переместите все в путь)git commit -m "ready to merge into main"
Вернитесь в основное репо после удаления подмодуля и очистки пути "foo/bar/myLib":
git merge --allow-unrelated-histories SubmoduleOriginRemote/mergeMe
бум сделано
истории сохранены
без проблем
Обратите внимание, что это почти идентично некоторым другим ответам. Но это предполагает, что у вас есть репозиторий подмодулей. Также это упрощает получение будущих изменений для подмодуля в восходящем направлении.
Мне было удобнее (также?) Извлекать данные локального коммита из подмодуля, потому что в противном случае я бы их потерял. (Не могу их подтолкнуть, так как у меня нет доступа к этому пульту). Поэтому я добавил подмодуль /.git в качестве remote_origin2, извлек его фиксацию и объединил из этой ветви. Не уверен, что мне все еще нужен удаленный субмодуль в качестве источника, так как я еще недостаточно знаком с git.