Git octopus merge con repositorios no relacionados

5 minutos de lectura

avatar de usuario
eric

Tengo un montón de repositorios de git, cada uno con un archivo. Me gustaría fusionarlos todos juntos, preferiblemente en un solo paso. Lo que estoy buscando es este gráfico:

*----¬ mergedrepo/master
| \ \ \
| | | * repoA/master
| | * repoB/master
| | |
| | * repoB/...
| * repoC/master
* repoD/master
|
* repoD/...

probé un git mergepero parece que la estrategia del pulpo no funciona para árboles disjuntos

$ git merge a/master b/master c/master d/master
Unable to find common commit with a/master
Automatic merge failed; fix conflicts and then commit the result.

tambien me dijeron que git merge --squash ayudaría, pero eso dio el mismo error.

Esto genera el gráfico correcto, pero pierde todos los archivos:

$ git merge -s ours a/master b/master c/master d/master

¿Cómo voy a hacer esto?

  • Su combinación de pulpo funcionará, solo tiene que solucionar los conflictos como dice el mensaje. Una vez que haya solucionado los conflictos, debería poder simplemente comprometerse.

    –CB Bailey

    3 de junio de 2012 a las 21:52

  • @CharlesBailey: ¡No hay conflictos!

    –Eric

    3 de junio de 2012 a las 22:18

  • Sí, hay: “Falló la fusión automática; solucione los conflictos y luego confirme el resultado”. Para resolver el conflicto, probablemente solo desee agregar todas las rutas en conflicto si los archivos no comparten ninguno de los mismos nombres en los repositorios.

    –CB Bailey

    3 de junio de 2012 a las 23:21

avatar de usuario
Vértigo

Me las arreglé para resolver el problema con la combinación de pulpo de ramas no relacionadas. Desafortunadamente, lo siguiente se aplica solo a casos bastante simples en los que no existen conflictos de fusión reales.

  1. Haga una fusión como se describe en el OP.

    git merge --allow-unrelated-histories a/master b/master c/master d/master

    Se “fracasará” alegando sobre conflictos. Pero la fusión se ha realizado y registrado ese hecho aunque no hay cambios en el índice.

  2. Agregue contenido de ramas al índice con el read-tree dominio:

    git read-tree a/master b/master c/master d/master

    Esto no afectará el árbol de trabajo, solo se actualizará el índice. (Es posible que desee agregar su rama actual al final. Debe agregarlas todas en un solo comando).

    Si las ramas no son totalmente independientes, ajuste su orden teniendo en cuenta que este último sobrescribirá el contenido de las ramas anteriores.

  3. Comprometerse normalmente. (git commit -m "octopus-merged remote 'a', 'b', 'c', 'd'")
  4. Realice un restablecimiento completo (git reset --hard) para obtener un árbol de trabajo consistente.

Supongo que luego se puede editar y modificar este compromiso, pero personalmente no lo he intentado.

  • probablemente te refieres git read-tree ... en vez de git read-subtree .... También apuesto a que los lectores apreciarán si das ejemplos de código para cada paso, como git reset --hard para el paso 4.

    –Shlomo Georg Konwisser

    20 de septiembre de 2016 a las 0:44

  • En caso de que alguien más tenga problemas para resolver esto: si bien no incluye el nombre de la sucursal en la que se está fusionando en el git merge comando, debe incluirlo en el git read-tree dominio.

    – david4420

    13 de septiembre de 2017 a las 13:15

  • Para mí, agregar los contenidos de las ramas no funcionó porque tuve que integrar 11 de ellas y git read-tree me dijo: fatal: no puedo leer más de 8 árboles. Sin embargo, al leer los árboles uno por uno en el índice con git read-tree a/master y luego llevarlos al área de trabajo con git checkout-index -a es posible enviar todos los archivos a la vez y, por lo tanto, superar esta limitación.

    – fantasma de jade

    16 de septiembre de 2019 a las 8:22


Crear una confirmación vacía

git commit -m "Common commit" --allow-empty

mira su hachís con log -1 y agregarlo al archivo de injertos sin padres

echo HASH_OF_COMMON_COMMIT >> .git/info/grafts

luego encuentra todas las demás raíces con log --max-parents=0 y para cada uno agregue su hash al archivo de injerto con el hash anterior como padre

each HASH_OF_ROOT HASH_OF_COMMON_COMMIT >> .git/info/grafts

y ahora, fusionar!

El archivo de injertos es una forma de cambiar los padres de una confirmación y una vez que todos sus repositorios tengan una confirmación común, su repositorio debería aceptar la fusión.

Tenía muchas ramas no relacionadas para fusionar desde una estructura SVN heredada a un repositorio de Git recién inicializado. La respuesta de Vértigo me llevó casi todo el camino, pero git read-tree solo puede leer hasta 8 árboles a la vez.

Mi solución fue escribir un nuevo árbol después de cada lectura, luego usar ese nuevo árbol en la siguiente lectura:

tree_head=HEAD
to_merge="a/master ... z/master"

# If you're using a variable here you want word splitting, so don't use quotes.
git merge --allow-unrelated-histories $to_merge

for merging in $to_merge; do
    git read-tree "$tree_head" "$merging"
    tree_head=$(git write-tree)
done

git checkout-index -fua
git commit -m "All repos merged."

Si bien parece más complicado, creo que la solución de @akuhn es la única que realmente funciona de la manera en que Git lo pretende. También para mí, las otras soluciones solo funcionan cuando fusiono las ramas no relacionadas una vez. En mi caso de uso, quería realizar fusiones sucesivas con diferentes versiones de las ramas no relacionadas. Las otras soluciones no arrojan errores, pero el estado del archivo se desordena.

La solución descrita en realidad se volvió mucho más fácil desde Git 1.6.5, que introdujo el git replace dominio. Sin embargo, la idea sigue siendo la misma: haces que Git piense que tus confirmaciones tienen una confirmación común para que la fusión funcione. Para hacerlo, debe averiguar el SHA-1 de la confirmación inicial de su rama actual:

common_commit=$(git rev-list --max-parents=0 HEAD)

Una vez que tenga eso, le dice a git que cada una de las ramas que desea fusionar se origina a partir de ese compromiso:

for branch in a/master b/master c/master d/master
do
  branch_initial_commit=$(git rev-list --max-parents=0 $branch)
  git replace --graft $branch_initial_commit $common_commit
done

Después de eso, simplemente puede ejecutar el comando de combinación. Git no se quejará, ya que todas las ramas ahora se originan desde el mismo compromiso común.

git merge a/master b/master c/master d/master

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad