Pergunta

Considere o seguinte cenário:

Eu tenho desenvolvido um pequeno projeto experimental Um em seu próprio repositório Git.Ele tem amadurecido, e eu gostaria de Um para ser parte de um grande projeto B, que tem a sua própria grande repositório.Eu agora gostaria de adicionar Uma como um subdiretório do B.

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

Foi útil?

Solução

Um único ramo de outro repositório pode ser facilmente colocado em um subdiretório mantendo a sua história. Por exemplo:

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

Esta aparecerá como uma única confirmação onde todos os arquivos de Rails branch master são adicionados no diretório "rails". No entanto, a cometer o título contém uma referência à árvore velha história:

Adicione 'rails /' de cometer <rev>

Onde <rev> é um SHA-1 cometer hash. Você ainda pode ver a história, a culpa algumas mudanças.

git log <rev>
git blame <rev> -- README.md

Note que você não pode ver o prefixo de diretório a partir daqui uma vez que este é um ramo de idade real deixada intacta. Você deve tratar isso como um movimento de arquivo de costume cometer: você vai precisar de um salto extra quando alcançá-lo

.
# finishes with all files added at once commit
git log rails/README.md

# then continue from original tree
git log <rev> -- README.md

Existem soluções mais complexas como fazer isso manualmente ou reescrever a história como descrito em outras respostas.

O comando git-sub é uma parte do git-contrib oficial, alguns gestores de pacotes instalá-lo por padrão (OS X Homebrew). Mas você pode ter que instalá-lo por si mesmo, além de git.

Outras dicas

Se você quiser project-a fundem em project-b:

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

Retirado de:? git merge diferentes repositórios

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

Nota: O parâmetro --allow-unrelated-histories só existe desde git> = 2,9. Consulte Git - merge git Documentação / --allow- alheios-histórias

Atualizar :. --tags Adicionado como sugerido por @jstadler a fim de manter as tags

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

submódulos

De qualquer copiar repositório A em um diretório separado no maior projeto de B, ou (talvez melhor) clone repositório A em um subdiretório no projecto B. Em seguida, use git submódulo para tornar este um repositório < strong> submodule de um repositório B.

Esta é uma boa solução para repositórios fracamente acoplados, onde o desenvolvimento em um repositório continua, e a maior parte do desenvolvimento é um desenvolvimento autônomo separado na A. Veja também SubmoduleSupport e GitSubmoduleTutorial páginas Git Wiki.

Subtree fundir

Você pode mesclar repositório A em um subdiretório de um projeto B usando o merge sub estratégia. Isto é descrito em Subtree Mesclando e você 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

(é necessária --allow-unrelated-histories Opção para GIT> = 2.9.0.)

Ou você pode usar git subárvore ferramenta ( repositório no GitHub ) por apenwarr (Avery Pennarun), anunciou, por exemplo, em seu blog Uma nova alternativa para submódulos git:. git subárvore


Eu acho que no seu caso (A é ser parte do maior projeto B) a solução correta seria a utilização de merge sub .

A abordagem submodule é bom se você quiser manter o projeto separadamente. No entanto, se você realmente quer fundir os dois projetos no mesmo repositório, então você tem um pouco mais 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 acabam. Então, ao invés de foo.c, bar.html, você teria projb/foo.c e projb/bar.html.

Em seguida, você deve ser capaz de fazer algo como o seguinte:

git remote add projb [wherever]
git pull projb

O git pull vai fazer um git fetch seguido por um git merge. Não deve haver conflitos, se o repositório você está puxando para ainda não tem um diretório projb/.

Além disso a pesquisa indica que algo semelhante foi feito para gitk fundem em git. Junio ??C Hamano escreve sobre ele aqui: http: //www.mail -archive.com/git@vger.kernel.org/msg03395.html

git-subtree é agradável, mas provavelmente não é o que você quiser.

Por exemplo, se projectA é o diretório criado no B, após git subtree,

git log projectA

