====== 5. Control de versiones ====== En este tema vamos a ver tareas básicas en Git. Mas información * [[http://rogerdudler.github.io/git-guide/index.es.html|Git - La guía sencilla]] * [[http://git-scm.com/book/es|Pro Git]]: Libro * [[https://github.com/git-tips/tips|Git Tips]]: Muchas recetas de Git * [[https://medium.com/blue-harvest-tech-blog/git-2-23-0-forget-about-checkout-and-switch-to-restore-ac2682b737b3|Git 2.23.0: Forget about checkout, and switch to restore.]]: Nuevas ordenes ''git restore'' y ''git switch'' * [[https://github.com/tj/git-extras|Git Extras]]: Son //recetas// para hacer cosas con Git * [[https://git-scm.com/book/en/v2/Git-Tools-Stashing-and-Cleaning|Git Tools - Stashing and Cleaning]]: Guardar cosas temporalmente con ''git stash'' y ''git stash pop'' {{chiste_git.png|}} {{:clase:daw:daw:1eval:git-command-line.jpg?330|}} [[https://survey.stackoverflow.co/2022#version-control-vc-interaction|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**// o //**arbol de trabajo**//. Es que hay comandos que usan el modificador ''--cached'' para referirse a la //staged area// otros usan ''--staged'' y otro ''--worktree''.. * El comando [[https://git-scm.com/docs/git-rm|git rm]] usa el modificador ''--cached'' para referirse a la //staged area// * El comando [[https://git-scm.com/docs/git-restore|git restore]] usa el modificador ''--staged'' o ''--worktree'' para referirse a la //staged area// * El comando [[https://git-scm.com/docs/git-restore|git diff]] usa el modificador ''--cached'' o ''--staged'' para referirse a la //staged area// ^ ^ ''--cached'' ^ ''--staged'' ^ ''--worktree'' ^ ^ ''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''. {{:clase:daw:daw:1eval:git-estados.png|}} * 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" {{:clase:daw:daw:1eval:commit-all.png|}} \\ \\ * 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. \\ \\ * Para deshacer los cambios de un fichero que hay en el area 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. \\ \\ * Para ver el estado de cada uno de los ficheros git status En la rama master Cambios a ser confirmados: (usa "git restore --staged ..." para sacar del área de stage) nuevos archivos: facturas.html Cambios no rastreados para el commit: (usa "git add ..." para actualizar lo que será confirmado) (usa "git restore ..." para descartar los cambios en el directorio de trabajo) modificados: main.js Archivos sin seguimiento: (usa "git add ..." 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'' 8-o 8-o 8-o 8-o * ''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: * [[https://stackoverflow.com/questions/65434544/whats-the-difference-between-git-rm-cached-git-restore-staged-and-gi|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. {{:clase:daw:daw:1eval:chiste-git-force.jpg?direct|}} 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 llamada''master'' {{:clase:daw:daw:1eval:repositorios.png|}} * 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 '' 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 | vi fichero git add fichero git commit -m "mensaje malo" | vi fichero git add fichero git commit -m "mensaje malo" | vi fichero git add fichero git commit -m "mensaje malo" | ^ Borrar el commit | git reset --soft HEAD~1 | git reset --mixed HEAD~1 | git reset --hard HEAD~1 | ^ Rehacer el commit | git commit -m "mensaje bueno" | git add fichero git commit -m "mensaje bueno" | vi fichero git add fichero git commit -m "mensaje bueno" | \\ \\ Mas información en: * {{deshaciendo-cambios-en-git.pdf|Deshaciendo cambios en git}} * [[https://git-scm.com/book/en/v2/Git-Basics-Undoing-Things|2.4 Git Basics - Undoing Things]] * [[https://styde.net/deshaciendo-cosas-en-git/|Deshaciendo cosas en Git]] * [[https://devconnected.com/how-to-git-reset-to-head/|How To Git Reset to HEAD]] ===== 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 que está en la //stage area//. git stash push * Guarda todos los ficheros aunque NO estén en la //stage area//. Es decir, también los ficheros nuevos git contiene una orden para hacer eso: git stash push --all Y si luego queremos recuperar los cambios se hace con: git stash pop ===== 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 [[https://devhints.io/git-log-format|Git log format string cheatsheet]]. El formato de fecha se puede ver en [[http://man7.org/linux/man-pages/man3/strftime.3.html|strftime]] o en [[https://devhints.io/strftime|strftime format cheatsheet]] git --no-pager log --pretty=tformat:"%h %cn %cd %s" --date=format:"%d/%m/%Y %H:%M:%S" \\ \\ **Campos a mostrar** {{:clase:daw:daw:1eval:git-log-format.png?direct|}} **Formato de Fechas** {{:clase:daw:daw:1eval:strftime.png?direct|}} ===== 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: [[https://guides.github.com/features/issues/|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: * [[https://github.com/logongas/styleguide-git-commit-message/blob/master/README.md|Git Commit Message StyleGuide]] * [[http://chris.beams.io/posts/git-commit/|How to Write a Git Commit Message]] * [[https://docs.google.com/document/d/1QrDFcIiPjSLDn3EL15IJygNPiHORgU1_OOAqWjiDU5Y|Git Commit Message Conventions en el proyecto de AngularJS]] * [[http://365git.tumblr.com/post/3308646748/writing-git-commit-messages|Writing Git commit messages]] * [[http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html|A Note About Git Commit Messages]] * [[https://gitmoji.carloscuesta.me/|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''