Ejercicios

Esta guía de ejercicios se divide en dos secciones:

  1. Usuario único y
  2. Multi-usuario.

En “usuario único”, vamos a:

En “Multi-usuario”, vamos a:

1 Requisitos

Para los siguientes ejercicios, se requiere solamente tener instalado git.

Si se tiene instalado VS Code, se puede abrir en la carpeta donde se creará el repositorio de git. Con VS Code, vamos a poder:

  • visualizar los cambios que hacen los comandos de git en la solapa “Control de Versiones”,
  • correr los comandos de git en la terminal de VS Code.

2 Ayuda y documentación

Para ver la ayuda de git, correr git help.

Para ver la ayuda de un subcommando, correr git help [SUBCOMMAND].

3 Usuario único

TipNuevos comandos
  • git init
  • git add
  • git rm
  • git commit
  • git restore
  • git reset
  • git checkout
  • git branch
  • git merge
  • git rebase

3.1 Crear repositorio

Objetivo: crear un repositorio local y configurar la identidad.

  1. Crear un directorio para el proyecto y entrar en ese directorio.
  2. Inicializar Git en ese directorio.
  3. Configurar nombre de usuario y correo.
mkdir <NAME>  # MaKe DIRectory
cd <NAME>     # Change Directory
git init
git config user.name <NOMBRE>
git config user.email <EMAIL>

El nombre de usuario y mail no tienen porque ser reales. Simplemente se utilizan como parte de los mensajes de commit para identificar al autor.

3.2 Añadir un archivo

Objetivo: versionar un archivo nuevo con el primer commit.

  1. git status: no hay nada para commitear.
  2. Crear un archivo de texto file.txt con algún contenido.
  3. git status: hay un untracked file.
  4. git add file.txt: agrega el archivo al staging area.
  5. git status: hay cambios para commitear
  6. git commit: crear un commit con mensaje descriptivo.
  7. git status: no hay nada para commitear.

3.3 Modificar el archivo y deshacer los cambios

Objetivo: descartar cambios desde el último commit.

  1. Editar el archivo creado, agregar una nueva línea y guardarlo.
  2. git status: hay un archivo modificado.
  3. git diff: ver las modificaciones.
  4. git restore file.txt: descartar los cambios.
  5. git status: nada para commitear.

3.4 Modificar el archivo y guardar los cambios

Objetivo: registrar una nueva versión del archivo.

  1. Volver a editar el archivo y agregar contenido.
  2. Agregar los cambios al staging area.
  3. Crear un nuevo commit con mensaje descriptivo.

3.6 Trabajar en una rama

Objetivo: desarrollar un cambio aislado en una rama nueva.

  1. git branch dev: crear una nueva rama llamada dev.
  2. git log: dos ramas apuntan al commit actual main y dev. Pero estamos parados en main (HEAD apunta a main).
  3. git checkout dev: cambiar a la nueva rama.
  4. git log: se invierte (HEAD -> rama, main)
  5. Modificar el archivo y commitear el cambio en esa rama.
  6. git log

3.7 Unir una rama: sin conflictos

Objetivo: integrar en la rama main los cambios de la rama dev.

  1. git checkout main: cambiamos a la rama main.
  2. git merge dev: unimos la rama dev a la rama main. Si no hay conflictos, porque no hubo ningun commit en main desde que se separaron las ramas, la rama main simplemente “avanza” al commit de dev.

Si ya no la necesitaramos, podríamos borrar la rama dev.

3.8 Unir una rama: con conflictos

  1. git reset --hard <HASH_ANTERIOR>: descartemos el último commit en main volviendo al commit anterior.

  2. Hacer un cambio en main y commitearlo.

  3. git merge dev: hay un conflicto al unir las ramas.

  4. git status: unmerged paths.

  5. git diff: para la(s) línea(s) donde hubo cambios en ambas ramas, vamos a ver las dos versiones:

<<<<<<< HEAD
archivo en mi rama
=======
archivo en la otra rama
>>>>>>> DEV
  1. Resolver manualmente el estado final del archivo: puede ser el cambio que realizó una rama, la otra, o un nuevo cambio que unifica los dos.
  2. Marcar como resuelto con git add file.txt.
  3. git status: cambios para commitear
  4. git commit: realizar el commit de merge con el conflicto resuelto.
  5. git log --oneline --parents: notar que el commit de merge tiene dos padres.
  6. git log --oneline --parents --graph.

3.9 Reescribir la historia

Objetivo: practicar una reescritura simple del historial local.

  1. Crear dos commits pequeños seguidos.
  2. Usar git rebase -i HEAD~2 para combinarlos en uno solo (squash).
  3. git log --oneline: Verificar el nuevo historial.

4 Multi-usuario

TipNuevos comandos
  • git clone
  • git fetch
  • git pull
  • git remote

En dos repositorios locales, vamos a simular ser dos usuarios, Alice y Bob, que quieren compartir su trabajo.

En este ejercicio, vamos a asumir que crearon los repositorios uno al lado del otro:

ejercicio/
├── alice/
  ├── .git/
  └──file.txt
└── bob/
   ├── .git/
   └── file.txt

4.1 Alice: crear repositorio y primer commit

