Git (3/3): repositorios remotos, stash y tags

Git (3/3): repositorios remotos, stash y tags

En las partes anteriores vimos el control de versiones en local y el trabajo con ramas. En este último capítulo damos el salto a los **repositorios remotos**, creamos **alias** para ir más rápido, guardamos cambios sin comprometerlos con el **stash** y marcamos versiones con **tags**. 🎸 💾 #GITandRoll

📚 **Esta entrada es parte de la serie _Guía de Git_**, dividida en tres capítulos que se leen en orden:

> * Parte 1: Control de versiones en local

* Parte 2: Ramas

* 👉 **Parte 3: Repositorios remotos, stash y tags**

Trabajando con repositorios remotoslink image 1

Cuando desarrollamos software no lo solemos hacer solos, solemos trabajar en equipo, por lo que no tiene sentido tener un repositorio local de git, sino tener un repositorio remoto con el que todos trabajemos y compartamos los avances

Nota: El objetivo de este post es explicar git. Para la conexión con repositorios remotos voy a explicarlo con GitHub porque es el servicio de hosting de repositorios git más usado, pero no voy a entrar en profundidad en GitHub

Clonar un repositorio remoto (git clone <repositorio>)link image 2

Si empezamos a trabajar en un repositorio que ya ha sido creado, lo primero que tenemos que hacer es clonarlo, para ello lo que tenemos que hacer es git clone <repositorio>, donde <repositorio> puede ser una URL o una dirección SSH. Como ahora mismo todo el tema de los grandes modelos de lenguaje y ChatGPT están muy de moda, vamos a clonar un repositorio opensource creado por la comunidad, Open-Assistant, para ello haremos git clone https://github.com/LAION-AI/Open-Assistant

	
< > Input
Python
!git clone https://github.com/LAION-AI/Open-Assistant.git
Copied
>_ Output
			
Clonando en 'Open-Assistant'...
remote: Enumerating objects: 29769, done.
remote: Counting objects: 100% (673/673), done.
remote: Compressing objects: 100% (310/310), done.
remote: Total 29769 (delta 398), reused 577 (delta 354), pack-reused 29096
Recibiendo objetos: 100% (29769/29769), 33.61 MiB | 29.29 MiB/s, listo.
Resolviendo deltas: 100% (19967/19967), listo.

Esto nos crea la carpeta Open-Assistant con todo el código del repositorio, podemos entrar adentro y ver todo el código

	
< > Input
Python
!cd Open-Assistant && ls
Copied
>_ Output
			
ansible deploy model safety
assets discord-bots notebooks scripts
backend docker oasst-data setup.cfg
CODEOWNERS docker-compose.yaml oasst-shared text-frontend
CONTRIBUTING.md docs pyproject.toml website
copilot inference README.md
data LICENSE redis.conf

Eliminó la carpeta

	
< > Input
Python
!rm -r Open-Assistant
Copied

Enlazar un repositorio local en uno remotolink image 3

Si ocurre al revés, si primero empezamos a desarrollar en local y luego lo queremos sincronizar con un repositorio remoto, tenemos que hacer lo siguiente

  • Primero crear un repositorio remoto vacío, en mi caso he creado el repositorio notebook_git en GitHub que más tarde borraré
  • Obtener la URL del repositorio o dirección SSH
  • Sincronizarlos mediante git remote add origin <URL>

El repositorio vacío que he creado en GitHub se ve así

notebook git repo

En mi caso voy a usar la dirección SSH que es git@github.com:maximofn/notebook_git.git

	
< > Input
Python
!cd notebook_git && git remote add origin git@github.com:maximofn/notebook_git.git
Copied

Ya están enlazados, pero para asegurarnos podemos hacer git remote -v

	
< > Input
Python
!cd notebook_git && git remote -v
Copied
>_ Output
			
origin git@github.com:maximofn/notebook_git.git (fetch)
origin git@github.com:maximofn/notebook_git.git (push)

Subir los cambios de un repositorio local a un repositorio remoto (git push)link image 4

Como hemos dicho, están enlazados, pero si voy a mi repositorio en GitHub se sigue viendo así

notebook git repo

Están enlazados el repositorio local y el remoto, pero ahora hay que mandar todos los cambios del repositorio local al remoto, para ello habría que usar git push origin <rama local>:<rama remota>, es decir, como nuestra rama principal se llama main y la rama principal en GitHub se llama main habría que hacer git push origin main:main.

Si te acuerdas git por defecto llamaba a la rama principal master, pero GitHub llama por defecto a la rama principal main por lo que si cada persona llama en su repositorio local a sus ramas de una manera hay que especificar qué rama en local escribe a qué rama en remoto

