5. Control de versiones

En este tema vamos a ver tareas básicas en Git.

Mas información

Git - La guía sencilla

Pro Git: Libro

Git Tips: Muchas recetas de Git

Git 2.23.0: Forget about checkout, and switch to restore.: Nuevas ordenes git restore y git switch

Git Extras: Son recetas para hacer cosas con Git

Git Tools - Stashing and Cleaning: Guardar cosas temporalmente con git stash y git stash pop

Source:Stack overflow Developer survey 2022

Código nuestro que estas en GitHub.
Santificados sean tus commits.
Venga a nosotros tu fetch.
Hágase tu merge tanto en remoto como en local.
Danos hoy nuestro branch de cada día.
Perdona nuestras pulls, así como también nosotros perdonamos a los que nos pushean más rápido;
No nos dejes caer en los conflictos,
y líbranos del checkout.
Amén
---Javier del Moral (2023)

Instalando Git

Al instalar Git en Windows hay varias opciones a tener en cuenta que explicaremos en clase.

Una vez instalado Git, hay que indicar cual es nuestro nombre y nuestro correo.

git config --global user.name "juan"
git config --global user.email juan@micorreo.com

Por último , si estás en el instituto , habrá que configurar el proxy:

git config --global http.proxy http://172.16.0.9:8080
git config --global https.proxy http://172.16.0.9:8080

Para instalar Git en Linux y que sea la última versión usaremos los siguientes comandos (Ya que por ejemplo en Ubuntu 18.04 la versión e un poco vieja):

sudo add-apt-repository ppa:git-core/ppa
sudo apt update
sudo apt install git

El inicio de Git

Los comandos básicos de Git son:

Para iniciar un directorio y que se pueda usar Git

git init

Para descargar un repositorio que ya existe

git clone https://github.com/lgonzalezmislata/prueba.git

Estados de un fichero

untracked o archivos sin seguimiento: Son ficheros que existe en el área de trabajo pero no existen para git

staged o cambios a ser confirmados: Son ficheros modificados que se añadirán al siguiente commit.

committed: Son ficheros que se guardaron en el ultimo commit y que no han sido modificados desde dicho commit.

modified o cambios no rastreados para el commit: Son ficheros que se han modificado desde el último commit pero que aun no se han añadido para el próximo commit.

La staged area en git también se llama a veces cached o index. Es que hay comandos que usan el modificador --cached para referirse a la staged area otros usan --staged El comando git rm usa el modificador --cached para referirse a la staged area El comando git restore usa el modificador --staged para referirse a la staged area El comando git diff usa el modificador --cached o --staged para referirse a la staged area --cached --staged git rm ✓ git restore ✓ git diff ✓ ✓
Por defecto todos los ficheros del anterior commit ya está en el staged area, por eso para quitar un fichero de git hay que borrarlo del staged area. git rm --cached fichero.

Por defecto el comando commit solo se hace "commit" de los ficheros que estén en la staging area.

git commit -m "mensaje"

Para añadir un fichero a la staged area usamos el comando

git add fichero

Podemos hacer que el comando commit incluya ademas de los ficheros que están staged se incluya ademas todos aquellos ficheros que están modified , usaremos el parámetro -a ( o --all )

git commit -a -m "mensaje"

Para quitar un fichero de la staged area. Es como lo opuesto a git add.

git restore --staged fichero

También se puede usar el comando git reset fichero pero se creo el nuevo comando git restore que se puede usar la para hacer lo mismo así que recomendamos usar el comando más nuevo.

Para borrar un fichero y que ya no esté a partir de ahora en "git". Se usa --cached para que no se borre del área de trabajo y si solo de la staged area.

git rm --cached fichero

También se puede usar el comando git rm fichero y en ese caso se borra tanto del staged area como del área de trabajo. No hay forma de borrarlo solo del área de trabajo ya que para eso está rm de Linux.

Para deshacer los cambios de un fichero que hay en el área de trabajo y se cambie por la versión que hay en el último commit.

git restore fichero

