Pregunta

tengo un repositorio Git en una carpeta llamada XXX , y tengo segundo repositorio Git llama YYY .

Quiero importar el XXX repositorio en el YYY repositorio como un subdirectorio llamado ZZZ y añadir todos los XXX 's historial de cambios para YYY .

Estructura de la carpeta antes:

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

estructura de carpetas después de:

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

Se puede hacer esto, o tengo que recurrir al uso de sub-módulos?

¿Fue útil?

Solución

Probablemente la forma más sencilla sería la de tirar de la XXX cosas en una rama en YYY y lo combinan con maestría:

En YYY :

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

Yo en realidad sólo intentado esto con un par de mis repos y funciona. A diferencia respuesta de Jörg no le permitirá continúa utilizando el otro repo, pero no creo que haya especificado que de todos modos.

Nota: Puesto que esto fue escrito originalmente en 2009, GIT ha añadido la fusión subárbol mencionado en la respuesta a continuación. Probablemente me volvería a usar ese método de hoy, aunque, por supuesto, este método aún funciona.

Otros consejos

Si desea conservar la historia exacta confirmar sobre el segundo repositorio y, por tanto, también conserva la capacidad de combinar fácilmente los cambios ascendentes en el futuro, entonces aquí es el método que desee. Es el resultado de la historia no modificada del sub-árbol importado en tu repositorio más uno de combinación se comprometen a mover el repositorio fusionada al subdirectorio.

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."

Puede seguir los cambios aguas arriba, así:

git pull -s subtree XXX_remote master

Git se da cuenta de por sí, donde las raíces son antes de hacer la fusión, por lo que no es necesario especificar el prefijo de las siguientes fusiones.

versiones Git antes 2.9 . No es necesario pasar a la opción --allow-unrelated-histories git merge

El método de la otra respuesta que utiliza read-tree y se salta el paso merge -s ours es, efectivamente, no es diferente de la copia de los archivos con cp y cometer el resultado.

Fuente original era de de github "subárbol Combinar" artículo de ayuda .

git-subtree es un guión diseñado precisamente para este caso de uso de la fusión de múltiples repositorios en uno y preservar la historia (y / o la historia de la división sub-estructuras, aunque eso es parece ser irrelevante para esta pregunta). Se distribuye como parte del árbol git desde el lanzamiento 1.7.11 .

Para combinar un <repo> repositorio en <rev> revisión como <prefix> subdirectorio, utilice git subtree add como sigue:

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

git-subárbol implementa el estrategia de combinación de sub-árbol en un usuario más amigable manera.

En su caso, en el interior YYY repositorio, debe ejecutar:

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

No es un ejemplo bien conocido de este en el propio repositorio Git, que se conoce colectivamente en la comunidad Git como " el más frío jamás se fusionan " (después de la línea de asunto Linus Torvalds utiliza en la dirección de correo a la lista de correo Git que describe esta fusión). En este caso, el gitk Git GUI que ahora es parte de Git adecuado, en realidad solía ser un proyecto independiente. Linus logró fusionar ese repositorio en el repositorio Git de una manera que

  • aparece en el repositorio Git como si siempre hubiera sido desarrollado como parte de Git,
  • toda la historia se mantiene intacto y
  • que todavía puede ser desarrollada de forma independiente en su repositorio de edad, con los cambios simplemente ser git pulled.

El correo electrónico contiene los pasos necesarios para reproducir, pero no es para los débiles de corazón: en primer lugar, Linus escribió Git, por lo que probablemente sabe un poco más sobre él que usted o yo , y en segundo lugar, esto fue hace casi 5 años y Git ha mejorado considerablemente desde entonces, así que tal vez ahora es mucho más fácil.

En particular, creo que hoy en día se podría usar un submódulo gitk, en ese caso específico.

La forma más sencilla de hacerlo es utilizar git formato de parche.

Supongamos que tenemos 2 repositorios Git foo y bar .

foo contiene:

  • foo.txt
  • .git

bar contiene:

  • bar.txt
  • .git

y queremos terminar arriba con foo que contiene el bar historia y estos archivos:

  • foo.txt
  • .git
  • foobar / bar.txt