Se puede configurar la conexión por defecto entre ramas en git, para ello hay que hacer git push --set-upstream origin main. Esto establece relación entre la rama local main y la rama remota main. Una vez hecha esta relación ya solo es necesario hacer git push para subir los cambios que se hacen en local al servidor remoto.

De modo que establecemos la conexión entre ramas

	
< > Input
Python
!cd notebook_git && git push --set-upstream origin main
Copied
>_ Output
			
Enumerando objetos: 51, listo.
Contando objetos: 100% (51/51), listo.
Compresión delta usando hasta 12 hilos
Comprimiendo objetos: 100% (38/38), listo.
Escribiendo objetos: 100% (51/51), 4.21 KiB | 2.11 MiB/s, listo.
Total 51 (delta 18), reusado 0 (delta 0)
remote: Resolving deltas: 100% (18/18), done.
To github.com:maximofn/notebook_git.git
* [new branch] main -&gt; main
Rama 'main' configurada para hacer seguimiento a la rama remota 'main' de 'origin'.

Ahora ya podemos hacer solo git push para subir los cambios locales al repositorio remoto

	
< > Input
Python
!cd notebook_git && git push
Copied
>_ Output
			
Everything up-to-date

Si ahora volvemos a nuestro repositorio de GitHub, se ve así

notebook git repo push

Si hacemos un ls en nuestro repositorio local podremos ver que los archivos que tenemos en el repositorio remoto los tenemos en el repositorio local, es decir, hemos sincronizado el repositorio local y remoto

	
< > Input
Python
!cd notebook_git && ls -a
Copied
>_ Output
			
. .. api_keys.py archivo1.py archivo2.py archivo8.py .git .gitignore

Los únicos que no están en el repositorio remoto son api_keys.py, que es el que añadimos al archivo .gitignore, es decir, el que dijimos a git que no siguiera. Y .git, que es donde está la configuración de nuestro repositorio local y que no se tiene que subir al repositorio remoto, porque cada persona tendrá su propia configuración de git y por tanto no se tiene que sincronizar

Descargar los cambios de un repositorio remoto a un repositorio local (git pull)link image 5

Ahora vamos a hacer al revés, vamos a bajar los nuevos cambios que se hayan hecho en el repositorio remoto. Si nos fijamos en cómo está el repositorio remoto podremos ver que hay un botón que pone Add a README, por lo que le damos para añadirlo

notebook git repo push

Al hacer clic se nos abrirá un editor, dejamos lo que ha puesto GitHub y guardamos los cambios dándole al botón de Commit changes...

noteboot git repo readme

Nos saldrá una ventana en la que nos pedirá un mensaje de commit, dejamos el que viene por defecto y le damos a Commit changes

notebook git repo commit message

Al hacer eso el repositorio se nos quedará así

notebook git repo pull

Se ha creado un nuevo archivo llamado README.md, pero si hacemos ls en el repositorio local no lo encontraremos

	
< > Input
Python
!cd notebook_git && ls | grep README
Copied

Por lo que nos tenemos que traer los cambios del repositorio remoto al local, para ello hay que hacer git pull origin <rama remota> para decirle a git sobre qué rama remota vamos a traer los datos, pero al igual que antes podemos establecer una relación entre la rama remota y la rama local de la siguiente manera git branch --set-upstream-to=origin/<rama local> <rama remota>, pero como nuestra rama local se llama main y la rama remota de GitHub la llama main habría que cambiar lo anterior por git branch --set-upstream-to=origin/main main.

Una vez hecho esto para descargar los nuevos cambios del repositorio remoto al local solo habría que hacer git pull

Vamos a establecer la relación entre ramas con git branch --set-upstream-to=origin/main main

	
< > Input
Python
!cd notebook_git && git branch --set-upstream-to=origin/main main
Copied
>_ Output
			
Rama 'main' configurada para hacer seguimiento a la rama remota 'main' de 'origin'.

Ahora podemos traer los cambios del repositorio remoto al repositorio local con git pull

	
< > Input
Python
!cd notebook_git && git pull
Copied
>_ Output
			
remote: Enumerating objects: 4, done.
remote: Counting objects: 100% (4/4), done.
remote: Compressing objects: 100% (2/2), done.
remote: Total 3 (delta 1), reused 0 (delta 0), pack-reused 0
Desempaquetando objetos: 100% (3/3), 646 bytes | 646.00 KiB/s, listo.
Desde github.com:maximofn/notebook_git
679bb49..527e07a main -&gt; origin/main
Actualizando 679bb49..527e07a
Fast-forward
README.md | 1 +
1 file changed, 1 insertion(+)
create mode 100644 README.md

Como vemos, dice que se ha añadido README.md, lo comprobamos haciendo ls

	
< > Input
Python
!cd notebook_git && ls | grep README
Copied
>_ Output
			