También se puede usar el comando git checkout fichero pero se creo el nuevo comando git restore que se puede usar la para hacer lo mismo así que recomendamos usar el comando más nuevo.
También se puede añadir el argumento --worktree de forma que quede git restore --worktree fichero pero no es necesario ya que se hace por defecto. Pero si se usara --staged no se modifica el área de trabajo por defecto y en ese caso sería necesario añadirlo. git restore fichero git restore --staged fichero git restore --worktree fichero git restore --staged --worktree fichero Restaura el fichero en el área de trabajo ✓ ✓ ✓ Restaura el fichero en el staged area ✓ ✓

Para ver el estado de cada uno de los ficheros

git status

En la rama master
Cambios a ser confirmados:
(usa "git restore --staged <archivo>..." para sacar del área de stage)
nuevos archivos: facturas.html
Cambios no rastreados para el commit:
(usa "git add <archivo>..." para actualizar lo que será confirmado)
(usa "git restore <archivo>..." para descartar los cambios en el directorio de trabajo)
modificados: main.js
Archivos sin seguimiento:
(usa "git add <archivo>..." para incluirlo a lo que será confirmado)
cliente.html

Para simplificar el uso de Git se recomienda hacer el git add justo antes de hace el git commit ya que sino hacemos los siguiente: modificar un ficherio git add modificar el fichero de nuevo Si hacemos ahora un git status. El fichero modificado está a la vez en el estado staged y modified git commit En el commit solo se guardan los primeros cambios y no los segundos. Ya que al hacer el git add , git se guarda como está el fichero en ese momento y en el git commit solo se hace como está en ese momento.Aunque si hiciéramos el git commit -a si que se guardaría el segundo cambio.

Más información:

What's the difference between 'git rm --cached', 'git restore --staged', and 'git reset'

Sincronización de Git

Una misma rama en git puede estar en varios sitios a la vez. El repositorio es el historio donde están todos los commits que se han hecho.

Vamos a explicar cada uno de ellos suponiendo que estamos en la rama master.

Carpeta local o área de trabajo o árbol de trabajo: Es donde están los ficheros con lo que trabajamos nosotros directamente mientras programamos.

Repositorio local: Es el repositorio que tenemos en nuestro ordenador.

Rama master:Es donde se guardan al hacer commit

Rama origin/master: Es una copia que hay del repositorio remoto. El nombre de la rama se llama origin/master. Hay que fijarse que hay va todo junto y no con un espacio en medio. Ya es es simplemente el nombre de una rama local nuestra. Solo que es una copia de un remoto llamado origin de su rama master.

Repositorio remoto: Es un repositorio que está en otra máquina.

Rama master: Es donde queremos copiar los commits de nuestra rama master. Al hacer referencia a una rama de un repositorio remoto, se usara la expresión origin master con un espacio en medio. Ya que por separado está el nombre del remote llamado origin y de la rama llamadamaster

Para poner en el área de trabajo una copia del último commit de master que es lo mismo que moverse a la rama master. Tambien se puede usar con un hash de un commit.

git switch master

Para copiar los commits que hay en nuestro repositorio local al repositorio remoto (y también a la copia del repositorio remoto origin/master)

git push
o
git push origin master

Para copiar lo que hay en el repositorio remoto en una rama local que es después del fetch siempre copia del repositorio remoto, es decir en origin/master.

git fetch
o
git fetch origin master

Para copiar lo que hay en el repositorio remoto en nuestras ramas locales, es decir en master y origin/master. Como nuestra rama puede ser distinta, se hace un merge sobre nuestra rama. Lo cual es peligroso.

git pull
o
git pull origin master

El problema es que si hacemos git pull origin master y estamos en otra rama , se hace un merge de origin/master en la rama que estemos y eso no suele ser lo que queríamos hacer. Así que mi recomendación es nunca usar git pull y usar siempre git fetch.
Recuerda: Nunca uses git pull y usa siempre git fetch.

Para mergear lo que hay en la rama origin/master en la rama master.

git merge origin/master

Remotes

