Como você mescla dois repositorys Git?

Considere o seguinte cenário:

Eu desenvolvi um pequeno projeto experimental A em seu próprio repository Git. Ela agora amadureceu e eu gostaria que A fizesse parte do projeto maior B, que tem seu próprio grande repository. Gostaria agora de adicionar A como subdiretório de B.

Como faço para mesclar A em B, sem perder o histórico de qualquer lado?

    Um único ramo de outro repository pode ser facilmente colocado sob um subdiretório mantendo seu histórico. Por exemplo:

    git subtree add --prefix=rails git://github.com/rails/rails.git master 

    Isto irá aparecer como um único commit, onde todos os arquivos do branch master do Rails serão adicionados ao diretório “rails”. No entanto, o título da confirmação contém uma referência à antiga tree de históricos:

    Adicionar ‘rails /’ from commit

    Onde é um hash de confirmação SHA-1. Você ainda pode ver o histórico, culpar algumas mudanças.

     git log  git blame  -- README.md 

    Observe que você não pode ver o prefixo do diretório daqui, já que este é um ramo antigo real que permanece intacto. Você deve tratar isso como um commit usual de movimento de arquivo: você precisará de um salto extra ao alcançá-lo.

     # finishes with all files added at once commit git log rails/README.md # then continue from original tree git log  -- README.md 

    Existem soluções mais complexas, como fazer isso manualmente ou rewrite o histórico, conforme descrito em outras respostas.

    O comando git-subtree é uma 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.

    Se você quiser mesclar o project-a no project-b :

     cd path/to/project-b git remote add project-a path/to/project-a git fetch project-a git merge --allow-unrelated-histories project-a/master # or whichever branch you want to merge git remote remove project-a 

    Retirado de: git mesclar diferentes repositorys?

    Este método funcionou muito bem para mim, é mais curto e na minha opinião muito mais limpo.

    Nota: O --allow-unrelated-histories existe apenas desde git> = 2.9. Veja Git – git merge Documentation / – all-unrelated-histories

    Aqui estão duas soluções possíveis:

    Submódulos

    Copie o repository A em um diretório separado no projeto B maior, ou (talvez melhor) o repository clone A em um subdiretório no projeto B. Em seguida, use o submódulo git para tornar esse repository um submódulo de um repository B.

    Esta é uma boa solução para repositorys fracamente acoplados, onde o desenvolvimento no repository A continua, e a maior parte do desenvolvimento é um desenvolvimento independente separado em A. Veja também as páginas SubmoduleSupport e GitSubmoduleTutorial no Git Wiki.

    Mesclagem de subtree

    Você pode mesclar o repository A em um subdiretório de um projeto B usando a estratégia de mesclagem de subtree . Isso é descrito em Subtree Merging and You por Markus Prinz.

     git remote add -f Bproject /path/to/B git merge -s ours --allow-unrelated-histories --no-commit Bproject/master git read-tree --prefix=dir-B/ -u Bproject/master git commit -m "Merge B project as our subdirectory" git pull -s subtree Bproject master 

    (A opção – --allow-unrelated-histories é necessária para o Git> = 2.9.0.)

    Ou você pode usar a ferramenta git subtree ( repository no GitHub ) por apenwarr (Avery Pennarun), anunciada por exemplo em sua postagem no blog Uma nova alternativa para os submódulos Git: git subtree .


    Eu acho que no seu caso (A é para ser parte do projeto maior B) a solução correta seria usar a mesclagem de subtree .

    A abordagem do submódulo é boa se você quiser manter o projeto separadamente. No entanto, se você realmente quiser mesclar os dois projetos no mesmo repository, terá um pouco mais de trabalho a fazer.

    A primeira coisa seria usar git filter-branch para rewrite os nomes de tudo no segundo repository para estar no subdiretório onde você gostaria que eles terminassem. Então, ao invés de foo.c , bar.html , você teria projb/foo.c e projb/bar.html .

    Então, você deve ser capaz de fazer algo como o seguinte:

     git remote add projb [wherever] git pull projb 

    O git pull fará uma git fetch pelo git merge seguido por uma git merge . Não deve haver conflitos, se o repository para o qual você está puxando ainda não tiver um diretório projb/ .

    Pesquisas adicionais indicam que algo semelhante foi feito para mesclar o gitk ao git . Junio ​​C Hamano escreve sobre isso aqui: http://www.mail-archive.com/git@vger.kernel.org/msg03395.html

    git-subtree é legal, mas provavelmente não é o que você quer.

    Por exemplo, se projectA for o diretório criado em B, após a git subtree ,

     git log projectA 

    lista apenas um commit: a mesclagem. Os commits do projeto mesclado são para caminhos diferentes, então eles não aparecem.

    A resposta de Greg Hewgill chega mais perto, embora na verdade não diga como rewrite os caminhos.


    A solução é surpreendentemente simples.

    (1) em A,

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

    Nota: Isso reescreve o histórico, portanto, se você pretende continuar usando este repo A, poderá clonar (copiar) uma cópia descartável dele primeiro.

    (2) Então, em B, execute

     git pull path/to/A 

    Voila! Você tem um diretório projectA em B. Se você executar git log projectA , verá todos os commits de A.


    No meu caso, eu queria dois subdiretórios, projectA e projectB . Nesse caso, eu fiz o passo (1) para B também.

    Se ambos os repositorys tiverem o mesmo tipo de arquivos (como dois repositorys do Rails para projetos diferentes), você poderá buscar dados do repository secundário em seu repository atual:

     git fetch git://repository.url/repo.git master:branch_name 

    e, em seguida, mesclá-lo ao repository atual:

     git merge --allow-unrelated-histories branch_name 

    Se sua versão do Git for menor que 2.9, remova os --allow-unrelated-histories .

    Depois disso, conflitos podem ocorrer. Você pode resolvê-los, por exemplo, com o git mergetool . kdiff3 pode ser usado somente com o teclado, portanto, 5 arquivos de conflito são usados ​​ao ler o código em apenas alguns minutos.

    Lembre-se de terminar a fusão:

     git commit 

    Eu continuei perdendo o histórico ao usar o merge, então acabei usando o rebase, já que no meu caso os dois repositorys são diferentes o suficiente para não acabar se fundindo a cada commit:

     git clone git@gitorious/projA.git projA git clone git@gitorious/projB.git projB cd projB git remote add projA ../projA/ git fetch projA git rebase projA/master HEAD 

    => resolver conflitos, depois continuar, quantas vezes forem necessárias …

     git rebase --continue 

    Isso faz com que um projeto tenha todos os commits do projA seguido por commits do projB

    No meu caso, eu tinha um repository my-plugin e um repository main-project , e queria fingir que my-plugin sempre foi desenvolvido no subdiretório plugins do main-project .

    Basicamente, eu reescrevi o histórico do repository my-plugin para que aparecesse que todo o desenvolvimento ocorria no subdiretório plugins/my-plugin . Em seguida, adicionei o histórico de desenvolvimento do my-plugin no histórico do main-project e juntei as duas trees. Como não havia nenhum diretório plugins/my-plugin já presente no repository main-project , essa era uma mesclagem trivial de não-conflitos. O repository resultante continha todo o histórico de ambos os projetos originais e tinha duas raízes.

    TL; DR

     $ cp -R my-plugin my-plugin-dirty $ cd my-plugin-dirty $ git filter-branch -f --tree-filter "zsh -c 'setopt extended_glob && setopt glob_dots && mkdir -p plugins/my-plugin && (mv ^(.git|my-plugin) plugins/my-plugin || true)'" -- --all $ cd ../main-project $ git checkout master $ git remote add --fetch my-plugin ../my-plugin-dirty $ git merge my-plugin/master --allow-unrelated-histories $ cd .. $ rm -rf my-plugin-dirty 

    Versão longa

    Primeiro, crie uma cópia do repository my-plugin , porque iremos rewrite o histórico deste repository.

    Agora, navegue até a raiz do repository my-plugin , verifique sua ramificação principal (provavelmente master ) e execute o seguinte comando. Claro, você deve replace o my-plugin e os plugins quaisquer que sejam seus nomes reais.

     $ git filter-branch -f --tree-filter "zsh -c 'setopt extended_glob && setopt glob_dots && mkdir -p plugins/my-plugin && (mv ^(.git|my-plugin) plugins/my-plugin || true)'" -- --all 

    Agora, para uma explicação. git filter-branch --tree-filter (...) HEAD executa o comando (...) em cada confirmação que é alcançável a partir do HEAD . Observe que isso opera diretamente nos dados armazenados para cada confirmação, portanto, não precisamos nos preocupar com as noções de “diretório de trabalho”, “índice”, “armazenamento temporário” e assim por diante.

    Se você executar um comando de filter-branch que falhe, ele deixará alguns arquivos no diretório .git e, na próxima vez que você tentar filter-branch ele reclamará sobre isso, a menos que você forneça a opção -f para filter-branch .

    Quanto ao comando real, não tive muita sorte em fazer o que queria, então, ao invés disso, eu uso o zsh -c para fazer o zsh executar um comando. Primeiro eu defino a opção extended_glob , que é o que habilita a syntax ^(...) no comando mv , bem como a opção glob_dots , que me permite selecionar dotfiles (como .gitignore ) com um glob ( ^(...) ).

    Em seguida, uso o comando mkdir -p para criar plugins e plugins/my-plugin ao mesmo tempo.

    Finalmente, eu uso o recurso zsh “glob negativo” ^(.git|my-plugin) para combinar todos os arquivos no diretório raiz do repository, exceto para .git e a pasta my-plugin recém-criada. (Excluindo .git pode não ser necessário aqui, mas tentar mover um diretório para si mesmo é um erro.)

    No meu repository, o commit inicial não incluiu nenhum arquivo, então o comando mv retornou um erro no commit inicial (já que nada estava disponível para mover). Portanto, eu adicionei um || true || true para que o git filter-branch não abortasse.

    A opção --all diz filter-branch para rewrite o histórico de todas as ramificações no repository, e o extra -- é necessário dizer ao git para interpretá-lo como parte da lista de opções para que as ramificações sejam reescritas, em vez de como uma opção para filter-branch se.

    Agora, navegue até o seu repository do main-project e confira o ramo em que você deseja se fundir. Adicione sua cópia local do repository my-plugin (com seu histórico modificado) como um controle remoto do main-project com:

     $ git remote add --fetch my-plugin $PATH_TO_MY_PLUGIN_REPOSITORY 

    Agora você terá duas trees não relacionadas em seu histórico de commits, que você pode visualizar bem usando:

     $ git log --color --graph --decorate --all 

    Para mesclá-los, use:

     $ git merge my-plugin/master --allow-unrelated-histories 

    Note que no Git pré-2.9.0, a opção --allow-unrelated-histories não existe. Se você estiver usando uma dessas versões, apenas omita a opção: a mensagem de erro que --allow-unrelated-histories previne também foi adicionada na 2.9.0.

    Você não deve ter conflitos de mesclagem. Se você fizer isso, isso provavelmente significa que o comando filter-branch não funcionou corretamente ou já havia um diretório plugins/my-plugin no main-project .

    Certifique-se de inserir uma mensagem de confirmação explicativa para quaisquer futuros contribuintes se perguntando o que hackers estavam fazendo para criar um repository com duas raízes.

    Você pode visualizar o novo gráfico de confirmação, que deve ter dois commits root, usando o comando git log acima. Observe que apenas o ramo master será mesclado . Isso significa que, se você tiver um trabalho importante em outras ramificações do my-plugin que deseja mesclar na tree do main-project , evite excluir o controle remoto do my-plugin até que tenha feito essas mesclagens. Se não o fizer, os commits dessas ramificações ainda estarão no repository do main-project , mas alguns estarão inacessíveis e suscetíveis a uma eventual garbage collection. (Além disso, você terá que se referir a eles pelo SHA, porque a exclusão de um controle remoto remove suas ramificações de rastreamento remoto.)

    Opcionalmente, depois de ter mesclado tudo o que você deseja manter do my-plugin , você pode remover o controle remoto do my-plugin usando:

     $ git remote remove my-plugin 

    Agora você pode excluir com segurança a cópia do repository my-plugin cujo histórico você alterou. No meu caso, também adicionei uma notificação de depreciação ao repository real do my-plugin depois que a mesclagem foi concluída e enviada.


    Testado no Mac OS X El Capitan com git --version 2.9.0 e zsh --version 5.2 . Sua milhagem pode variar.

    Referências:

    Eu tenho tentado fazer a mesma coisa por dias, estou usando o git 2.7.2. A subtree não preserva o histórico.

    Você pode usar esse método se não estiver usando o projeto antigo novamente.

    Eu sugiro que você ramifique B primeiro e trabalhe no ramo.

    Aqui estão os passos sem ramificação:

     cd B # You are going to merge A into B, so first move all of B's files into a sub dir mkdir B # Move all files to B, till there is nothing in the dir but .git and B git mv  B git add . git commit -m "Moving content of project B in preparation for merge from A" # Now merge A into B git remote add -f A  git merge A/ mkdir A # move all the files into subdir A, excluding .git git mv  A git commit -m "Moved A into subdir" # Move B's files back to root git mv B/* ./ rm -rf B git commit -m "Reset B to original state" git push 

    Se você agora registra qualquer um dos arquivos no subdireto A, você obterá o histórico completo

     git log --follow A/ 

    Este foi o post que me ajudou a fazer isso:

    http://saintgimp.org/2013/01/22/merging-two-git-repositories-to-one-repository-without-losing-file-history/

    Eu sei que é muito depois do fato, mas eu não estava feliz com as outras respostas que encontrei aqui, então escrevi o seguinte:

     me=$(basename $0) TMP=$(mktemp -d /tmp/$me.XXXXXXXX) echo echo "building new repo in $TMP" echo sleep 1 set -e cd $TMP mkdir new-repo cd new-repo git init cd .. x=0 while [ -n "$1" ]; do repo="$1"; shift git clone "$repo" dirname=$(basename $repo | sed -e 's/\s/-/g') if [[ $dirname =~ ^git:.*\.git$ ]]; then dirname=$(echo $dirname | sed s/.git$//) fi cd $dirname git remote rm origin git filter-branch --tree-filter \ "(mkdir -p $dirname; find . -maxdepth 1 ! -name . ! -name .git ! -name $dirname -exec mv {} $dirname/ \;)" cd .. cd new-repo git pull --no-commit ../$dirname [ $x -gt 0 ] && git commit -m "merge made by $me" cd .. x=$(( x + 1 )) done 

    Reuni muitas informações aqui no Stack OverFlow, etc., e consegui juntar um script que resolve o problema para mim.

    A ressalva é que ele leva em conta apenas o ramo “desenvolver” de cada repository e o mescla em um diretório separado em um repository completamente novo.

    Tags e outras ramificações são ignoradas – isso pode não ser o que você deseja.

    O script até lida com ramos e tags de resources – renomeando-os no novo projeto para que você saiba de onde eles vieram.

     #!/bin/bash # ################################################################################ ## Script to merge multiple git repositories into a new repository ## - The new repository will contain a folder for every merged repository ## - The script adds remotes for every project and then merges in every branch ## and tag. These are renamed to have the origin project name as a prefix ## ## Usage: mergeGitRepositories.sh   ## - where  is the name of the new project to create ## - and  is a file containing the URLs to the repositories ## which are to be merged on separate lines. ## ## Author: Robert von Burg ## eitch@eitchnet.ch ## ## Version: 0.2.0 ## Created: 2015-06-17 ## ################################################################################ # # Disallow using undefined variables shopt -s -o nounset # Script variables declare SCRIPT_NAME="${0##*/}" declare SCRIPT_DIR="$(cd ${0%/*} ; pwd)" declare ROOT_DIR="$PWD" # Detect proper usage if [ "$#" -ne "2" ] ; then echo -e "ERROR: Usage: $0  " exit 1 fi # Script functions function failed() { echo -e "ERROR: Merging of projects failed:" echo -e "$1" exit 1 } function commit_merge() { current_branch="$(git symbolic-ref HEAD 2>/dev/null)" CHANGES=$(git status | grep "working directory clean") MERGING=$(git status | grep "merging") if [[ "$CHANGES" != "" ]] && [[ "$MERGING" == "" ]] ; then echo -e "INFO: No commit required." else echo -e "INFO: Committing ${sub_project}..." if ! git commit --quiet -m "[Project] Merged branch '$1' of ${sub_project}" ; then failed "Failed to commit merge of branch '$1' of ${sub_project} into ${current_branch}" fi fi } ## Script variables PROJECT_NAME="${1}" PROJECT_PATH="${ROOT_DIR}/${PROJECT_NAME}" REPO_FILE="${2}" REPO_URL_FILE="${ROOT_DIR}/${REPO_FILE}" # Make sure the REPO_URL_FILE exists if [ ! -e "${REPO_URL_FILE}" ] ; then echo -e "ERROR: Repo file ${REPO_URL_FILE} does not exist!" exit 1 fi # Make sure the required directories don't exist if [ -e "${PROJECT_PATH}" ] ; then echo -e "ERROR: Project ${PROJECT_NAME} already exists!" exit 1 fi # Create the new project echo -e "INFO: Creating new git repository ${PROJECT_NAME}..." echo -e "====================================================" cd ${ROOT_DIR} mkdir ${PROJECT_NAME} cd ${PROJECT_NAME} git init echo "Initial Commit" > initial_commit # Since this is a new repository we need to have at least one commit # thus were we create temporary file, but we delete it again. # Deleting it guarantees we don't have conflicts later when merging git add initial_commit git commit --quiet -m "[Project] Initial Master Repo Commit" git rm --quiet initial_commit git commit --quiet -m "[Project] Initial Master Repo Commit" echo # Merge all projects into th branches of this project echo -e "INFO: Merging projects into new repository..." echo -e "====================================================" for url in $(cat ${REPO_URL_FILE}) ; do # Extract the name of this project export sub_project=${url##*/} sub_project=${sub_project%*.git} echo -e "INFO: Project ${sub_project}" echo -e "----------------------------------------------------" # Fetch the project echo -e "INFO: Fetching ${sub_project}..." git remote add "${sub_project}" "${url}" if ! git fetch --no-tags --quiet ${sub_project} 2>/dev/null ; then failed "Failed to fetch project ${sub_project}" fi # Add remote branches echo -e "INFO: Creating local branches for ${sub_project}..." while read branch ; do branch_ref=$(echo $branch | tr " " "\t" | cut -f 1) branch_name=$(echo $branch | tr " " "\t" | cut -f 2 | cut -d / -f 3-) echo -e "INFO: Creating branch ${branch_name}..." # Create and checkout new merge branch off of master git checkout --quiet -b "${sub_project}/${branch_name}" master git reset --hard --quiet git clean -d --force --quiet # Merge the project echo -e "INFO: Merging ${sub_project}..." if ! git merge --quiet --no-commit "remotes/${sub_project}/${branch_name}" 2>/dev/null ; then failed "Failed to merge branch 'remotes/${sub_project}/${branch_name}' from ${sub_project}" fi # And now see if we need to commit (maybe there was a merge) commit_merge "${sub_project}/${branch_name}" # Relocate projects files into own directory if [ "$(ls)" == "${sub_project}" ] ; then echo -e "WARN: Not moving files in branch ${branch_name} of ${sub_project} as already only one root level." else echo -e "INFO: Moving files in branch ${branch_name} of ${sub_project} so we have a single directory..." mkdir ${sub_project} for f in $(ls -a) ; do if [[ "$f" == "${sub_project}" ]] || [[ "$f" == "." ]] || [[ "$f" == ".." ]] ; then continue fi git mv -k "$f" "${sub_project}/" done # Commit the moving if ! git commit --quiet -m "[Project] Move ${sub_project} files into sub directory" ; then failed "Failed to commit moving of ${sub_project} files into sub directory" fi fi echo done < <(git ls-remote --heads ${sub_project}) # Checkout master of sub probject if ! git checkout "${sub_project}/master" 2>/dev/null ; then failed "sub_project ${sub_project} is missing master branch!" fi # Copy remote tags echo -e "INFO: Copying tags for ${sub_project}..." while read tag ; do tag_ref=$(echo $tag | tr " " "\t" | cut -f 1) tag_name=$(echo $tag | tr " " "\t" | cut -f 2 | cut -d / -f 3) # hack for broken tag names where they are like 1.2.0^{} instead of just 1.2.0 tag_name="${tag_name%%^*}" tag_new_name="${sub_project}/${tag_name}" echo -e "INFO: Copying tag ${tag_name} to ${tag_new_name} for ref ${tag_ref}..." if ! git tag "${tag_new_name}" "${tag_ref}" 2>/dev/null ; then echo -e "WARN: Could not copy tag ${tag_name} to ${tag_new_name} for ref ${tag_ref}" fi done < <(git ls-remote --tags ${sub_project}) # Remove the remote to the old project echo -e "INFO: Removing remote ${sub_project}..." git remote rm ${sub_project} echo done # Now merge all project master branches into new master git checkout --quiet master echo -e "INFO: Merging projects master branches into new repository..." echo -e "====================================================" for url in $(cat ${REPO_URL_FILE}) ; do # extract the name of this project export sub_project=${url##*/} sub_project=${sub_project%*.git} echo -e "INFO: Merging ${sub_project}..." if ! git merge --quiet --no-commit "${sub_project}/master" 2>/dev/null ; then failed "Failed to merge branch ${sub_project}/master into master" fi # And now see if we need to commit (maybe there was a merge) commit_merge "${sub_project}/master" echo done # Done cd ${ROOT_DIR} echo -e "INFO: Done." echo exit 0 

    Você também pode obtê-lo em http://paste.ubuntu.com/11732805

    Primeiro crie um arquivo com o URL para cada repository, por exemplo:

     git@github.com:eitchnet/ch.eitchnet.parent.git git@github.com:eitchnet/ch.eitchnet.utils.git git@github.com:eitchnet/ch.eitchnet.privilege.git 

    Em seguida, chame o script fornecendo um nome do projeto e o caminho para o script:

     ./mergeGitRepositories.sh eitchnet_test eitchnet.lst 

    O script em si tem muitos comentários que devem explicar o que ele faz.

    Se você está tentando simplesmente colar dois repositorys juntos, submódulos e subtrees são a ferramenta errada a ser usada porque eles não preservam todo o histórico de arquivos (como as pessoas notaram em outras respostas). Veja esta resposta aqui para a maneira simples e correta de fazer isso.

    Se você quiser colocar os arquivos de uma filial no repository B em uma subtree do repository A e também preservar o histórico, continue lendo. (No exemplo abaixo, estou assumindo que queremos que o branch master do repo B seja incorporado na ramificação master do repo A.)

    No repo A, primeiro faça o seguinte para disponibilizar o repo B:

     git remote add B ../B # Add repo B as a new remote. git fetch B 

    Agora criamos um novo branch (com apenas um commit) no new_b_root A que chamamos de new_b_root . O commit resultante terá os arquivos que foram confirmados no primeiro commit do branch master do repo B, mas colocados em um subdiretório chamado path/to/b-files/ .

     git checkout --orphan new_b_root master git rm -rf . # Remove all files. git cherry-pick -n `git rev-list --max-parents=0 B/master` mkdir -p path/to/b-files git mv README path/to/b-files/ git commit --date="$(git log --format='%ai' $(git rev-list --max-parents=0 B/master))" 

    Explicação: A opção --orphan para o comando de checkout verifica os arquivos do branch master de A, mas não cria nenhum commit. Poderíamos ter selecionado qualquer commit porque, em seguida, limpamos todos os arquivos de qualquer maneira. Então, sem confirmar ainda ( -n ), escolhemos o primeiro commit do branch master de B. (O cherry-pick preserva a mensagem de commit original que um checkout direto não parece fazer.) Então nós criamos a subtree onde queremos colocar todos os arquivos do repository B. Nós então temos que mover todos os arquivos que foram introduzidos no arquivo. escolha cereja para a subtree. No exemplo acima, há apenas um arquivo README para mover. Em seguida, comprometemos nosso commit raiz do B-repo e, ao mesmo tempo, também preservamos o timestamp do commit original.

    Agora, criaremos uma nova ramificação B/master na parte superior do new_b_root recém-criado. Nós chamamos o novo ramo b :

     git checkout -bb B/master git rebase -s recursive -Xsubtree=path/to/b-files/ new_b_root 

    Agora, nós mesclamos nossa ramificação em A/master :

     git checkout master git merge --allow-unrelated-histories --no-commit b git commit -m 'Merge repo B into repo A.' 

    Finalmente, você pode remover os ramos remoto e temporário B :

     git remote remove B git branch -D new_b_root b 

    O gráfico final terá uma estrutura como esta:

    insira a descrição da imagem aqui

    I had a similar challenge, but in my case, we had developed one version of the codebase in repo A, then cloned that into a new repo, repo B, for the new version of the product. After fixing some bugs in repo A, we needed to FI the changes into repo B. Ended up doing the following:

    1. Adding a remote to repo B that pointed to repo A (git remote add…)
    2. Pulling the current branch (we were not using master for bug fixes) (git pull remoteForRepoA bugFixBranch)
    3. Pushing merges to github

    Worked a treat 🙂

    Similar to @Smar but uses file system paths, set in PRIMARY and SECONDARY:

     PRIMARY=~/Code/project1 SECONDARY=~/Code/project2 cd $PRIMARY git remote add test $SECONDARY && git fetch test git merge test/master 

    Then you manually merge.

    (adapted from post by Anar Manafov )

    When you want to merge three or more projects in a single commit, do the steps as described in the other answers ( remote add -f , merge ). Then, (soft) reset the index to old head (where no merge happened). Add all files ( git add -A ) and commit them (message “Merging projects A, B, C, and D into one project). This is now the commit-id of master.

    Now, create .git/info/grafts with following content:

       

    Run git filter-branch -- head^..head head^2..head head^3..head . If you have more than three branches, just add as much head^n..head as you have branches. To update tags, append --tag-name-filter cat . Do not always add that, because this might cause a rewrite of some commits. For details see man page of filter-branch , search for “grafts”.

    Now, your last commit has the right parents associated.

    To merge a A within B:

    1) In the project A

     git fast-export --all --date-order > /tmp/ProjectAExport 

    2) In the project B

     git checkout -b projectA git fast-import --force < /tmp/ProjectAExport 

    In this branch do all operations you need to do and commit them.

    C) Then back to the master and a classical merge between the two branches:

     git checkout master git merge projectA 

    Merging 2 repos

     git clone ssh:// project1 cd project1 git remote add -f project2 project2 git merge --allow-unrelated-histories project2/master git remote rm project2 delete the ref to avoid errors git update-ref -d refs/remotes/project2/master 

    Given command is the best possible solution I suggest.

     git subtree add --prefix=MY_PROJECT git://github.com/project/my_project.git master 

    This function will clone remote repo into local repo dir, after merging all commits will be saved, git log will be show the original commits and proper paths:

     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 

    If make a little changes you can even move files/dirs of merged repo into different paths, for example:

     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" 

    Notices
    Paths replaces via sed , so make sure it moved in proper paths after merging.
    The --allow-unrelated-histories parameter only exists since git >= 2.9.

    I merge projects slightly manually, which allows me to avoid needing to deal with merge conflicts.

    first, copy in the files from the other project however you want them.

     cp -R myotherproject newdirectory git add newdirectory 

    next pull in the history

     git fetch path_or_url_to_other_repo 

    tell git to merge in the history of last fetched thing

     echo 'FETCH_HEAD' > .git/MERGE_HEAD 

    now commit however you normally would commit

     git commit