listas único cometer: a mesclagem. As submissões do projeto mesclada são para diferentes caminhos, para que eles não aparecem.

A resposta de Greg Hewgill mais se aproxima, embora não realmente dizer como reescrever 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: Este reescreve a história, por isso, se você pretende continuar usando este repo A, você pode querer clone (cópia) uma cópia descartável nisso antes

(2) Então, em B, run

git pull path/to/A

Voila! Você tem um diretório projectA em B. Se você executar git log projectA, você verá todas as submissões 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 repositórios têm o mesmo tipo de arquivos (como dois trilhos repositórios para diferentes projetos), você pode buscar dados do repositório secundário para o seu repositório atual:

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

e, em seguida, fundi-lo ao repositório atual:

git merge --allow-unrelated-histories branch_name

Se a sua versão Git é menor do que 2,9, remove --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, de modo 5 arquivo de conflito leva ao ler o código de apenas alguns minutos.

Lembre-se de concluir a fusão:

git commit

Eu continuava a perder história quando usando mala, então acabei usando rebase já que em meu caso, os dois repositórios são suficientes diferente para não acabar fundindo em 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
conflitos

=> resolver, em seguida, continuar, tantas vezes quanto necessário ...

git rebase --continue

Fazendo isso leva a um projecto que tem todas as submissões de proja seguido por commits de projB

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

Basicamente, eu reescreveu a história do my-plugin repositório para que ele apareceu todo o desenvolvimento teve lugar no plugins/my-plugin subdiretório.Em seguida, acrescentei o desenvolvimento histórico das my-plugin no main-project história, e fundiram-se as duas árvores juntas.Como não houve plugins/my-plugin o diretório já presente no main-project repositório, este foi um trivial sem conflitos de mesclagem.A resultante repositório contida toda a história 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|plugins) 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

A versão longa

Primeiro, crie uma cópia do my-plugin repositório, porque nós vamos reescrever a história deste repositório.

Agora, navegue até a raiz do my-plugin repositório, confira o seu ramo principal (provavelmente master), e execute o seguinte comando.Claro, você deve substituir por my-plugin e plugins qualquer que seja a sua nomes reais são.

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

Agora uma explicação. git filter-branch --tree-filter (...) HEAD executa o (...) comando em cada confirmar que é acessível a partir de HEAD.Note que este atua diretamente sobre os dados armazenados para cada commit, por isso não tem de se preocupar com as noções de "diretório de trabalho", "índice", "teste", e assim por diante.

Se você executar um filter-branch comando falhar, ele vai deixar para trás alguns arquivos no .git diretório e da próxima vez que você tentar filter-branch ele vai reclamar sobre isso, a menos que você fornecer a -f opção para filter-branch.

Como para o comando real, eu não tenho muita sorte para obter bash para fazer o que eu queria, então, em vez disso, eu uso zsh -c para fazer zsh executar um comando.Primeiro eu definir o extended_glob opção, que é o que permite a ^(...) sintaxe no mv comando, bem como o glob_dots opção, o que me permite selecionar arquivos (tais como .gitignore) com uma bola (^(...)).

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

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

No meu repositório, a inicial cometer não incluir todos os arquivos, de modo que o mv comando retornou um erro na inicial commit (uma vez que nada estava disponível para se mover).Por isso, eu adicionei um || true de modo que git filter-branch não iria abortar.

O --all opção diz filter-branch para reescrever a história para todos ramos do repositório, e o extra -- é necessário dizer git a interpretá-lo como uma parte da lista de opção para ramos, reescrever, em vez de como uma opção para filter-branch em si.

Agora, navegue até o seu main-project repositório e confira tudo o ramo que você deseja mesclar.Adicionar a sua cópia local do my-plugin repositório (com sua história modificada) como um controle remoto de main-project com:

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

Agora você terá dois relacionados árvores em seu cometer história, que você pode visualizar muito bem a utilizar:

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

Para fundi-los, use:

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