Objetivo: inicializar el repositorio que se compartirá.

  1. Crear un directorio alice e inicializar Git.
  2. Configurar usuario y mail de Alice.
  3. Crear un archivo inicial.
  4. Agregarlo al staging area.
  5. Crear el primer commit.
git init alice
cd alice

git config user.name "Alice"
git config user.email "alice@example.com"

echo "Soy Alice." > file.txt
git add file.txt
git commit -m "Commit inicial de Alice"

4.2 Bob: clonar el repositorio

Objetivo: clonar el repositorio para empezar a colaborar.

  1. Clonar el repositorio remoto.
  2. Entrar en el repositorio clonado y revisar el estado.
git clone alice bob
cd bob

El comando git clone configura dos cosas que vamos a tener que configurar manualmente para Alice.

Si abren el archivo de texto bob/.git/config, van a ver que tiene:

[remote "origin"]
    url = .../alice
    fetch = +refs/heads/*:refs/remotes/origin/*
[branch "main"]
    remote = origin
    merge = refs/heads/main

4.3 De Alice a Bob: sincronizar cambios

Objetivo: que Bob obtenga nuevos commits de Alice

Modificar los archivos de Alice y realizar un commit.

¿Están estos cambios reflejados en el repositorio de Bob?

En esta forma que estamos trabajando, Alice no puede enviarle los cambios a Bob, sino que Bob tiene que pedir los cambios:

  1. git fetch: descarga los objetos nuevos.
  2. git merge: hace un merge de la rama origin/main a main.

Se puede hacer en un paso con git pull.

4.4 De Bob a Alice: sincronizar cambios

Objetivo: que Alice obtenga nuevos commits de Bob.

Primero, hagan un cambio y commit en el repositorio de Bob.

Si revisan la configuración de Git de Alice, alice/.git/config, este no tiene conocimiento de ningún repositorio remoto El de Bob se configuró automáticamente al realizar un clonado. Configuremoslo:

git remote add robert ../bob

Ahora, el repositorio de Alice conoce un repositorio remoto llamado “robert” que está ubicado en el directorio ../bob.

Si corremos git fetch, git le va a pedir al repositorio de Bob todos los objetos nuevos que no conozca. Si queremos mergear los cambios, no podemos hacer simplemente git merge:

> git merge
fatal: No remote for the current branch.

La rama en la que estamos parados en el repositorio de Alice no está asociada a ninguna rama remota. Hay dos opciones:

  1. Especificamos explicitamente la rama:
git merge robert/main
  1. Asociamos la rama main de Alice con la rama main de Bob:
git branch -u robert/main

Luego de lo cual ya podemos usar git merge sin especificar la rama.

5 Multi-usuario con GitHub

TipNuevos comandos
  • git push

La versión multi-usuario de la sección anterior tiene una limitación: necesitamos acceso a la computadora de la otra persona para sincronizar. Generalmente, nuestras computadoras no están conectadas continuamente a internet ni permiten el acceso desde afuera (sino que se inicia la comunicación hacia afuera).

Para resolver este problema, podemos usar un repositorio intermedio de intercambio que permita el accesso a través de internet. Hay que crear un repositorio bare, con git init --bare, que permite que le enviemos nuestros cambios con el comando git push. Si no, estaríamos en el mismo problema de antes: ese repositorio necesitaría acceso a nuestra computadora para pedir (fetch) los cambios. Hay varios sitios que proveen este servicio: GitHub, GitLab, Codeberg, entre otros.

5.1 Publicar en GitHub

Objetivo: publicar el repositorio local en GitHub.

  1. Crear un repositorio local y realizar un commit.
  2. Crear un repositorio público vacío en https://github.com/new. Para sincronizar nuestro repositorio local, GitHub nos dice de correr los siguientes comandos:
git remote add origin <URL>
git push -u origin main

que agregan el remoto origin en la dirección provista por GitHub, y envian la rama main a dicho remoto, configurandola como upstream para que los siguientes fetch o push sean automáticos.

5.2 Compartir con otro usuario

Si el repositorio es público, otra persona puede verlo y/o clonarlo a través de su dirección:

https://github.com/<USER>/<REPO>

Pero no puede pushear a este.

Si el repositorio es privado, tenemos que darle acceso (de sólo lectura) con su nombre de usuario de GitHub a nuestro repositorio en

https://github.com/<USER>/<REPO>/settings/access

5.3 Recibir cambios de otro usuario

Si queremos recibir cambios de otro usuario, tenemos dos opciones:

5.3.1 Opción 1: dar acceso al repositorio

Podemos darle acceso de escritura a nuestro repositorio:

https://github.com/<USER>/<REPO>/settings/access

5.3.2 Opción 2: hacer un fork y pull request

En GitHub, un fork es un clon que hace otro usuario de nuestro repositorio en su cuenta de GitHub:

https://github.com/<USUARIO>/<REPO>/fork

Luego, la otra persona puede pushear cambios a esta copía, ya que es su repositorio.

Para sincronizar a nuestro repositorio, el otro usuario puede generar un pull request con el botón “Contribute”:

En nuestro repositorio de GitHub, va a aparecer este pedido y darnos la opción de hacer un merge de esos cambios.