Nossos repositórios Git começaram como partes de um único repositório SVN monstro, onde os projetos individuais tinham sua própria árvore da seguinte maneira:
project1/branches
/tags
/trunk
project2/branches
/tags
/trunk
Obviamente, era muito fácil mover arquivos de um para outro svn mv
. Mas no Git, cada projeto está em seu próprio repositório, e hoje me pediram para mover um subdiretório de project2
para project1
. Eu fiz algo parecido com isto:
$ git clone project2
$ cd project2
$ git filter-branch --subdirectory-filter deeply/buried/java/source/directory/A -- --all
$ git remote rm origin # so I don't accidentally overwrite the repo ;-)
$ mkdir -p deeply/buried/different/java/source/directory/B
$ for f in *.java; do
> git mv $f deeply/buried/different/java/source/directory/B
> done
$ git commit -m "moved files to new subdirectory"
$ cd ..
$
$ git clone project1
$ cd project1
$ git remote add p2 ../project2
$ git fetch p2
$ git branch p2 remotes/p2/master
$ git merge p2 # --allow-unrelated-histories for git 2.9+
$ git remote rm p2
$ git push
Mas isso parece bastante complicado. Existe uma maneira melhor de fazer esse tipo de coisa em geral? Ou adotei a abordagem correta?
Observe que isso envolve mesclar o histórico em um repositório existente, em vez de simplesmente criar um novo repositório independente a partir de parte de outro ( como em uma pergunta anterior ).
git
repository
ebneter
fonte
fonte
git fetch p2 && git merge p2
vez degit fetch p2 && git branch .. && git merge p2
? Edit: tudo bem, parece que você deseja obter as alterações em um novo ramo chamado p2, não no ramo atual.--allow-unrelated-histories
o últimogit merge
para fazê-lo funcionar.Respostas:
Sim, bater no
--subdirectory-filter
defilter-branch
era a chave. O fato de você usá-lo essencialmente prova que não há uma maneira mais fácil - você não teve escolha a não ser reescrever o histórico, pois queria terminar com apenas um subconjunto (renomeado) dos arquivos, e isso por definição altera os hashes. Como nenhum dos comandos padrão (por exemplopull
) reescreve o histórico, não há como usá-los para fazer isso.Você pode refinar os detalhes, é claro - algumas clonagens e ramificações não eram estritamente necessárias - mas a abordagem geral é boa! É uma pena que seja complicado, mas é claro que o objetivo do git não é facilitar a reescrita da história.
fonte
--index-filter
na página defilter-branch
manual.Se seu histórico estiver correto, você pode retirar os commits como patches e aplicá-los no novo repositório:
Ou em uma linha
(Extraído dos documentos de Exherbo )
fonte
git log --pretty=email --patch-with-stat --full-index --binary --reverse -- client > patch
. Funciona sem problemas AFAICT.--committer-date-is-author-date
opção para preservar a data de confirmação original em vez da data em que os arquivos foram movidos.git log
, para que ele não funcione com os dois--follow
e--reverse
corretamente). Eu usei esta resposta , e aqui é um script completo que eu uso agora aos arquivos de movimentoTendo tentado várias abordagens para mover um arquivo ou pasta de um repositório Git para outro, o único que parece funcionar de maneira confiável é descrito abaixo.
Envolve clonar o repositório do qual você deseja mover o arquivo ou pasta, movê-lo para a raiz, reescrever o histórico do Git, clonar o repositório de destino e puxar o arquivo ou pasta com o histórico diretamente para esse repositório de destino.
Estágio um
Faça uma cópia do repositório A, pois as etapas a seguir fazem grandes alterações nessa cópia que você não deve enviar!
cd nele
Exclua o link do repositório original para evitar alterações acidentais remotas (por exemplo, pressionando)
Percorra seu histórico e arquivos, removendo qualquer coisa que não esteja no diretório 1. O resultado é o conteúdo do diretório 1 espalhado na base do repositório A.
Apenas para mover um arquivo: percorra o que resta e remova tudo, exceto o arquivo desejado. (Pode ser necessário excluir arquivos que você não deseja com o mesmo nome e confirmar.)
Estágio Dois
Etapa de limpeza
Etapa de limpeza
Etapa de limpeza
Você pode importar esses arquivos para o repositório B em um diretório que não seja a raiz:
Crie esse diretório
Mover arquivos para esse diretório
Adicionar arquivos a esse diretório
Confirme suas alterações e estamos prontos para mesclar esses arquivos no novo repositório
Estágio Três
Faça uma cópia do repositório B se você ainda não tiver um
(assumindo que FOLDER_TO_KEEP é o nome do novo repositório para o qual você está copiando)
cd nele
Crie uma conexão remota para o repositório A como uma ramificação no repositório B
Puxe desta ramificação (contendo apenas o diretório que você deseja mover) para o repositório B.
O pull copia os arquivos e o histórico. Nota: Você pode usar uma mesclagem em vez de um pull, mas o pull funciona melhor.
Por fim, você provavelmente deseja limpar um pouco removendo a conexão remota ao repositório A
Empurre e está tudo pronto.
fonte
Achei isso muito útil. É uma abordagem muito simples em que você cria patches que são aplicados ao novo repositório. Veja a página vinculada para mais detalhes.
Ele contém apenas três etapas (copiadas do blog):
O único problema que tive foi que não era possível aplicar todos os patches de uma só vez usando
No Windows, recebi um erro InvalidArgument. Então eu tive que aplicar todos os patches um após o outro.
fonte
MANTER O NOME DO DIRETÓRIO
O filtro de subdiretório (ou o comando mais curto subárvore git) funciona bem, mas não funcionou para mim, pois remove o nome do diretório das informações de confirmação. No meu cenário, só quero mesclar partes de um repositório em outro e manter o histórico com o nome do caminho completo.
Minha solução foi usar o filtro em árvore e simplesmente remover os arquivos e diretórios indesejados de um clone temporário do repositório de origem e puxá-lo para o meu repositório de destino em 5 etapas simples.
fonte
O que eu sempre uso é aqui http://blog.neutrino.es/2012/git-copy-a-file-or-directory-from-another-repository-preserving-history/ . Simples e rápido.
Para conformidade com os padrões stackoverflow, aqui está o procedimento:
fonte
Esta resposta fornece comandos interessantes baseados
git am
e apresentados usando exemplos, passo a passo.Objetivo
Procedimento
git log --pretty=email -p --reverse --full-index --binary
git am
1. Extrair histórico no formato de email
Exemplo: história Extrato de
file3
,file4
efile5
Limpe o destino do diretório temporário
Limpe sua fonte de repo
Extrair histórico de cada arquivo no formato de email
Infelizmente opção
--follow
ou--find-copies-harder
não pode ser combinada com--reverse
. É por isso que o histórico é cortado quando o arquivo é renomeado (ou quando um diretório pai é renomeado).Depois: histórico temporário no formato de email
2. Reorganize a árvore de arquivos e atualize a alteração do nome do arquivo no histórico [opcional]
Suponha que você queira mover esses três arquivos neste outro repositório (pode ser o mesmo repositório).
Portanto, reorganize seus arquivos:
Seu histórico temporário é agora:
Altere também nomes de arquivos no histórico:
Nota: Isso reescreve o histórico para refletir a alteração de caminho e nome do arquivo.
(ou seja, a alteração do novo local / nome no novo repositório)
3. Aplique novo histórico
Seu outro repo é:
Aplicar confirmações de arquivos de histórico temporários:
Seu outro repo é agora:
Use
git status
para ver a quantidade de confirmações prontas para serem enviadas :-)Nota: Como o histórico foi reescrito para refletir a alteração do caminho e do nome do arquivo:
(por exemplo, comparado ao local / nome no repositório anterior)
git mv
alterar o local / nome do arquivo.git log --follow
acessar o histórico completo.Truque extra: detecte arquivos renomeados / movidos em seu repositório
Para listar os arquivos que foram renomeados:
Mais personalizações: você pode concluir o comando
git log
usando as opções--find-copies-harder
ou--reverse
. Você também pode remover as duas primeiras colunas usandocut -f3-
e grepping o padrão completo '{. * =>. *}'.fonte
Tendo tido uma coceira semelhante ao zero (embora apenas para alguns arquivos de um determinado repositório), este script provou ser realmente útil: git-import
A versão curta é que ele cria arquivos de correção do arquivo ou diretório (
$object
) fornecido a partir do repositório existente:que são aplicadas a um novo repositório:
Para mais detalhes, consulte:
fonte
Tente isto
cd repo1
Isso removerá todos os diretórios, exceto os mencionados, preservando o histórico apenas para esses diretórios
Agora você pode adicionar seu novo repositório ao seu git remote e enviá-lo para esse
adicionar
-f
para substituirfonte
Usando a inspiração de http://blog.neutrino.es/2012/git-copy-a-file-or-directory-from-another-repository-preserving-history/ , criei essa função do Powershell para fazer o mesmo, que tem funcionou muito bem para mim até agora:
Uso para este exemplo:
Depois de fazer isso, você pode reorganizar os arquivos na
migrate-from-project2
ramificação antes de mesclá-los.fonte
Eu queria algo robusto e reutilizável (função de comando-e-ir + desfazer), então escrevi o seguinte script bash. Trabalhou para mim em várias ocasiões, então pensei em compartilhar aqui.
É capaz de mover uma pasta arbitrária
/path/to/foo
derepo1
para/some/other/folder/bar
pararepo2
(os caminhos da pasta podem ser iguais ou diferentes, a distância da pasta raiz pode ser diferente).Como ele só repassa as confirmações que tocam os arquivos na pasta de entrada (nem todas as confirmações do repositório de origem), deve ser bastante rápido, mesmo nos repositórios de fontes grandes, se você extrair uma subpasta profundamente aninhada que não foi tocada em todas as confirmar.
Como o que isso faz é criar uma ramificação órfã com todo o histórico do repositório antigo e depois mesclá-lo com o HEAD, ele funcionará mesmo em caso de conflito de nome de arquivo (você precisará resolver uma mesclagem no final do curso) .
Se não houver conflitos de nome de arquivo, basta
git commit
finalizar a mesclagem.A desvantagem é que ele provavelmente não seguirá as renomeações de arquivos (fora da
REWRITE_FROM
pasta) nas solicitações de repo-pull de origem bem-vindas no GitHub para acomodar isso.Link do GitHub: git-move-folder-between-repos-keep-history
fonte
No meu caso, não precisei preservar o repositório do qual estava migrando ou preservar qualquer histórico anterior. Eu tinha um patch do mesmo ramo, de um controle remoto diferente
Nessas duas etapas, consegui que a ramificação do outro repositório aparecesse no mesmo repositório.
Por fim, configurei esse ramo (importado do outro repositório) para seguir a linha principal do repositório de destino (para que eu os diferencie com precisão)
Agora ele se comportava como se fosse apenas mais um ramo que eu havia pressionado contra o mesmo repositório.
fonte
Se os caminhos para os arquivos em questão forem os mesmos nos dois repositórios e você desejar trazer apenas um arquivo ou um pequeno conjunto de arquivos relacionados, uma maneira fácil de fazer isso é usar
git cherry-pick
.O primeiro passo é trazer os commits do outro repositório para o seu próprio repositório local usando
git fetch <remote-url>
. Isso deixaráFETCH_HEAD
apontando para o commit principal do outro repositório; se você quiser preservar uma referência a esse commit depois de fazer outras buscas, convém marcá-logit tag other-head FETCH_HEAD
.Você precisará criar um commit inicial para esse arquivo (se ele não existir) ou um commit para trazer o arquivo para um estado que possa ser corrigido com o primeiro commit do outro repositório que você deseja trazer. Você pode poderá fazer isso com a
git cherry-pick <commit-0>
secommit-0
os arquivos desejados forem apresentados ou poderá ser necessário construir o commit 'manualmente'. Adicione-n
às opções de seleção de cereja se precisar modificar o commit inicial para, por exemplo, soltar arquivos desse commit que você não deseja incluir.Depois disso, você pode continuar com
git cherry-pick
as confirmações subsequentes, usando novamente sempre-n
que necessário. No caso mais simples (todos os commits são exatamente o que você quer e aplicar de forma limpa) você pode dar a lista completa de commits na linha de comando cherry-pick:git cherry-pick <commit-1> <commit-2> <commit-3> ...
.fonte
Isso se torna mais simples usando o git-filter-repo.
Para passar
project2/sub/dir
paraproject1/sub/dir
:Para instalar a ferramenta simplesmente:
pip3 install git-filter-repo
( mais detalhes e opções no README )fonte
O método abaixo para migrar meu GIT Stash para o GitLab, mantendo todas as ramificações e preservando o histórico.
Clone o repositório antigo para local.
Crie um repositório vazio no GitLab.
O que fiz acima quando migramos nosso código do stash para o GitLab e funcionou muito bem.
fonte