Iagovar - Gestionar conflictos en Git, volver a commits anteriores y destruír datos

Gestionar conflictos en Git, volver a commits anteriores y destruír datos

¿Cómo tratar los conflictos en Git? ¿Y si tenemos que volver a un commit anterior? ¿Y si hay archivos confidenciales publicados y tenemos que eliminarlos?

¿Cuándo suceden los conflictos en Git?

Los conflictos en Git ocurren cuando se hacen cambios en el mismo archivo y las mismas líneas en diferentes ramas, y luego se intenta combinarlas mediante un merge o al hacer un pull o push, git no puede decidir cuál de las dos versiones es la correcta, y genera un conflicto que debe ser resuelto manualmente.

Algunos ejemplos de estas situaciones:

Siempre que suceda un conflicto, Git te lo indicará, y te dirá que lo resuelvas (editando los archivos en conflicto), además de proporcionate los pasos que puedes dar después de la edición para marcar el conflicto como solucionado.

Lo que NO puede hacer Git por tí es decidir qué cambios necesita el archivo. Eso te lo tienes que cocinar tú mismo.

Un ejemplo de un conflicto y cómo se soluciona

Conflicto al ejecutar un git pull

  1. Supongamos que tienes un repositorio local en el que estás trabajando en un archivo llamado archivo.txt, y haces un cambio en la línea 10.

  2. Mientras tanto, otra persona también está trabajando en el mismo archivo en su repositorio local y hace un cambio en la línea 10 también.

  3. Luego, la otra persona hace un git push para subir sus cambios al repositorio remoto.

  4. Ahora, intentas hacer un git pull en tu repositorio local para traer los cambios remotos más recientes, pero Git detecta un conflicto en la línea 10 del archivo archivo.txt.

  5. Para resolver el conflicto, debes abrir el archivo archivo.txt y buscar las líneas marcadas con los conflictos, que tendrán el siguiente formato:

     
     <<<<<<< HEAD
     Tu cambio local
     =======
     Cambio remoto
     >>>>>>> Nombre_de_la_rama_remota
     
     
  6. Decide cuál de los cambios quieres mantener o edita el archivo para combinar ambos cambios y guarda los cambios.

  7. Una vez resuelto el conflicto, debes hacer un git add archivo.txt para indicar que el conflicto ha sido resuelto.

  8. Luego debes hacer un git commit para crear un nuevo commit con los cambios resueltos.

  9. Finalmente, puedes hacer un git pushpara subir los cambios resueltos al repositorio remoto.

Conflicto al ejecutar un git push

  1. Supongamos que tienes un repositorio local en el que estás trabajando en un archivo llamado archivo.txt, y haces un cambio en la línea 10.

  2. Mientras tanto, otra persona también está trabajando en el mismo archivo en su repositorio local y hace un cambio en la línea 10 también.

  3. Luego, la otra persona hace un git push para subir sus cambios al repositorio remoto.

  4. Ahora, tú intentas hacer un git push en tu repositorio local para subir tus cambios al repositorio remoto, pero Git detecta un conflicto en la línea 10 del archivo archivo.txt debido a que los cambios que intentas subir ya han sido realizados por otra persona.

  5. Entonces, debes resolver el conflicto como se explicó en el ejemplo anterior: abrir el archivo, decidir cuál de los cambios quieres mantener o editar el archivo para combinar ambos cambios, y luego hacer un git addy un git commit para finalizar la resolución del conflicto.

  6. Finalmente, puedes hacer un git push para subir los cambios resueltos al repositorio remoto.

Conflictos desde la interfaz web de github y gitlab

Apunte: Github tiene su propia documentacion al respecto, y Gitlab también.

Github y Gitlab tienen unas herramientas llamadas pull request y merge request respectivamente, que permiten mejorar el flujo de trabajo cuando se trabaja en equipo.

Estas funcionalidades no están incluídas en git per sé, sino que alguien las ha desarrollado como una capa por encima de git.

Como hemos visto en los flujos de trabajo habituales en git, hay un punto donde integramos los cambios entre ramas, ya sea con merge o rebase.

Esto tiene sentido cuando eres el único desarrollador, o, quizá, cuando es un equipo muy pequeño fácil de coordinar, donde todo el mundo sabe lo que está haciendo.

Sin embargo, bien sea porque hay mucha gente, bien sea porque el equipo que integras quiere centrar su energía en el desarrollo y no en resolver conflictos, se puede designar a una persona para ejecutar esta función.

El flujo de trabajo (muy resumido) es el siguiente:

  1. Ejecutas un desarrollo en tu rama feature y comiteas los cambios.

  2. Haces un git push de tus rama al repositorio remoto.

  3. Aquí es donde está el meollo:

    • En un setup sin una interfaz web como Github, Gitlab o Gitea, alguien que controla el repositorio --bare es el encargado de revisar cómo está tu rama, resolver conflictos e integrar tu rama con la rama principal.

      Tendrías que avisarle por algún medio, o tendría que estar constantemente pendiente de los cambios que se van subiendo, lo cual no es muy práctico para esta persona, ni para el quipo en general.


    • Si existe alguna herramienta web sobre git, simplemente vas a la URL de tu repo remoto, y solicitas un pull request de tu rama, a la rama master. Esta persona recibirá notificación por algún medio, o simplemente verá en la interfaz web que tiene solicitudes de pull request pendientes.

      La propia interfaz le guiará en el proceso, indicándole si hay conflictos, dónde, etc. No es magia, pero como hay botones, colores, indicadores, es más intuitivo y fácil de manjear.

      Esto tampoco hará que Git resuelva ningún conflicto, sigue siendo un problema que debe resolver el encargado de manejar los pull request y los involucrados en las ramas afectadas

Comandos útiles para ayudar en la resolución de conflictos

Muchos comandos ya los habrás visto en otros posts sobre Git.


Ejemplo de caso de uso con git reset --hard:

Es importante mencionar que el uso de git reset --hard es peligroso ya que elimina la historia del repositorio desde el commit especificado, por lo que se debe tener precaución al utilizarlo. Si se quiere mantener un registro de los cambios realizados se recomienda utilizar git revert o git branchantes de utilizar git reset --hard.