Pergunta

Eu tenho um repositório Git em uma pasta chamada XXX, e eu tenho segundo repositório Git chamado YYY.

Eu quero importar o XXX repositório para o YYY repositório como um subdiretório chamado ZZZ e adicionar todos XXX's mudar a história, para YYY.

A estrutura de pasta de antes:

XXX
 |- .git
 |- (project files)
YYY
 |- .git
 |- (project files)

Estrutura de pasta depois:

YYY
 |- .git  <-- This now contains the change history from XXX
 |-  ZZZ  <-- This was originally XXX
      |- (project files)
 |-  (project files)

Isso pode ser feito, ou é preciso recorrer ao uso de sub-módulos?

Foi útil?

Solução

Provavelmente a maneira mais simples seria puxar o Xxx encher em um ramo em AAA e depois mescla -o em mestre:

Dentro AAA:

git remote add other /path/to/XXX
git fetch other
git checkout -b ZZZ other/master
mkdir ZZZ
git mv stuff ZZZ/stuff                      # repeat as necessary for each file/dir
git commit -m "Moved stuff to ZZZ"
git checkout master                
git merge ZZZ --allow-unrelated-histories   # should add ZZZ/ to master
git commit
git remote rm other
git branch -d ZZZ                           # to get rid of the extra branch before pushing
git push                                    # if you have a remote, that is

Na verdade, eu apenas tentei isso com alguns dos meus repositórios e funciona. Diferente A resposta de Jörg Isso não permite que você continue a usar o outro repositório, mas acho que você não especificou isso de qualquer maneira.

Nota: Como isso foi originalmente escrito em 2009, o Git adicionou a mesclagem da subárvore mencionada na resposta abaixo. Eu provavelmente usaria esse método hoje, embora, é claro, esse método ainda funcione.

Outras dicas

Se você deseja manter o histórico de comprometimento exato do segundo repositório e, portanto, também mantém a capacidade de mesclar facilmente as alterações a montante no futuro, aqui está o método que você deseja. Isso resulta em histórico não modificado da subárvore sendo importada para o seu repo, além de uma mesclagem, se compromete a mover o repositório mesclado para o subdiretório.

git remote add XXX_remote <path-or-url-to-XXX-repo>
git fetch XXX_remote
git merge -s ours --no-commit --allow-unrelated-histories XXX_remote/master
git read-tree --prefix=ZZZ/ -u XXX_remote/master
git commit -m "Imported XXX as a subtree."

Você pode rastrear mudanças a montante como assim:

git pull -s subtree XXX_remote master

O Git descobre por conta própria onde as raízes estão antes de fazer a mesclagem, para que você não precise especificar o prefixo nas mesclars subsequentes.

Versões Git antes de 2.9: Você não precisa passar pelo --allow-unrelated-histories opção para git merge.

O método na outra resposta que usa read-tree e pula o merge -s ours A etapa não é efetivamente diferente de copiar os arquivos com CP e cometer o resultado.

A fonte original era de Artigo de ajuda "Subture Merge" do Github.

git-subtree é um script projetado para exatamente esse caso de uso de fusão de vários repositórios em um, preservando o histórico (e/ou o histórico de divisões de subárvores, embora isso pareça ser irrelevante para esta questão). É distribuído como parte da árvore git Desde a versão 1.7.11.

Para mesclar um repositório <repo> na revisão <rev> como subdiretório <prefix>, usar git subtree add do seguinte modo:

git subtree add -P <prefix> <repo> <rev>

Git-Subtree implementa o Estratégia de mesclagem de subárvore de uma maneira mais amigável.

Para o seu caso, dentro do repositório AAA, você correria:

git subtree add -P ZZZ /path/to/XXX.git master

Há um conhecido exemplo desta no repositório Git em si, que é coletivamente conhecido no Git comunidade "o mais legal da série nunca"(após a linha de assunto Linus Torvalds usou o endereço de e-mail para o Git mailinglist que descreve essa série).Neste caso, o gitk Git GUI, que agora é parte do Git adequada, na verdade, usado para ser um projeto separado.Linus conseguiu mesclar esse repositório o repositório Git em um caminho que

  • ele aparece no repositório Git como se tivesse sempre sido desenvolvido como parte do Git,
  • toda a história é mantido intacto e
  • ele ainda pode ser desenvolvido de forma independente em seu repositório antigo, com alterações simples fato de ser git pulled.

