¿Cómo se combina Git con los commits simultáneos?

Dado un repository con dos twigs, cada una con compromisos independientes:

Branch Commits ------ ----------- final: egi / \ master: abcdfh-? 

Las letras en el cuadro anterior son significativas: es decir, "maestro" y "final" estaban en desarrollo simultáneamente, y las confirmaciones en ambas twigs deben conservarse.

¿Es mejor (más seguro) fusionar "maestro" en "final", luego fusionarlo de nuevo en "maestro"? ¿O la fusión directa con "master" desde "final" retendrá los commits allí anteriormente?

Entiendo que es probable que surjan conflictos de fusión. Estoy mucho más preocupado de que no perdamos nada del código comprometido a "master" después de fusionar "final" en él.

¿Es mejor (más seguro) fusionar "maestro" en "final", luego fusionarlo de nuevo en "maestro"? ¿O la fusión directa con "master" desde "final" retendrá los commits allí anteriormente?

El último: fusionarse directamente en "master" desde "final" retendrá (s) los commits allí previamente.

git es diferente de otros sistemas de control de versiones como SVN. Por lo que no es familiar, SVN trata twigs como "cubos": las twigs / cubos son fijas (estables) y debe tener cuidado al mover las confirmaciones entre cubos. En SVN, tenía que fusionar (copyr) cualquier confirmación 'maestra' reciente en 'final' antes de 'reintegrar' (pseudocombinar) la twig 'final' de return en 'maestro'. La 'Reintegración' efectivamente copy la punta de 'final' en la punta de 'maestro', reemplazando efectivamente 'maestro' con una copy exacta de la punta de 'final'. Podrías perder cualquier compromiso en 'maestro' que no se haya fusionado (copydo) a 'final' primero.

Como dije, git es diferente. En git, me gusta pensar que el tree de repository (los commits) es estable y más bien son las "twigs" que se mueven: piense en las twigs como pequeñas tags / decoraciones que cuelgan de commits.

Voy a dibujar tu tree un poco diferente, de hecho, lo dibujaré antes de que se haya creado el 'yo':

  final | e - g / a - b - c \ d - f - h | master 

Ahora comprometamos 'i':

  final | e - g - i / a - b - c \ d - f - h | master 

Solo 2 cosas cambiaron. (1) Haciendo caso omiso de las "decoraciones" de twig por el momento, vemos que se ha creado un nuevo compromiso 'i' a partir de su compromiso padre 'g', y (2) la 'decoración' final se movió de 'g' a 'i'.

Vamos a fusionar el final en el maestro, es decir, actualice el maestro para que incluya los cambios del final:

  final | e - g - i / \ a - b - cj \ /| d - f - h | | master 

Ahora la decoración de la twig 'maestro' se movió. La decoración de la twig 'final' se mantuvo. La confirmación 'j' es una combinación de fusión creada a partir de 2 padres: 'h' e 'i'.

Pero, ¿qué pasaría si hubiésemos combinado master en final, es decir, finalizado de forma que incluya los cambios de master?

  final | e - g - i | / \| a - b - cj \ / d - f - h | master 

Ahora 'final' se movió y 'maestro' se mantuvo en su lugar. Tenga en count que la fusión es una combinación real: los cambios de ambas twigs están en la confirmación 'j'. 'j' es el mismo, sin importar a qué twig se fusionó la única, es qué twig "decoración" se movió. (Y esas "decoraciones" de twig se mueven fácilmente).

Para completar, fusionemos la otra twig en la primera twig (no importa quién se fusionó con quién primero, siempre que nos fusionamos en la otra dirección esta vez). Tenga en count que solo se mueve la decoración, no fue necesaria ninguna nueva confirmación (en la jerga git, se 'reenvió rápidamente') porque 'j' ya contenía ambos sets de cambios de ambas twigs:

  final | e - g - i | / \| a - b - cj \ /| d - f - h | | master 

De hecho, veamos el tree unos días después (eliminé la twig 'final', ya terminé):

  e - g - i / \ a - b - cj - k - l - m \ / | d - f - h | | master 

De acuerdo, ¿puedes decir desde el tree qué twig fue originalmente 'master' frente a la que originalmente fue 'final': egi o dfh? ¿Importa?

En git, fusión son verdaderas fusiones. No hay "cubos" y no tiene que mover confirmaciones entre "cubos" / twigs como lo hace para SVN. Solo está el "tree" que está evolucionando y las twigs ("decoraciones") están marcando su (s) lugar (s) en él.

No hay diferencia Puedes ver los cambios que Git se fusionará haciendo

 git diff master...final 

y

 git diff final...master 

cada uno de los cuales mostrará las diferencias acumuladas en cada twig desde su base de combinación. git intenta juntar esos dos sets de cambios acumulados, y no tiene noción de "primacía de twig" o algo por el estilo.

git nunca pierde commits. Regrese a lo básico , busque enlaces desde allí y preste especial atención a dónde los documentos llaman diferencias de otros vcs.

Merge no se basa en el time, y el resultado 1 de una combinación no depende de qué twig está "fusionada" y cuál está "fusionada". 2

La forma abreviada de "cómo funciona la fusión" es esta. Está en alguna twig (actual / "nuestra" / "fusión") y emite el command git merge <commit-ID> , 3 así que git hace esto:

  1. Encuentre la "base de combinación" de la twig actual y la confirmación nombrada.

    En tu diagtwig, estás en la sucursal final , cuya comisión de punta es i , o estás en la twig master , cuya comisión de puntaje es h . Si está en la sucursal, asigne el valor h , y si está en el master sucursal, nombre commit i , de modo que las dos confirmaciones en cuestión sean h e i , y la base de fusión sea commit c : eso es donde se encuentran puntos en común cuando se trabaja hacia atrás desde ambas confirmaciones.

  2. Ejecute git diff (con la opción -M ) para comparar la base de fusión con la punta actual (por ejemplo, c vs h ).

  3. Ejecute git diff (nuevamente con -M ) para comparar la combinación-base con la otra confirmación (p. Ej., c vs i ).

  4. Utilizando el diff en el paso 3 -los cambios "ellos" hicieron-elimine cualquiera de los cambios ya encontrados en el paso 2, luego haga todos los mismos cambios en el consejo actual (por ejemplo, haga todos los cambios que no estén ya en h , aplicando ellos al tree en h ).

Los conflictos ocurren cuando, al intentar aplicar los "cambios restantes" del paso 4, el context circundante no coincide (o, para crear, eliminar o cambiar el nombre del file, no hay una elección correcta).

Si no hay conflictos y no has usado --no-commit , git entonces confirma el tree resultante, que por supuesto va en la twig actual ("nuestra"). Pero obtienes el mismo tree de cualquier manera. (Por supuesto, si hay conflictos, el tree que obtiene depende de cómo los resuelva. Me refiero al resultado automático).


1 Más precisamente, el resultado del tree . La confirmación que se realiza depende de qué twig es el "desde" y cuál es el "en": en particular, el primer padre de la confirmación de fusión resultante es la twig "en" o "nuestra", y el segundo padre es la twig "de" o "de ellos". Y, por supuesto, el compromiso se coloca en la twig actual "en" / "nuestro".

2 Opciones de --ours como --ours , -X ours , y similares, de todos modos.

3 Con git merge branchname , la parte del branchname del branchname se resuelve en un commit-ID. Sin embargo, Git retiene el argumento original para el post de compromiso de fusión, por lo que un nombre de twig suele ser mejor que un SHA-1 sin procesar.