Git: how to salir de esta, repositorios remotos
Tags: git, howto, introduccion, repositorioremoto
Published: 2021-09-26 09:26:55

## How to 
 * [Introducción](#introducci%C3%B3n)
 * [Trastear con repositorios remotos](#trastear-con-repositorios-remotos)
 * [Problemas al subir una rama](#problemas-al-subir-una-rama)
 * [Modificar algo en el repositorio remoto](#modificar-algo-en-el-repositorio-remoto)
 * [Git pull, ese comando problemático](#git-pull-ese-comando-problem%C3%A1tico)
 * [Renombrar una rama](#renombrar-una-rama)
 * [Mantener el  entorno limpio: cuando las ramas no dejan ver el bosque](#mantener-el-entorno-limpio-cuando-las-ramas-no-dejan-ver-el-bosque)
 * [Modificar un tag](#modificar-un-tag)


## Introducción

Un repositorio de código, dentro del contexto de los sistema de control de versiones, es un sistema para almacenar código y metadatos, como pueden ser el histórico de los archivos.

Git es un sistema de control de versiones distribuido, en el que cada repositorio guarda una copia completa del código y los metadatos, pudiendo funcionar de manera independiente; en contraposición están los sistemas centralizados, en los que existe un único repositorio de código y los clientes se comunican con él para obtener o subir código, pero en ningún caso tienen una copia de los metadatos.

Se denomina "[flujo de trabajo](https://es.wikipedia.org/wiki/Control_de_versiones#Flujos_de_trabajo)" al cómo colaboran entre sí los repositorios de un sistema de control de versiones distribuido. El más común de ellos es el "*flujo de trabajo centralizado*", en el que hay un repositorio central o de referencia, por ejemplo un repositorio en Bitbucket o GitHub; los desarrolladores se clonan ese repositorio en su local, obteniendo así un repositorio privado sobre el que pueden trabajar; hacen cambios en su repositorio local, como por ejemplo desarrollador una nueva feature, y envian esos cambios al repositorio central; y actualizan el repositorio privado con los cambios que otros desarrolladores han enviado al repositorio central. 

![flujo trabajo centralizado](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/45yc2sbnwowezbvidxyp.png)

El tener un *"sistema de control de versiones distribuido"* y acabar usando un *"flujo de trabajo centralizado"* puede sonar contradictorio o exagerado ¿Por qué complicarse? ¿Por qué no usar simplemente un sistema de control de versiones centralizado como SVN? 
En un sistema de control de versiones centralizado cualquier operación, desde hacer un nuevo commit a consultar el log, implica comunicaciones con el servidor donde está alojado el repositorio, lo cual es mucho más lento que consultando una copia local como en Git; además de la lentitud, una perdida de conexión con el servidor central implica no poder ejecutar ningún comando; y la gestión de conflictos es más complicada, ya que habrá que resolverlos sobre cambios que todavía no se han podido commitear.
Personalmente he trabajado con SVN y Git y me quedo sin ninguna duda con Git, pero como todo la elección dependerá de gustos y necesidades.

Adicionalmente, un sistema de control de versiones distribuido permite evolucionar el flujo de trabajo dependiendo de como evolucione o crezca el proyecto; por poner un ejemplo, en el kernel de Linux usan el *"[flujo de trabajo dictador y tenientes](https://git-scm.com/book/es/v2/Git-en-entornos-distribuidos-Flujos-de-trabajo-distribuidos#rwfdiag_c)"*.

Tras hacer `git clone <url repositorio>` en el repositorio recién clonado habrá dos conjuntos de ramas: [ramas locales](https://dev.to/narkha/git-how-to-salir-de-esta-ramas-4f74#introducci%C3%B3n) y ramas remotas, 

| En un GUI | En consola |
|---|---|
|![ramas remota en IDE](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0rah24p8u2ral10kvyoi.png)|![ramas remotas en consola](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tbgfm21tb4zcl8pymh9x.png)|

Las ramas remotas son referencias a ramas que existen en el repositorio remoto que se ha clonado, al que por defecto se le da el nombre de `origin`, y que se actualizan con `git fetch` si ha habido cambios en el repositorio remoto, o cuando este acepta los cambios que se le proponen al hacer `git push`.
Un ejemplo de rama remota sería `origin/master`, y haría referencia a la rama `master` en el repositorio remoto con nombre `origin`.
Una rama remota puede ser el "*upstream"* de una rama local, pudiéndose entender "*lo que hay rio arriba*" como que la rama local mira o sigue a la rama remota: cuando se hace `git pull` se intenta actualizar la rama local con lo que haya en su *upstream*; cuando se hace `git push` se intenta actualizar el *upstream* con los contenidos de la rama local. Y en ambos casos digo intentar porqué si la rama local ha divergido respecto del *upstream*, sera necesarias algunas acciones que se irán viendo a lo largo del post.

A la hora de visualizarlo, se puede hacer igual que con las ramas locales, solo que ahora habrá unas cuantas ramas más.
Con un GUI

![Arbol ramas en IDE](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8pndr8c2sg81gdoelidh.png)

O por consola

```bash
$ git log --graph --format='%C(auto)%h %s%n%C(auto)%d%n' --all
```

![Arbol ramas en consola](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4lfq57dlrhvmxyj572s2.png)

Las ramas de los repositorios remotos añaden un nuevo nivel de complejidad a la gestión de las ramas: hay ramas como `pippin-merry` que han adelantado a su *upstream* `origin/pippin-merry`; ramas como `aragorn-legolas-gimli` que se han quedado atrás respecto a su *upstream* `origin/pippin-merry`; y ramas como `master` que han divergido respecto a su *upstream* `origin/master`; también habrá ramas remotas que no serán seguidas por ninguna rama local o ramas locales que todavía no se han subido al repositorio remoto y que no tengan su equivalente en `origin`.

Y para terminar la introducción, algo muy importante: 

> ***
> 🚨 **No hay que editar nada que ya este en el repositorio remoto y que este siendo usado por otros desarrolladores, ya que se puede liar parda.**
> ***

Imagina que:

1. Mortadelo hace `git push` a `master` con un commit que tiene un bug.
2. Filemón se actualiza su rama con `master`, obteniendo así el commit con el bug.
3. Mortadelo se da cuenta de su error, y sin avisar a nadie: borra el commit con el bug de `master`; intenta hacer `git push` a `master`, pero no puede por no se que error extraño; así que hace `git push -f`.
4. Filemón integra su rama en `master`, intenta subirla, y o bien vuelve a añadir a `master` el commit que Mortadelo había borrado, o bien se encuentra con conflictos y tiene que perder un buen rato en resolverlos.

El tener que usar `git push -f` (o borrar la rama remota y volver a crearla con commits diferentes) es algo que debe hacer saltar todas las alarmas, y hacer pensar muy mucho si realmente es necesario o hay otras opciones.

Dicho esto, si la rama es tuya, y estas seguro de que nadie más la ha usado ni ha usado ninguno de sus commits, es licito modificar los commits que hagan falta y usar `git push -f`, porque a todos nos ha pasado que queramos cambiar algo que hemos subido por error.

`git push -f` es una herramienta más de Git, y si sigue hay después de tantos años por algo será, pero hay que usarla con mucho cuidado.

## Trastear con repositorios remotos

Personalmente, como más aprendo sobre las cosas es trasteando con ellas, y cómo más aprendí sobre los repositorios remotos de Git fue creándome un repositorio en local y clonándolo.
En un minuto es posibles tener un área de pruebas donde ver como afectan los comando `git push` y `git pull` al repositorio remoto, crear situaciones para probar las cosas, probar cosas antes de aplicarlas al repositorio remoto de verdad, etc.
También se puede crear un repositorio privado en GitHub o similares, pero me parece mucho más rápido y ágil probar en local.

El proceso sería más o menos así

```bash
# Creo una carpeta donde poner los repositorios para las pruebas.
~$ mkdir -p sandbox/remotes 
~$ cd sandbox/remotes/

# Creo una carpeta donde creare el repositorio remoto.
~/sandbox/remotes$ mkdir origin
~/sandbox/remotes$ cd origin/
# Inicio el repositorio.
~/sandbox/remotes/origin$ git init
		Inicializado repositorio Git vacío en ~/sandbox/remotes/origin/.git/

# Le añado algún commit.
~/sandbox/remotes/origin$ echo "hola git" > README.md
~/sandbox/remotes/origin$ git add .
~/sandbox/remotes/origin$ git commit -am "creado archivo index.html"
		[master (commit-raíz) 246ceaf] creado archivo README.md
		 1 file changed, 1 insertion(+)
		 create mode 100644 README.md
# Me voy a otra rama para evitar los problemas del tipo
# "remote: error: Por defecto, actualizar la rama actual 
# en un repositorio no vacío".
# https://stackoverflow.com/a/2933656/1587302
~/sandbox/remotes/origin$ git checkout -b no-editar

# Voy atras.
~/sandbox/remotes/origin$ cd ..

# Clono el repositorio origin en la carpeta local.
~/sandbox/remotes$ git clone origin local 
		Clonando en 'local'...
		hecho.

# Entro en él y veo que tengo un clone de "origin".
~/sandbox/remotes$ cd local/
~/sandbox/remotes$ git checkout master
		Rama 'master' configurada para hacer seguimiento a la rama remota 'master' de 'origin'.
		Cambiado a nueva rama 'master'
~/sandbox/remotes/local$ git status
		En la rama master
		Tu rama está actualizada con 'origin/master'.
	
		nada para hacer commit, el árbol de trabajo está limpio

~/sandbox/remotes/local$ cat README.md 
		hola git
```

## Problemas al subir una rama

*Hago push pero lo cambios no se suben; al intentar hacer push sale un error; hago push pero no pasa nada; etc*

Hay varios motivos por los que `git push` puede dar error:

- No se tienen permisos para escribir es un repositorio.
Por poner un ejemplo, es posible clonarse cualquier repositorio público de GitHub, pero al hacer `git push` se obtendrá un error como este `remote: Permission to 30-seconds/30-seconds-of-code.git denied to Pepito.`
- No se tienen permisos para escribir en una rama.
Es posible restringir los permisos de escritura a ramas sensibles como `master`, de tal forma que sólo ciertos usuarios puedan escribir sobre ellas o solo se pueda escribir sobre ellas al darle a *Merge* en las *"Pull request"* o *"Merge request".*
- Todavía no existe la rama remota.
- La rama local está detrás o no está al día con la rama remota
- La rama local ha divergido respecto a la rama remota y no es posible actualizarla.

Para los dos primeros casos habrá que consultar al administrador del repositorio.

En el tercer caso, `git push` da error porque se le pide actualizar algo que no existe, y la solución es tan simple como crearla siguiendo las instrucciones que da Git en el mensaje de error.
La siguientes veces que se ejecute `git push` ya no se obtendrá el error, porque ya sabrá que debe actualizar.
Aunque esto es algo que solo suelen experimentar los usuarios de consola, porque muchos muchos GUI por debajo controlan este caso y crean la rama remata si no existe.

```bash
# Intento hacer push de una rama que no existe y obtengo un error.
$ git push 
		fatal: La rama actual nueva-feature no tiene una rama upstream.
		Para realizar un push de la rama actual y configurar el remoto como upstream, use
		
			git push --set-upstream origin nueva-feature

# Así que la creo.
$ git push --set-upstream origin HEAD
		Total 0 (delta 0), reusado 0 (delta 0)
		To ~/sandbox/remotes/origin
		 * [new branch]      HEAD -> nueva-feature
		Rama 'nueva-feature' configurada para hacer seguimiento a la rama remota 'nueva-feature' de 'origin'.

$ git status
		En la rama nueva-feature
		Tu rama está actualizada con 'origin/nueva-feature'.
		
		nada para hacer commit, el árbol de trabajo está limpio
```

En el ejemplo se utiliza `git push --set-upstream origin HEAD` para crear la rama en lugar del comando sugerido por Git `git push --set-upstream origin nueva-feature`.
Es un pequeño atajo no tener que pensar en el nombre de la rama a la hora de crearla.

Respecto a los dos últimos casos, si `git push` da error, es importante hacer `git fetch` para actualizar las ramas remotas, porque puede pasar que todo parezca estar en orden al ejecutar `git status`, pero que no lo este porque otro desarrollador haya hecho cambios en el repositorio remoto desde la última vez se actualizo el repositorio local, y las referencias a las ramas remotas no estén actualizadas.

Si la rama local está detrás de la rama remota no hay cambios que subir, y aunque el mensaje puede ser un poco agresivo es normal que no pase nada.
Si se actualiza la rama seguirá sin pasar nada, pero el mensaje no será tan agresivo.

```bash
# Hago push y obtengo un mensaje un poco agresivo
$ git push 
		To ~/sandbox/remotes/origin
		 ! [rejected]        master -> master (non-fast-forward)
		error: falló el push de algunas referencias a '~/sandbox/remotes/origin'
		ayuda: Actualizaciones fueron rechazadas porque la punta de tu rama actual está
		ayuda: detrás de su contraparte remota. Integra los cambios remotos (es decir
		ayuda: 'git pull ...') antes de hacer push de nuevo.
		ayuda: Mira 'Note about fast-forwards' en 'git push --help' para más detalles.
	
# Compruebo el estado y veo que mi rama se ha quedado detras
$ git status
		En la rama master
		Tu rama está detrás de 'origin/master' por 1 commit, y puede ser avanzada rápido.
		  (usa "git pull" para actualizar tu rama local)
	
		nada para hacer commit, el árbol de trabajo está limpio

# Actualizo la rama
$ git pull
		Actualizando 246ceaf..47be068
		Fast-forward
		 index.html | 1 +
		 1 file changed, 1 insertion(+)

# Y el git push segirá sin hacer nada, aunque el mensaje 
# no es tan agresivo
$ git push 
		Everything up-to-date
```

Y si la rama remota y la rama local han evolucionado de manera distinta, es que han divergido.

```bash
# Compruebo el estado de la rama y veo que está por delante 
# de la rama remota, así que debería poder hacer push.
$ git status
		En la rama master
		Tu rama está adelantada a 'origin/master' por 1 commit.
		  (usa "git push" para publicar tus commits locales)
		
		nada para hacer commit, el árbol de trabajo está limpio

# Pero falla.
$ git push 
		To sandbox/remotes/origin
		 ! [rejected]        master -> master (fetch first)
		error: falló el push de algunas referencias a 'sandbox/remotes/origin'
		ayuda: Actualizaciones fueron rechazadas porque el remoto contiene trabajo que
		ayuda: no existe localmente. Esto es causado usualmente por otro repositorio 
		ayuda: realizando push a la misma ref. Quizás quiera integrar primero los cambios
		ayuda: remotos (ej. 'git pull ...') antes de volver a hacer push.
		ayuda: Vea 'Notes about fast-forwards0 en git push --help' para detalles.
	
# Actualizo las ramas remotas y veo que la rama remota 
# se ha actualizado.
$ git fetch 
		remote: Enumerando objetos: 5, listo.
		remote: Contando objetos: 100% (5/5), listo.
		remote: Total 3 (delta 0), reusado 0 (delta 0)
		Desempaquetando objetos: 100% (3/3), 277 bytes | 277.00 KiB/s, listo.
		Desde sandbox/remotes/origin
		   246ceaf..792a7bf  master     -> origin/master

# Intento obtener información más concreta y veo que 
# las ramas han divergido.
$ git status
		En la rama master
		Tu rama y 'origin/master' han divergido,
		y tienen 1 y 1 commits diferentes cada una respectivamente.
		  (usa "git pull" para fusionar la rama remota en la tuya)
		
		nada para hacer commit, el árbol de trabajo está limpio
```

![rama divergente](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8i9b5g4585yvdy0e5ouc.png)

En el ejemplo se puede ver que la rama local `master` y la rama remota `origin/master` partieron de commit `creado archivo index.html` y que en ambas se han hecho commits.

`git push` solo funciona cuando los commits que se suben salen del último commit de la rama remota, es decir, se produce un [fast forward](https://dev.to/narkha/git-how-to-salir-de-esta-ramas-4f74#git-merge-ese-comando-problem%C3%A1tico), en caso contrario falla y habrá que actualizar la rama local con el *upstream*.

La forma de salir del paso depende de [como se integren](https://dev.to/narkha/git-how-to-salir-de-esta-ramas-4f74#c%C3%B3mo-integrar-una-rama) las ramas en el proyecto:

- Si se usan rebases, el comando sería `git rebase <upstream>`, por ejemplo `git rebase origin/master` si por ejemplo mi el *upstream* es `origin/master`.
- Si se usan merges `git pull` .

Una vez integrada, y si no se da la casualidad de que justo se ha vuelto a actualizar la rama remota entre medias, se podrá hacer `git push`.

## Modificar algo en el repositorio remoto

*He visto después de hacer push que el mensaje de un commit tiene una errata; he mergeado la rama que no era y he hecho push; he subido algo por error; me acabo de dar cuenta de que he subido un bug; etc.*

Como se vio en la [introducción](#introducci%C3%B3n)

> ***
> 🚨 **No hay que editar nada que ya este en el repositorio remoto y que este siendo usado por otros desarrolladores, ya que se puede liar parda.**
> ***

Pero ¿quién no la ha cometido errores alguna vez?

Como también se dijo en la introducción, si la rama es tuya, y estas seguro de que nadie más la ha usado ni ha usado ninguno de sus commits, es licito modificar los commits que hagan falta y usar `git push -f`

```bash
$ git commit -am "Hañado titúlo"
		[mi-rama 13c9acf] Hañado titúlo
		 1 file changed, 1 insertion(+)

$ git push 
		Enumerando objetos: 5, listo.
		Contando objetos: 100% (5/5), listo.
		Compresión delta usando hasta 12 hilos
		Comprimiendo objetos: 100% (2/2), listo.
		Escribiendo objetos: 100% (3/3), 314 bytes | 314.00 KiB/s, listo.
		Total 3 (delta 0), reusado 0 (delta 0)
		To sandbox/remotes/origin
		   207ba5a..13c9acf  mi-rama -> mi-rama

# Me doy cuenta de que he metido algunos errores en el mensaje 
# y los corrijo.
$ git amend -m "Añado título"
		[mi-rama ec27a36] Añado título
		 Date: Sat May 8 21:05:27 2021 +0200
		 1 file changed, 1 insertion(+)

# Pero el push falla porque he modificado un commit que 
# ya estaba en el repositorio remoto.
$ git push 
		To sandbox/remotes/origin
		 ! [rejected]        mi-rama -> mi-rama (non-fast-forward)
		error: falló el push de algunas referencias a 'sandbox/remotes/origin'
		ayuda: Actualizaciones fueron rechazadas porque la punta de tu rama actual está
		ayuda: detrás de su contraparte remota. Integra los cambios remotos (es decir
		ayuda: 'git pull ...') antes de hacer push de nuevo.
		ayuda: Mira 'Note about fast-forwards' en 'git push --help' para más detalles.

# Compruebo el estado.
$ git status
		En la rama mi-rama
		Tu rama y 'origin/mi-rama' han divergido,
		y tienen 1 y 1 commits diferentes cada una respectivamente.
		  (usa "git pull" para fusionar la rama remota en la tuya)
		
		nada para hacer commit, el árbol de trabajo está limpio

# Estando muy muy seguro de que no va a afectar a otras personas.
$ git push -f
		Enumerando objetos: 5, listo.
		Contando objetos: 100% (5/5), listo.
		Compresión delta usando hasta 12 hilos
		Comprimiendo objetos: 100% (2/2), listo.
		Escribiendo objetos: 100% (3/3), 318 bytes | 318.00 KiB/s, listo.
		Total 3 (delta 0), reusado 0 (delta 0)
		To ~/sandbox/remotes/origin
		 + 13c9acf...ec27a36 mi-rama -> mi-rama (forced update)

$ git status
		En la rama mi-rama
		Tu rama está actualizada con 'origin/mi-rama'.
		
		nada para hacer commit, el árbol de trabajo está limpio
```

Si el `git push` se hizo a una rama compartida, las acciones a tomar dependen de la situación:

- Si los commits son para corregir una errata en un mensaje, lo mejor es dejarlo correr, porque arreglarlo no merece la pena.
- Si es algo que hay que deshacer, lo más correcto sería usar `git revert` para revertir un [commit individual](https://dev.to/narkha/git-how-to-salir-de-esta-commits-455b#revertir-commit) o un [merge commit](https://dev.to/narkha/git-how-to-salir-de-esta-ramas-4f74#deshacer-un-merge), probar bien los cambios, y después hacer `git push`.
- Si es algo como un bug, siempre se pueden hacer commits para corregirlo.

Aunque lo más correcto no siempre es lo más cómodo, para que mentir. 
Si se ha hecho `git push` a una rama compartida con otros desarrolladores y ninguno ha usado ninguno de los commits que se han subido (no han hecho `git pull` en la rama comprometida, ni `git pull origin <rama>`, ni `git cherry-pick` de ninguno de los commits) se puede:

1. Avisar al resto de desarrolladores de que se ha subido algo por error y que hasta nuevo aviso no hagan ni `git pull` en la rama, `git pull origin <rama>` ni `git cherry-pick` de los commits recién subidos.
2. Hacer lo cambios que se tengan que hacer y hacer `git push -f`.
3. Pedir al resto de desarrolladores que se actualicen las ramas remotas con `git fetch` y decir que ya se puede trabajar con normalidad.

¿Y si algún otro desarrollador ya ha hecho uso de alguno de los commits subidos?
Siempre es posible seguir los pasos de arriba y que el resto de desarrolladores rehagan parte del trabajo, pero igual es demasiado trastorno y no merece la pena.

¿Y si se ha modificado algún commit en local y se decide que lo mejor es dar marcha atrás porque no se puede hacer `git push -f`?

Se puede volver a lo que había antes de modificar el commit siguiendo los mismos pasos que para [recuperar una rama borrada por error](https://dev.to/narkha/git-how-to-salir-de-esta-ramas-4f74#recuperar-una-rama-borrada-por-error).

## Git pull, ese comando problemático

*"git pull" cada vez hace una cosa distinta; después de hacer "git pull" la aplicación ya no funciona.*

`git pull` sirve para actualizar una rama local con una rama del repositorio remoto. 
Por debajo primero hace `git fetch` , actualizando todas las ramas del repositorio remoto, y después a `git merge <rama-remota>` .
Ciertamente es muy cómodo, pero hay que tener todas las [consideraciones que al mergear una rama](https://dev.to/narkha/git-how-to-salir-de-esta-ramas-4f74#git-merge-ese-comando-problem%C3%A1tico) (pueden surgir conflictos, hay revisar que todo sigue funcionando después de un merge commit, etc) y alguna más.

La forma más verbosa de invocar a `git pull` sería `git pull origin <rama>`, que por debajo vendría a hacer:

- `git fetch origin` , que actualizaría todas las ramas remotas (las ramas `origin/<rama>` que se mencionaban en la introducción).
- `git merge origin/<rama>` para mergear la rama remota `origin/<rama>` en la rama local.

Es una forma muy útil para por ejemplo actualizar la rama local en la que se está desarrollando una nueva feature con la última versión de `master` en el repositorio remoto.

```bash
$ git status
		En la rama mi-feature
		nada para hacer commit, el árbol de trabajo está limpio

# Actualizo mi-feature con la rama master del repositorio remoto.
$ git pull origin master 

		# Internamente hace git fetch.
		remote: Enumerando objetos: 5, listo.
		remote: Contando objetos: 100% (5/5), listo.
		remote: Comprimiendo objetos: 100% (2/2), listo.
		remote: Total 3 (delta 0), reusado 0 (delta 0)
		Desempaquetando objetos: 100% (3/3), 291 bytes | 291.00 KiB/s, listo.
	
		# La rama master ha cambiado en el repositorio remoto
		# así que actualiza origin/master.
		Desde ~/sandbox/remotes/origin
		 * branch            master     -> FETCH_HEAD
		   792a7bf..1a16ce3  master     -> origin/master
	
		# Hace el merge.
		Merge made by the 'recursive' strategy.
		 index.html | 2 ++
		 1 file changed, 2 insertions(+)

# Reviso la el úlitmo commit para ver que ha sido un merge.
$ git log -1
		commit 73365ef70d893c3b3b9680e0676bda0c78c36505 (HEAD -> mi-feature)
		Merge: 504b167 1a16ce3
		
		    Merge branch 'master' of ~/sandbox/remotes/origin into mi-feature
```

Otra forma es `git pull` a secas, bastante útil para actualizar la rama local con el *upstream*.
Por debajo vendría a hacer:

- `git fetch <remoto>`, donde `<remoto>` sería el repositorio remoto del *upstream* (si el *upstream* fuese `origin/master` haría `git fetch origin`).
- `git merge <upstream>`, para mergear el *upstream* en la rama local (si el *upstream* fuese `origin/master` haría `git merge master`).
Si la rama no tuviese *upstream* indicaría como añadirlo.

```bash
# Hago git status y veo que no estoy siguiendo 
# a ninguna rama remota.
$ git status
		En la rama mi-feature
		nada para hacer commit, el árbol de trabajo está limpio

# git pull hace internamente el git fetch, y luego falla, 
# porque la rama actual no sigue a ninguna rama remota
$ git pull
		remote: Enumerando objetos: 5, listo.
		remote: Contando objetos: 100% (5/5), listo.
		remote: Comprimiendo objetos: 100% (2/2), listo.
		remote: Total 3 (delta 0), reusado 0 (delta 0)
		Desempaquetando objetos: 100% (3/3), 290 bytes | 290.00 KiB/s, listo.
		Desde ~/sandbox/remotes/origin
		   792a7bf..01d5c54  master     -> origin/master
		No hay información de rastreo para la rama actual.
		Por favor especifica a qué rama quieres fusionar.
		Ver git-pull(1) para detalles.
		
		    git pull <remoto> <rama>
		
		Si deseas configurar el rastreo de información para esta rama, puedes hacerlo con:
		
		    git branch --set-upstream-to=origin/<rama> mi-feature
		
# Cambio a otra rama que si sigue a una rama remota.
$ git checkout master 
		Cambiado a rama 'master'
		Tu rama está detrás de 'origin/master' por 1 commit, y puede ser avanzada rápido.
		  (usa "git pull" para actualizar tu rama local)

# Hago git pull.
# Internamente hace el git fecth, pero como no hay cambios
# en el repositorio remoto no imprime nada.
$ git pull
		Actualizando 792a7bf..01d5c54
		Fast-forward
		 index.html | 1 +
		 1 file changed, 1 insertion(+)

```

Todas las opciones que se pueden aplicar a `git merge` se pueden aplicar también a `git pull`.

Si por ejemplo se está en la rama `master` y se quiere actualizar con el repositorio remoto puede ser útil ejecutar `git pull --ff-only` ; haciéndolo así `git pull` fallaría si las ramas han divergido, evitando así que la rama `master` se diferente a la de otros desarrolladores.

Se puede ejecutar `git pull --no-ff --no-commit origin hotfix` para que si el merge resultase en un *merge commit* tener la posibilidad de revisar los cambios antes de completar el commit, por si hubiese alguna incompatibilidad entre las dos ramas. 

## Renombrar una rama

*Corregir un texto incorrecto, se copio la clave del issute tracker que no era, etc.*

Las acciones para renombrar ramas solo renombran ramas locales, no actúan sobre los *upstream*; como tal no existe la opción de renombrar una rama en un repositorio remoto, aunque si se puede borrar la rama con el nombre antiguo y crear una rama con el nombre nuevo. Adicionalmente habría que actualizar el *upstream* de la rama, porque no se actualiza solo.

```bash
# Hago un git status y veo que estoy en la rama dovolop 
# y que estoy siguiendo a la rama remota origin/dovolop.
$ git status
		En la rama dovolop
		Tu rama está actualizada con 'origin/dovolop'.
	
		nada para hacer commit, el árbol de trabajo está limpio
	
# Otra opción para ver esto es mirarlo es 
$ git branch -vv -a
		* dovolop                d63cc7b [origin/dovolop] crear hoja de estilos home.css
		  master                 d63cc7b [origin/master] crear hoja de estilos home.css
		  remotes/origin/dovolop d63cc7b crear hoja de estilos home.css
		  remotes/origin/master  d63cc7b crear hoja de estilos home.css

# El nombre debería ser develop, así que renombro la rama.
$ git branch -m develop

# Si verifico el estado veo que he renombrado la rama local, 
# pero no la remota, y que mi rama local todavía sigue 
# a origin/dovolop.
$ git branch -a -vv
		* develop                d63cc7b [origin/dovolop] crear hoja de estilos home.css
		  master                 d63cc7b [origin/master] crear hoja de estilos home.css
		  remotes/origin/dovolop d63cc7b crear hoja de estilos home.css
		  remotes/origin/master  d63cc7b crear hoja de estilos home.css

# "Actualizo" la rama remota borrando la vieja y creando la nueva.
$ git push origin :dovolop develop
		Total 0 (delta 0), reusado 0 (delta 0)
		To ~/sandbox/git
		 - [deleted]         dovolop
		 * [new branch]      develop -> develop

# Si consulto el estado veo que la rama remota se ha renombrado
# y que mi rama local sigue a una rama "desaparecida" o "gone".
$ git branch -a -vv
		* develop                d63cc7b [origin/dovolop: desaparecido] crear hoja de estilos home.css
		  master                 d63cc7b [origin/master] crear hoja de estilos home.css
		  remotes/origin/develop d63cc7b crear hoja de estilos home.css
		  remotes/origin/master  d63cc7b crear hoja de estilos home.css
	
# Así que tengo que actualizar el upstream para que siga 
# a la rama renombrada.
$ git push --set-upstream origin develop 
		Rama 'develop' configurada para hacer seguimiento a la rama remota 'develop' de 'origin'.
		Everything up-to-date

# Y ya está todo en orden.
$ git branch -a -vv
		* develop                d63cc7b [origin/develop] crear hoja de estilos home.css
		  master                 d63cc7b [origin/master] crear hoja de estilos home.css
		  remotes/origin/develop d63cc7b crear hoja de estilos home.css
		  remotes/origin/master  d63cc7b crear hoja de estilos home.css
```

> ***
> 🚨 **Los comandos anteriores pueden afectar a otros desarrolladores.**
> ***

Si el Filemón tenía una rama siguiendo a `dovolop` y Mortadelo la "renombra" en el repositorio remoto, pueden ocurrir varias cosas: que Filemón no sea capaz de actualizarse con los commits que haga Mortadelo, ya que ha perdido la referencia la rama remota; o que Filemón haga nuevos commits y al subirlos al repositorio remoto cree una nueva sin darse cuenta.

```bash
# Mortadelo comprueba el estado de las ramas.
$ git branch -vv -a
		* dovolop                d63cc7b [origin/dovolop] crear hoja de estilos home.css
		  master                 d63cc7b [origin/master] crear hoja de estilos home.css
		  remotes/origin/dovolop d63cc7b crear hoja de estilos home.css
		  remotes/origin/master  d63cc7b crear hoja de estilos home.css

# Hace un fetch y no le presta mucha antención, porque 
# no es extraño que se creen o borren ramas.
$ git fetch 
		Desde ~/sandbox/git
		 - [eliminado]       (nada)     -> origin/dovolop
		 * [nueva rama]      develop    -> origin/develop

# Si Filemón comprobase el estado de la rama vería algo raro, 
# pero supongamos que no lo hace.
$ git branch -vv -a
		* dovolop                d63cc7b [origin/dovolop: desaparecido] crear hoja de estilos home.css
		  feature                d63cc7b [origin/feature: desaparecido] crear hoja de estilos home.css
		  master                 d63cc7b [origin/master] crear hoja de estilos home.css
		  remotes/origin/develop d63cc7b crear hoja de estilos home.css
		  remotes/origin/master  d63cc7b crear hoja de estilos home.css

# Hace algunos cambios y los commitea.
$ git commit -am "algunos cambios"
		[dovolop 190e822] algunos cambios
		 1 file changed, 1 insertion(+)

# Sube el nuevo commit al repositorio remoto...
# y ve que se ha creado una nueva rama, aunque el esperaba 
# que se hubiese actualizado la rama dovolop
$ git push 
		Enumerando objetos: 5, listo.
		Contando objetos: 100% (5/5), listo.
		Compresión delta usando hasta 12 hilos
		Comprimiendo objetos: 100% (3/3), listo.
		Escribiendo objetos: 100% (3/3), 305 bytes | 305.00 KiB/s, listo.
		Total 3 (delta 1), reusado 0 (delta 0)
		To ~/sandbox/git
		 * [new branch]      dovolop -> dovolop

# Y sin darse cuenta ha recreado la rama que mi Mortadelo 
# con tanto esfuerzo renombro.
$ git branch -a -vv
		* dovolop                190e822 [origin/dovolop] algunos cambios
		  master                 d63cc7b [origin/master] crear hoja de estilos home.css
		  remotes/origin/develop d63cc7b crear hoja de estilos home.css
		  remotes/origin/dovolop 190e822 algunos cambios
		  remotes/origin/master  d63cc7b crear hoja de estilos home.css
```

## Mantener el entorno limpio: cuando las ramas no dejan ver el bosque

*"git branch" y tengo muchas ramas que ya no se usan; "git branch -a " muestra ramas del repositorio remoto que ya fueron borradas; "git status" dice que el upstream ha desaparecido o "is gone".*

En repositorios muy activos y con muchos desarrolladores es fácil que `git branch` y `git brach -a` devuelvan decenas de resultados, de los cuales la inmensa mayoría serán ramas que no se han usado en meses.
Git nunca borra nada a menos que se le diga explícitamente, lo cual esta bien porque nunca se pierde información, pero tiene la contra de que se puede acabar con información que ya no es relevante y que dificulta encontrar lo que se esta buscando, como cuando se tiene el escritorio tan lleno de archivos, carpetas y enlaces directos que lleva un rato encontrar algo que no uses todos los días.

### Ramas del repositorio remoto que ya no existen

Las más fáciles de limpiar son las ramas remotas que ya no existen en el repositorio remoto, es decir aquellas que cuando:

1. Pin subió la rama `feature/tiger` al repositorio remeto.
2. Pon hizo un `git fetch`, el cual creo la rama `origin/feature/tiger`.
Si ejecutase hiciese `git branch -a` mostraría, entre otras, `origin/feature/tiger`
3. Pin borro `feature/tiger`.
4. Pon hizo `git fetch`, que no cambio nada.
Si ejecutase hiciese `git branch -a` seguiría mostrando `origin/feature/tiger`.

Se le puede indicar a Git que borre esas ramas llamando a `git fetch` o a `git pull` con la opción `--prune`, para indicar que tiene que *podar* las ramas que ya no existen.
Si se usa un GUI, puede que ya haga el *prune* automáticamente, o puede que tenga una opción para configurarlo.

Es posible configurar Git para que por defecto haga *prune* , ahorrando así tener que usar la opción `--prune` en cada llamada, para ello, añadir lo siguiente al archivo `~/.gitconfig`

```bash
[fetch]
        prune = true
```

Un ejemplo paso a paso

```bash
### Pin publica la rama feature/tiger.

# Pon hace un fetch.
$ git fetch 
		remote: Enumerando objetos: 5, listo.
		remote: Contando objetos: 100% (5/5), listo.
		remote: Comprimiendo objetos: 100% (2/2), listo.
		remote: Total 3 (delta 0), reusado 0 (delta 0)
		Desempaquetando objetos: 100% (3/3), 294 bytes | 294.00 KiB/s, listo.
		Desde ~/sandbox/remotes/origin
		 * [nueva rama]      feature/tiger -> origin/feature/tiger

$ git branch -a
		* master
		  remotes/origin/HEAD -> origin/master
		  remotes/origin/feature/tiger
		  remotes/origin/master

### Pin borra del repositorio remoto feature/tiger.

# Pon hago un fecth y no pasa nada.
$ git fetch 

# La rama origin/feature/tiger sigue existiendo.
$ git branch -a
		* master
		  remotes/origin/HEAD -> origin/master
		  remotes/origin/feature/tiger
		  remotes/origin/master

# Si hiciese fetch con la opción --prune
# origin/feature/tiger se borraría.
$ git fetch --prune
		Desde ~/sandbox/remotes/origin
		 - [eliminado]       (nada)     -> origin/feature/tiger
	
$ git branch -a
		* master
		  remotes/origin/HEAD -> origin/master
		  remotes/origin/master
```

### Ramas locales que se han quedado sin rama remota

1. Pin subió la rama `feature/duck`
2. Pon hizo un `git fech` y `git checkout feature/duck`, lo cual creo la rama local `feature/duck` cuyo *upstream* es `origin/feature/duck`.
3. Pin mergeó `feature/duck` en master y la borro del repositorio remoto para no deja una rama que ya terminado su ciclo de vida.
4. Pon hizo `git fecth` con la opción de *autoprune*, con lo cual Git borro la rama `origin/feature/duck` , pero no `feature/duck`, no vaya a ser que por alguna razón se quiera conservar en local, o que la rama local hubiese divergido de la rama remota y se pierdan commits.
5. Pon ejecuta `git status` y ve el siguiente mensaje
*Tu rama está basada en 'origin/feature/duck', pero upstream ha desaparecido.*

La manera de limpiar estar ramas es [borrarlas a mano](https://dev.to/narkha/git-how-to-salir-de-esta-ramas-4f74#recuperar-una-rama-borrada-por-error): `git branch -d feature/duck`

Pero con el tiempo se vuelve una tarea repetitiva y pesada, sobre todo cuando hay varias ramas en ese estado.

Una forma de hacerlo menos pesado es definiendo un par de alias en `~/.gitconfig`

```rust
[alias]
	clear-pruned-branches = "!f() { git branch -vv | grep -E ': (desaparecido|gone)\\]' | grep -v '*' | awk '{print $1}' | xargs -r git branch -d; }; f"
  fetch-and-clear = !sh -c 'git fetch && git clear-pruned-branches'
```

`git clear-pruned-branches` borraría las ramas desaparecidas que no estén mergeadas en la rama actual (`git branch -d`). 
Si alguna rama no se puede borrar porque no está mergeada mostrará un mensaje tal que *`error: La rama 'feature/gorilla' no ha sido fusionada completamente.`*, y tal vez haya que prestarle un poco más de atención para no borrar algún commit que se olvido subir.

`git fetch-and-clear` serviría como atajo para evitar tener que escribir `git fecth && git clear-pruned-branches`.
Para mantener el sistema limpio se puede usar `git clear-pruned-branches` o usar `git fetch-and-clear` en lugar de `git fetch`.

Un ejemplo paso a paso

```bash
### Pin publica la rama feature/duck.

# Pon hace un fetch y va a la ramara a cotillear.
$ git fetch
		Desde ~/sandbox/remotes/origin
		 * [nueva rama]      duck       -> origin/duck
$ git checkout duck 
		Rama 'duck' configurada para hacer seguimiento a la rama remota 'duck' de 'origin'.
		Cambiado a nueva rama 'duck'
$ git status
		En la rama duck
		Tu rama está actualizada con 'origin/duck'.
		
		nada para hacer commit, el árbol de trabajo está limpio

### Pin borra del repositorio remoto feature/duck.

# Pon hace un fecth y como tiene el autoprune activo 
# borra la rama remota.
$ git fetch
		Desde ~/sandbox/remotes/origin
		 - [eliminado]       (nada)     -> origin/duck

# git status dice que el upstream ha desaparecido.
$ git status
		En la rama duck
		Tu rama está basada en 'origin/duck', pero upstream ha desaparecido.
		  (usa "git branch --unset-upstream" para arreglar)
		
		nada para hacer commit, el árbol de trabajo está limpio
	
# Para borrarla tendría que ir a otra rama y borrarla
$ git checkout master 
		Cambiado a rama 'master'
		Tu rama está actualizada con 'origin/master'.

$ git branch -d duck
		Eliminada la rama duck (era 01d5c54)..
```

### Ramas remotas que han sido mergeadas pero no borradas

1. Pin subió la rama `feature/dolphin`.
2. Mergeó la rama `feature/dolphine` en `master` pero no la borro.
3. La rama sigue ahí 3 meses después.

Muchos sistemas de control de versiones como GitHub o Bitbucket tienen una opción para borrar la rama al mergear si se usan *Pull Request* o *Merge Request*, pero no es raro que se olvide marcarla, o que el merge se haga en local y se olvide borrar la rama después.

Una solución es ir al repositorio remoto y borrar una por una, asegurándose de borrar solo las ramas mergeadas para no perder ningún commit
Pero de nuevo es una tarea manual y tediosa, que se puede simplificar con un comando de consola

```bash
# Ir a la rama de integración, master en ese caso
git checkout master \
	`# Actualizarla a la última versión` \
	&& git pull --ff-only \
	`# Listar todas ramas remotas mergeadas en master` \
	&& git branch -r --merged \
  `# Excluir del listado las ramas que no hay que borrar;` \
  `# con expresiones regulares se exluyen las que contienen el texto` \
  `# "master", "production" o "RC"` \
	| grep -vE 'master|production|RC' \
  `# Quitar del texto el nombre del repositorio remoto` \
  | sed 's/origin\///' \
  `# Borrar las ramas del repositorio remoto` \
  | xargs -r -n 1 git push --delete origin
```

No es un comando precisamente sencillo, y personalmente siempre me da un poco de respecto ejecutarlo.
Por ello, primero listo las ramas que se irían a borrar.

```bash
# Ir a la rama de integración, master en ese caso
git checkout master \
	`# Actualizarla a la última versión` \
	&& git pull --ff-only \
	`# Listar todas ramas remotas mergeadas en master` \
	&& git branch -r --merged \
  `# Excluir del listado las ramas que no hay que borrar` \
  `# con expresiones regulares se exluyen las que contienen el texto` \
  `# "master", "production" o "RC"` \
	| grep -vE 'master|production|RC' \
  `# Quitar del texto el nombre del repositorio remoto` \
  | sed 's/origin\///'
```

Las reviso, y si todo me cuadra ya lanzo el comando completo.

Y como medida de seguridad adicional, siempre se puede clonar el proyecto original para tener un backup.

```bash
git clone <repository-url> <project-name>-bck-"$(date)"
```

```bash
# Hago 'git branch' y veo que hay muchas ramas,
# muchas de las cuales creo que ya no se usan.
$ git branch 
		* contenido-home
		  master
		  rama-dos
		  rama-nueve
		  rama-seis
		  rama-siete
		  rama-tres
		  rama-uno
		  seccion-contacto

# Intento borrar alguna que ya se haya borrado del repositorio 
# remoto,pero nada.
# (fetch-and-clear es un alias descrito algo más arriba en 
# "Ramas locales que se han quedado sin rama remota")
$ git fetch-and-clear

$ git checkout master 
# Miro cuantas ramas hay mergeadas en master y hay unas cuantas...
$ git branch -r --merged
		origin/HEAD -> origin/master
	  origin/master
	  origin/production
	  origin/rama-cinco
	  origin/rama-cuatro
	  origin/rama-diez
	  origin/rama-dos
	  origin/rama-nueve
	  origin/rama-ocho
	  origin/rama-seis
	  origin/rama-siete
	  origin/rama-tres
	  origin/rama-uno
	  origin/seccion-contacto

# Asi que quiero borrarlas todas menos 'production'. 
# Como borrarlas una a una sería demasiado tedioso uso un script.
$ git branch -r --merged \
   `# Excluir del listado las ramas que no hay que borrar;` \
   `# con expresiones regulares se exluyen las que contienen el texto` \
   `# "master", "production" o "RC"` \
   | grep -vE 'master|production|RC' \
   `# Quitar del texto el nombre del repositorio remoto` \
   | sed 's/origin\///' \
   `# Borrar las ramas del repositorio remoto` \
   | xargs -r -n 1 git push --delete origin
				To ~/sandbox/remotes/origin
         - [deleted]         rama-cinco
        To ~/sandbox/remotes/origin
         - [deleted]         rama-cuatro
        To ~/sandbox/remotes/origin
         - [deleted]         rama-diez
        To ~/sandbox/remotes/origin
         - [deleted]         rama-dos
        To ~/sandbox/remotes/origin
         - [deleted]         rama-nueve
        To ~/sandbox/remotes/origin
         - [deleted]         rama-ocho
        To ~/sandbox/remotes/origin
         - [deleted]         rama-seis
        To ~/sandbox/remotes/origin
         - [deleted]         rama-siete
        To ~/sandbox/remotes/origin
         - [deleted]         rama-tres
        To ~/sandbox/remotes/origin
         - [deleted]         rama-uno
        To ~/sandbox/remotes/origin
         - [deleted]         seccion-contacto

# Listo todas las ramas y veo que se han borrado muchas 
# del repositorio remoto, pero todavía quedan muchas en local.	
$ git branch -a
		  contenido-home
		* master
		  rama-dos
		  rama-nueve
		  rama-seis
		  rama-siete
		  rama-tres
		  rama-uno
		  seccion-contacto
		  remotes/origin/HEAD -> origin/master
		  remotes/origin/contenido-home
		  remotes/origin/master
		  remotes/origin/production

# Puede ser que me falte hacer el prune y borrar las ramas locales
# que se han quedado sin rama remota.
$ git fetch-and-clear 
		Eliminada la rama rama-dos (era 211751a)..
		Eliminada la rama rama-nueve (era 211751a)..
		Eliminada la rama rama-seis (era 211751a)..
		Eliminada la rama rama-siete (era 211751a)..
		Eliminada la rama rama-tres (era 211751a)..
		Eliminada la rama rama-uno (era 211751a)..
		Eliminada la rama seccion-contacto (era 2656b99)..

# Reviso por última vez y veo que está más limpio.
$ git branch -a
		  contenido-home
		* master
		  remotes/origin/HEAD -> origin/master
		  remotes/origin/contenido-home
		  remotes/origin/master
		  remotes/origin/production
```

## Modificar un tag

*He creado un tag en el commit que no era; me he equivocado en el nombre del tag; he creado un tag del tipo que no era; etc*

Si no se ha subido todavía al repositorio remoto, basta con borrarlo y volver a crearlo.

> ***
> 🚨 **Si ya se ha subido al repositorio remoto... se podría reemplazar, pero eso tendría consecuencias para otros desarrolladores.**
> ***

**Los tags no están pensados para ser modificados**, así que si alguien modifica un tag en el repositorio remoto, ya sea haciendo `git push -f` o borrándolo y recreándolo, el resto de desarrolladores seguirían teniendo en sus locales la versión original del tag, ya que `git fetch` no modifica tags.

Y puede ser un problema si en algún momento hay que revertir alguna rama a ese tag o hay que sacar un fix para esa versión, ya que dependiendo de quien haga la operación los resultados serían distintos.

En la [documentación de Git se analiza este caso](https://git-scm.com/docs/git-tag#_on_re_tagging), y vienen a proponer dos soluciones.

La primera es crear un nuevo tag, añadiéndole por ejemplo un sufijo (si el tag original era `1.0.0` llamar al nuevo `1.0.0-the-good-one`).

La segunda opción es modificar el tag en el repositorio remoto y escribir al resto de desarrolladores explicando lo ocurrido y describiendo los pasos que hay que seguir para obtener el tag correcto (borrar el tag en local con `git tag --delete TAG`, hacer `git fetch` para obtener la nueva versión, y verificar que tienen el tag correcto con `git rev-parse TAG` o `git show TAG`) 

Aquí un ejemplo práctico.

```bash
# Moratadelo crea un nuevo tag y lo sube al repositorio remoto.
mortadelo@tia$ git commit -am "Implementado algoritmo secreto"
		[master 2da5ad7] Implementado algoritmo secreto
		 1 file changed, 1 insertion(+)

mortadelo@tia$ git push 
		Enumerando objetos: 5, listo.
		Contando objetos: 100% (5/5), listo.
		Compresión delta usando hasta 12 hilos
		Comprimiendo objetos: 100% (2/2), listo.
		Escribiendo objetos: 100% (3/3), 285 bytes | 285.00 KiB/s, listo.
		Total 3 (delta 0), reusado 0 (delta 0)
		To ~/sandbox/remotes/origin/
		   409db7e..2da5ad7  master -> master

mortadelo@tia$ git tag -a "1.0.0" -m "Implementado algoritmo secreto"

mortadelo@tia$ git push origin 1.0.0
		Enumerando objetos: 1, listo.
		Contando objetos: 100% (1/1), listo.
		Escribiendo objetos: 100% (1/1), 169 bytes | 169.00 KiB/s, listo.
		Total 1 (delta 0), reusado 0 (delta 0)
		To ~/sandbox/remotes/origin/
		 * [new tag]         1.0.0 -> 1.0.0

# Se lo comenta a Filemón, que hace git fectch para traerse todas
# las novedades
filemon@tia$ git fetch
		remote: Enumerando objetos: 6, listo.
		remote: Contando objetos: 100% (6/6), listo.
		remote: Comprimiendo objetos: 100% (3/3), listo.
		remote: Total 4 (delta 0), reusado 0 (delta 0)
		Desempaquetando objetos: 100% (4/4), 402 bytes | 402.00 KiB/s, listo.
		Desde ~/sandbox/remotes/origin
		   409db7e..2da5ad7  master     -> origin/master
		 * [nuevo tag]       1.0.0      -> 1.0.0

# Por algúna razón Mortadelo se da cuenta de que hay un error 
# en código y decide actualizarlo.
mortadelo@tia$ git commit \
                   -am "Implementación sin errores del algoritmo secreto"
		[master e985b90] Implementación sin errores del algoritmo secreto
		 1 file changed, 1 insertion(+)

mortadelo@tia$ git push 
		Enumerando objetos: 5, listo.
		Contando objetos: 100% (5/5), listo.
		Compresión delta usando hasta 12 hilos
		Comprimiendo objetos: 100% (2/2), listo.
		Escribiendo objetos: 100% (3/3), 311 bytes | 311.00 KiB/s, listo.
		Total 3 (delta 0), reusado 0 (delta 0)
		To ~/sandbox/remotes/origin/
		   2da5ad7..e985b90  master -> master

# Mortadelo no quiere que el primer tag del proyecto tenga 
# ese bug, así que intenta recrearlo, pero Git le da error.
mortadelo@tia$ git tag -a "1.0.0" -m "Implementado algoritmo secreto"
		fatal: el tag '1.0.0' ya existe
# Decidido a cubrir sus huellas opta por borrar el tag 
# y crear el bueno.
mortadelo@tia$ git tag --delete 1.0.0
		Etiqueta '1.0.0' eliminada (era b017cde)

mortadelo@tia$ git tag -a "1.0.0" -m "Implementado algoritmo secreto"

# Intenta pushearlo, pero el repositorio remoto lo rechaza
mortadelo@tia$ git push origin 1.0.0
		To ~/sandbox/remotes/origin/
		 ! [rejected]        1.0.0 -> 1.0.0 (already exists)
		error: falló el push de algunas referencias a '~/sandbox/remotes/origin/'
		ayuda: Actualizaciones fueron rechazadas porque el tag ya existe en el remoto.

# Resuelto quema todas las naves con un git push -f
mortadelo@tia$ git push --force origin 1.0.0
		Enumerando objetos: 1, listo.
		Contando objetos: 100% (1/1), listo.
		Escribiendo objetos: 100% (1/1), 171 bytes | 171.00 KiB/s, listo.
		Total 1 (delta 0), reusado 0 (delta 0)
		To ~/sandbox/remotes/origin/
		 + b017cde...7a1b39e 1.0.0 -> 1.0.0 (forced update)

# Revisa la información del tag para comprobar que es la esperada.
mortadelo@tia$ git show 1.0.0
		tag 1.0.0
		Tagger: mortadelo <mortadelo@tia.es>
		Date:   Sun Sep 19 17:19:08 2021 +0200
		
		Implementado algoritmo secreto
		
		commit e985b904626ff98b77b2478d15abd929d4b6237d (HEAD -> master, tag: 1.0.0, origin/master, origin/HEAD)
		Author: mortadelo <mortadelo@tia.es>
		Date:   Sun Sep 19 17:18:34 2021 +0200
		
		    Implementación sin errores del algoritmo secreto
		

# Filemón, hace git fetch, pero el tag no se actualiza.
filemon@tia$ git fetch
		remote: Enumerando objetos: 6, listo.
		remote: Contando objetos: 100% (6/6), listo.
		remote: Comprimiendo objetos: 100% (3/3), listo.
		remote: Total 4 (delta 0), reusado 0 (delta 0)
		Desempaquetando objetos: 100% (4/4), 430 bytes | 430.00 KiB/s, listo.
		Desde ~/sandbox/remotes/origin
		   2da5ad7..e985b90  master     -> origin/master

# Revisa su tag en local y es distinto al de Mortadelo, 
# ya que apunta a la versión con el bug.
filmemon@tia$ git show 1.0.0
		tag 1.0.0
		Tagger: mortadelo <mortadelo@tia.es>
		Date:   Sun Sep 19 17:14:32 2021 +0200
		
		Implementado algoritmo secreto
		
		commit 2da5ad761654cd67bbd8bdfe1a9ac9891f04970f (tag: 1.0.0)
		Author: mortadelo <mortadelo@tia.es>
		Date:   Sun Sep 19 17:11:17 2021 +0200
		
		    Implementado algoritmo secreto

# Para tener el mismo tag que Mortadelo tendría que 
# borrar el tag en local.
filmemon@tia$ git tag --delete 1.0.0
		Etiqueta '1.0.0' eliminada (era b017cde)
# Y hacer git fetch para obtener el nuevo tag
filmemon@tia$ git fetch
		Desde /home/claudio/sandbox/remotes/origin
		 * [nuevo tag]       1.0.0      -> 1.0.0

# Revisa la información y ya ve lo mismo que Mortadelo.
filmemon@tia$ git show 1.0.0
		tag 1.0.0
		Tagger: mortadelo <mortadelo@tia.es>
		Date:   Sun Sep 19 17:19:08 2021 +0200
		
		Implementado algoritmo secreto
		
		commit e985b904626ff98b77b2478d15abd929d4b6237d (HEAD -> master, tag: 1.0.0, origin/master, origin/HEAD)
		Author: mortadelo <mortadelo@tia.es>
		Date:   Sun Sep 19 17:18:34 2021 +0200
		
		    Implementación sin errores del algoritmo secreto

```

<br/>
Si has llegado hasta aquí, gracias 🙂
Este era el último post de la serie; espero que este post, así como los anteriores, te hayan resultado útiles :wave:

## Créditos
Cover: https://www.pexels.com/photo/photography-of-forest-during-daytime-1068508/
Generación de diagramas: https://excalidraw.com/