O e-mail contém os passos necessários para o reproduzir, mas não é para os fracos de coração:primeiro, Linus escreveu O Git, então ele provavelmente conhece um pouco mais sobre ele do que você ou a mim, e em segundo lugar, este foi de cerca de 5 anos atrás e o Git tem melhorado consideravelmente desde então, talvez por isso agora é muito mais fácil.

Em particular, eu acho que hoje em dia pode-se usar um gitk submódulo, neste caso específico.

A maneira simples de fazer isso é usar o Git Format-Patch.

Suponha que tenhamos 2 repositórios Git foo e bar.

foo contém:

  • foo.txt
  • .git

bar contém:

  • bar.txt
  • .git

E queremos acabar com foo contendo o bar História e esses arquivos:

  • foo.txt
  • .git
  • Foobar/bar.txt

Então, para fazer isso:

 1. create a temporary directory eg PATH_YOU_WANT/patch-bar
 2. go in bar directory
 3. git format-patch --root HEAD --no-stat -o PATH_YOU_WANT/patch-bar --src-prefix=a/foobar/ --dst-prefix=b/foobar/
 4. go in foo directory
 5. git am PATH_YOU_WANT/patch-bar/*

E se quisermos reescrever todas as mensagens de compensação do bar, podemos fazer, por exemplo, no Linux:

git filter-branch --msg-filter 'sed "1s/^/\[bar\] /"' COMMIT_SHA1_OF_THE_PARENT_OF_THE_FIRST_BAR_COMMIT..HEAD

Isso adicionará [bar] no início de cada mensagem de confirmação.

Com base sobre este artigo, usando subárvore é que funcionou para mim e só aplicável a história foi transferido.Postar aqui caso alguém precisa de passos (certifique-se de substituir os espaços com os valores aplicáveis para você):

em seu repositório de origem dividir subpasta em um novo ramo

git subtree split --prefix=<source-path-to-merge> -b subtree-split-result

no seu destino de recompra direta no resultado dividida ramo

git remote add merge-source-repo <path-to-your-source-repository>
git fetch merge-source-repo
git merge -s ours --no-commit merge-source-repo/subtree-split-result
git read-tree --prefix=<destination-path-to-merge-into> -u merge-source-repo/subtree-split-result

verifique as alterações e cometer

git status
git commit

Não se esqueça de

Limpar eliminando o subtree-split-result ramo

git branch -D subtree-split-result

Remover o controle remoto você adicionou para buscar os dados a partir da fonte de recompra

git remote rm merge-source-repo

Esta função clonará o repositório remoto no repo local, depois de mesclar todas as confirmações serão salvas, git log mostrará os compromissos originais e os caminhos adequados:

function git-add-repo
{
    repo="$1"
    dir="$(echo "$2" | sed 's/\/$//')"
    path="$(pwd)"

    tmp="$(mktemp -d)"
    remote="$(echo "$tmp" | sed 's/\///g'| sed 's/\./_/g')"

    git clone "$repo" "$tmp"
    cd "$tmp"

    git filter-branch --index-filter '
        git ls-files -s |
        sed "s,\t,&'"$dir"'/," |
        GIT_INDEX_FILE="$GIT_INDEX_FILE.new" git update-index --index-info &&
        mv "$GIT_INDEX_FILE.new" "$GIT_INDEX_FILE"
    ' HEAD

    cd "$path"
    git remote add -f "$remote" "file://$tmp/.git"
    git pull "$remote/master"
    git merge --allow-unrelated-histories -m "Merge repo $repo into master" --edit "$remote/master"
    git remote remove "$remote"
    rm -rf "$tmp"
}

Como usar:

cd current/package
git-add-repo https://github.com/example/example dir/to/save

Se fizer um pouco de alterações, você pode até mover arquivos/diretos de repositórios mesclados para diferentes caminhos, por exemplo:

repo="https://github.com/example/example"
path="$(pwd)"

tmp="$(mktemp -d)"
remote="$(echo "$tmp" | sed 's/\///g' | sed 's/\./_/g')"

git clone "$repo" "$tmp"
cd "$tmp"

GIT_ADD_STORED=""

function git-mv-store
{
    from="$(echo "$1" | sed 's/\./\\./')"
    to="$(echo "$2" | sed 's/\./\\./')"

    GIT_ADD_STORED+='s,\t'"$from"',\t'"$to"',;'
}

# NOTICE! This paths used for example! Use yours instead!
git-mv-store 'public/index.php' 'public/admin.php'
git-mv-store 'public/data' 'public/x/_data'
git-mv-store 'public/.htaccess' '.htaccess'
git-mv-store 'core/config' 'config/config'
git-mv-store 'core/defines.php' 'defines/defines.php'
git-mv-store 'README.md' 'doc/README.md'
git-mv-store '.gitignore' 'unneeded/.gitignore'

git filter-branch --index-filter '
    git ls-files -s |
    sed "'"$GIT_ADD_STORED"'" |
    GIT_INDEX_FILE="$GIT_INDEX_FILE.new" git update-index --index-info &&
    mv "$GIT_INDEX_FILE.new" "$GIT_INDEX_FILE"
' HEAD

GIT_ADD_STORED=""

cd "$path"
git remote add -f "$remote" "file://$tmp/.git"
git pull "$remote/master"
git merge --allow-unrelated-histories -m "Merge repo $repo into master" --edit "$remote/master"
git remote remove "$remote"
rm -rf "$tmp"

Avisos
Os caminhos substituem via sed, por isso, certifique -se de que ele se moveu em caminhos adequados após a fusão.
o --allow-unrelated-histories O parâmetro existe apenas desde o git> = 2.9.

Adicionando outra resposta, pois acho que isso é um pouco mais simples. Uma atração de repo_dest é feita em repo_to_import e depois um push-URL-Upstream URL: repo_dest mestre está concluído.

Esse método funcionou para mim, importando vários repositórios menores para um maior.

Como importar: repo1_to_import para repo_dest

# checkout your repo1_to_import if you don't have it already 
git clone url:repo1_to_import repo1_to_import
cd repo1_to_import

# now. pull all of repo_dest
git pull url:repo_dest
ls 
git status # shows Your branch is ahead of 'origin/master' by xx commits.
# now push to repo_dest
git push --set-upstream url:repo_dest master

# repeat for other repositories you want to import

Renomeie ou mova arquivos e diretos para a posição desejada no repositório original antes de fazer a importação. por exemplo

cd repo1_to_import
mkdir topDir
git add topDir
git mv this that and the other topDir/
git commit -m"move things into topDir in preparation for exporting into new repo"
# now do the pull and push to import

O método descrito no link a seguir inspirou esta resposta. Gostei, pois parecia mais simples. Mas cuidado! Haverá dragões! https://help.github.com/articles/importing-an-external-git-repository git push --mirror url:repo_dest Empurra o histórico e o estado de repositórios locais para remoto (URL: repo_dest). Mas exclui a antiga história e o estado do controle remoto. Diversão se segue! : -E

Eu queria importar apenas alguns arquivos do outro repositório (xxx) no meu caso. A subárvore era muito complicada para mim e as outras soluções não funcionaram. Isso é o que eu fiz:

ALL_COMMITS=$(git log --reverse --pretty=format:%H -- ZZZ | tr '\n' ' ')

Isso oferece uma lista separada por espaço de todas as confirmações que afetam os arquivos que eu queria importar (ZZZ) na ordem inversa (você pode precisar adicionar--seguir para capturar as renomeias também). Entrei então ao repositório de destino (AAA), adicionei o outro repositório (xxx) como remoto, fiz uma busca dele e finalmente:

git cherry-pick $ALL_COMMITS

O que adiciona todas as confirmações à sua filial, você terá todos os arquivos com o histórico deles e pode fazer o que quiser com eles como se eles sempre estivessem neste repositório.

Eu estava em uma situação em que estava procurando -s theirs Mas é claro que essa estratégia não existe. Minha história era que eu havia bifurcado um projeto no Github, e agora por algum motivo, meu local master não poderia ser fundido com upstream/master Embora eu não tenha feito mudanças locais nessa filial. (Realmente não sei o que aconteceu lá - acho que a montante fez alguns empurrões sujos nos bastidores, talvez?)

O que acabei fazendo foi

# as per https://help.github.com/articles/syncing-a-fork/
git fetch upstream
git checkout master
git merge upstream/master
....
# Lots of conflicts, ended up just abandonging this approach
git reset --hard   # Ditch failed merge
git checkout upstream/master
# Now in detached state
git branch -d master # !
git checkout -b master   # create new master from upstream/master

Então agora meu master está novamente em sincronia com upstream/master (E você pode repetir o acima para qualquer outra ramificação que você também deseja sincronizar da mesma forma).

Ver Exemplo básico dentro Este artigo e considere esse mapeamento em repositórios:

  • A <-> YYY,
  • B <-> XXX

Depois de todas as atividades descritas neste capítulo (após a fusão), remova o ramo B-master:

$ git branch -d B-master

Em seguida, empurre as mudanças.

Funciona para mim.

Eu posso sugerir outra solução (alternativa a Git-submodules) para o seu problema - Ferramenta GIL (Git Links)

Ele permite descrever e gerenciar dependências complexas de repositórios Git.

Também fornece uma solução para o Problema de dependência do Git Recursive Submodules.

Considere que você tem as seguintes dependências do projeto:Gráfico de dependência do repositório Git de amostra

Então você pode definir .gitlinks Arquivo com repositórios Relacionamento Descrição:

# Projects
CppBenchmark CppBenchmark https://github.com/chronoxor/CppBenchmark.git master
CppCommon CppCommon https://github.com/chronoxor/CppCommon.git master
CppLogging CppLogging https://github.com/chronoxor/CppLogging.git master

# Modules
Catch2 modules/Catch2 https://github.com/catchorg/Catch2.git master
cpp-optparse modules/cpp-optparse https://github.com/weisslj/cpp-optparse.git master
fmt modules/fmt https://github.com/fmtlib/fmt.git master
HdrHistogram modules/HdrHistogram https://github.com/HdrHistogram/HdrHistogram_c.git master
zlib modules/zlib https://github.com/madler/zlib.git master

# Scripts
build scripts/build https://github.com/chronoxor/CppBuildScripts.git master
cmake scripts/cmake https://github.com/chronoxor/CppCMakeScripts.git master

Cada linha descreve o link Git no seguinte formato:

  1. Nome único do repositório
  2. Caminho relativo do repositório (iniciado a partir do caminho do arquivo .gitlinks)
  3. Repositório Git que será usado na filial do Git Clone Command Repository para fazer o checkout
  4. A linha ou linha vazia iniciada com # não são analisadas (tratadas como comentários).

Finalmente, você deve atualizar seu repositório de amostra root:

# Clone and link all git links dependencies from .gitlinks file
gil clone
gil link

# The same result with a single command
gil update

Como resultado, você clonará todos os projetos necessários e os vinculará de maneira adequada.

Se você deseja cometer todas as alterações em algum repositório com todas as mudanças nos repositórios vinculados à criança, você pode fazer isso com um único comando:

gil commit -a -m "Some big update"

Puxe, os comandos push funcionam de maneira semelhante:

gil pull
gil push

A ferramenta GIL (GIT Links) suporta os seguintes comandos:

usage: gil command arguments
Supported commands:
    help - show this help
    context - command will show the current git link context of the current directory
    clone - clone all repositories that are missed in the current context
    link - link all repositories that are missed in the current context
    update - clone and link in a single operation
    pull - pull all repositories in the current directory
    push - push all repositories in the current directory
    commit - commit all repositories in the current directory

Mais sobre Problema de dependência do Git Recursive Submodules.

Não conheço uma maneira fácil de fazer isso. Você poderia fazer isso:

  1. Use git filtro rale para adicionar um super-diretório do ZZZ no repositório XXX
  2. Empurre o novo ramo para o repositório AAA
  3. Mesclar o galho empurrado com o porta -malas de Yyy.

Posso editar com detalhes se isso parecer atraente.

Eu acho que você pode fazer isso usando 'Git MV' e 'Git Pull'.

Eu sou um bom git noob - então tenha cuidado com o seu repositório principal - mas eu apenas tentei isso em um diretor temporário e parece funcionar.

Primeiro - renomeie a estrutura do XXX para corresponder à sua aparência quando estiver dentro de AA:

cd XXX
mkdir tmp
git mv ZZZ tmp/ZZZ
git mv tmp ZZZ

Agora xxx se parece com o seguinte:

XXX
 |- ZZZ
     |- ZZZ

Agora use 'Git Pull' para buscar as alterações:

cd ../YYY
git pull ../XXX

Agora você se parece com o seguinte:

YYY
 |- ZZZ
     |- ZZZ
 |- (other folders that already were in YYY)
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top