README.md

Tenemos el archivo en local

Sincronizar las ramas remotas y localeslink image 6

Como hemos visto, hemos tenido que sincronizar las ramas remotas y locales para poder subir y bajar los datos. Sin embargo, si primero creamos el repositorio en GitHub y después lo clonamos, ya no es necesaria dicha sincronización.

Aliaslink image 7

Cada vez que hemos querido hacer un log hemos usado este comando git log --graph --oneline --decorate, sin embargo acordarse de este comando es bastante complicado, de hecho yo no me acuerdo de él, cada vez que lo he querido usar lo he tenido que buscar porque no lo recordaba, por lo que estaría muy bien tener una manera de abreviarlo.

Para esto git ofrece los alias, de manera que puedes crearte alias de los comandos que tú quieras, para ello tienes que hacer git config --global alias.<nombre del alias> "comando"

Por tanto vamos a llamar git tree al comando git log --graph --oneline --decorate, ya que nos permite ver el historial, con la bifurcación y fusión de ramas como si fuese el crecimiento de un árbol, por lo que hacemos git config --global alias.tree "log --graph --oneline --decorate"

**Importante**: No hay que poner la palabra git dentro del comando

	
< > Input
Python
!git config --global alias.tree "log --graph --oneline --decorate"
Copied

Si ahora nos vamos a nuestro repositorio y hacemos git tree veremos el historial como lo hacíamos antes

	
< > Input
Python
!cd notebook_git && git tree
Copied
>_ Output
			
* 527e07a (HEAD -&gt; main, origin/main) Create README.md
* 679bb49 archivo1.py con el merge resuelto
|\
| * 32851c3 archivo1.py en rama rama_con_conflicto
* | 53f909b archivo1.py en rama main
|/
* 52acb97 Merge squash de los commits de la rama branch_squash
* 274529c Merge branch 'branch_no_fast_forward' into main
|\
| * 8df3429 (branch_no_fast_forward) file2
| * e4e23c9 file1
* | 8bdf4d8 file3
|/
* 94149fc (branch_fast_forward) Eliminado hola.py
* 4484e70 Eliminado archivo4.py
* 564ccfb (new_branch2) Commit con el archivo 8
* 5168f78 Eliminado archivo7.py
* 4bb9d75 (new_branch) Commit con el archivo 7
* ea615a9 Eliminado archivo5.py
* e3153a5 Commit con los archivos 4 y 5
* 0b09cfa Añadido .gitignore
* 04ebd1f Commit con los archivos 1 y 2
* c4930d7 Tercer commit, hola.py
* 6e99e73 Segundo commit, hola.py
* 1c95e4f Primer commit, hola.py

Como vemos, se ha creado nuestro alias

Alias de comandos existentes de gitlink image 8

Se pueden crear alias de comandos que ya existen en git, de hecho, es una práctica que usa mucha gente, sobre todo para abreviar, por lo que vamos a hacer uno. Vamos a hacer un alias del comando git status y lo vamos a renombrar como git st de la siguiente manera git config --global alias.st "status"

	
< > Input
Python
!git config --global alias.st "status"
Copied

Lo probamos ahora

	
< > Input
Python
!cd notebook_git && git st
Copied
>_ Output
			
En la rama main
Tu rama está actualizada con 'origin/main'.
nada para hacer commit, el árbol de trabajo está limpio

Ya tenemos el comando git status simplificado a git st

Alias de comandos que no son de gitlink image 9

Podemos crearnos alias de comandos que no sean de git, por ejemplo, porque creamos que git necesita ese nuevo comando, se hace igual con la excepción de que el comando tiene que ir precedido de !, es decir, sería git config --global alias.<nombre del alias> "!comando"

Cuando hemos visto los conflictos, hemos visto que git nos decía dónde estaban, pero para solucionarlos tenemos que editar el código nosotros mismos, por lo que podemos crear un alias de git de manera que podamos abrir un archivo con el editor de textos que queramos, en mi caso voy a crear un alias que me abrirá los archivos con vscode para ello tengo que hacer git config --global alias.code "!code"

	
< > Input
Python
!git config --global alias.code "!code"
Copied

Lo probamos

	
< > Input
Python
!cd notebook_git && git code README.md
Copied

Tras hacer esto, se me ha abierto README.md en VSCode

Lista con todos los aliaslink image 10

En caso de no acordarnos de los alias que hemos creado podemos ver la configuración global de git, pero como esto puede ser un poco abrumador porque nos da mucha información, podemos filtrar para que nos muestre solo los alias que hemos creado, para ello usamos git config --get-regexp ^alias\.

	
< > Input
Python
!git config --get-regexp ^alias.
Copied
>_ Output
			