El Git un "remote" hace referencia a un servidor de Git donde subimos el código. Por defecto cuando hacemos git clone se crea un remote llamado origin.

Para ver la lista de remotes.

git remote -v
origin	https://github.com/lgonzalezmislata/prueba.git (fetch)
origin	https://github.com/lgonzalezmislata/prueba.git (push)

Para añadir un nuevo remote

git remote add otroservidor https://www.otroservidor.com/lgonzalezmislata/prueba.git

Una vez creado el remote otroservidor ya podremos usarlo en los comandos git push, git fetch o git pull además de usar origin

Borrado de commits

El comando git reset <commit> vuelve a un commit anterior y borrando todos los siguientes.

Volver a un commit anterior y borrando todos los posteriores pero modificando el área de trabajo al dejar ahí los ficheros del commit y vaciando la staged area.

git reset --hard 0d588ed

Volver a un commit anterior y borrando todos los posteriores pero sin modificar ni el área de trabajo ni la staged area.

git reset --soft 0d588ed

Volver a un commit anterior y borrando todos los posteriores , sin modificar ni el área de trabajo pero vaciando la staged area.

git reset --mixed 0d588ed

Si queremos deshacer el último commit (es decir como si no hubiéramos lanzado el comando git commit) se usa la siguiente orden. Ya que HEAD es el nombre del último commit y HEAD~1 el del penúltimo commit.

git reset --soft HEAD~1

La siguiente tabla resume lo que hace cada modificador:

Borra los commits siguientes	Se modifica área de trabajo	Modifica staged area
--hard	Si	Si	Si
--mixed	Si	No	Si
--soft	Si	No	No

Ejemplos de como hacer un commit y lo que se necesita hacer después de borrarlo

soft	mixed	hard
Hacer el commit
Borrar el commit
Rehacer el commit

Mas información en:

Deshaciendo cambios en git

2.4 Git Basics - Undoing Things

Deshaciendo cosas en Git

How To Git Reset to HEAD

Deshacer commits

Una vez que hemos subido un commit a GitHub es una muy mala práctica borrar un commit sin embargo si queremos deshacer algo , podemos usar git revert <commit>.

Se hace un nuevo commit que deshace el último commit

git revert HEAD~1

Prepara un nuevo commit que deshace el último commit pero sin realmente hacer el commit. Lo que hace el modificar los ficheros y añadirlos a la staged area.Después se hace el commit.

git revert --no-commit HEAD~1
git commit -am "Mensaje de commit"

La opción --no-commit es útil porque nos permite revisar que va a hacer el commit y nos permite hacer alguna modificación más.Si no estamos interesados en hacer el commit lo podemos abortar con git revert --abort o usar el ya conocido git restore --staged --worktree fichero.
En caso de un conflicto se deberá resolver el conflicto en el fichero, hacer el git add <fichero> y por último ya hacer el commit.
La diferencia entre git revert y git reset es que git revert crea un nuevo commit que deshace el trabajo de otro commit , mientras que git reset borra realmente los commits.

Moverse por git

En git podemos movermos a otras ramas o a otros commit. Para movernos se usa el comando git switch.

También se puede usar el comando git checkout pero se creo el nuevo comando git switch que se puede usar la para hacer lo mismo así que recomendamos usar el comando más nuevo.

Ramas

Para movernos por ramas solo hay que indicar el nombre de la rama

git switch develop

Si la rama es del tipo origin/nombre_rama hay que indicar --detach ya que sobre esa rama no se pueden crear commit puesto que esa rama viene del remoto con git fetch

git switch --detach origin/develop

Commits

Podemos movernos a un commit anterior de forma que los ficheros de ese commit estén en nuestro directorio de trabajo. El lanzar esta orden no modifica ningún commit.Su utilidad es ver como estaba el código en estados anteriores. Hay que indicar --detach ya que sobre commit no se pueden añadir más commits yaque ya están añadidos ya que es un commit que está a mitad del arbol de git.

git switch --detach 2308b63

Si ahora lanzamos el comando git status se mostrará lo siguiente