Observe que na pré-2.9.0 Git, o --allow-unrelated-histories opção não existe.Se você estiver usando uma dessas versões, basta omitir a opção:a mensagem de erro que --allow-unrelated-histories impede que foi também adicionado em 2.9.0.

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

Certifique-se de introduzir uma exposição de mensagem de commit para qualquer futuro contribuintes está imaginando para que hackery estava indo para fazer um repositório com duas raízes.

Você pode visualizar o novo commit gráfico, que deve ter duas raiz compromete-se, usando o acima git log de comando.Note que só o master ramo será mesclado.Isso significa que se você tem um trabalho importante em outros my-plugin ramos que você deseja mesclar com o main-project árvore, você deve abster-se de eliminar o my-plugin controle remoto até que você tem feito essas mescla.Se você não o fizer, então a compromete-se a partir desses ramos ainda estará no main-project repositório, mas alguns serão inacessíveis e suscetíveis a eventual coleta de lixo.(Além disso, você terá que se referir a eles pelo SHA, porque a exclusão de um remoto remove sua remoto-controle de ramos.)

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

$ git remote remove my-plugin

Agora você pode excluir com segurança a cópia do my-plugin repositório cuja história você mudou.No meu caso, eu também adicionei um aviso de substituição para o real my-plugin repositório após a fusão foi concluída e empurrou.


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 para os dias, estou usando git 2.7.2. não Subtree não preservar a história.

Você pode usar este método se você não vai usar o velho projeto novamente.

Gostaria de sugerir que você ramo B em primeiro lugar e trabalho 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 <files> 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 <A repo url>

git merge A/<branch>

mkdir A

# move all the files into subdir A, excluding .git
git mv <files> 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ê já log qualquer um dos arquivos em subdir A você vai ter a história completa

git log --follow A/<file>

Este foi o post que me ajudar a fazer isso:

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

Se você deseja colocar os arquivos de uma filial em repo B em um sub de repo A e também preservar a história, continue lendo. (No exemplo abaixo, eu estou supondo que queremos repo branch master do B fundidos em repo de um branch master.)

Em repo A, primeiro faça o seguinte para fazer repo B disponíveis:

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

Agora vamos criar um novo ramo da marca (com apenas um commit) em repo A que chamamos new_b_root. O resultando cometer terá os arquivos que foram cometidos na primeira confirmação de branch master repo do B, mas colocados em uma subpasta chamada 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 ao comando check-out cheques fora os arquivos de um branch master mas não cria qualquer cometer. Poderíamos ter escolhido qualquer cometer porque seguinte, limpar todos os arquivos de qualquer maneira. Então, sem cometer ainda (-n), nós cereja-escolher o primeiro comprometer a partir branch master do B. (The cherry-pick preserva o original mensagem de confirmação que um checkout reta parece não fazer.) Então vamos criar a subárvore onde queremos colocar todos os arquivos do repo B. Em seguida, tem que mover todos os arquivos que foram introduzidas no cherry-pick para a sub-árvore. No exemplo acima, há apenas um arquivo README para mover. Então nós nos comprometemos nossa raiz B-repo cometer, e, ao mesmo tempo, preserve o timestamp do original cometer.

Agora, vamos criar um novo ramo B/master no topo do new_b_root recém-criado. Chamamos a nova b ramo:

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

Agora, nos fundimos o nosso ramo b 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 o B remoto e ramos temporários:

git remote remove B
git branch -D new_b_root b

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

 enter descrição da imagem aqui

Eu sei que é muito tempo após o fato, mas eu não estava feliz com as outras respostas que eu encontrei aqui, então eu 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

Eu recolhi muita informação aqui no estouro de pilha, etc., e têm conseguem colocar um conjunto de script que resolve o problema para mim.

A ressalva é que ele só leva em conta o ramo 'desenvolver' de cada repositório e funde-o em um diretório separado em um completamente novo repositório.

tags e outros ramos são ignoradas -. Isto pode não ser o que você quer

