Git: Guía para resolver conflictos de combinación

En esta guía, veremos cómo resolver un conflicto de fusión de Git al extraer, enviar o fusionar cambios entre sucursales locales y remotas, con ejemplos prácticos.

Los Conflictos de fusión surgen cuando múltiples agentes modifican la misma parte de un archivo y envían sus cambios a una rama remota. Cuando intenta fusionar, extraer o empujar hacia estas ramas, hay un conflicto y Git no está seguro de qué conjunto de cambios aceptar y cuáles rechazar, ya que no hay una medida objetiva de qué cambio es Correcto.

{.icon aria-hidden=“true”}

Los conflictos de combinación solo surgen cuando es imposible discernir por adelantado qué cambios mantener y, en este caso, debe intervenir y tomar una decisión.

Hay tres formas de lidiar con un conflicto de fusión: puede continuar con la fusión, actualizando su archivo local para que coincida con lo que ya existe en un repositorio remoto, puede abortar una fusión, lo que generalmente se hace si hay Es un conflicto importante que no se soluciona fácilmente o puede mantener los cambios locales del directorio de trabajo y forzarlos en el repositorio remoto.

En esta guía, veremos las tres formas en que puede resolver un conflicto de combinación con Git.

¿Cómo ocurren los conflictos de fusión? {#cómo suceden los conflictos de fusión}

Vamos a crear rápidamente un repositorio y un conflicto de combinación para que podamos observar qué cambios lo causaron y cómo se ven los archivos cuando lo resolvamos. Emularemos un entorno de trabajo remoto creando dos carpetas y dos repositorios Git dentro de ellas:

1
2
3
4
5
6
7
$ cd Jane
$ git init
$ git remote add origin https://github.com/DavidLandup0/solving-merge-conflicts.git
$ cd..
$ cd John
$ git init
$ git remote add origin https://github.com/DavidLandup0/solving-merge-conflicts.git

Jane y John están trabajando juntos en un proyecto y comparten el mismo archivo: README.md. John escribió el archivo, dejando accidentalmente un error tipográfico, y lo empujó al origen remoto. Jane captó esto, arregló el error tipográfico y empujó el archivo al origen remoto nuevamente.

Una vez que John quiso agregar una nueva línea al archivo y fusionar su rama con la rama principal - su README.md (que tiene el error tipográfico) estaba en conflicto con ``main\'s README.md `, que tenía el error tipográfico corregido.

git merge conflict

En la rama de funciones de John's, agregó un archivo README.md:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
$ git branch feature_john
$ git checkout feature_john
Switched to branch 'feature_john'
$ echo 'Welcome to our READMW.md!' >> README.md
$ git add README.md
$ git commit -m "Added README.md"
[feature_john c44d65f] Added README.md
 1 file changed, 1 insertion(+)
 create mode 100644 README.md

$ git push origin feature_john
git push origin feature_john
Enumerating objects: 4, done.
Counting objects: 100% (4/4), done.
...
To https://github.com/DavidLandup0/solving-merge-conflicts.git
 * [new branch]      feature_john -> feature_john
 
$ git checkout main
Switched to branch 'main'

$ git merge feature_john
Updating 48f09c2..c44d65f
Fast-forward
 README.md | 1 +
 1 file changed, 1 insertion(+)
 create mode 100644 README.md
 
$ git push origin main
Total 0 (delta 0), reused 0 (delta 0), pack-reused 0
To https://github.com/DavidLandup0/solving-merge-conflicts.git
   48f09c2..c44d65f  main -> main

John agregó un nuevo archivo a su rama y lo empujó a su rama remota y luego se fusionó con main; no hubo problemas, no había ningún archivo allí antes de eso.

Ahora, Jane quiere actualizarse con la rama principal extrayendo los cambios realizados allí, se da cuenta del error tipográfico, lo corrige y vuelve a la principal para evitar que otros extraigan la pieza errónea:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
$ cd Jane
$ git pull origin main
remote: Enumerating objects: 4, done.
remote: Counting objects: 100% (4/4), done.
...
Updating 48f09c2..c44d65f
Fast-forward
 README.md | 1 +
 1 file changed, 1 insertion(+)
 create mode 100644 README.md
 
$ git branch feature_jane
$ git checkout feature_jane

$ echo 'Welcome to our README.md!' > README.md

$ git add README.md

$ git commit -m "Fixed typo in README.md file"
[feature_jane 60f64fc] Fixed typo in README.md file
 1 file changed, 1 insertion(+), 1 deletion(-)

$ git push origin feature_jane
Enumerating objects: 5, done.
Counting objects: 100% (5/5), done.
Writing objects: 100% (3/3), 292 bytes | 292.00 KiB/s, done.
...
To https://github.com/DavidLandup0/solving-merge-conflicts.git
 * [new branch]      feature_jane -> feature_jane

$ git checkout main
Switched to branch 'main'

$ git merge feature_jane
Updating c44d65f..60f64fc
Fast-forward
 README.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

$ git push origin main
Total 0 (delta 0), reused 0 (delta 0), pack-reused 0
To https://github.com/DavidLandup0/solving-merge-conflicts.git
   c44d65f..60f64fc  main -> main

Ahora main está limpio, no hay errores tipográficos en README.md. Sin embargo, el archivo de John ahora no está sincronizado.

  • Si intenta extraer el principal del origen, se producirá un Conflicto de fusión.
  • Si intenta empujar un cambio con un cambio en conflicto en la rama remota, se producirá un Conflicto de fusión.
  • Si intenta ejecutar $ git merge en dos ramas que tienen cambios en conflicto, se producirá un Conflicto de fusión.

Digamos que John agregó una nueva línea al archivo, la extrajo de la rama “principal” y luego intentó fusionar su nueva adición en “principal” antes de presionar su:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
$ echo 'New line!' >> README.md
$ git add README.md

$ git commit -m "Added new line to README.md"
[feature_john ba27684] Added new line to README.md
 1 file changed, 1 insertion(+)

$ git checkout main
Switched to branch 'main'

$ git pull origin main
From https://github.com/DavidLandup0/solving-merge-conflicts
 * branch            main       -> FETCH_HEAD
Updating c44d65f..60f64fc
Fast-forward
 README.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

$ git merge feature_john
Auto-merging README.md
CONFLICT (content): Merge conflict in README.md
Automatic merge failed; fix conflicts and then commit the result.

Ahí está: Conflicto de fusión en README.md. Hay tres cosas que John puede hacer para resolver este conflicto, ya que ahora se encuentra en la fase FUSIÓN. Cuando Git encuentra un conflicto, no abandona la fusión: le permite intentar solucionar el problema en el acto o abandonarlo si lo desea.

Buscar fuente de conflicto de combinación

El primer paso que debe tomar es averiguar por qué hay un conflicto de fusión en primer lugar. Cuando esté en la fase MERGING, como estamos nosotros, Git anotará el archivo que está causando el conflicto. Si abrimos el archivo README.md en la máquina local de John, veremos:

1
2
3
4
5
6
<<<<<<< HEAD
Welcome to our README.md!
=======
'Welcome to our READMW.md!' 
New line!
>>>>>>> feature_john

<<<<<<< denota la causa del conflicto y la referencia actual le sigue (HEAD). Este es el cambio de main. Luego, tenemos la línea ======= (solo un separador) antes del conjunto de cambios de John.

Finalmente, >>>>>>> denota que ese es el final del conflicto, con el nombre de la rama que estamos tratando de fusionar en la parte superior de =======.

{.icon aria-hidden=“true”}

Nota: Si estuviéramos fusionando los cambios main en feature_john, el orden de los cambios sería el opuesto, ya que la referencia actual estaría en feature_john y los cambios en main serían * debajo* de la línea =======. Sin embargo, tenga en cuenta que esta no es una buena práctica, ya que la rama de funciones está destinada a contener cambios separados de la rama principal.

Resolver conflicto de fusión con git merge --abort

Una forma válida de resolver el conflicto es cancelarlo y detener la fase FUSIÓN. Por lo general, esto se hace cuando la solución no es corregir una sola línea, y cuando es necesario realizar cambios importantes. Por lo general, esto también requiere un plan con un miembro del equipo.

Si abortamos la combinación, las líneas de conflicto añadidas se eliminarán y tendremos el archivo README.md de John una vez más.

Mientras todavía estamos en la fase de fusión, cancelemos la fusión por completo:

1
$ git merge --abort

Esto simplemente aborta la combinación y su archivo vuelve a su estado anterior al conflicto:

1
2
'Welcome to our READMW.md!' 
New line!

Si está utilizando el Editor de línea de comandos de Git (u otros shells que admitan la función), también podrá ver en qué fase se encuentra:

1
2
3
4
5
6
7
8
9
(main)
$ git merge feature_john
Auto-merging README.md
CONFLICT (content): Merge conflict in README.md
Automatic merge failed; fix conflicts and then commit the result.
(main|MERGING)
$ git merge --abort
(main)
$

Ahora, con su archivo fuera de peligro, llame a su colega y discuta sus próximos pasos. Alternativamente, si acepta sus cambios, puede continuar con la fusión.

Resolver conflicto de fusión con git merge --continue

Puedes continuar la combinación con un conflicto, pero Git no sobrescribirá los archivos automáticamente. Si intenta fusionarse, encuentra un conflicto e intenta $ git merge --continue, se encontrará con otro error:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
$ git merge feature_john
Auto-merging README.md
CONFLICT (content): Merge conflict in README.md
Automatic merge failed; fix conflicts and then commit the result.

$ git merge --continue
error: Committing is not possible because you have unmerged files.
hint: Fix them up in the work tree, and then use 'git add/rm <file>'
hint: as appropriate to mark resolution and make a commit.
fatal: Exiting because of an unresolved conflict.
U       README.md

Si fuera posible continuar, Git ya habría continuado. Todavía estás en la fase MERGING, por lo que puedes cambiar tu README.md para que se ajuste a la versión de main, marcar la resolución agregando o eliminando el archivo nuevamente, y luego ejecuta el comando $ git merge --continue.

Arreglemos el archivo primero. En nuestro caso, dado que la línea "Bienvenido..." estaba causando un problema, pero "¡Nueva línea!" no, podemos dejar la nueva línea en - John' s nueva característica - y corregir el error tipográfico que está en conflicto:

1
2
Welcome to our README.md!
New line!

Ahora, agregamos el archivo una vez más y ejecutamos el comando $ git merge --continue:

1
2
3
4
5
Fix the file...
$ git add README.md

$ git merge --continue
[main fea8fbb] Merge branch 'feature_john'

Ha aceptado los cambios de main y ha adaptado su archivo local para reflejarlo y volver a agregarlo.

{.icon aria-hidden=“true”}

Nota: En las versiones más recientes de Git, cuando ejecutas el comando $ git merge --continue, confirmará esa fusión automáticamente, por lo que no tienes que hacerlo tú, sin embargo, tienes que añadir el archivo modificado de nuevo. Cuando ejecute el comando, se abrirá un editor de texto con el mensaje de confirmación predeterminado Fusionar rama 'nombre_sucursal'. Simplemente puede salir, guardar el mensaje, para confirmar el cambio y fusionar las ramas.

Resuelva el conflicto de combinación forzando cambios locales a remotos

En lugar de abortar o ceder a los cambios, si está seguro de que los cambios realizados en su directorio de trabajo son los que debe conservar, puede conservar los cambios locales en lugar de adaptarse a los remotos.

Cuando se produce un conflicto de combinación, puede $ git checkout el archivo de feature_john y luego agregarlo a la rama main.

{.icon aria-hidden=“true”}

Nota: Recuerde que $ git checkout actualiza los archivos en el árbol de trabajo para que coincidan con la versión en el índice.

Al actualizar, puede mantener los cambios realizados en una rama diferente y aplicarlos a esta rama. En la rama principal, en la que deseamos fusionar feature_john, actualicemos el archivo README.md para que contenga los cambios de la rama feature_john.

En el contexto de principal, estos cambios se denominan suyos, mientras que los cambios en principal se denominan nuestros. Si desea mantener los cambios de main, cambie el indicador --theirs con --ours:

1
2
3
4
5
6
$ git checkout --theirs README.md
Updated 1 path from the index
$ git add README.md

$ git commit -m "Accepting changes from feature_john"
[main 5541f29] Accepting changes from feature_john

Ahora, puede combinar el resto de los cambios que no están en conflicto de forma limpia, ya que solo hemos creado una combinación indirecta para el único archivo que causó un conflicto de esta manera.

Conclusión

En esta guía, hemos analizado cómo resolver conflictos de combinación en Git. Hemos explorado las tres formas en que puede toparse con este error común y cómo surgen.

Luego, hemos explorado tres soluciones al problema, con los dos repositorios locales ficticios y un repositorio remoto que hemos creado en los ejemplos. os.

Licensed under CC BY-NC-SA 4.0