alias.tree log --graph --oneline --decorate
alias.st status
alias.code !code

Obtenemos los alias que hemos creado

Pero aún mejor, podemos crear un alias para obtener los alias, para ello hacemos git config --global alias.alias "config --get-regexp ^alias\."

	
< > Input
Python
!git config --global alias.alias "config --get-regexp ^alias."
Copied

Si ahora hacemos git alias

	
< > Input
Python
!git alias
Copied
>_ Output
			
alias.tree log --graph --oneline --decorate
alias.st status
alias.code !code
alias.alias config --get-regexp ^alias.

Obtenemos la lista con todos nuestros alias

Almacén stash (git stash)link image 11

Supongamos que estamos trabajando en una rama, tenemos varios archivos modificados, no hemos hecho commit, y por la razón que sea tenemos que pasar a otra rama. Por ejemplo, estamos en una rama desarrollando una nueva funcionalidad, y la tenemos que dejar a medias porque hay un bug crítico en la rama main

Una solución sería hacer un commit para guardar los cambios y volver más adelante. Pero a lo mejor hemos dejado el código a medias y no queremos hacer un commit. Así que para ello se inventó el stash, que es como un almacén, donde dejas almacenado tu código para luego poder volver a recuperarlo.

Es una pila, eso quiere decir que lo último que entra es lo primero en salir

Veamos cómo hacerlo, en primer lugar, creamos una nueva rama que vamos a llamar new_feature

	
< > Input
Python
!cd notebook_git && git branch new_feature
Copied

Cambiamos a ella

	
< > Input
Python
!cd notebook_git && git switch new_feature
Copied
>_ Output
			
Cambiado a rama 'new_feature'

Vamos a modificar archivo2.py y archivo8.py

	
< > Input
Python
!cd notebook_git && echo "print('new_feature')" &gt;&gt; archivo2.py && echo "print('new_feature')" &gt;&gt; archivo8.py
Copied

Hacemos un git status para comprobar que se hayan modificado

	
< > Input
Python
!cd notebook_git && git status
Copied
>_ Output
			
En la rama new_feature
Cambios no rastreados para el commit:
(usa "git add &lt;archivo&gt;..." para actualizar lo que será confirmado)
(usa "git restore &lt;archivo&gt;..." para descartar los cambios en el directorio de trabajo)
modificados: archivo2.py
modificados: archivo8.py
sin cambios agregados al commit (usa "git add" y/o "git commit -a")

Vamos a meter a archivo8.py al área de staged

	
< > Input
Python
!cd notebook_git && git add archivo8.py
Copied

Volvemos a hacer un git status

	
< > Input
Python
!cd notebook_git && git status
Copied
>_ Output
			
En la rama new_feature
Cambios a ser confirmados:
(usa "git restore --staged &lt;archivo&gt;..." para sacar del área de stage)
modificados: archivo8.py
Cambios no rastreados para el commit:
(usa "git add &lt;archivo&gt;..." para actualizar lo que será confirmado)
(usa "git restore &lt;archivo&gt;..." para descartar los cambios en el directorio de trabajo)
modificados: archivo2.py

Como vemos tenemos dos archivos modificados, de los cuales uno de ellos además está en el área de staged. Si ahora tuviésemos que cambiar de rama, para no perder los cambios podríamos hacer un commit, o guardarlos en el almacén stash, así que vamos a hacer esto último mediante git stash

	
< > Input
Python
!cd notebook_git && git stash
Copied
>_ Output
			
Directorio de trabajo y estado de índice WIP on new_feature: 527e07a Create README.md guardados

Si ahora volvemos a hacer git status, veamos qué pasa

	
< > Input
Python
!cd notebook_git && git status
Copied
>_ Output
			
En la rama new_feature
nada para hacer commit, el árbol de trabajo está limpio

Ya no aparecen los archivos con modificaciones, es como si hubiéramos hecho un commit

**Importante**: Los archivos nuevos creados, que nunca han sido seguidos por Git, no se irán al almacén, por lo que con los archivos nuevos es necesario, al menos, hacer primero un git add

Si yo ahora creo un archivo nuevo y lo intento llevar al almacén, me dará un error

	
< > Input
Python
!cd notebook_git && touch archivo9.py
Copied
	
< > Input
Python
!cd notebook_git && git status
Copied
>_ Output
			
En la rama new_feature
Archivos sin seguimiento:
(usa "git add &lt;archivo&gt;..." para incluirlo a lo que se será confirmado)
archivo9.py
no hay nada agregado al commit pero hay archivos sin seguimiento presentes (usa "git add" para hacerles seguimiento)
	
< > Input
Python
!cd notebook_git && git stash
Copied
>_ Output
			