O script até mesmo alças apresentam ramos e rótulos - renomeando-os no novo projeto para que você saiba de onde 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 <new_project> <my_repo_urls.lst>
## - where <new_project> is the name of the new project to create
## - and <my_repo_urls.lst> is a file contaning the URLs to the respositories
##   which are to be merged on separate lines.
##
## Author: Robert von Burg
##            eitch@eitchnet.ch
##
## Version: 0.3.2
## Created: 2018-02-05
##
################################################################################
#

# disallow using undefined variables
shopt -s -o nounset

# Script variables
declare SCRIPT_NAME="${0##*/}"
declare SCRIPT_DIR="$(cd ${0%/*} ; pwd)"
declare ROOT_DIR="$PWD"
IFS=$'\n'

# Detect proper usage
if [ "$#" -ne "2" ] ; then
  echo -e "ERROR: Usage: $0 <new_project> <my_repo_urls.lst>"
  exit 1
fi


## Script variables
PROJECT_NAME="${1}"
PROJECT_PATH="${ROOT_DIR}/${PROJECT_NAME}"
TIMESTAMP="$(date +%s)"
LOG_FILE="${ROOT_DIR}/${PROJECT_NAME}_merge.${TIMESTAMP}.log"
REPO_FILE="${2}"
REPO_URL_FILE="${ROOT_DIR}/${REPO_FILE}"


# Script functions
function failed() {
  echo -e "ERROR: Merging of projects failed:"
  echo -e "ERROR: Merging of projects failed:" >>${LOG_FILE} 2>&1
  echo -e "$1"
  exit 1
}

function commit_merge() {
  current_branch="$(git symbolic-ref HEAD 2>/dev/null)"
  if [[ ! -f ".git/MERGE_HEAD" ]] ; then
    echo -e "INFO:   No commit required."
    echo -e "INFO:   No commit required." >>${LOG_FILE} 2>&1
  else
    echo -e "INFO:   Committing ${sub_project}..."
    echo -e "INFO:   Committing ${sub_project}..." >>${LOG_FILE} 2>&1
    if ! git commit -m "[Project] Merged branch '$1' of ${sub_project}" >>${LOG_FILE} 2>&1 ; then
      failed "Failed to commit merge of branch '$1' of ${sub_project} into ${current_branch}"
    fi
  fi
}