HEAD desacoplada en 2308b63
nada para hacer commit, el árbol de trabajo está limpio

El significado de HEAD desacoplada o HEAD detached es que ahí no podemos trabajar, no pudiendo hacer nuevos commits desde ahí aunque si que podríamos hacer una nueva rama.

Para volver al commit último de la rama solo hay que poner el nombre de la rama en vez de un commit

git switch master

Configuración

.gitkeep

Por defecto Git no sube las carpetas vacías, por lo que si queremos que las suba hay que añadir algún fichero. Por convención se suele crear un fichero llamado .gitkeep y al ya haber algún fichero, git subirá esa carpeta.

.gitignore

El fichero .gitignore permite indicar que carpetas no se deben subir a git. El fichero debe estar en el raíz del proyecto de git.

Por ejemplo para que no se suba el fichero node_modules hay que crear el fichero .gitignore con la siguiente línea:

/node_modules

Almacenando contraseñas

Normalmente trabajamos siempre en nuestro ordenador y no queremos todas las veces volver a poner la contraseña. En ese caso se puede decir a git que la almacene y no nos la vuelva a pedir:

No te vuelve a pedir la contraseña en 15 min

git config --global credential.helper cache

Almacena la contraseña en el disco en texto plano y nunca mas te la vuelve a pedir.

git config --global credential.helper store

Stash

Hay veces que queremos guardar temporalmente los cambios para después volver a ellos, por ejemplo si queremos cambiar de rama.

git contiene una orden para hacer eso que es git stash.

Guardar solo lo modificado (estén o no en el staged area)

Guarda todos los ficheros aunque NO estén en la stage area. Es decir, también los ficheros nuevos. Y al hacer el pop se restaura el estado en el staged area.

Por defecto	--include-untracked	--all
Los ficheros modificados (estén o no en el staged area)	✓	✓	✓
Los ficheros nuevos	✓	✓
Los ficheros los ignorados	✓

Y si luego queremos recuperar los cambios se hace con:

Si no se añade --index, no se restaurará el staged area.

Log

El comando git log permite ver el histórico de commits

Ver los commits en una única línea cada commit

git --no-pager log --pretty=oneline

Ver los commits indicando que campos queremos mostrar e indicando el formato de la fecha. Los campos a mostrar se pueden ver en Git log format string cheatsheet. El formato de fecha se puede ver en strftime o en strftime format cheatsheet

git --no-pager log --pretty=tformat:"%h %cn %cd %s" --date=format:"%d/%m/%Y %H:%M:%S"

Campos a mostrar

Formato de Fechas

Mensajes de commit

Los mensajes de commit deben seguir el siguiente formato:

