Git es un sistema de control de versiones.
Introducción
Hoy en día todo el mundo espera que utilices un sistema de gestión de control de versiones, que suele ser git.
Por eso las IDEs ya no se encargan de recuperar archivos borrados, o versiones anteriores de un archivo porque dan por supuesto que estás utilizando git.
¿Pero tú utilizas git? Más bien no.
Instal.la git
:
> scoop install git
Primeros pasos
Configura git:
> git config --global init.defaultBranch main
Crea una carpeta test
para empezar a trabajar y entra dentro de ella:
> mkdir test
> cd test
Crea un archivo hello.ts
, escribe algo en él y borra el archivo:
> echo "console.log('Hello')" > hello.ts
> bun .\hello.ts
Hello
> rm .\hello.ts
Te puede parecer un ejercicio algo tonto, pero borrar lo que no toca suele ocurrir y no es nada divertido.
Esta vez vamos a versionar el directorio con git
:
> git init -b main
Initialized empty Git repository in C:/Users/david/test/.git/
Vuelve ha hacer las mismas operaciones que antes:
> echo "console.log('Hello!')" > hello.ts
> bun .\hello.ts
Hello
> rm .\hello.ts
Cómo puedes verificar hemos vuelto a perder el archivo porque Git sólo guarda aquellos archivos que le has dicho que guarde!
Volvemos a empezar, pero esta vez preguntamos a git
:
> git status
On branch main
No commits yet
Untracked files:
(use "git add <file>..." to include in what will be committed)
hello.ts
Puedes ver que git
sabe que el archivo hello.ts
está en la carpeta, y te avisa de que no le está controlando.
Si quieres que git
controle el archivo debes decir a git que lo haga con git add
:
> git status
On branch main
No commits yet
Changes to be committed:
(use "git rm --cached <file>..." to unstage)
new file: hello.ts
Perfecto, ahora borramos el archivo y listo:
> rm hello.ts
> ls
>
Pues el archivo se ha borrado
¿Y que ha hecho git
al respecto?
> git status
On branch main
No commits yet
Changes to be committed:
(use "git rm --cached <file>..." to unstage)
new file: hello.ts
Changes not staged for commit:
(use "git add/rm <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
deleted: hello.ts
Pues sabe que le habías dicho que controlara el archivo hello.ts
y que lo has borrado
Git te da dos opciones:
git rm hello.ts
para que se olvide del archivogit restore hello.ts
para restaurar el archivo
En este caso lo que queremos es recuperar el archivo:
> git restore hello.ts
Si has borrado el archivo ¿cómo es que git
lo puede recuperar? Pues porque cuando ejecutaste git add hello.ts
se guardó una copia en la carpeta .git
Si haces un ls -force
puedes ver la carpeta oculta que creaste antes con la orden git init
:
> ls -force
Directory: C:\Users\david\test
Mode LastWriteTime Length Name
---- ------------- ------ ----
d--h-- 8/3/2025 16:28 .git
-a---- 8/3/2025 16:28 46 hello.ts
Modifica el archivo hello.ts
:
> echo "console.log('I think therefore I am.')" >> .\hello.ts
> bun .\hello.ts
Hello!
I think therefore I am.
Mira que ocurre cuando ejecutas git status
:
> git status
On branch main
No commits yet
Changes to be committed:
(use "git rm --cached <file>..." to unstage)
new file: hello.ts
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: hello.ts
Git tiene una copia del archivo hello.ts
que añadiste cuando hiciste git add
, y también sabe que no se corresponde al archivo actual (alguien lo ha modificado).
Commit
Pero lo que hace más interesante a un sistema de control de versiones es que puedes ir guardando los cambios uno detrás de otro y volver atrás cuando quieras.
Si quieres guardar los cambios de forma permanente debes hacer un commit:
> git commit -m "Created hello.ts"
Author identity unknown
*** Please tell me who you are.
Run
git config --global user.email "[email protected]"
git config --global user.name "Your Name"
to set your account's default identity.
¡Y no te dejan hacer un commit!
Para realizar un commit debes configurar tu nombre y correo electrónico para que quede constancia de quién ha hecho qué. ¿Y a quién le interesa esto si yo ya se quién soy?
Pues porque git
crea lo que se conoce como un repositorio que se puede compartir de forma colaborativa con otra gente y debe quedar constancia de quien ha hecho qué.
Configura tu nombre y correo electrónico:
> git config --global user.email "[email protected]""
> git config --global user.name "David de Mingo"
Ayuda por el profesor. Para verificar que el alumno ha hecho lo que tenía que hacer ejecuta esta orden:
> git config --list | select-string user
user.email=[email protected]
user.name=David de Mingo
Y ya puedes hacer un commit
:
> git commit -m "Created hello.ts"
[main (root-commit) f6a40db] Created hello.ts
1 file changed, 0 insertions(+), 0 deletions(-)
create mode 100644 hello.ts
Si pides a git que te muestre el log puedes ver que David de Mingo ha añadido el archivo hello.ts
al repositorio.
> git log
commit f6a40db05b638df4d2508631178daee11e8c8f6e (HEAD -> main)
Author: David de Mingo <[email protected]>
Date: Sat Mar 8 16:37:50 2025 +0100
Created hello.ts
gitGraph commit id:"f6a40db..."
Supongo que todavía no sabes exactamente de que va todo esto y lo encuentras poco interesante
Pues vamos a hacer un commit:
> echo "console.log('United we stand, divided we fall.')" >> .\hello.ts
PS C:\Users\david\Workspace\test> git add .\hello.ts
PS C:\Users\david\Workspace\test> git commit -m "Add quote from Aesop"
[main a2347c6] Add quote from Aesop
1 file changed, 0 insertions(+), 0 deletions(-)
gitGraph commit id:"f6a40db..." commit id:"a2347c6..."
Y ahora otro commit:
> echo "console.log('Knowledge is power.')" >> .\hello.ts
> git add .\hello.ts
> git commit -m "Add quote from Sir Francis Bacon"
[main d57500e] Add quote from Sir Francis Bacon
1 file changed, 0 insertions(+), 0 deletions(-)
gitGraph commit id:"f6a40db..." commit id:"a2347c6..." commit id:"d57500e..."
Si ejecutas git log
puedes ver todas las modificaciones que se han guardado:
> git log
commit d57500e16b24c40ec5ce900364a840336ac7624a (HEAD -> main)
Author: David de Mingo
Date: Sat Mar 8 16:49:39 2025 +0100
Add quote from Sir Francis Bacon
commit a2347c6d6f0c70cf1befb3baddec0b75cf5178c2
Author: David de Mingo
Date: Sat Mar 8 16:46:40 2025 +0100
Add quote from Aesop
commit f6a40db05b638df4d2508631178daee11e8c8f6e
Author: David de Mingo
Date: Sat Mar 8 16:37:50 2025 +0100
Created hello.ts
Checkout
Cada modificación (commit) tiene un identificador único tal y como puedes ver en la salida de git log
.
Este identificador es un hash criptográfico (ver Criptografia)
¿Qué debo hacer para poder ver las diferentes versiones del archivo hola?
Pedir a git que restaure la versión que tú quieres con la orden checkout
:
> git checkout f6a4
...
HEAD is now at f6a40db Created hello.ts
> bun .\hello.ts
Hello
> git checkout a234
Previous HEAD position was f6a40db Created hello.ts
HEAD is now at a2347c6 Add quote from Aesop
> bun .\hello.ts
Hello
I think therefore I am.
United we stand, divided we fall.
Puedes ver que no hace falta que escribas todo el identificador, con los primeros dígitos es suficiente
Y para volver a la versión más actual puedes utilizar el identificador del commit o el de la rama (hablamos después):
> git checkout main
Previous HEAD position was a2347c6 Add quote from Aesop
Switched to branch 'main'
> bun .\hello.ts
Hello
I think therefore I am.
United we stand, divided we fall.
Knowledge is power.
Tag
Esto de los identificadores está muy bien por los ordenadores, pero las personas utilizamos nombres porque es más fácil para nosotros. ¿Te acuerdas de memoria de algún número de teléfono?
Git permite etiquetar commits con la etiqueta que nosotros queramos.
Por ejemplo, podemos etiquetar el commit actual con la etiqueta v0.0.1
> git tag -a v0.0.1 -m "We have added some quotes"
Y puedes etiquetar el commit "Created hello.ts" con la etiqueta v0.0.0
(esta vez tengo que indicar el id del commit):
> git tag -a v0.0.0 -m "Created hello" f6a4
Puedes ver que tenemos dos etiquetas:
> git tag
v0.0.0
v0.0.1
gitGraph commit id:"f6a40db..." tag: "v0.0.0" commit id:"a2347c6..." commit id:"d57500e..." tag: "v0.0.1"
Y ver la información de un "tag":
> git show v0.0.1
tag v0.0.1
Tagger: David de Mingo
Date: Sat Mar 8 17:05:48 2025 +0100
We have added some quotes
commit d57500e16b24c40ec5ce900364a840336ac7624a (HEAD, tag: v0.0.1, main)
Author: David de Mingo
Date: Sat Mar 8 16:49:39 2025 +0100
Add quote from Sir Francis Bacon
Y puedes cambiar de versión haciendo checkout del tag:
> git checkout v0.0.0
Previous HEAD position was d57500e Add quote from Sir Francis Bacon
HEAD is now at f6a40db Created hello.ts
Y volver al otro tag:
> git checkout v0.0.1
Previous HEAD position was f6a40db Created hello.ts
HEAD is now at d57500e Add quote from Sir Francis Bacon
También puedes borrar etiquetas;
> git tag -d v0.0.0
Deleted tag 'v0.0.0' (was 1a0f409)
> git tag
v0.0.1
Vuelve al main
!!:
> git checkout main
Observación. Más información en Basics - Tagging
Ignore
Quizás te sorprenda, pero git sólo lo utilizan los programadores
Y los programadores escribimos código que debe compilarse. ¿Y que significa compilar? Pues que se crean nuevos archivos y ejecutables, que necesitamos librerías, etc. y que naturalmente no se deben versionar !
¿Y cómo puede saber git que debe versionarse y que no debe versionarse? Pues no tiene ni idea, ¡es tu trabajo!
Por eso debes decir explícitamente qué archivos debe controlar con git add
.
Pero también debes saber que un buen programador siempre encuentra la forma de hacer menos para conseguir más.
Por eso se añadió el flag --all
en git add
.
Crea dos archivos:
> echo "console.log('Blue moon')" > moon.ts
> echo "console.log('Red sea')" > sea.ts
Añade los ficheros a git
:
> git add --all
PS C:\Users\david\Workspace\test> git status
Changes to be committed:
(use "git restore --staged ..." to unstage)
new file: moon.ts
new file: sea.ts
A continuación crea un proyecto con Bun:
> bun init
> git add --all
Puedes ver que todo el contenido de la carpeta node_modules
no se ha añadido:
> git status
Changes to be committed:
(use "git restore --staged ..." to unstage)
new file: .gitignore
new file: README.md
new file: bun.lockb
new file: index.ts
new file: moon.ts
new file: package.json
new file: sea.ts
new file: tsconfig.json
Esto es porque bun ha creado un fichero .gitignore
donde constan todas las carpetas y ficheros que git ha de ignorar:
gc .\.gitignore | select-string node_modules
node_modules/
Haz un commit con los nuevos ficheros:
> git commit -a -m "Created bun project"
[main 165359c] Created bun project
8 files changed, 229 insertions(+)
create mode 100644 .gitignore
create mode 100644 README.md
create mode 100644 bun.lockb
create mode 100644 index.ts
create mode 100644 moon.ts
create mode 100644 package.json
create mode 100644 sea.ts
Ramas
¡Y ahora empezamos una sesión de jardinería!
Cuando haces un proyecto de programación, cuando realizas un examen de programación, nunca te sale nada a la primera. Vas haciendo, te equivocas, vuelves atrás (debes deshacer cosas), intentas cosas nuevas, etc., y todo es un galimatías. La programación es así, y la mayoría de las labores de tu vida diaria son así.
Para poder trabajar en condiciones siempre debes avanzar desde una posición segura a otra posición segura. Y más importante, poder volver a una posición segura en la que todo funciona. Por eso git permite crear ramas de trabajo.
Branch
Crear una rama es muy fácil:
> git branch test
> git branch
* main
test
Ahora mismo las dos ramas (main
es una rama como cualquier otra) son iguales porque ambas están apuntando al mismo commit.
Lo que vamos a hacer ahora es cambiar a la rama test
y hacer un commit:
> git checkout test
Switched to branch 'test'
Puedes ver que cambiar de rama es hacer un checkout
.
> echo "console.log('Keep your friends close, but your enemies closer.')" > corleone.ts
> git add .\corleone.ts
> git commit -m "Michael Corleone was right."
[test 38c6f10] Michael Corleone was right.
1 file changed, 0 insertions(+), 0 deletions(-)
create mode 100644 corleone.ts
Si volvemos a la rama main
puedes ver que todo los commit nuevos de la rama test
no aparecen.
Por tanto el archivo corleone.ts
no existe:
> git checkout main
Switched to branch 'main'
> bun corleone.ts
error: Module not found "corleone.ts"
Si queremos borrar la rama test
, git nos advierte de que no es conveniente porque todos los nuevos commits de la rama test se perderán.
> git branch -d test
error: the branch 'test' is not fully merged
hint: If you are sure you want to delete it, run 'git branch -D test'
De todas formas, ¿por qué querría borrar la rama test? Pues porque un proyecto sólo tiene un objetivo:
- Al final todo lo que funciona debe terminar en la rama principal
- Y lo que no ha funcionado y es un disparate debe eliminarse.
Merge
Si lo que he hecho en la rama test está bien y funcional, puedo fusionar los cambios en la rama principal:
> git merge test
Updating 165359c..38c6f10
Fast-forward
corleone.ts | Bin 0 -> 134 bytes
1 file changed, 0 insertions(+), 0 deletions(-)
create mode 100644 corleone.ts
Ahora ya tienes el archivo corleone.ts
en la rama principal:
> bun .\corleone.ts
Keep your friends close, but your enemies closer.
Y puedes borrar la rama test
:
> git branch -d test
Deleted branch test (was 38c6f10).
De todos modos muchos alumnos no ven la necesidad de crear una rama por algo tan simple.
Pues vamos a realizar una actividad bien divertida (al menos por el profe 🙄).
Actividad
1. Crea un archivo main.ts
que haga algo y realiza un commit del nuevo archivo.
2. Descarga una librería con bun add
3. Crea una nueva rama dev
, cambia de rama, añade nuevas funciones a main.ts
y haz un commit.
4. Vuelve a la rama main
y haz un merge de la rama dev
.
5. Haz un checkout del primer commit y verifica que es la primera versión de main.ts
.
6. Vuelve al último commit y haz un tag v0.0.2
7. Modifica main.ts
y haz otro commit.
8. Haz uno git log
para ver todo el historial