# 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: Logging to ${LOG_FILE}"
echo -e "INFO: Creating new git repository ${PROJECT_NAME}..."
echo -e "INFO: Creating new git repository ${PROJECT_NAME}..." >>${LOG_FILE} 2>&1
echo -e "===================================================="
echo -e "====================================================" >>${LOG_FILE} 2>&1
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 the branches of this project
echo -e "INFO: Merging projects into new repository..."
echo -e "INFO: Merging projects into new repository..." >>${LOG_FILE} 2>&1
echo -e "===================================================="
echo -e "====================================================" >>${LOG_FILE} 2>&1
for url in $(cat ${REPO_URL_FILE}) ; do

  if [[ "${url:0:1}" == '#' ]] ; then
    continue
  fi

  # extract the name of this project
  export sub_project=${url##*/}
  sub_project=${sub_project%*.git}

  echo -e "INFO: Project ${sub_project}"
  echo -e "INFO: Project ${sub_project}" >>${LOG_FILE} 2>&1
  echo -e "----------------------------------------------------"
  echo -e "----------------------------------------------------" >>${LOG_FILE} 2>&1

  # Fetch the project
  echo -e "INFO:   Fetching ${sub_project}..."
  echo -e "INFO:   Fetching ${sub_project}..." >>${LOG_FILE} 2>&1
  git remote add "${sub_project}" "${url}"
  if ! git fetch --tags --quiet ${sub_project} >>${LOG_FILE} 2>&1 ; then
    failed "Failed to fetch project ${sub_project}"
  fi

  # add remote branches
  echo -e "INFO:   Creating local branches for ${sub_project}..."
  echo -e "INFO:   Creating local branches for ${sub_project}..." >>${LOG_FILE} 2>&1
  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}..."
    echo -e "INFO:   Creating branch ${branch_name}..." >>${LOG_FILE} 2>&1

    # create and checkout new merge branch off of master
    if ! git checkout -b "${sub_project}/${branch_name}" master >>${LOG_FILE} 2>&1 ; then failed "Failed preparing ${branch_name}" ; fi
    if ! git reset --hard ; then failed "Failed preparing ${branch_name}" >>${LOG_FILE} 2>&1 ; fi
    if ! git clean -d --force ; then failed "Failed preparing ${branch_name}" >>${LOG_FILE} 2>&1 ; fi

    # Merge the project
    echo -e "INFO:   Merging ${sub_project}..."
    echo -e "INFO:   Merging ${sub_project}..." >>${LOG_FILE} 2>&1
    if ! git merge --allow-unrelated-histories --no-commit "remotes/${sub_project}/${branch_name}" >>${LOG_FILE} 2>&1 ; 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."
      echo -e "WARN:   Not moving files in branch ${branch_name} of ${sub_project} as already only one root level." >>${LOG_FILE} 2>&1
    else
      echo -e "INFO:   Moving files in branch ${branch_name} of ${sub_project} so we have a single directory..."
      echo -e "INFO:   Moving files in branch ${branch_name} of ${sub_project} so we have a single directory..." >>${LOG_FILE} 2>&1
      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" >>${LOG_FILE} 2>&1 ; then
    failed "sub_project ${sub_project} is missing master branch!"
  fi

  # copy remote tags
  echo -e "INFO:   Copying tags for ${sub_project}..."
  echo -e "INFO:   Copying tags for ${sub_project}..." >>${LOG_FILE} 2>&1
  while read tag ; do
    tag_ref=$(echo $tag | tr " " "\t" | cut -f 1)
    tag_name_unfixed=$(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_unfixed%%^*}"

    tag_new_name="${sub_project}/${tag_name}"
    echo -e "INFO:     Copying tag ${tag_name_unfixed} to ${tag_new_name} for ref ${tag_ref}..."
    echo -e "INFO:     Copying tag ${tag_name_unfixed} to ${tag_new_name} for ref ${tag_ref}..." >>${LOG_FILE} 2>&1
    if ! git tag "${tag_new_name}" "${tag_ref}" >>${LOG_FILE} 2>&1 ; then
      echo -e "WARN:     Could not copy tag ${tag_name_unfixed} to ${tag_new_name} for ref ${tag_ref}"
      echo -e "WARN:     Could not copy tag ${tag_name_unfixed} to ${tag_new_name} for ref ${tag_ref}" >>${LOG_FILE} 2>&1
    fi
  done < <(git ls-remote --tags --refs ${sub_project})

  # Remove the remote to the old project
  echo -e "INFO:   Removing remote ${sub_project}..."
  echo -e "INFO:   Removing remote ${sub_project}..." >>${LOG_FILE} 2>&1
  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 "INFO: Merging projects master branches into new repository..." >>${LOG_FILE} 2>&1
