Básicos Git (III): Revert

VisualStudio proporciona una opción de deshacer cambios que permite deshacer los cambios a los ficheros antes de crear el commit, esta opción de deshacer no realiza ninguna acción en Git.

Por ejemplo si realizamos cambios en uno o varios ficheros veremos que en la sección changes del plugin nos indica los ficheros que hemos cambiado y tendremos la opción de realizar undo changes.

Esta imagen tiene un atributo ALT vacío; su nombre de archivo es image.png
undo changes

Esto eliminará los cambios pendientes y dejará los archivos en su estado original sin crear un commit.

Revert pertenece a ese grupo de comandos pensado para deshacer cambios, en este caso revert lo que hace es crear un nuevo commit que contiene los ficheros originales previos al cambio, en este caso Git si se ve afectado.

El historial se mantiene por lo que no hay problema de usarlo con el equipo de desarrollo, si alguien del equipo está trabajando con la rama o la fusiona con una existente solo tendré que añadir el nuevo commit.

En el ejemplo hemos creado una rama develop y queremos deshacer el commit marcado en rojo (#3).

situación inicial

A continuación para deshacer un commit solo tendremos que ir al historial de la rama, seleccionar el commit que queremos deshacer y seleccionar la opción revert.

creando un revert

Esto provocará un nuevo commit con el nombre por defecto Revert «comentario del commit a deshacer».

comentario de un revert

Este revert se queda en el historial, por lo tanto se podrá ver tanto el commit original como el commit que lo deshizo, este nuevo commit lo que contiene son los contenidos originales que había en el commit #2 que fueron modificados por el commit #3.

A su vez este contenido de la forma habitual se puede fusionar con la rama master por lo que nos quedaría un árbol así.

merge con master de un revert.
Básicos Git (III): Revert

Básicos Git (II): Rebase

Existe otra forma de hacer una fusión de ramas y es con un comando rebase. Rebase lo que hace es insertar una rama dentro de otra en el mismo punto donde se creo de tal forma que parezca una sola rama.

Situación inicial

Para poder entenderlo bien vamos a manejar dos ramas, master y develop, creamos dos commits en develop y otros dos en master, el orden no es importante (Figura 1).

Situación inicial

En este punto master y develop divergen y tienen commits diferentes, vamos a aplicar un rebase para crear una sola rama con todos los cambios.

Aplicando rebase

Para tener una sola rama rama vamos a hacer un rebase, nos posicionamos en la rama master con los parámetros que se ven en la imagen y pulsamos rebase.

comando rebase usando el plugin de VisualStudio

Conflictos

Esto provocará varios conflictos, el rebase irá aplicando primero cada cambio de master a develop, los conflictos que se aplicarán a develop son:

situación final
  1. El primer commit de develop (que sería el existente) con el primer commit de master (Commit C1).
  2. El resultado del commit anterior con el segundo commit de master (Commit C2).
  3. Así sería sucesivamente si hubiera más commits.

Estos nuevos commits tendrán como comentarios los correspondientes a su commit de master.

Ahora para terminar el rebase solo queda aplicar estos cambios de la rama develop en la rama master provocando un último conflicto (Commit R), en este caso si que habrá que indicar un comentario.

Resultado

En la imagen anterior si nos fijamos en la rama master vemos que están todos los commits de develop insertados en la rama master (recuadro rojo) justo en el punto que se creo la rama develop, por lo que en master queda un arbol límpio con todos los commits.

Este sistema se suele utilizar más para actualizar ramas locales que para actualizar la rama master ya que se está alterando el historial de una rama (master en este caso).

Esto también provoca que se altere el cronograma por lo que podemos ver commits más recientes que aparecen más tarde en el historial, ya que prima el orden de creación más que la fecha de los mismos.

alteración del cronograma de git por rebase

Este sistema también puede crear un problema para el resto de desarrolladores que hayan creado otras ramas de master a posteriori lo que genera conflictos a veces complicados de resolver.

Generalmente no se recomienda hacer un rebase sobre ramas remotas por el peligro que conlleva para otros desarrolladores, sin embargo en ramas locales suele haber más disparidad de criterio.

Básicos Git (II): Rebase

Básicos Git (I): Cherry pick

El uso básico de Git es bastante conocido sin embargo hay algunos conceptos que se suelen nombrar y quizás a veces inducen al error, en este caso comentaré los más habituales que se suelen ver en el plugin de VS2017 para Git.

Para este ejemplo se ha creado una rama master y una rama develop que hereda de master. En el historial de Git del plugin de VisualStudio se vería de la siguiente forma.

historial de Git

Aquí podemos ver que nos está indicando con la marca roja master que master ha tenido commits hasta el commit id 9e3acdb5, con la marca roja develop vemos que esta rama está avanzada y tiene más commits que master.

Para este ejemplo solo hemos añadido un fichero en master y después en la rama develop hemos continuado haciendo modificaciones sobre este fichero.

Cuando fusionemos las ramas las dos estarán sincronizadas por lo que veremos algo como esto, las dos marcas rojas alineadas.

master y develop sincronizado

Cherry pick

El concepto de cherry pick es poder coger uno o varios commits de una rama y copiarlos a otra rama distinta, este concepto lo vamos a aplicar a la fusión (merge) de ramas.

La idea de un merge es fusionar dos ramas entre sí, la forma más habitual es fusionar en una rama destino (en este caso master con los commits en blanco) una rama origen (develop con los commits en azul) incluyendo en la rama destino tantos commits y en el mismo orden como commits tiene la rama origen. (Figura 1).

Existe otra opción que consiste en fusionar todos los cambios de una rama origen en una rama destino en un solo commit, es decir todos los commits de la rama origen se agrupan en uno solo y es este el que se inserta en la rama destino. (Figura 2).

merge vs cherry pick

El comando merge por defecto no hace un cherry pick, el plugin de VisualStudio permite hacerlo de dos formas diferentes, en ambos casos el comentario que se verá en la rama destino será el del último commit.

Cherry pick de una rama

En el caso de una rama entera habría que posicionarse en la rama origen de los commits y después hacer click en la opción cherry pick en la rama que va a recibir los commits.

En este ejemplo la rama develop va a recibir todos los commits de master.

cherry pick de una rama

Esta acción genera un conflicto al contrario que un merge normal, esto se debe a que cherry pick solo transfiere commits no es un merge en el sentido estricto, los errores de conflicto se solucionan de la forma habitual.

Cherry pick de commits individuales

Otra opción es seleccionar específicamente uno o varios commits y transferirlos a otra rama, en este caso nos situamos en la rama destino y abrimos el historial de la rama origen, seleccionamos los commits que nos interesen y hacemos click en cherry pick.

En este caso solo habrá conflictos si seleccionamos varios commits o elegimos uno que no sea inmediatamente posterior tanto en la rama origen como destino, es decir que si hicieramos cherry pick de cada commit en el mismo orden en la rama destino no deberíamos tener conflictos, realmente esta última forma es lo mismo que emular un merge normal.

cherry pick de commits individuales
Básicos Git (I): Cherry pick