Considere o seguinte cenário:
Eu desenvolvi um pequeno projeto experimental A em seu próprio repositório Git. Agora ele amadureceu e eu gostaria que A fizesse parte de um projeto maior B, que possui seu próprio repositório. Gostaria agora de adicionar A como um subdiretório de B.
Como mesclar A em B, sem perder o histórico de nenhum lado?
git
merge
repository
git-subtree
static_rtti
fonte
fonte
Respostas:
Uma única ramificação de outro repositório pode ser facilmente colocada em um subdiretório, mantendo seu histórico. Por exemplo:
Isso aparecerá como uma única confirmação, na qual todos os arquivos da ramificação mestre do Rails são adicionados ao diretório "rails". No entanto, o título do commit contém uma referência à antiga árvore de histórico:
Onde
<rev>
está um hash de confirmação do SHA-1. Você ainda pode ver a história, culpar algumas mudanças.Observe que você não pode ver o prefixo do diretório a partir daqui, pois esse é um ramo antigo real deixado intacto. Você deve tratar isso como um commit normal de movimentação de arquivos: você precisará de um salto extra ao alcançá-lo.
Existem soluções mais complexas, como fazer isso manualmente ou reescrever o histórico, conforme descrito em outras respostas.
O comando git-subtree faz parte do git-contrib oficial; alguns gerenciadores de pacotes o instalam por padrão (OS X Homebrew). Mas você pode ter que instalá-lo sozinho, além do git.
fonte
git co v1.7.11.3
por... v1.8.3
).git log rails/somefile
o histórico de confirmações desse arquivo não será exibido, exceto a confirmação de mesclagem. Como sugeriu @artfulrobot, verifique a resposta de Greg Hewgill . E você pode precisar usargit filter-branch
o repositório que deseja incluir.git subtree
pode não fazer o que você pensa! Veja aqui uma solução mais completa.Se você deseja mesclar
project-a
emproject-b
:Retirado de: git mesclar repositórios diferentes?
Esse método funcionou muito bem para mim, é mais curto e, na minha opinião, muito mais limpo.
Caso queira colocar
project-a
em um subdiretório, você pode usargit-filter-repo
(filter-branch
é desencorajado ). Execute os seguintes comandos antes dos comandos acima:Um exemplo de mesclagem de 2 grandes repositórios, colocando um deles em um subdiretório: https://gist.github.com/x-yuri/9890ab1079cf4357d6f269d073fd9731
Nota: O
--allow-unrelated-histories
parâmetro existe apenas desde git> = 2.9. Veja a documentação do Git - git merge / --allow-unrelated-histororiesAtualização : adicionada
--tags
conforme sugerido por @jstadler para manter as tags.fonte
git mv source-dir/ dest/new-source-dir
git merge
etapa falha aqui comfatal: refusing to merge unrelated histories
;--allow-unrelated-histories
corrige isso conforme explicado nos documentos .--allow-unrelated-histories
foi introduzido no git 2.9 . Nas versões anteriores, era o comportamento padrão.git fetch /path/to/project-a master; git merge --allow-unrelated-histories FETCH_HEAD
.Aqui estão duas soluções possíveis:
Submodules
Copie o repositório A em um diretório separado no projeto maior B, ou (talvez melhor) clone o repositório A em um subdiretório no projeto B. Em seguida, use o submodule git para tornar esse repositório um submódulo de um repositório B.
Isto é uma boa solução para repositórios fracamente acopladas, onde o desenvolvimento de um repositório continua, e a maior parte do desenvolvimento é um desenvolvimento independente separado em A. Ver também SubmoduleSupport e GitSubmoduleTutorial páginas GIT Wiki.
Mesclar subárvore
Você pode mesclar o repositório A em um subdiretório de um projeto B usando a estratégia de mesclagem de subárvore . Isso é descrito em Subárvore Mesclando e Você por Markus Prinz.
(A opção
--allow-unrelated-histories
é necessária para Git> = 2.9.0.)Ou você pode usar a ferramenta de subárvore git ( repositório no GitHub ) por apenwarr (Avery Pennarun), anunciada, por exemplo, em sua postagem no blog. Uma nova alternativa aos submódulos do Git: subárvore git .
Eu acho que no seu caso (A deve fazer parte de um projeto maior B), a solução correta seria usar a mesclagem de subárvore .
fonte
git log dir-B/somefile
não mostrará nada, exceto a mesclagem. Veja a resposta de Greg Hewgill faz referência a esta importante questão.A abordagem do submódulo é boa se você deseja manter o projeto separadamente. No entanto, se você realmente deseja mesclar os dois projetos no mesmo repositório, terá um pouco mais de trabalho a fazer.
A primeira coisa seria usar
git filter-branch
para reescrever os nomes de tudo no segundo repositório para estar no subdiretório onde você gostaria que eles terminassem. Então, em vez defoo.c
,bar.html
você teriaprojb/foo.c
eprojb/bar.html
.Então, você poderá fazer algo como o seguinte:
O
git pull
fará umgit fetch
seguido de umgit merge
. Não deve haver conflitos, se o repositório para o qual você está puxando ainda não tiver umprojb/
diretório.Além disso a pesquisa indica que algo semelhante foi feito para fundir
gitk
emgit
. Junio C Hamano escreve sobre isso aqui: http://www.mail-archive.com/[email protected]/msg03395.htmlfonte
git filter-branch
para conseguir isso. Na página de manual, diz o contrário: fazer subdir / tornar-se a raiz, mas não o contrário.git-subtree
é legal, mas provavelmente não é o que você deseja.Por exemplo, se
projectA
é o diretório criado no B, depoisgit subtree
,lista apenas uma confirmação: a mesclagem. As confirmações do projeto mesclado são para caminhos diferentes, para que não apareçam.
A resposta de Greg Hewgill chega mais perto, embora na verdade não diga como reescrever os caminhos.
A solução é surpreendentemente simples.
(1) Em A,
Nota: Isso reescreve o histórico; portanto, se você pretende continuar usando esse repo A, convém clonar (copiar) uma cópia descartável primeiro.
Nota: É necessário modificar o script substituto dentro do comando sed no caso de você usar caracteres não-ascii (ou caracteres em branco) nos nomes ou caminho dos arquivos. Nesse caso, o local do arquivo em um registro produzido por "ls-files -s" começa com aspas.
(2) Então em B, corra
Voila! Você tem um
projectA
diretório em B. Se você executargit log projectA
, verá todos os commits de A.No meu caso, eu queria dois subdiretórios,
projectA
eprojectB
. Nesse caso, eu também dei o passo (1) para B.fonte
"$GIT_INDEX_FILE"
deve ser citado (duas vezes), caso contrário, seu método falhará se, por exemplo, o caminho contiver espaços.Ctrl-V <tab>
Se os dois repositórios tiverem o mesmo tipo de arquivo (como dois repositórios Rails para projetos diferentes), você poderá buscar dados do repositório secundário no seu repositório atual:
e mescle-o ao repositório atual:
Se sua versão do Git for menor que 2.9, remova
--allow-unrelated-histories
.Depois disso, podem ocorrer conflitos. Você pode resolvê-los, por exemplo, com
git mergetool
.kdiff3
pode ser usado apenas com o teclado; portanto, um arquivo de conflito leva 5 minutos ao ler o código em apenas alguns minutos.Lembre-se de terminar a mesclagem:
fonte
Eu continuei perdendo o histórico ao usar a mesclagem, então acabei usando a rebase, pois no meu caso os dois repositórios são diferentes o suficiente para não acabar mesclando a cada commit:
=> resolva conflitos e continue quantas vezes for necessário ...
Isso leva a um projeto com todas as confirmações do projA seguidas pelas confirmações do projB
fonte
No meu caso, eu tinha um
my-plugin
repositório e ummain-project
repositório e queria fingir quemy-plugin
sempre havia sido desenvolvido noplugins
subdiretório demain-project
.Basicamente, reescrevi o histórico do
my-plugin
repositório para que parecesse que todo o desenvolvimento ocorreu noplugins/my-plugin
subdiretório. Em seguida, adicionei o histórico de desenvolvimentomy-plugin
aomain-project
histórico e mesclei as duas árvores. Como não haviaplugins/my-plugin
diretório já presente nomain-project
repositório, essa foi uma mesclagem trivial sem conflitos. O repositório resultante continha todo o histórico dos dois projetos originais e tinha duas raízes.TL; DR
Versão longa
Primeiro, crie uma cópia do
my-plugin
repositório, porque vamos reescrever o histórico desse repositório.Agora, navegue até a raiz do
my-plugin
repositório, verifique sua ramificação principal (provavelmentemaster
) e execute o seguinte comando. Obviamente, você deve substituirmy-plugin
eplugins
quaisquer que sejam seus nomes reais.Agora, para uma explicação.
git filter-branch --tree-filter (...) HEAD
executa o(...)
comando em todas as confirmações acessíveisHEAD
. Observe que isso opera diretamente nos dados armazenados para cada confirmação, portanto, não precisamos nos preocupar com noções de "diretório de trabalho", "índice", "preparo" e assim por diante.Se você executar um
filter-branch
comando que falhar, ele deixará para trás alguns arquivos no.git
diretório e, na próxima vez em que tentarfilter-branch
, reclamará disso, a menos que você forneça a-f
opçãofilter-branch
.Quanto ao comando real, não tive muita sorte
bash
em fazer o que queria, então, em vez disso, usozsh -c
parazsh
executar um comando. Primeiro, defino aextended_glob
opção, que é o que habilita a^(...)
sintaxe nomv
comando, bem como aglob_dots
opção, que permite selecionar arquivos de ponto (como.gitignore
) com um globo (^(...)
).Em seguida, uso o
mkdir -p
comando para criar os doisplugins
eplugins/my-plugin
ao mesmo tempo.Por fim, uso o
zsh
recurso "glob negativo"^(.git|plugins)
para corresponder a todos os arquivos no diretório raiz do repositório, exceto.git
amy-plugin
pasta recém-criada . (A exclusão.git
pode não ser necessária aqui, mas tentar mover um diretório para si mesmo é um erro.)No meu repositório, o commit inicial não incluía nenhum arquivo; portanto, o
mv
comando retornou um erro no commit inicial (já que nada estava disponível para mover). Portanto, adicionei um|| true
para quegit filter-branch
não abortasse.A
--all
opção dizfilter-branch
para reescrever o histórico de todas as ramificações no repositório, e o extra--
é necessário dizergit
para interpretá-la como parte da lista de opções para reescrever ramificações, em vez de ser uma opção parafilter-branch
si mesma.Agora, navegue até o seu
main-project
repositório e verifique em qual filial você deseja mesclar. Adicione sua cópia local domy-plugin
repositório (com seu histórico modificado) como um controle remoto demain-project
com:Agora você terá duas árvores não relacionadas em seu histórico de consolidação, que podem ser visualizadas com bom uso:
Para mesclá-los, use:
Observe que no Git anterior à 2.9.0, a
--allow-unrelated-histories
opção não existe. Se você estiver usando uma dessas versões, apenas omita a opção: a mensagem de erro que--allow-unrelated-histories
impede também foi adicionada na 2.9.0.Você não deve ter nenhum conflito de mesclagem. Se o fizer, provavelmente significa que o
filter-branch
comando não funcionou corretamente ou já havia umplugins/my-plugin
diretóriomain-project
.Certifique-se de inserir uma mensagem de confirmação explicativa para qualquer colaborador futuro, imaginando que hackery estava acontecendo para criar um repositório com duas raízes.
Você pode visualizar o novo gráfico de confirmação, que deve ter duas confirmações raiz, usando o
git log
comando acima . Observe que apenas amaster
ramificação será mesclada . Isso significa que, se você tiver um trabalho importante em outrosmy-plugin
ramos que deseja mesclar namain-project
árvore, evite excluir omy-plugin
controle remoto até fazer essas mesclagens. Caso contrário, as confirmações dessas ramificações ainda estarão nomain-project
repositório, mas algumas estarão inacessíveis e suscetíveis a eventual coleta de lixo. (Além disso, você precisará consultá-los pelo SHA, porque a exclusão de um controle remoto remove suas ramificações de rastreamento remoto.)Opcionalmente, depois de mesclar tudo o que você deseja impedir
my-plugin
, você pode remover omy-plugin
controle remoto usando:Agora você pode excluir com segurança a cópia do
my-plugin
repositório cujo histórico foi alterado. No meu caso, também adicionei um aviso de descontinuação aomy-plugin
repositório real depois que a mesclagem foi concluída e enviada por push.Testado no Mac OS X El Capitan com
git --version 2.9.0
ezsh --version 5.2
. Sua milhagem pode variar.Referências:
fonte
--allow-unrelated-histories
vem?man git-merge
. Por padrão, o comando git merge se recusa a mesclar históricos que não compartilham um ancestral comum. Essa opção pode ser usada para substituir essa segurança ao mesclar históricos de dois projetos que iniciaram suas vidas independentemente. Como essa é uma ocasião muito rara, não existe variável de configuração para habilitar isso por padrão e não será adicionada.git version 2.7.2.windows.1
?Estou tentando fazer a mesma coisa há dias, estou usando o git 2.7.2. A subárvore não preserva o histórico.
Você pode usar esse método se não usar o projeto antigo novamente.
Eu sugiro que você ramifique B primeiro e trabalhe no ramo.
Aqui estão as etapas sem ramificação:
Se você agora registrar qualquer um dos arquivos no subdiretório A, obterá o histórico completo
Este foi o post que me ajudou a fazer isso:
http://saintgimp.org/2013/01/22/merging-two-git-repositories-into-one-repository-without-losing-file-history/
fonte
Se você deseja colocar os arquivos de uma ramificação no repositório B em uma subárvore do repositório A e também preservar o histórico, continue lendo. (No exemplo abaixo, estou assumindo que queremos que a ramificação mestre do repositório B seja mesclada no ramo mestre do repositório A.)
No repositório A, primeiro faça o seguinte para disponibilizar o repositório B:
Agora, criamos um novo ramo (com apenas um commit) no repositório A que chamamos
new_b_root
. A confirmação resultante terá os arquivos que foram confirmados na primeira confirmação da ramificação principal do repositório B, mas colocados em um subdiretório chamadopath/to/b-files/
.Explicação: A
--orphan
opção para o comando checkout faz check-out dos arquivos da ramificação principal de A, mas não cria nenhuma confirmação. Poderíamos ter selecionado qualquer confirmação, porque a seguir, limpamos todos os arquivos de qualquer maneira. Então, sem confirmar ainda (-n
), escolhemos o primeiro commit no ramo principal de B. (A seleção de cereja preserva a mensagem de confirmação original que um checkout direto não parece fazer.) Em seguida, criamos a subárvore onde queremos colocar todos os arquivos do repositório B. Em seguida, precisamos mover todos os arquivos que foram introduzidos no escolha cereja para a subárvore. No exemplo acima, há apenas umREADME
arquivo para mover. Em seguida, confirmamos nosso commit raiz B-repo e, ao mesmo tempo, também preservamos o registro de data e hora do commit original.Agora, criaremos uma nova
B/master
ramificação em cima da recém-criadanew_b_root
. Chamamos o novo ramob
:Agora, mesclamos nossa
b
ramificação emA/master
:Por fim, você pode remover as
B
ramificações remotas e temporárias:O gráfico final terá uma estrutura como esta:
fonte
Reuni muitas informações aqui no Stack OverFlow, etc., e consegui montar um script que resolve o problema para mim.
A ressalva é que ele leva em consideração apenas a ramificação 'develop' de cada repositório e a mescla em um diretório separado em um repositório completamente novo.
Tags e outras ramificações são ignoradas - pode não ser o que você deseja.
O script ainda lida com ramificações e tags de recursos - renomeando-os no novo projeto para que você saiba de onde eles vieram.
Você também pode obtê-lo em http://paste.ubuntu.com/11732805
Primeiro, crie um arquivo com a URL para cada repositório, por exemplo:
Em seguida, chame o script fornecendo um nome para o projeto e o caminho para o script:
O script em si tem muitos comentários que devem explicar o que ele faz.
fonte
Eu sei que é muito tempo depois do fato, mas não fiquei feliz com as outras respostas que encontrei aqui, então escrevi o seguinte:
fonte
if [[ $dirname =~ ^.*\.git$ ]]; then
Se você estiver tentando simplesmente colar dois repositórios, os submódulos e as mesclagens de subárvores são a ferramenta errada a ser usada, porque eles não preservam todo o histórico do arquivo (como as pessoas observaram em outras respostas). Veja esta resposta aqui para a maneira simples e correta de fazer isso.
fonte
Tive um desafio semelhante, mas, no meu caso, desenvolvemos uma versão da base de código no repositório A e clonamos isso em um novo repositório, o repositório B, para a nova versão do produto. Depois de corrigir alguns bugs no repositório A, precisávamos FI as alterações no repositório B. Acabamos fazendo o seguinte:
Trabalhou um deleite :)
fonte
Semelhante ao @Smar, mas usa caminhos do sistema de arquivos, definidos em PRIMARY e SECONDARY:
Então você mescla manualmente.
(adaptado do post de Anar Manafov )
fonte
Mesclando 2 repos
fonte
Quando você deseja mesclar três ou mais projetos em uma única confirmação, execute as etapas conforme descrito nas outras respostas (
remote add -f
,merge
). Em seguida, (suave) redefina o índice para o cabeçalho antigo (onde nenhuma mesclagem aconteceu). Adicione todos os arquivos (git add -A
) e confirme-os (mensagem "Mesclando os projetos A, B, C e D em um projeto). Agora é o ID de confirmação do mestre.Agora, crie
.git/info/grafts
com o seguinte conteúdo:Corra
git filter-branch -- head^..head head^2..head head^3..head
. Se você tiver mais de três ramificações, adicione o máximohead^n..head
que tiver. Para atualizar tags, acrescente--tag-name-filter cat
. Nem sempre adicione isso, pois isso pode causar uma reescrita de algumas confirmações. Para detalhes, consulte a página de manual do filtro-branch , procure por "enxertos".Agora, seu último commit tem os pais certos associados.
fonte
Para mesclar um A em B:
1) No projeto A
2) No projeto B
Nesta ramificação, execute todas as operações necessárias e as confirme.
C) Depois, de volta ao mestre e uma fusão clássica entre os dois ramos:
fonte
Essa função clonará o repo remoto no dir repo local, após a mesclagem de todas as confirmações serem salvas,
git log
serão mostradas as confirmações originais e os caminhos adequados:Como usar:
Se fizer algumas alterações, você pode até mover arquivos / diretórios de repositório mesclado para caminhos diferentes, por exemplo:
Avisos
Caminhos substitui via
sed
, portanto, verifique se ele se moveu nos caminhos adequados após a mesclagem.O
--allow-unrelated-histories
parâmetro existe apenas desde git> = 2.9.fonte
O comando dado é a melhor solução possível, eu sugiro.
fonte
Mesclo projetos levemente manualmente, o que me permite evitar a necessidade de lidar com conflitos de mesclagem.
primeiro, copie os arquivos do outro projeto da maneira que desejar.
próxima atração na história
diga ao git para se fundir na história da última coisa buscada
agora comprometa, no entanto, você normalmente comprometeria
fonte
Eu queria mover um projeto pequeno para um subdiretório de um projeto maior. Como meu pequeno projeto não tinha muitos commits, usei
git format-patch --output-directory /path/to/patch-dir
. Então, no projeto maior, eu useigit am --directory=dir/in/project /path/to/patch-dir/*
.Isso parece muito menos assustador e muito mais limpo que um ramo de filtro. Concedido, pode não ser aplicável a todos os casos.
fonte