empujando en git con múltiples conflictos de files de desarrolladores

Escenario: 2 desarrolladores trabajan en el mismo proyecto y cada desarrollador crea una nueva característica. Algunos files del proyecto son utilizados por ambos desarrolladores y, por lo tanto, son cambiados por ellos. cuando el primer dev empuja hacia el sistema de producción, todo está bien. pero cuando el segundo dev empuja, ¿qué sucederá?

Preguntas: ¿Git cambia los deltas EN los files afectados para que ningún desarrollador tenga que revisarlos para encontrar y corregir los cambios manualmente? ¿O Git solo rastrea las versiones y sobrescribe el primer impulso con el segundo? O ocurre una cosa incluso diferente? Gracias 🙂

Estás comenzando con una idea errónea: Git no mantiene confirmaciones como deltas. 1 Cualquier confirmación dada almacena un tree de directorys completo con todos sus files completos. Dado un ID de compromiso SHA-1, para ver el contenido de un file cuyo nombre es, por ejemplo, top/mid/bottom.ext , usted:

  1. extraiga el tree para la confirmación (extrayendo la confirmación, que tiene la ID de SHA-1 de este tree) y encuentre su subtree denominado top . Esto incluye la identificación SHA-1 de ese subtree.
  2. extrae ese tree y encuentra el subtree llamado mid (que da otro SHA-1)
  3. extrae ese tree y encuentra el blob (file) llamado bottom.ext , que te da un último SHA-1
  4. extraer la burbuja usando su SHA-1. Este es el contenido completo del file.

Esto es diferente de muchos otros sistemas de control de versiones, que almacenan los cambios como deltas y deben rebuild el file "más nuevo" (la primera versión se almacena directamente, los diffs avanzan en el time) o cualquier file "anterior" (última versión almacenada directamente, diffs retroceder en el time).

Aparte de eso, cada commit tiene, junto con un tree SHA-1 ID, un set de "parent commits". Al hacer un cierre transitivo sobre los padres de cada compromiso, se genera un gráfico acíclico dirigido (o posiblemente varios DAG). Los bordes de este gráfico son lo que a la gente le gusta pensar como "twigs" (aunque git los computa dinámicamente, un "nombre de twig" simplemente label un nodo en el gráfico).

Dicho todo esto, cuando haces un git push , te contactas con el repository remoto y entérate de qué nombres de las sucursales corresponden a los identificadores de commit, y propones que algún nombre de twig particular se mueva a diferentes ID de confirmación (s) ) También envía los ID de SHA-1 "faltantes" y los datos necesarios para rebuild los treees, files, tags y / o compromisos para ellos. El repository remoto considera su request ("cambie el develop desde la ID de confirmación 1234567, para confirmar la identificación ba98765") y la acepta o rechaza, generalmente sobre la base de si esto agrega nuevas confirmaciones a la sucursal, sin eliminar las anteriores.

Si el desarrollador 1 presiona primero y agrega algunos nuevos commit (s) para develop branch, todo va bien hasta el momento. Luego, cuando el desarrollador 2 presiona, las confirmaciones que ella agregó para develop son nuevas, pero instruyen al repository remoto para que elimine la (s) confirmación (es) del desarrollador 1. Cuando todos comenzaron tenían algo como esto (clonado del server central):

 ...<--B<--C<--D<--E <-- develop 

donde B , C , D y E representan los nodos de compromiso (identificados por esas identidades SHA-1, que son demasiado dolorosas para que las usen los humanos, así que tenemos el nombre develop para hacer un seguimiento de la ID SHA-1 para E ).

Cuando el desarrollador 1 agrega una confirmación, se convierte en (en su propio repository):

 ...<--B<--C<--D<--E<--F <-- develop 

Si empuja esto al server central, entonces agregar F está bien, es un nuevo compromiso que está "río abajo". Entonces el server agrega F y los cambios se develop para tener esa ID.

Mientras tanto, el desarrollador 2 agrega un compromiso, pero ella obtiene esto:

 ...<--B<--C<--D<--E<--G <-- develop 

Su ID de SHA-1 no coincide (porque los ID de SHA-1 son globalmente únicos: son un hash criptográfico de la confirmación, incluidos todos sus treees y files). Cuando vaya a presionar esto, el server central verá que ella está proponiendo agregar G , pero para hacerlo, elimine F (Recuerde que la ID de confirmación contiene la ID de padre, por lo que G debe apuntar a E No se puede modificar: cambiar incluso un bit en cualquier parte de la confirmación o su contenido cambia la ID de SHA-1).