No hay cambios locales para guardar
	
< > Input
Python
!cd notebook_git && git status
Copied
>_ Output
			
En la rama new_feature
Archivos sin seguimiento:
(usa "git add &lt;archivo&gt;..." para incluirlo a lo que se será confirmado)
archivo9.py
no hay nada agregado al commit pero hay archivos sin seguimiento presentes (usa "git add" para hacerles seguimiento)

Como vemos archivo9.py no lo ha guardado en el almacén, por lo que habría que haberlo añadido con git add.

	
< > Input
Python
!cd notebook_git && rm archivo9.py
Copied

Todo al almacén stash (git stash -u -a)link image 12

Como hemos visto solo se mandan al almacén los archivos a los que git les está haciendo seguimiento, pero si tenemos nuevos archivos creados, o archivos ignorados, no se mandarán, así que para solucionar esto podemos usar los flags -u o --include-untracked para que manden los nuevos archivos a los que git aún no ha hecho seguimiento, y el flag -a o --all para incluir todo, incluso los archivos ignorados

Lista de almacenes (git stash list)link image 13

Como hemos comentado, el almacén actúa como una pila, por lo que si hacemos uso de este almacén muchas veces, en realidad vamos a tener una lista de almacenes, y para ver los que tenemos almacenados podemos usar git stash list

	
< > Input
Python
!cd notebook_git && git stash list
Copied
>_ Output
			
stash@{0}: WIP on new_feature: 527e07a Create README.md

Como vemos, solo tenemos una, que nos indica la rama (on new_feature), el último commit (Create README.md) y un identificador (527e07a)

Almacén con descripción (git stash push -m <descripción>))link image 14

Como hemos visto, la lista nos devuelve la rama y el último commit, pero esta información solo nos vale para saber desde dónde hemos empezado a modificar antes de guardar en el historial. Además nos devuelve un identificador que no nos dice mucho, así que podemos añadir una primera descripción al stash con git stash push -m <descripción>

Primero hacemos un git status para ver qué tenemos sin commitear

	
< > Input
Python
!cd notebook_git && git status
Copied
>_ Output
			
En la rama new_feature
Archivos sin seguimiento:
(usa "git add &lt;archivo&gt;..." para incluirlo a lo que se será confirmado)
archivo9.py
no hay nada agregado al commit pero hay archivos sin seguimiento presentes (usa "git add" para hacerles seguimiento)

Tenemos archivo9.py, pero recordemos que nunca ha sido seguido por git, por lo que para incluirlo en un stash tenemos que usar el flag -u o el flag -a, de modo que creamos un nuevo stash con una descripción con el comando git stash push -u -m <descripción>

	
< > Input
Python
!cd notebook_git && git stash push -u -m "archivo9.py"
Copied
>_ Output
			
Directorio de trabajo y estado de índice On new_feature: archivo9.py guardados

Sacamos la lista del stash

	
< > Input
Python
!cd notebook_git && git stash list
Copied
>_ Output
			
stash@{0}: On new_feature: archivo9.py
stash@{1}: WIP on new_feature: 527e07a Create README.md

Ya aparece el nuevo de manera mucho más clara

Recuperar el último stash (git stash pop)link image 15

Como hemos dicho, el stash es una pila con almacenes, por lo que a la hora de recuperarlos lo haremos igual que una pila, recuperando siempre el último.

git stash push pop

Para recuperar el último stash tenemos que hacer git stash pop

Primero hacemos un git status para ver qué no tengamos ningún cambio pendiente

	
< > Input
Python
!cd notebook_git && git status
Copied
>_ Output
			
En la rama new_feature
nada para hacer commit, el árbol de trabajo está limpio

Ahora recuperamos el último stash

	
< > Input
Python
!cd notebook_git && git stash pop
Copied
>_ Output
			
En la rama new_feature
Cambios a ser confirmados:
(usa "git restore --staged &lt;archivo&gt;..." para sacar del área de stage)
nuevos archivos: archivo9.py
Descartado refs/stash@{0} (0246b0e922f654e7fc68cfeaf26e24fc511feb37)

Si volvemos a hacer git status, veremos que volvemos a tener archivo9.py pendiente de hacer un commit

	
< > Input
Python
!cd notebook_git && git status
Copied
>_ Output
			
En la rama new_feature
Cambios a ser confirmados:
(usa "git restore --staged &lt;archivo&gt;..." para sacar del área de stage)
nuevos archivos: archivo9.py

Y si comprobamos la lista de stash veremos que ya solo tenemos uno

	
< > Input
Python
!cd notebook_git && git stash list
Copied
>_ Output
			
stash@{0}: WIP on new_feature: 527e07a Create README.md

Eliminar un stash (git stash drop <posición>)link image 16

