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:

  1. git rm hello.ts para que se olvide del archivo
  2. git 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:

  1. Al final todo lo que funciona debe terminar en la rama principal
  2. 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