Git (3/3): remote repositories, stash and tags

Git (3/3): remote repositories, stash and tags

In the previous parts we saw local version control and working with branches. In this final chapter we make the jump to **remote repositories**, create **aliases** to go faster, save changes without committing them with **stash**, and mark versions with **tags**. 🎸 💾 #GITandRoll

Disclaimer: This post has been translated to English using a machine translation model. Please, let me know if you find any mistakes.

📚 **This entry is part of the _Git Guide_ series**, divided into three chapters that are read in order:

> * Part 1: Local version control

* Part 2: Branches

* 👉 **Part 3: Remote repositories, stash and tags**

Working with remote repositorieslink image 24

When we develop software, we usually do not do it alone; we usually work as a team, so it does not make sense to have a local git repository, but rather to have a remote repository that we all work with and share progress on.

Note: The goal of this post is to explain git. For connecting to remote repositories, I’m going to explain it with GitHub because it is the most widely used git repository hosting service, but I’m not going to go into depth on GitHub

Clone a remote repository (git clone <repository>)link image 25

If we start working on a repository that has already been created, the first thing we need to do is clone it. To do that, we need to use git clone <repository>, where <repository> can be a URL or an SSH address. Since large language models and ChatGPT are very much in fashion right now, we’re going to clone an open-source repository created by the community, Open-Assistant; to do that, we’ll run 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.

This creates the Open-Assistant folder for us with all the repository code; we can go inside and see all the code.

	
< > 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

Deleted the folder

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

If it happens the other way around, if we first start developing locally and then want to synchronize it with a remote repository, we have to do the following

  • First, create an empty remote repository; in my case, I created the notebook_git repository on GitHub, which I will delete later
  • Obtain the repository URL or SSH address
  • Synchronize them using git remote add origin <URL>

The empty repository I created on GitHub looks like this

!notebook git repo

In my case I am going to use the SSH address, which is git@github.com:maximofn/notebook_git.git

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

They are already linked, but to make sure we can run 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)

Upload the changes from a local repository to a remote repository (git push)link image 27

As we said, they are linked, but if I go to my repository on GitHub, it still looks like this

notebook git repo

The local and remote repositories are linked, but now all changes from the local repository need to be sent to the remote one. To do this, you would use git push origin <local-branch>:<remote-branch>, that is, since our main branch is called main and the main branch on GitHub is called main, you would need to run git push origin main:main.

If you remember, Git by default used to call the main branch master, but GitHub by default calls the main branch main, so if each person names their branches locally in different ways, you have to specify which local branch pushes to which remote branch.

You can configure the default connection between branches in git by running git push --set-upstream origin main. This establishes a relationship between the local branch main and the remote branch main. Once this relationship is set up, it is only necessary to run git push to upload the changes made locally to the remote server.

So we establish the connection between branches

	
< > 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'.

Now we can simply do git push to upload the local changes to the remote repository

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

If we now go back to our GitHub repository, it looks like this

notebook git repo push

If we run an ls in our local repository, we can see that the files we have in the remote repository are also in the local repository; in other words, we have synchronized the local and remote repositories

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

The only ones not in the remote repository are api_keys.py, which is the one we added to the .gitignore file, that is, the one we told git not to track. And .git, which is where the configuration of our local repository is stored and which should not be pushed to the remote repository, because each person will have their own git configuration and therefore it does not need to be synchronized

Download changes from a remote repository to a local repository (git pull)link image 28

Now we’re going to do the reverse: we’re going to pull down the new changes that have been made in the remote repository. If we look at how the remote repository is set up, we can see that there is a button that says Add a README, so we click it to add one

notebook git repo push

When you click, an editor will open. We leave what GitHub has filled in and save the changes by clicking the Commit changes... button.

noteboot git repo readme

A window will appear asking us for a commit message; we leave the default one and click Commit changes

notebook git repo commit message

By doing that, the repository will look like this.

!notebook git repo pull

A new file called README.md has been created, but if we run ls in the local repository we will not find it

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

So we need to bring the changes from the remote repository into the local one; to do this, we have to run git pull origin <rama remota> to tell Git which remote branch we want to fetch data from, but just like before we can establish a relationship between the remote branch and the local branch in the following way git branch --set-upstream-to=origin/<rama local> <rama remota>, but since our local branch is called main and GitHub’s remote branch is also called main, we would have to change the above to git branch --set-upstream-to=origin/main main.

Once this is done, to download the new changes from the remote repository to the local one, you only need to run git pull

Let's set up the branch relationship with 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'.

Now we can bring the changes from the remote repository to the local repository with 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

As we can see, it says that README.md has been added, we verify it by running ls

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

We have the file locally

Synchronize remote and local brancheslink image 29