Si queremos eliminar un stash, tenemos que hacer git stash drop <posición>, donde <posición> es la posición que ocupa el stash en la lista.

Obtenemos la lista de los stashs

	
< > Input
Python
!cd notebook_git && git stash list
Copied
>_ Output
			
stash@{0}: WIP on new_feature: 527e07a Create README.md

En nuestro caso solo tenemos uno y en la posición 0 (stash@{0}), por lo que para eliminarlo tendríamos que hacer git stash drop 0, sin embargo no lo voy a hacer porque lo voy a eliminar ahora después con otro comando

Eliminar todos los stash (git stash clear)link image 17

Si queremos vaciar la lista entera de stash tenemos que hacer git stash clear

	
< > Input
Python
!cd notebook_git && git stash clear
Copied

Si ahora pedimos la lista de stash

	
< > Input
Python
!cd notebook_git && git stash list
Copied

No obtenemos nada porque hemos eliminado todo

Vamos a dejar todo como estaba, hacemos un git status para recordar los cambios que teníamos pendientes

	
< > Input
Python
!cd notebook_git && git status
Copied
>_ Output
			
En la rama new_feature
Cambios a ser confirmados:
(usa "git restore --staged &lt;archivo&gt;..." para sacar del área de stage)
nuevos archivos: archivo9.py

Vemos que estamos en la rama new_feature y que tenemos archivo9.py pendiente de hacer un commit. Como lo hemos creado para el ejemplo, lo eliminamos y volvemos a la rama principal

	
< > Input
Python
!cd notebook_git && git reset archivo9.py
Copied
	
< > Input
Python
!cd notebook_git && rm archivo9.py
Copied
	
< > Input
Python
!cd notebook_git && git switch main
Copied
>_ Output
			
Cambiado a rama 'main'
Tu rama está actualizada con 'origin/main'.

Tagslink image 18

Cuando estamos desarrollando código llegan momentos en los que generamos versiones, por ejemplo la v1.1, v1.2, etc. Para tener esto más controlado Git nos proporciona los tags.

Crear un tag nuevo (git tag -a <nombre_de_etiqueta> -m "<mensaje>")link image 19

Para crear un tag tenemos que hacer git tag -a <nombre_de_etiqueta> -m "<mensaje>"

Por ejemplo, vamos a crear un tag en la versión actual del repositorio, para ello haré git tag -a v_tag -m "Tag con el repositorio en la parte final, en la que explicamos los tags"

	
< > Input
Python
!cd notebook_git && git tag -a v_tag -m "Tag con el repositorio en la parte final, en la que explicamos los tags"
Copied

Lista de tags (git tag)link image 20

Para ver los tags que hemos creado podemos hacer git tag

	
< > Input
Python
!cd notebook_git && git tag
Copied
>_ Output
			
v_tag

Crear un tag de un commit antiguo (git tag -a <nombre_de_etiqueta> -m "<mensaje>" <hash>)link image 21

Hagamos un git tree para ver el histórico

	
< > Input
Python
!cd notebook_git && git tree
Copied
>_ Output
			
* 527e07a (HEAD -&gt; main, tag: v_tag, origin/main, new_feature) Create README.md
* 679bb49 archivo1.py con el merge resuelto
|\
| * 32851c3 archivo1.py en rama rama_con_conflicto
* | 53f909b archivo1.py en rama main
|/
* 52acb97 Merge squash de los commits de la rama branch_squash
* 274529c Merge branch 'branch_no_fast_forward' into main
|\
| * 8df3429 (branch_no_fast_forward) file2
| * e4e23c9 file1
* | 8bdf4d8 file3
|/
* 94149fc (branch_fast_forward) Eliminado hola.py
* 4484e70 Eliminado archivo4.py
* 564ccfb (new_branch2) Commit con el archivo 8
* 5168f78 Eliminado archivo7.py
* 4bb9d75 (new_branch) Commit con el archivo 7
* ea615a9 Eliminado archivo5.py
* e3153a5 Commit con los archivos 4 y 5
* 0b09cfa Añadido .gitignore
* 04ebd1f Commit con los archivos 1 y 2
* c4930d7 Tercer commit, hola.py
* 6e99e73 Segundo commit, hola.py
* 1c95e4f Primer commit, hola.py

Aunque no lo pone en la descripción, cuando hicimos el commit 4bb9d75 fue cuando terminamos la parte de control de versiones de manera local, por lo que también estaría bien tener un tag de ese momento. Para ello lo que tenemos que hacer es crear un tag añadiendo el hash de ese momento

	
< > Input
Python
!cd notebook_git && git tag -a v_local -m "Tag con el repositorio en la parte de control de versiones de manera local" 4bb9d75
Copied