Con una inserción normal (no "forzada"), el server rechazará esto.

El desarrollador 2 debe entonces git fetch (o su equivalente) para recoger el compromiso F , dándole esto:

 ...<--B<--C<--D<--E<--G <-- develop \ `-F <-- origin/develop 

(el origin es el "remoto" que nombra al server central).

Ahora depende de ella descubrir cómo combinar F y G Las dos alternativas fáciles y automatizadas son:

  • rebase G en F
  • hacer un "commit de fusión" combinando G y F en M

Para volver a ubicar G en F , solo necesita ejecutar git rebase (asumiendo la configuration habitual de la twig de seguimiento). Esto diferirá G contra E (para get un delta-git no está almacenando deltas!), Luego intenta aplicar el delta a F Si la aplicación Delta automática funciona, obtiene una copy modificada de G -llamada G' :

 ...<--B<--C<--D<--E<--G \ F <-- origin/develop \ G' <-- develop 

El viejo G ya no tiene una label, por lo que se abandona y finalmente se recolecta basura. 2 El nuevo G' es un descendiente directo de F y ahora puede ser empujado.

Su otra opción, la combinación, crea un nuevo compromiso M , al hacer una combinación de tres vías estándar:

 ...-B--C--D--E--G--M <-- develop \ / F <-- origin/develop 

La nueva confirmación puede ser devuelta al server porque M tiene F como antecesor, por lo que conserva la confirmación F en el server.

La opción force-push (en lugar de rebasar o fusionar) sigue siendo una opción, pero generalmente no es buena, porque elimina el commit F de la cadena de commits en la twig cuyo consejo está labeldo como develop .

La cuestión de si volver a establecer la base o fusionar es una de preference. Las fusiones agregan nodos de confirmación adicionales y hacen que sea más difícil ver lo que sucedió, pero la razón principal por la que es más difícil es que representa lo que realmente sucedió. Rebasar hace que parezca mucho más simple: "parece" que el desarrollador 2 esperó a que el desarrollador 1 terminara su trabajo, y luego escribió el suyo basado en el suyo. Pero eso no es realmente lo que sucedió, y con frecuencia suficiente, los sellos de time de compromiso lo mostrarán.


1 Git hace la compression delta internamente, pero de una manera muy diferente. En teoría, git podría comprimir los contenidos de un file contra el post en una confirmación, por ejemplo (o viceversa). Esta compression delta mantiene pequeño el tamaño de lo que git llama un "file de package". Los treees se pueden comprimir contra otros treees, de modo que si agrega o remueve un único file en un directory grande, el subtree correspondiente puede tener aplicada la compression delta. Por su performance y conveniencia, los objects de git se mantienen "desdilucionados" como "objects sueltos" y "re-empacados" en packages nuevos automáticamente. Los objects sueltos se desinflan comprimiendo , y la compression también se usa en los packages.

2 La recolección de basura también hace el reempaquetado que se menciona arriba. La mayoría de las confirmaciones se conservan por un time (90 días por defecto) a través del mecanismo de "reflog" de git, que le permite (1) encontrar ID de confirmación de sucursal por date y (2) recuperar confirmaciones borradas por crash, hasta que caduque la input del reflog

Cuando el segundo desarrollador intente insert su file, git dirá que su copy del repository no está actualizada, por lo que lo obligará a extraer / recuperar el repository. Entonces, si git puede arreglar el file compartido automáticamente (por ejemplo, porque el primer desarrollador ha estado trabajando en las líneas 1-10 y el segundo en las líneas 50-100), entonces lo hará. De lo contrario, se notificará al segundo desarrollador que ocurrió un conflicto, y él tendrá que arreglar manualmente el file.

(Supongo que están trabajando en la misma twig. Si no lo están, entonces el problema de fusión ocurrirá cuando las twigs se fusionen, pero lo que sucederá es esencialmente lo mismo).

Git no sobrescribe nada, en este caso, Git le pedirá que se fusione manualmente.

Si los dos desarrolladores han trabajado en diferentes twigs, el desarrollador 2 debería ser capaz de realizar los cambios realizados por el desarrollador 1 en su propio repository, y la fusión es cambios.