Como desmódulo um submódulo git (trago todo o código de volta ao núcleo)?
Como em "deveria" eu, como em "Melhor procedimento" ...
git submodule deinit asubmodule ; git rm asubmodule
basta um simples , como ilustrado na minha resposta abaixo
Como desmódulo um submódulo git (trago todo o código de volta ao núcleo)?
Como em "deveria" eu, como em "Melhor procedimento" ...
git submodule deinit asubmodule ; git rm asubmodule
basta um simples , como ilustrado na minha resposta abaixo
Respostas:
Se tudo o que você deseja é colocar o código do submódulo no repositório principal, basta remover o submódulo e adicionar novamente os arquivos ao repositório principal:
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"
Se você também deseja preservar o histórico do submódulo, pode fazer um pequeno truque: "mescle" o submódulo no repositório principal para que o resultado seja o mesmo de antes, exceto que os arquivos do submódulo estão agora no diretório repositório principal.
No módulo principal, você precisará fazer o seguinte:
# 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
O repositório resultante parecerá um pouco estranho: haverá mais de um commit inicial. Mas isso não causará problemas para o git.
Nesta segunda solução, você terá a grande vantagem de ainda poder executar o git blame ou git log nos arquivos que estavam originalmente nos submódulos. De fato, o que você fez aqui é renomear muitos arquivos dentro de um repositório, e o git deve detectar isso automaticamente. Se você ainda tiver problemas com o git log, tente algumas opções (--follow, -M, -C) que melhor renomear / detecção de cópia.
git merge
assegura que haverá uma "confirmação anterior" para cada arquivo (em um dos dois "lados" da mesclagem).
mkdir foo && git mv !(foo) foo && git commit
.
--allow-unrelated-histories
para forçar a fusão no falso merge como eu estava ficando fatal: refusing to merge unrelated histories
, mais aqui: github.com/git/git/blob/master/Documentation/RelNotes/...
Desde o git 1.8.5 (novembro de 2013 ) ( sem manter o histórico do submódulo ):
mv yoursubmodule yoursubmodule_tmp
git submodule deinit yourSubmodule
git rm yourSubmodule
mv yoursubmodule_tmp yoursubmodule
git add yoursubmodule
Aquilo vai:
deinit
daí o mv
primeiro ),.gitmodules
para você ( rm
),rm
).Depois que a remoção do submódulo estiver concluída ( deinit
e git rm
), você poderá renomear a pasta novamente para seu nome original e adicioná-la ao repositório git como uma pasta comum.
Nota: se o submódulo foi criado por um antigo Git (<1.8), pode ser necessário remover a .git
pasta aninhada dentro do próprio submódulo, conforme comentado por Simon East
Se você precisar manter o histórico do submódulo, consulte a resposta de jsears , que usa .git filter-branch
deinit
sozinho limpou a árvore de trabalho do seu submódulo?
Eu criei um script que traduzirá um submódulo para um diretório simples, mantendo todo o histórico do arquivo. Não sofre com os git log --follow <file>
problemas que as outras soluções sofrem. Também é uma chamada de uma linha muito fácil que faz todo o trabalho para você. Boa sorte.
Ele se baseia no excelente trabalho de Lucas Jenß, descrito em sua postagem no blog " Integrando um submódulo no repositório pai ", mas automatiza todo o processo e limpa alguns outros casos de canto.
O código mais recente será mantido com correções de bugs no github em https://github.com/jeremysears/scripts/blob/master/bin/git-submodule-rewrite , mas por uma questão de protocolo de resposta de fluxo de pilha adequado, incluí o solução em sua totalidade abaixo.
Uso:
$ git-submodule-rewrite <submodule-name>
git-submodule-rewrite:
#!/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
curl https://raw.githubusercontent.com/jeremysears/scripts/master/bin/git-submodule-rewrite > git-submodule-rewrite.sh
e./git-submodule-rewrite.sh <submodule-name>
git rm --cached the_submodule_path
.gitmodules
arquivo ou, se for o único submódulo, remova o arquivo.git add the_submodule_path
Ainda não encontrei nenhuma maneira mais fácil. Você pode comprimir 3-5 em uma etapa por git commit -a
questão de gosto.
.gitmodules
vez de .submodules
?
.gitmodules
não ser.submodules
.git
diretório do submodule antes git add
iria trabalhar na pasta submodule
Muitas respostas aqui, mas todas elas parecem muito complexas e provavelmente não fazem o que você deseja. Tenho certeza que a maioria das pessoas quer manter sua história.
Neste exemplo, o repositório principal será git@site.com:main/main.git
e o repositório do submódulo será git@site.com:main/child.git
. Isso pressupõe que o submódulo esteja localizado no diretório raiz do repositório pai. Ajuste as instruções conforme necessário.
Comece clonando o repositório pai e removendo o submódulo antigo.
git clone git@site.com:main/main.git
git submodule deinit child
git rm child
git add --all
git commit -m "remove child submodule"
Agora, adicionaremos os repositórios derivados a montante ao repositório principal.
git remote add upstream git@site.com:main/child.git
git fetch upstream
git checkout -b merge-prep upstream/master
A próxima etapa pressupõe que você deseja mover os arquivos na ramificação de preparação para mesclagem para o mesmo local que o submódulo acima, embora seja possível alterar facilmente o local alterando o caminho do arquivo.
mkdir child
mova todas as pastas e arquivos, exceto a pasta .git, para a pasta filho.
git add --all
git commit -m "merge prep"
Agora você pode simplesmente mesclar seus arquivos de volta ao ramo principal.
git checkout master
git merge merge-prep # --allow-unrelated-histories merge-prep flag may be required
Olhe em volta e verifique se tudo está bem antes de executar git push
A única coisa que você deve se lembrar agora é que o log do git não segue os arquivos movidos por padrão; no entanto, ao executar, git log --follow filename
você pode ver o histórico completo dos seus arquivos.
git merge merge-prep
e recebi o erro fatal: refusing to merge unrelated histories
. Solução alternativa é esta: git merge --allow-unrelated-histories merge-prep
.
child
diretório, para que você não precise movê-los mais tarde? Eu tenho o mesmo nome de arquivo em um submódulo e o repositório principal ... então, acabei de ter um conflito de mesclagem, pois ele está tentando mesclar os dois arquivos.
Aconteceu-nos que criamos 2 repositórios para 2 projetos que eram tão acoplados que não faziam sentido separá-los, por isso os fundimos.
Vou mostrar como mesclar as ramificações principais em cada uma delas primeiro e depois explicarei como você pode estender isso para todas as ramificações que você obteve, espero que ajude.
Se você tem o submódulo funcionando e deseja convertê-lo em um diretório, você pode:
git clone project_uri project_name
Aqui fazemos um clone limpo para trabalhar. Para esse processo, você não precisa inicializar ou atualizar os submódulos; basta pular.
cd project_name
vim .gitmodules
Edite .gitmodules
com seu editor favorito (ou Vim) para remover o submódulo que você planeja substituir. As linhas que você precisa remover devem ter a seguinte aparência:
[submodule "lib/asi-http-request"]
path = lib/asi-http-request
url = https://github.com/pokeb/asi-http-request.git
Depois de salvar o arquivo,
git rm --cached directory_of_submodule
git commit -am "Removed submodule_name as submodule"
rm -rf directory_of_submodule
Aqui, removemos completamente a relação do submódulo para que possamos criar o outro repositório para o projeto no local.
git remote add -f submodule_origin submodule_uri
git fetch submodel_origin/master
Aqui, buscamos o repositório do submódulo para mesclar.
git merge -s ours --no-commit submodule_origin/master
Aqui começamos uma operação de mesclagem dos 2 repositórios, mas paramos antes de confirmar.
git read-tree --prefix=directory_of_submodule/ -u submodule_origin/master
Aqui enviamos o conteúdo do master no submódulo para o diretório em que estava antes de prefixar o nome do diretório
git commit -am "submodule_name is now part of main project"
Aqui, concluímos o procedimento realizando as alterações na mesclagem.
Após concluir isso, você pode enviar por push e iniciar novamente com qualquer outra ramificação para mesclar, basta fazer check-out da ramificação em seu repositório que receberá as alterações e alterará a ramificação que você está trazendo nas operações de mesclagem e árvore de leitura.
directory_of_submodule
git log original_path_of_file_in_submodule
o caminho registrado no repositório git para o arquivo (que não existe mais no sistema de arquivos), mesmo que o arquivo do submódulo agora vive emsubmodule_path/new_path_of_file
A melhor resposta para isso que encontrei está aqui:
http://x3ro.de/2013/09/01/Integrating-a-submodule-into-the-parent-repository.html
Este artigo explica muito bem o procedimento.
Aqui está uma versão ligeiramente melhorada (IMHO) da resposta do @ gyim. Ele está fazendo várias alterações perigosas na cópia principal de trabalho, onde acho muito mais fácil operar em clones separados e depois juntá-los no final.
Em um diretório separado (para facilitar a limpeza de erros e tente novamente), verifique o repo superior e o subrepo.
git clone ../main_repo main.tmp
git clone ../main_repo/sub_repo sub.tmp
Primeiro edite o subrepo para mover todos os arquivos para o subdiretório desejado
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"
Anote o HEAD
SUBREPO_HEAD=`git reflog | awk '{ print $1; exit; }'`
Agora remova o subrepo do repo principal
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"
E, finalmente, apenas mesclá-los
git fetch ../sub.tmp
# remove --allow-unrelated-histories if using git older than 2.9.0
git merge --allow-unrelated-histories $SUBREPO_HEAD
E feito! Com segurança e sem nenhuma mágica.
subrepo
com itens?
git merge $SUBREPO_HEAD fatal: refusing to merge unrelated histories
Devo usar git merge $SUBREPO_HEAD --allow-unrelated-histories
neste caso? Ou deveria funcionar sem e eu cometi um erro?
Para quando
git rm [-r] --cached submodule_path
retorna
fatal: pathspec 'emr/normalizers/' did not match any files
Contexto: eu fiz rm -r .git*
nas minhas pastas do submódulo antes de perceber que elas precisavam ser desmoduladas no projeto principal ao qual eu as adicionara. Eu recebi o erro acima ao desmodular alguns, mas não todos. Enfim, eu os consertei executando (depois, é claro, o 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"
Observe que isso não preserva o histórico.
Com base na resposta de VonC , criei um script bash simples que faz isso. O add
final deve usar caracteres curinga, caso contrário, desfará o anterior rm
para o próprio submódulo. É importante adicionar o conteúdo do diretório do submódulo e não nomear o diretório no add
comando.
Em um arquivo chamado git-integrate-submodule
:
#!/usr/bin/env bash
mv "$1" "${1}_"
git submodule deinit "$1"
git rm "$1"
mv "${1}_" "$1"
git add "$1/**"
Achei mais conveniente (também?) Buscar dados de confirmação local no submódulo, porque, caso contrário, eu os perderia. (Não foi possível enviá-los porque não tenho acesso ao controle remoto). Então, adicionei o submodule / .git como remote_origin2, busquei o commit e a mesclagem desse ramo. Não tenho certeza se ainda preciso do submódulo remoto como origem, pois ainda não estou familiarizado o suficiente com o git.
Aqui está o que eu achei melhor e mais simples.
No repositório do submódulo, a partir de HEAD, você deseja mesclar no repositório principal:
git checkout -b "mergeMe"
mkdir "foo/bar/myLib/"
(caminho idêntico ao local em que você deseja os arquivos no repositório principal)git mv * "foo/bar/myLib/"
(mova tudo para o caminho)git commit -m "ready to merge into main"
Volte ao repositório principal após remover o submódulo e limpar o caminho "foo / bar / myLib":
git merge --allow-unrelated-histories SubmoduleOriginRemote/mergeMe
boom feito
histórias preservadas
não se preocupe
Observe isso quase idêntico a algumas outras respostas. Mas isso pressupõe que você possui um repositório de submódulo. Além disso, isso facilita a obtenção de futuras alterações upstream para o submódulo.
git submodule deinit
, veja minha resposta abaixo