echo -e "===================================================="
echo -e "====================================================" >>${LOG_FILE} 2>&1
for url in $(cat ${REPO_URL_FILE}) ; do

  if [[ ${url:0:1} == '#' ]] ; then
    continue
  fi

  # extract the name of this project
  export sub_project=${url##*/}
  sub_project=${sub_project%*.git}

  echo -e "INFO:   Merging ${sub_project}..."
  echo -e "INFO:   Merging ${sub_project}..." >>${LOG_FILE} 2>&1
  if ! git merge --allow-unrelated-histories --no-commit "${sub_project}/master" >>${LOG_FILE} 2>&1 ; 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 -e "INFO: Done." >>${LOG_FILE} 2>&1
echo

exit 0

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

Primeiro, crie um arquivo com o URL para cada repositório, 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, chamar o script dar um nome do projeto e o caminho para o script:

./mergeGitRepositories.sh eitchnet_test eitchnet.lst

O script em si tem um monte de comentários que deve explicar o que faz.

Se você está tentando simplesmente cola dois repositórios juntos, submódulos e funde subárvore são a ferramenta errada para o uso porque não preservar toda a história de arquivo (como as pessoas têm notado em outras respostas). Veja esta resposta aqui para o simples e maneira correta de fazer isso.

Semelhante ao @Smar mas do sistema de arquivos usa caminhos, fixado em primária e secundária:

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

Em seguida, você mesclar manualmente.

(adaptado de mensagem por Anar Manafov )

Eu tinha um desafio semelhante, mas no meu caso, que havia desenvolvido uma versão da base de código em repo A, então clonado que em um novo repo, repo B, para a nova versão do produto. Depois de corrigir alguns bugs no repo A, precisávamos FI as alterações no repo B. acabou fazendo o seguinte:

  1. Adicionando um controle remoto para repo B que apontavam para repo A (git remoto adicionar ...)
  2. Puxar o ramo atual (que não estávamos usando mestre para correções de bugs) (git pull remoteForRepoA bugFixBranch)
  3. Empurrando fusões para github

Trabalhou um deleite :)

Quando você deseja mesclar três ou mais projetos em um único comprometer, fazer os passos como descrito em outras respostas (remote add -f, merge). Então, (soft) redefinir o índice para a cabeça de idade (onde nenhuma fusão aconteceu). Adicionar todos os arquivos (git add -A) e cometê-los (mensagem "Mesclar projectos A, B, C, e D em um projeto). Esta é agora a comprometer-id de mestre.

Agora, crie .git/info/grafts com seguinte conteúdo:

<commit-id of master> <list of commit ids of all parents>

Executar git filter-branch -- head^..head head^2..head head^3..head. Se você tiver mais de três ramos, basta adicionar o máximo head^n..head como você tem filiais. Para tags de atualização, --tag-name-filter cat acréscimo. Nem sempre acrescentar que, porque isso pode causar uma reescrita de alguns commits. Para mais detalhes consulte href="https://www.kernel.org/pub/software/scm/git/docs/git-filter-branch.html" de filter-branch , procurar "enxertos".

Agora, seu último commit tem os pais certos associado.

Para mesclar um A dentro B:

1) No projeto A

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

2) No projeto B

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

Neste ramo fazer todas as operações que você precisa fazer e cometê-los.

C) Em seguida, de volta para o mestre e uma fusão clássica entre os dois ramos:

git checkout master
git merge projectA

A fusão 2 repos

git clone ssh://<project-repo> 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

Esta função irá clonar repo remoto para dir repo local, após a fusão todas as submissões serão salvos, git log será mostrar os commits originais e 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 fazer um pouco muda você pode até mesmo mover arquivos / diretórios de repo incorporada em caminhos diferentes, 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
Caminhos substitui via sed, por isso certifique-se movia em caminhos adequados após a fusão.
O parâmetro --allow-unrelated-histories só existe desde git> = 2.9.

Eu fundir projectos ligeiramente manualmente, o que me permite evitar a necessidade de lidar com conflitos de mesclagem.

Primeiro, copie nos arquivos do outro projeto, contudo, você quer que eles.

cp -R myotherproject newdirectory
git add newdirectory

próxima atração na história

git fetch path_or_url_to_other_repo

dizer git para mesclar na história da última coisa buscada

echo 'FETCH_HEAD' > .git/MERGE_HEAD

Agora comprometer, contudo, você normalmente iria cometer

git commit

comando dado é a melhor solução possível, sugiro.

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

Eu queria mover um pequeno projeto para um subdiretório de um maior. Desde o meu pequeno projeto não tinha muitos commits, eu usei git format-patch --output-directory /path/to/patch-dir. Em seguida, no projeto maior, eu usei git am --directory=dir/in/project /path/to/patch-dir/*.

Este sente maneira menos assustador e muito mais limpo do que um filter-branch. Concedido, pode não ser aplicável a todos os casos.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top