As we have seen, we have had to synchronize the remote and local branches in order to upload and download the data. However, if we first create the repository on GitHub and then clone it, this synchronization is no longer necessary.

Aliaslink image 30

Each time we have wanted to make a log we have used this command git log --graph --oneline --decorate, however remembering this command is quite complicated, in fact I do not remember it, every time I have wanted to use it I have had to look it up because I did not remember it, so it would be very good to have a way to abbreviate it.

For this, Git offers alias, so you can create aliases for the commands you want. To do this, you have to run git config --global alias.<alias name> "command"

Therefore, we are going to call git tree the command git log --graph --oneline --decorate, since it allows us to see the history, with the branching and merging of branches as if it were the growth of a tree, so we do git config --global alias.tree "log --graph --oneline --decorate"

**Important**: The word git should not be included in the command

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

If we now go to our repository and run git tree, we will see the history as we did before

	
< > 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

As we can see, our alias has been created

Existing Git command aliaseslink image 31

Command aliases can be created for commands that already exist in Git; in fact, it is a practice many people use, especially to shorten them, so we’re going to create one. We’re going to make an alias for the git status command and rename it to git st like this: git config --global alias.st "status"

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

Let's test it now

	
< > 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

We already have the git status command simplified to git st

We can create command aliases that are not for git, for example, because we think git needs that new command. It is done the same way, with the exception that the command must be preceded by !, that is, it would be git config --global alias.<alias name> "!command"

When we have seen the conflicts, we have seen that git told us where they were, but to resolve them we have to edit the code ourselves, so we can create a git alias so that we can open a file with the text editor we want. In my case I’m going to create an alias that will open the files with vscode; for that I have to run git config --global alias.code "!code"

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

We tested it

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

After doing this, README.md opened in VSCode for me

List with all the aliaseslink image 33

In case we do not remember the aliases we have created, we can view the global Git configuration, but since this can be a bit overwhelming because it gives us a lot of information, we can filter it so it shows only the aliases we have created. To do this, we use 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

We obtain the aliases that we have created

But even better, we can create an alias to get the aliases; to do this, we run git config --global alias.alias "config --get-regexp ^alias\."

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

If we now run 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.

We get the list with all our aliases

Stash store (git stash)link image 34

Suppose we are working on a branch, we have several modified files, we have not made a commit, and for whatever reason we have to switch to another branch. For example, we are on a branch developing a new feature, and we have to leave it halfway done because there is a critical bug on the main branch

One solution would be to make a commit to save the changes and come back later. But maybe we've left the code halfway done and don't want to make a commit. So for that, the stash was invented, which is like a storage place where you leave your code stored so you can recover it later.

It is a stack, which means that the last thing in is the first thing out

Let's see how to do it; first, we create a new branch that we'll call new_feature

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

We switched to her

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

Let's modify archivo2.py and archivo8.py

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

We run a git status to check that they have been modified

	
< > 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")

Let's add archivo8.py to the staged area

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

We run git status again

	
< > 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

As we can see, we have two modified files, one of which is also in the staged area. If we now had to switch branches, to avoid losing the changes we could make a commit, or save them in the stash store, so we are going to do the latter using 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

If we now run git status again, let's see what happens

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

The modified files no longer appear; it’s as if we had made a commit

**Important**: Newly created files, which have never been tracked by Git, will not go to the repository, so with new files it is necessary, at least, to first run a git add

If I now create a new file and try to move it to the repository, it will give me an 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)

As we can see, archivo9.py has not been saved in the repository, so it should have been added with git add.

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

Everything to stash (git stash -u -a)link image 35

As we have seen, only the files that Git is tracking are sent to the staging area, but if we have newly created files, or ignored files, they will not be sent, so to solve this we can use the flags -u or --include-untracked to send the new files that Git has not yet tracked, and the flag -a or --all to include everything, even ignored files

List of stashes (git stash list)link image 36

As we mentioned, the stash acts like a stack, so if we use this stash many times, in reality we will have a list of stashes, and to see the ones we have stored we can use git stash list

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

As we can see, we only have one, which indicates the branch (on new_feature), the latest commit (Create README.md), and an identifier (527e07a)

Stash with description (git stash push -m <description>))link image 37

As we have seen, the list gives us the branch and the last commit, but this information is only useful for knowing where we started modifying before saving to the history. It also returns us an identifier that doesn’t tell us much, so we can add an initial description to the stash with git stash push -m <description>

First, we run git status to see what we have uncommitted

	
< > 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)

We have archivo9.py, but remember that it has never been tracked by git, so to include it in a stash we need to use the -u flag or the -a flag, so we create a new stash with a description using the command 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

We pulled the list from the 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

The new one already appears much more clearly

Retrieve the latest stash (git stash pop)link image 38

As we have said, the stash is a stack with stores, so when it comes to retrieving them we will do it the same way as a stack, always retrieving the last one.

