Crie um repositório de submódulo de uma pasta e mantenha seu histórico de commit do git

111

Tenho um aplicativo da web que explora outros aplicativos da web de uma maneira particular. Ele contém alguns demos da web em uma demospasta e um dos demos agora deve ter seu próprio repositório. Eu gostaria de criar um repositório separado para este aplicativo de demonstração e torná-lo umsubpacote submódulo do repositório principal sem perder seu histórico de commits.

É possível manter o histórico de commits dos arquivos na pasta de um repositório e criar um repositório a partir dele e usá-lo como um submódulo ?

GabLeRoux
fonte
Tenho pesquisado como mover o diretório 1 do repositório Git A para o repositório Git B. +1 para o link do artigo.
Chetabahana
Sim, isso é realmente muito semelhante, as soluções diferem um pouco, obrigado por compartilhar isso
GabLeRoux

Respostas:

191

Solução Detalhada

Veja a nota no final desta resposta (último parágrafo) para uma alternativa rápida aos submódulos git usando npm;)

Na seguinte resposta, você saberá como extrair uma pasta de um repositório e fazer um repositório git a partir dela e então incluí-la como um submódulo em vez de uma pasta.

Inspirado no artigo de Gerg Bayer Movendo Arquivos de um Repositório Git para Outro, Preservando a História

No início, temos algo assim:

<git repository A>
    someFolders
    someFiles
    someLib <-- we want this to be a new repo and a git submodule!
        some files

Nas etapas abaixo, irei me referir a isso someLibcomo <directory 1>.

No final, teremos algo assim:

<git repository A>
    someFolders
    someFiles
    @submodule --> <git repository B>

<git repository B>
    someFolders
    someFiles

Crie um novo repositório git de uma pasta em outro repositório

Passo 1

Obtenha uma nova cópia do repositório para dividir.

git clone <git repository A url>
cd <git repository A directory>

Passo 2

A pasta atual será o novo repositório, portanto, remova o remoto atual.

git remote rm origin

etapa 3

Extraia o histórico da pasta desejada e submeta-o

git filter-branch --subdirectory-filter <directory 1> -- --all

Agora você deve ter um repositório git com os arquivos directory 1da raiz do seu repo com todo o histórico de commits relacionado.

Passo 4

Crie seu repositório online e envie seu novo repositório!

git remote add origin <git repository B url>
git push

Você pode precisar definir o upstreambranch para seu primeiro envio

git push --set-upstream origin master

Limpo <git repository A>(opcional, ver comentários)

Queremos eliminar traços (arquivos e comprometer história) de <git repository B>partir <git repository A>de modo história para esta pasta está lá apenas uma vez.

Isso se baseia na remoção de dados confidenciais do github.

Vá para uma nova pasta e

git clone <git repository A url>
cd <git repository A directory>
git filter-branch --force --index-filter 'git rm --cached --ignore-unmatch <directory 1> -r' --prune-empty --tag-name-filter cat -- --all

Substitua <directory 1>pela pasta que você deseja remover. -rfará isso recursivamente dentro do diretório especificado :). Agora empurre para origin/mastercom--force

git push origin master --force

Boss Stage (veja a nota abaixo)

Crie um submódulo de <git repository B>em<git repository A>

git submodule add <git repository B url>
git submodule update
git commit

Verifique se tudo funcionou conforme o esperado e push

git push origin master

Nota

Depois de fazer tudo isso, percebi no meu caso que era mais apropriado usar o npm para gerenciar minhas próprias dependências. Podemos especificar urls e versões do git , ver os urls do git package.json como dependências .

Se você fazê-lo desta forma, o repositório que você deseja usar como um requisito deve ser um módulo npm para que ele deve conter um package.jsonarquivo ou você obterá este erro: Error: ENOENT, open 'tmp.tgz-unpack/package.json'.

tldr (solução alternativa)

Você pode achar mais fácil usar o npm e gerenciar dependências com urls git :

  • Mova a pasta para um novo repositório
  • executar npm initdentro de ambos os repositórios
  • execute npm install --save git://github.com/user/project.git#commit-ishonde você deseja que suas dependências sejam instaladas
GabLeRoux
fonte
39
A etapa "Limpar <repositório git A>" deve ser evitada. Fazendo isso, você não pode restaurar / fazer check-out de versões / commits mais antigos de seu histórico. Você deve apenas acessar a pasta e adicionar o submódulo. Então você garante ter uma cópia totalmente funcional ao verificar commits mais antigos.
Cybot
Você não deveria fazer cd someLibantes da Etapa 2? Você diz "A pasta atual será o novo repositório", mas na verdade não será; o novo repositório (submódulo) está dentro dessa pasta.
Jago,
1
confirmando: sim, funciona para mais de um submódulo. Muito obrigado pela resposta detalhada. Além disso, não precisava usar o npm.
Breno Inojosa de
2
Eu adicionaria informações sobre o refs/original/...que é criado na etapa 3.
Emile Bergeron
6
O GitHub fez um artigo sobre como conseguir a extração de uma pasta em um novo repositório: help.github.com/articles/…
jrobichaud
9

A solução de @GabLeRoux esmaga os branches e os commits relacionados.

Uma maneira simples de clonar e manter todos os branches e commits extras:

1 - Certifique-se de ter este alias git

git config --global alias.clone-branches '! git branch -a | sed -n "/\/HEAD /d; /\/master$/d; /remotes/p;" | xargs -L1 git checkout -t'

2 - Clone o remoto, puxe todos os branches, mude o remoto, filtre seu diretório, empurre

git clone [email protected]:user/existing-repo.git new-repo
cd new-repo
git clone-branches
git remote rm origin
git remote add origin [email protected]:user/new-repo.git
git remote -v
git filter-branch --subdirectory-filter my_directory/ -- --all
git push --all
git push --tags
oodavid
fonte
3

A solução de GabLeRoux funciona bem, exceto se você usar git lfse tiver arquivos grandes no diretório que deseja desanexar. Nesse caso, após a etapa 3, todos os arquivos grandes permanecerão como arquivos de ponteiro em vez de arquivos reais. Eu acho que é provavelmente devido ao .gitattributesarquivo sendo removido no processo de ramificação do filtro.

Percebendo isso, acho que a seguinte solução funciona para mim:

cp .gitattributes .git/info/attributes

Copiar o .gitattributesque git lfs usa para rastrear arquivos grandes para o .git/diretório para evitar que sejam excluídos.

Quando filter-branch estiver pronto, não se esqueça de colocar de volta o .gitattributesse você ainda quiser usar git lfs para o novo repositório:

mv .git/info/attributes .gitattributes
git add .gitattributes
git commit -m 'added back .gitattributes'
ls.
fonte