Si ahora hacemos un listado de los tags, aparece el nuevo

	
< > Input
Python
!cd notebook_git && git tag
Copied
>_ Output
			
v_local
v_tag

Y si vemos el historial de commits

	
< > Input
Python
!cd notebook_git && git tree
Copied
>_ Output
			
* 527e07a (HEAD -&gt; main, tag: v_tag, origin/main, new_feature) Create README.md
* 679bb49 archivo1.py con el merge resuelto
|\
| * 32851c3 archivo1.py en rama rama_con_conflicto
* | 53f909b archivo1.py en rama main
|/
* 52acb97 Merge squash de los commits de la rama branch_squash
* 274529c Merge branch 'branch_no_fast_forward' into main
|\
| * 8df3429 (branch_no_fast_forward) file2
| * e4e23c9 file1
* | 8bdf4d8 file3
|/
* 94149fc (branch_fast_forward) Eliminado hola.py
* 4484e70 Eliminado archivo4.py
* 564ccfb (new_branch2) Commit con el archivo 8
* 5168f78 Eliminado archivo7.py
* 4bb9d75 (tag: v_local, new_branch) Commit con el archivo 7
* ea615a9 Eliminado archivo5.py
* e3153a5 Commit con los archivos 4 y 5
* 0b09cfa Añadido .gitignore
* 04ebd1f Commit con los archivos 1 y 2
* c4930d7 Tercer commit, hola.py
* 6e99e73 Segundo commit, hola.py
* 1c95e4f Primer commit, hola.py

En el punto del historial donde hemos creado el tag, ahora aparece el tag que hemos creado * 4bb9d75 (tag: v_local, new_branch) Commit con el archivo 7

Cambiar entre tags (git reset --hard <tag> o git reset --soft <tag>)link image 22

Al igual que nos podemos mover entre distintos commits del historial, también nos podemos mover entre tags. Esto tiene la ventaja de que podemos movernos a otro momento del historial sin tener que saberse el hash, con saber el nombre del tag que hemos puesto en ese momento podemos movernos simplemente haciendo git reset --hard <tag> o git reset --soft <tag>

Primero vamos a hacer un ls para ver los archivos que tenemos

	
< > Input
Python
!cd notebook_git && ls
Copied
>_ Output
			
api_keys.py archivo1.py archivo2.py archivo8.py README.md

Hacemos un git tree también para ver en qué momento del historial estamos

	
< > Input
Python
!cd notebook_git && git tree
Copied
>_ Output
			
* 527e07a (HEAD -&gt; main, tag: v_tag, origin/main, new_feature) Create README.md
* 679bb49 archivo1.py con el merge resuelto
|\
| * 32851c3 archivo1.py en rama rama_con_conflicto
* | 53f909b archivo1.py en rama main
|/
* 52acb97 Merge squash de los commits de la rama branch_squash
* 274529c Merge branch 'branch_no_fast_forward' into main
|\
| * 8df3429 (branch_no_fast_forward) file2
| * e4e23c9 file1
* | 8bdf4d8 file3
|/
* 94149fc (branch_fast_forward) Eliminado hola.py
* 4484e70 Eliminado archivo4.py
* 564ccfb (new_branch2) Commit con el archivo 8
* 5168f78 Eliminado archivo7.py
* 4bb9d75 (tag: v_local, new_branch) Commit con el archivo 7
* ea615a9 Eliminado archivo5.py
* e3153a5 Commit con los archivos 4 y 5
* 0b09cfa Añadido .gitignore
* 04ebd1f Commit con los archivos 1 y 2
* c4930d7 Tercer commit, hola.py
* 6e99e73 Segundo commit, hola.py
* 1c95e4f Primer commit, hola.py

Ahora cambiamos al punto en el que hemos creado el tag v_local mediante git reset --hard v_local

	
< > Input
Python
!cd notebook_git && git reset --hard v_local
Copied
>_ Output
			
HEAD está ahora en 4bb9d75 Commit con el archivo 7

Si ahora volvemos a hacer un ls vemos que no tenemos los mismos archivos

	
< > Input
Python
!cd notebook_git && ls
Copied
>_ Output
			
api_keys.py archivo1.py archivo2.py archivo4.py archivo7.py hola.py

Si además vemos el historial, vemos que hemos cambiado de momento del historial

	
< > Input
Python
!cd notebook_git && git tree
Copied
>_ Output
			
* 4bb9d75 (HEAD -&gt; main, tag: v_local, new_branch) Commit con el archivo 7
* ea615a9 Eliminado archivo5.py
* e3153a5 Commit con los archivos 4 y 5
* 0b09cfa Añadido .gitignore
* 04ebd1f Commit con los archivos 1 y 2
* c4930d7 Tercer commit, hola.py
* 6e99e73 Segundo commit, hola.py
* 1c95e4f Primer commit, hola.py