git stash push pop

To recover the latest stash, we have to run git stash pop

First, we run a git status to make sure we don’t have any pending changes.

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

Now we recover the last 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)

If we run git status again, we will see that we once again have archivo9.py pending to be committed

	
< > 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

And if we check the stash list, we’ll see that we only have one left

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

Delete a stash (git stash drop <position>)link image 39

If we want to delete a stash, we have to run git stash drop <position>, where <position> is the position the stash occupies in the list.

We obtain the list of stashes

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

In our case, we only have one and at position 0 (stash@{0}), so to delete it we would have to run git stash drop 0; however, I’m not going to do it because I’m going to remove it right afterward with another command

Delete all stashes (git stash clear)link image 40

If we want to empty the entire stash list, we have to run git stash clear

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

If we now request the stash list

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

We get nothing because we have removed everything

Let's leave everything as it was, let's do a git status to remember the changes we had pending

	
< > 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

We see that we are on the new_feature branch and that we have archivo9.py pending to be committed. Since we created it for the example, we delete it and return to the main branch

	
< > 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 41

When we are developing code, there come moments when we generate versions, for example v1.1, v1.2, etc. To have this more controlled, Git provides us with tags.

Create a new tag (git tag -a <tag_name> -m "<message>")link image 42

To create a tag, we have to run git tag -a <tag_name> -m "<message>"

For example, we are going to create a tag in the current version of the repository, for this I will do 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

Tag list (git tag)link image 43

To see the tags we have created, we can run git tag

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

Create a tag from an old commit (git tag -a <tag_name> -m "<message>" <hash>)link image 44

Let's do a git tree to see the history

	
< > 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

Although it doesn’t say so in the description, when we made commit 4bb9d75 was when we finished the version control part locally, so it would also be good to have a tag from that moment. To do that, what we need to do is create a tag by adding the hash from that moment

	
< > 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

If we now make a list of the tags, the new one appears

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

And if we look at the commit history

	
< > 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

At the point in the history where we created the tag, the tag we created now appears: * 4bb9d75 (tag: v_local, new_branch) Commit with file 7

Switching between tags (git reset --hard <tag> or git reset --soft <tag>)link image 45

Just as we can move between different commits in the history, we can also move between tags. This has the advantage that we can move to another point in history without having to know the hash; by knowing the name of the tag we assigned at that moment, we can move simply by doing git reset --hard <tag> or git reset --soft <tag>

First, let's do an ls to see the files we have

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

We also do a git tree to see where in the history we are.

	
< > 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

Now we move to the point where we have created the v_local tag using 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

If we now run ls again, we see that we don't have the same files

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

If we also look at the history, we see that we have changed from one point in history to another

	
< > 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

To go back to the last point in the history, since we have also created a tag, it will be enough to run 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

We look back at the history to check that we have returned to the last moment in the history

	
< > 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

Continue reading

Last posts -->

Have you seen these projects?

Gymnasia

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

Mobile personal training app with AI assistant, exercise library, workout tracking, diet and body measurements

Horeca chatbot

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

Chatbot conversational for cooks of hotels and restaurants. A cook, kitchen manager or room service of a hotel or restaurant can talk to the chatbot to get information about recipes and menus. But it also implements agents, with which it can edit or create new recipes or menus

View all projects -->
>_ Available for projects

Do you have an AI project?

Let's talk.

maximofn@gmail.com

Machine Learning and AI specialist. I develop solutions with generative AI, intelligent agents and custom models.

Do you want to watch any talk?

Last talks -->

Do you want to improve with these tips?

Last tips -->

Use this locally

Hugging Face spaces allow us to run models with very simple demos, but what if the demo breaks? Or if the user deletes it? That's why I've created docker containers with some interesting spaces, to be able to use them locally, whatever happens. In fact, if you click on any project view button, it may take you to a space that doesn't work.

Flow edit

Flow edit Flow edit

FLUX.1-RealismLora

FLUX.1-RealismLora FLUX.1-RealismLora
View all containers -->
>_ Available for projects

Do you have an AI project?

Let's talk.

maximofn@gmail.com

Machine Learning and AI specialist. I develop solutions with generative AI, intelligent agents and custom models.

Do you want to train your model with these datasets?

short-jokes-dataset

HuggingFace

Dataset with jokes in English

Use: Fine-tuning text generation models for humor

231K rows 2 columns 45 MB
View on HuggingFace →

opus100

HuggingFace

Dataset with translations from English to Spanish

Use: Training English-Spanish translation models

1M rows 2 columns 210 MB
View on HuggingFace →

netflix_titles

HuggingFace

Dataset with Netflix movies and series

Use: Netflix catalog analysis and recommendation systems

8.8K rows 12 columns 3.5 MB
View on HuggingFace →
View more datasets -->