type(#issue):titulos
Explicación (opcional)

Siendo

type

feat: Si se añade una nueva funcionalidad (feature)

fix: Si se arregla (fix) un error

docs: Si solo cambia cosas de la documentación

style: Si solo se cambia el estilo del código como tabuladores, puntos y comas, formateo, etc.

refactor: Si se cambia el código para mejorar su calidad pero sin modificar la funcionalidad. Eso se llamada refactorizar.

test: Si se cambian cosas relacionadas con test automáticos.

chore: Si se cambian cosas relacionadas con el despliegue.

#issue: Es el Nº de la incidencia a la que hace referencia. Mas información sobre los issues de GitHub: aquí

Vamos ahora unos ejemplos:

Se arregla el fallo 45 que se llama "Falla si la fecha está vacía"

fix(#45):Falla si la fecha está vacía

Se añade la nueva funcionalidad llamada "Mostrar el listado de los pacientes" con Nº 456

feat(#456):Mostrar el listado de los pacientes
El listado se ha hecho en HTML en vez de generar un PDF.

Mas información:

Git Commit Message StyleGuide

How to Write a Git Commit Message

Git Commit Message Conventions en el proyecto de AngularJS

Writing Git commit messages

A Note About Git Commit Messages

GitMoji

Ejercicios

Para todos los ejercicios de este tema, se usarán los comandos de Git siempre que sea posible y los mensajes de los commits deberán seguir el formato explicado en clase.

Ejercicio 1

En tu ordenador instala Git y configura el usuario y el correo que usará Git.

En una carpeta vacía inicializala para poder usar Git.

Crea 3 ficheros y haces un commit con ellos. Los ficheros se llamarán

index.html

index.js

main.css

Haz otro commit con lo siguiente:

Modificación de index.html

Nuevo fichero cal.js

Modifica 3 ficheros pero que en el commit solo sea con dos de los ficheros modificados.

Deshaz los cambios del fichero que estaba modificado pero que no se incluyó en el commit.Y mira como está el estado de Git

Crea un nuevo fichero , modifica otro y haz que si se hiciera un commit se incluyeran esos ficheros (pero no hagas el commit). Y mira como está el estado de Git

Haz que los dos ficheros anteriores ahora no se incluyan si se hiciera el commit (pero no hagas el commit). Y mira como está el estado de Git

Haz que el fichero index.html se borre de git

Siempre antes de cada commit deberás lanzar el comando git status para ver el estado de los ficheros.

Ejercicio 2

En este ejercicio vamos a tener 2 carpetas que estarán "apuntando" al mismo repositorio de Git. Las carpetas las vamos a llamar "A" y "B".

Crea una cuenta en Github

Crea un repositorio llamado "banco" y al crearlo, indica que haya un fichero README.

Desde la carpeta "A", clona el repositorio.

Desde la carpeta "B", clona el repositorio.

Desde la carpeta "A", crea 2 ficheros y haz un commit. Sube el commit a GitHub.

Desde la carpeta "B", comprueba que no están los ficheros.

Desde la carpeta "B", baja los cambios con "pull" y comprueba que ya están los cambios en el área de trabajo.

Desde la carpeta "A", crea otros 2 ficheros y haz un nuevo commit. Sube el commit a GitHub.

Desde la carpeta "B", baja los cambios con "fetch y merge" y comprueba que ya están los cambios en el área de trabajo.

Desde la carpeta "A", crea otros 2 ficheros y haz un nuevo commit. Sube el commit a GitHub.

Desde la carpeta "B", baja los cambios UNICAMENTE con "fetch" y comprueba que NO están los cambios en el área de trabajo.

Desde la carpeta "B", ves a la rama "origin/master" y comprueba que ya están los cambios en el área de trabajo.

Desde la carpeta "B", ves a la rama "master" y comprueba que NO están los cambios en el área de trabajo.

Desde la carpeta "B", haz un merge de "origin/master" a "master" y comprueba que ya están los cambios en el área de trabajo.

Ejercicio 3

En una carpeta vacía inicializala para poder usar Git.

Crea otros 2 ficheros y haz un nuevo commit.

Crea otros 2 ficheros y haz un nuevo commit.

Crea otros 2 ficheros y haz un nuevo commit.

Crea otros 2 ficheros y haz un nuevo commit.

Muestra el histórico de commits haciendo que solo salga el hash abreviado ,la fecha del commit en formato "YYYY-MM-DD", el nombre de la persona que hace el commit y el mensaje del commit.

Borra los dos últimos commits. Y muestra ahora de nuevo el histórico como en el paso anterior.

Haz que en el area de trabajo se muestren los ficheros del primer commit que hiciste.

Haz que en el area de trabajo se muestren los ficheros del último commit que hay.

Crea otros 2 ficheros y haz un nuevo commit.

Modifica el commit que acabas de hacer para cambiar el mensaje del commit (deberás borrar el commit pero sin tocar los ficheros del area de trabajo).

Modifica un fichero , añade un fichero y y haz un nuevo commit.

Modifica el commit que acabas de hacer para añadir un tercer fichero.

Ejercicio 4

Crea un nuevo proyecto de NodeJS y haz lo siguiente:

Que esté el index.js

Instala alguna librería en la carpeta node_modules

Crea la carpeta dist pero que esté vacia.

Que sea un proyecto de Git

Modifica el proyecto de Git de forma que no se suba la carpeta node_moules y si que se suba la carpeta dist