Para volver al último momento del historial, como hemos creado también un tag, valdrá con hacer git reset --hard v_tag

	
< > Input
Python
!cd notebook_git && git reset --hard v_tag
Copied
>_ Output
			
HEAD está ahora en 527e07a Create README.md

Volvemos a ver el historial para comprobar que hemos vuelto al último momento del historial

	
< > Input
Python
!cd notebook_git && git tree
Copied
>_ Output
			
* 527e07a (HEAD -&gt; main, tag: v_tag, origin/main, new_feature) Create README.md
* 679bb49 archivo1.py con el merge resuelto
|\
| * 32851c3 archivo1.py en rama rama_con_conflicto
* | 53f909b archivo1.py en rama main
|/
* 52acb97 Merge squash de los commits de la rama branch_squash
* 274529c Merge branch 'branch_no_fast_forward' into main
|\
| * 8df3429 (branch_no_fast_forward) file2
| * e4e23c9 file1
* | 8bdf4d8 file3
|/
* 94149fc (branch_fast_forward) Eliminado hola.py
* 4484e70 Eliminado archivo4.py
* 564ccfb (new_branch2) Commit con el archivo 8
* 5168f78 Eliminado archivo7.py
* 4bb9d75 (tag: v_local, new_branch) Commit con el archivo 7
* ea615a9 Eliminado archivo5.py
* e3153a5 Commit con los archivos 4 y 5
* 0b09cfa Añadido .gitignore
* 04ebd1f Commit con los archivos 1 y 2
* c4930d7 Tercer commit, hola.py
* 6e99e73 Segundo commit, hola.py
* 1c95e4f Primer commit, hola.py

Seguir leyendo

Últimos posts -->

¿Has visto estos proyectos?

Gymnasia

Gymnasia Gymnasia
React Native
Expo
TypeScript
FastAPI
Next.js
OpenAI
Anthropic

Aplicación móvil de entrenamiento personal con asistente de IA, biblioteca de ejercicios, seguimiento de rutinas, dieta y medidas corporales

Horeca chatbot

Horeca chatbot Horeca chatbot
Python
LangChain
PostgreSQL
PGVector
React
Kubernetes
Docker
GitHub Actions

Chatbot conversacional para cocineros de hoteles y restaurantes. Un cocinero, jefe de cocina o camaeror de un hotel o restaurante puede hablar con el chatbot para obtener información de recetas y menús. Pero además implementa agentes, con los cuales puede editar o crear nuevas recetas o menús

Naviground

Naviground Naviground
Ver todos los proyectos -->
>_ Disponible para proyectos

¿Tienes un proyecto con IA?

Hablemos.

maximofn@gmail.com

Especialista en Machine Learning e Inteligencia Artificial. Desarrollo soluciones con IA generativa, agentes inteligentes y modelos personalizados.

¿Quieres ver alguna charla?

Últimas charlas -->

¿Quieres mejorar con estos tips?

Últimos tips -->

Usa esto en local

Los espacios de Hugging Face nos permite ejecutar modelos con demos muy sencillas, pero ¿qué pasa si la demo se rompe? O si el usuario la elimina? Por ello he creado contenedores docker con algunos espacios interesantes, para poder usarlos de manera local, pase lo que pase. De hecho, es posible que si pinchas en alún botón de ver proyecto te lleve a un espacio que no funciona.

Flow edit

Flow edit Flow edit

Edita imágenes con este modelo de Flow. Basándose en SD3 o FLUX puedes editar cualquier imagen y generar nuevas

FLUX.1-RealismLora

FLUX.1-RealismLora FLUX.1-RealismLora
Ver todos los contenedores -->
>_ Disponible para proyectos

¿Tienes un proyecto con IA?

Hablemos.

maximofn@gmail.com

Especialista en Machine Learning e Inteligencia Artificial. Desarrollo soluciones con IA generativa, agentes inteligentes y modelos personalizados.

¿Quieres entrenar tu modelo con estos datasets?

short-jokes-dataset

HuggingFace

Dataset de chistes en inglés

Uso: Fine-tuning de modelos de generación de texto humorístico

231K filas 2 columnas 45 MB
Ver en HuggingFace →

opus100

HuggingFace

Dataset con traducciones de inglés a español

Uso: Entrenamiento de modelos de traducción inglés-español

1M filas 2 columnas 210 MB
Ver en HuggingFace →

netflix_titles

HuggingFace

Dataset con películas y series de Netflix

Uso: Análisis de catálogo de Netflix y sistemas de recomendación

8.8K filas 12 columnas 3.5 MB
Ver en HuggingFace →
Ver más datasets -->