Así que para hacer eso:

 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/*

Y si queremos volver a escribir todos los mensajes se compromete a partir de barras que podemos hacer, por ejemplo, en Linux:

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

Esto añadirá "[bar]" al comienzo de cada mensaje de consignación.

en este artículo , utilizando subárbol es lo que funcionó para mí y sólo se transfiere la historia aplicable. Publicar aquí por si alguien necesita los pasos (Asegúrese de reemplazar los marcadores de posición con valores aplicables a usted):

en su repositorio fuente de división subcarpeta en una nueva rama

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

en la combinación de cesión temporal de destino en la rama dividida resultado

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

comprobar los cambios y comprometerse

git status
git commit

No se olvide

Limpiar mediante la supresión de la rama subtree-split-result

git branch -D subtree-split-result

Eliminar el control remoto que ha añadido a buscar los datos de recompra fuente

git remote rm merge-source-repo

Esta función clonar repo repo remoto en dir locales, después de la fusión de todos los envíos, será salvo; git log será mostrar las confirmaciones originales y caminos adecuados:

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"
}

Modo de empleo:

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

Si hacer unos pequeños cambios incluso se puede mover archivos / directorios de cesión temporal resultante de la fusión en diferentes caminos, por ejemplo:

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
Caminos sustituye a través de sed, así que asegúrese de que se movió en caminos adecuados después de la fusión.
El parámetro --allow-unrelated-histories sólo existe desde git> = 2,9.

La adición de otra respuesta ya que creo que es un poco más simple. Un tirón de repo_dest se realiza en repo_to_import y luego un empuje --set-aguas arriba url:. Repo_dest maestro se realiza

Este método ha funcionado para mí la importación de varios repositorios más pequeños en uno más grande.

Cómo importar: repo1_to_import a 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

Cambiar nombre o mover archivos y directorios en la posición deseada en repo original, antes de hacer la importación. p.ej.

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

El método descrito en el siguiente enlace inspiró esta respuesta. Me ha gustado mucho, ya que parecía más sencillo. ¡Pero cuidado! Hay dragones! https://help.github.com/articles/importing-an-external git push --mirror url:repo_dest -git-repositorio empuja su historial repo local y estatal a distancia (url: repo_dest). PERO elimina la vieja historia y el estado del control remoto. sobreviene diversión! : -E

quería importar sólo algunos archivos de la otra repositorio (XXX) en mi caso. El subárbol era demasiado complicado para mí y las otras soluciones no funcionó. Esto es lo que hice:

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

Esto le da una lista separada por espacios de todas las confirmaciones que afectan a los archivos que quería importar (ZZZ) en orden inverso (es posible que tenga que añadir a la captura --follow renombra también). entonces entré en el repositorio de destino (YYY), agregó el otro depósito (XXX) como a distancia, realizó un podido recuperar de ella y finalmente:

git cherry-pick $ALL_COMMITS

que añade todas las confirmaciones a su oficina, que por lo tanto tendrá todos los archivos con su historia y puede hacer lo que quiera con ellos como si siempre han estado en este repositorio.

Yo estaba en una situación en la que estaba buscando -s theirs pero por supuesto, no existe esta estrategia. Mi historia es que había bifurcado un proyecto en GitHub, y ahora, por alguna razón, mi master locales no puede mezclarse con upstream/master aunque había hecho cambios locales a esta rama. (Realmente no sé lo que pasó allí? - Supongo aguas arriba había hecho algunos empujones sucios detrás de las escenas, tal vez)

Lo que terminé haciendo fue

# 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

Así que ahora mi master es nuevo en sincronía con upstream/master (y se podría repetir lo anterior para cualquier otra rama también desea sincronizar de manera similar).

Consulte Ejemplo básico este artículo y examinará esa asignación en repositorios:

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

Después de toda la actividad descrita en este capítulo (después de la fusión), quite B-master rama:

$ git branch -d B-master

A continuación, empuje cambios.

A mí me funciona.

puedo sugerir otra solución (alternativa a git-submódulos ) para su problema - gil herramienta (enlaces GIT)

Se permite describir y gestionar complejas dependencias repositorios Git.

También proporciona una solución a la git recursiva submódulos dependencia problema .

Tenga en cuenta que usted tiene las siguientes dependencias del proyecto:

A continuación, se puede definir el archivo .gitlinks con la descripción repositorios relación:

# 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 línea describe enlace git en el siguiente formato:

  1. Nombre único del repositorio
  2. Ruta relativa del repositorio (iniciado desde el camino de .gitlinks archivo)
  3. repositorio Git que será utilizado en el comando git clone rama del repositorio a la caja
  4. línea de vacío o línea comenzaron con # no se analizan (tratados como comentario).

Por último hay que actualizar el repositorio de pruebas de raíz:

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

# The same result with a single command
gil update

Como el resultado podrás clonar todos los proyectos necesarios y vincularlos entre sí de una manera adecuada.

Si desea confirmar todos los cambios en algún repositorio con todos los cambios en los repositorios enlazados niño que puede hacer con un solo comando:

gil commit -a -m "Some big update"

Tire, empujar los comandos funciona de una manera similar:

gil pull
gil push

Gil (enlaces GIT) herramienta es compatible con los siguientes 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

Más información sobre el git recursiva submódulos problema de dependencia .

No sé de una manera fácil de hacer eso. Usted puede hacer esto:

  1. Uso git filter-branch para añadir un super-ZZZ directorio en el repositorio XXX
  2. Empuje la nueva rama en el repositorio YYY
  3. Combinar la rama empujó con el tronco de YYY.

puedo editar con detalles si eso suena atractivo.

Creo que se puede hacer esto utilizando 'git mv' y 'git pull'.

Soy un novato git justo - así que tenga cuidado con su repositorio principal - pero he intentado esto en un directorio temporal y parece que funciona

.

En primer lugar - cambiar el nombre de la estructura de XXX para que coincida con la forma en que quiere que se vea cuando está dentro de YYY:

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

Ahora XXX es el siguiente:

XXX
 |- ZZZ
     |- ZZZ

Ahora utilice 'git pull' a buscar a los cambios a través de:

cd ../YYY
git pull ../XXX

Ahora YYY es el siguiente:

YYY
 |- ZZZ
     |- ZZZ
 |- (other folders that already were in YYY)
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top