====== Git ====== Herramienta para el control de versiones de ficheros. Convierte nuestro ordenador en una máquina del tiempo. * [[https://git-scm.com/|Web oficial]] ===== Características ===== * Trabajo en equipo * Listado y fecha de cambios (commits) * Volver atrás en el tiempo para restablecer cambios * Ramas de desarrollo * Mezclar cambios entre compañeros * Repositorio distribuido (cada integrante del equipo tiene una copia del repositorio) ===== Terminlogía ===== * HEAD: último commit en la rama actual o la línea del tiempo en la que estamos trabajando. * Commit * stage * branch (rama) * tag (etiqueta): referencia a un commit específico. El repositorio local esta compuesto por tres "árboles" administrados por git: * **Working directory** (directorio de trabajo): donde están los archivos en los que trabajamos * **Staging area** / Index (zona de preparación): fase en la que los ficheros esperan a ser confirmados sus cambios. * **Repositorio Git** / **Head**: se guardan los cambios confirmados y se crea un punto. Por lo tanto, el flujo de trabajo en Git consiste: - Modificar archivos en el directorio de trabajo - Preparar los archivos añadiéndolos a la zona de preparación - Confirmar los cambios Acciones: * De working directory a staging: **stage** * De staging a head: **commit** * De HEAD a working dir: **checkout** {{ :informatica:software:git:secciones-git.png| }} ===== Ayuda ===== git help ===== Configuración ===== ==== Usuario ==== git config --global user.name "Nombre usuario" git config --global user.email "email@contacto.es" Para comprobarlo: git config user.name -> Nombre usuario git config user.email -> email@contacto.es O podemos verlo todo junto con: git config --global --list La opción ''list'' se puede abreviar como ''l'' ==== Editor ==== Por defecto, Git usa el editor que se haya definido en alguna de las variables de entorno ''VISUAL'' o ''EDITOR'' o el editor ''vi'' para crear mensajes de commit o etiqueta (//tag//). Para modificar este comportamiento: git config --global core.editor vim ==== Alias ==== git config --global alias.lg "log --oneline --decorate --all --graph" Acabamos de crear el alias ''lg'' de tal que manera que al escribir ''git lg'' se ejecutará ''git log %%--%%oneline %%--%%decorate %%--%%all %%--%%graph'' Podemos ver los alias que tenemos definidos en la configuración: git config --global -l ===== Repositorios ===== ==== Creación ==== Dentro del directorio donde queramos crear un repositorio Git, ejecutamos: git init Esto creará un directorio oculto llamado ''.git''. En ese directorio se guardarán los commits, las ramas, etc. todo lo relacionado con el repositorio actual. ==== Estado ==== git status Mostrará lo que existe en un directorio y si están siendo registradas o no por Git: On branch master No commits yet Untracked files: (use "git add ..." to include in what will be committed) README.md nothing added to commit but untracked files present (use "git add" to track) Si los archivos aparecen en rojo, no están siendo registrados por Git. Si están en verde quiere decir que están en STAGE y listos para incluirlos en un commit. Si queremos **añadir todos** los archivos del directorio para que Git los gestione: git add . Si queremos mostrar solo los ficheros que se han modificado: git status -s Para saber en qué rama estamos trabajando: git status -b ===== Renombrar y eliminar archivos ===== ==== Con Git ==== Para cambiar el nombre: git mv nombre-antiguo nombre-nuevo Para eliminar archivos: git rm nombre-archivo Al renombrar y borrar mediante Git, estas operaciones quedarán reflejadas en su histórico. ==== Sin Git ==== Cuando hacemos cambios a nivel de sistema de ficheros (renombrar ficheros o borrarlos), Git se da cuenta de que hay cambios no registrados. Actualizamos el repositorio para que esos cambios pasen al STAGE: git add -u Y luego ya podríamos realizar el commit. ===== Ignorar archivos ===== Si tenemos ficheros que no queremos que Git registre, debemos crear un fichero llamado ''.gitignore'' (lleva un punto inicial): fichero-excluir.txt *.log node_modules Con ese fichero le diremos a Git que no haga el seguimiento de ''fichero-excluir.txt'', los que terminen en ''.log'' y la carpeta ''node_modules''. ===== Commits ===== Para añadir los ficheros a stage y crear el commit con un mensaje en un único paso: git commit -am "Mensaje de commit" ==== Corregir mensajes ==== git commit -m "Mensaje de commit" Si no añadimos la opción ''m'', Git abrirá el editor de texto que se haya definido para escribir el mensaje de commit. ==== Corregir commit ==== Si queremos hacer algún cambio en cierto commit, identificado como 4e809d4, por ejemplo: git reset --soft 4e809d4 En ese momento tendremos en nuestro directorio los ficheros tal cual estaban en ese punto. Por defecto, al hacer un ''reset'' Git aplica la opción ''mixed'' avisando de los ficheros que cambiaron en el futuro, es decir, los ficheros que se modificaron después del commit al que hayamos hecho ''reset''. Esos ficheros tendrán los cambios, pero no estarán añadidos al STAGE. Si queremos hacer un reset y descartar los futuros cambios, utilizaremos la opción ''hard'': git reset --hard 4e809d4 ==== Recuperarse de reset ==== Aunque hayamos hecho reset sobre ciertos commits, Git internamente guarda todo lo que va sucediendo en el repositorio. Podemos verlo con: git reflog Como aparecerán todos los hashs de los commits, podremos recuperarlo con ''reset'': git reset --hard 4e809d4 Esto nos traerá todos los commits hasta ese indicado ==== Diferencias entre commits ==== Si queremos ver las diferencias entre el último commit y lo que tengamos en nuestro directorio: git diff Si queremos ver las diferencias entre lo que dejamos en STAGE y lo que había en el último commit: git diff --staged ==== Ver cambios ==== Listado completo de commits del repositorio git log Ejemplo: commit d6fc8b73db822cb4f8b755e7be31d55741f2512c (HEAD -> master) Author: Rodríguez, Fulanito Date: Thu Jul 8 11:32:17 2021 +0200 Creado archivo README.md Mostrar versión corta: git log --oneline Mostrar versión corta y decorada: git log --oneline --decorate --all --graph ==== Deshacer cambios ==== Si queremos dejar todo como estaba en el último commit, es decir, recuperar el último commit: git checkout -- . Esto es muy útil cuando por error borramos algunos archivos o guardamos modificaciones que no queríamos. Utilizando el comando anterior podremos volver al estado del último commit. Podemos seleccionar solo un archivo a revertir: git checkout -- archivo.ext ==== Corregir commits ==== Si queremos modificar el mensaje del último commit: git commit --amend -m "Nuevo mensaje de commit" Si queremos modificar el mensaje y ficheros del último commit: git reset --soft HEAD^ Hacemos los cambios que nos interesen y finalmente añadimos todo y corregimos: git commit -am "Commit corregido" ==== Añadir ficheros a seguimiento ==== Añadir un fichero o lista de ellos: git add fichero.ext [ ] Añadir grupo de ficheros por extensión del directorio actual: git add *.ext Añadir grupo de ficheros por extensión en **todo el directorio** (recursivo): git add "*.ext" Añadir directorio: git add css/ Añadir todos menos y descartar alguno. Para ello tenemos que hacerlo en dos fases. Primero añadimos todos a STAGE: git add . Y ahora lo excluiríamos: git reset archivo.txt ==== Clonación ==== ===== Snippets ===== ==== Log con nombre de ficheros ==== Para mostrar el nombre de los ficheros por commit: git log --name-only Ejemplo de salida: Author: Rodríguez, Fulanito Date: Tue Nov 24 13:24:41 2020 +0100 Modificado actualizador de datos Correcciones en la actualización de la información a través de la API que provocaba un desbordamiento. informes/proyecto/application/controllers/update.php informes/proyecto/application/models/bd_model.php ===== Ramas ===== ==== Introducción ==== Línea de tiempo de commits. Podremos hacer cambios en ellas sin afectar a la rama principal (normalmente master). Estas ramas, finalmente, podrán incorporarse a la rama maestra. Utilizamos las ramas para desarrollar nuevas funcionalidades. Estos cambios pueden unirse después a la rama principal o solo para hacer pruebas sobre la rama principal y acabar descartando la nueva rama. Por defecto solo existe la rama **master**. Cuando queremos unir una rama a otra realizamos un //merge//. Git lo hará por nosotros. Nos encontraremos con 3 escenarios: * **Fast-forward**: Git detecta que no hay cambios en la rama principal (master) y la nueva rama se puede incorporar directamente, sin problemas ni intervención del usuario. * **Unión automática**: Git detecta que en la rama principal hubo algún cambio que las otras ramas desconocen, pero dichos cambios no supone ningún conflicto y realiza la unión anotándolo en el historial. * **Unión manual**: Git no puede resolver el conflicto de forma automática y deja la responsabilidad al usuario. Una vez solucionado se creará un //merge commit// y se podrá seguir trabajando sin problema. ==== Creación ==== git branch minuevafuncionalidad Si queremos ver las ramas que hay en el repositorio: git branch Si queremos ver las ramas locales y remotas: git branch -a Nos movemos a la rama: git checkout minuevafuncionalidad Normalmente, cuando creamos una rama también queremos movernos a ella, así que para hacer todo en un paso: ''git checkout -b nombre-rama'' Para subir esa rama al repositorio remoto: git push -u origin minuevafuncionalidad ==== Eliminación ==== git branch -d nombre-rama ==== Comparar ==== Para ver diferencias entre ramas: git diff rama1 master ===== Unión de ramas ===== ==== Fast-forward ==== Debemos colocarnos en la rama master: git checkout master Para unir: git merge rama1 Ejemplo de salida: Updating 9391f7e..b605c52 Fast-forward villanos.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100755 villanos.md Al revisar el historial, veremos algo así: * b605c52 (HEAD -> master, villanos) Añadido Octopus * 0805ac7 Añadidos villanos * 9391f7e Actualización * 62c8a10 Agregando el gitignore * ac0d374 Borramos la historia de batman * b4c748c Cambiamos el nombre de la historia de superman * d877f01 Borrando archivo salvar mundo * c9ee153 Renombrando archivo a salvar-mundo * fa3cd3a Creando el archivo destruir el mundo * 4e809d4 Agregamos a Linterna verde y a Robin * 345d7de Editamos el readme.md * 860c6c2 Agregamos las historias de los heroes * bc1a1e5 Agregamos las ciudades * 6b8f60d Agregamos los heroes * da24862 Agregamos las misiones * 88a423d Se agrego el archivo readme ==== Unión automática ==== Cuando se fusiona una rama con la master y en la master ha habido cambios que no conoce la rama a unir, si no supone un conflicto, Git nos pedirá un mensaje de commit para que quede registrada la fusión. Git añade "Merge branch 'nombre rama'", pero podemos poner lo que queramos. Tras guardar el commit, mostrará un mensaje así: Merge made by the 'recursive' strategy. villanos.md | 1 + 1 file changed, 1 insertion(+) ==== Unión con conflictos ==== Cuando al unir dos ramas obtenemos este mensaje: Auto-merging misiones.md CONFLICT (content): Merge conflict in misiones.md Automatic merge failed; fix conflicts and then commit the result. Git nos informa de que existen conflictos que no puede solucionar de forma automática y requiere de nuestra intervención. Para resolver, abrimos el fichero que tiene conflictos: # Misiones 1. Acabar con el plan de Lex Luthor 2. Crear la liga de la justicia <<<<<<< HEAD 3. Buscar nuevos miembros que sean superhéroes 4. Necesitamos más comida ======= 3. Buscar nuevos miembros para la liga >>>>>>> rama-conflicto Las nuevas líneas que aparecen en el fichero indican los conflictos. La línea ''======='' es el "centro" del conflicto. Todo el contenido entre el centro y la línea ''%%<<<<<<<%% HEAD'' es contenido que existe en la rama maestra actual a la que apunta la referencia ''HEAD''. Por el contrario, todo el contenido entre el centro y ''%%>>>>>>>%% rama-conflicto'' es contenido que está presente en nuestra rama de fusión. ===== Etiquetas ===== Las etiquetas hacen referencia a un commit, el estado en que se encontraba un proyecto en un determinado momento. Normalmente las etiquetas se usan en commits para marcar versiones o //releases// de los programas. ==== Creación ==== git tag nombre-tag De una manera más completa: git tag -a v1.0.0 -m "Versión inicial" Esto asociará el tag al último commit. Si queremos especificar el commit: git tag -a v0.1.0 345d7de -m "Versión alfa" ''345d7de'' sería el identificar del commit. Para borrarlo: git tag -d nombre-tag Para visualizar los que hay: git tag ==== Listado ==== Para ver la lista de tags: git tag Para mostrar un tag: git show nombre-tag ===== Repositorios remotos ===== ==== Descargar repositorio ==== git clone direccion-repositorio Por ejemplo, para descargar un repositorio de GitHub por HTTPS: git clone https://github.com/laravel/laravel.git ==== Obtener últimos cambios ==== git pull origin master ==== Subir cambios ==== git push origin master ==== Publicar en varios repositorios remotos ==== Se añaden todos los que queramos: git remote add repo1 https://github.com/usuario1/proyecto.git git remote add repo2 https://github.com/usuario2/proyecto.git git remote add repo3 https://github.com/usuario3/proyecto.git Cada vez que queramos subir los cambios, decidimos a cuál. Por ejemplo, para subirlo solo al ''repo2'': git push repo2 master ===== Trabajando en equipo ===== El flujo de trabajo cuando se trabaja con más gente es crear una rama (//branch//) y trabajar sobre ella. La rama ''master'' no se debe tocar para evitar tocar los mismos archivos. Partimos de la rama master: git checkout master Descargamos la versión más actualizada: git pull origin master Creamos una rama: git branch minuevafuncionalidad Nos movemos a esa rama: git checkout minuevafuncionalidad Haríamos nuestros desarrollos y commits. Cada vez que queramos subir los cambios de la rama: git push -u origin minuevafuncionalidad Finalmente, para unir esa rama con la master, primero debemos cambiarnos a la rama master: git checkout master Asegurarnos de tener la última versión de master: git pull origin master Fusionamos las ramas: git merge minuevafuncionalidad Estos cambios están en local. Debemos subirlos al repositorio remoto: git push origin master Una vez fusionada, es una buena práctica eliminar las ramas que ya no vamos a usar más: git push origin --delete minuevafuncionalidad Y también (si queremos) la borramos de local: git branch -d minuevafuncionalidad ===== Resumen ===== * ''git init'': creación de repositorio git local. * ''git add'': añade ficheros a STAGE. * ''git commit'': se registran los cambios de los ficheros que estén en STAGE (se hace una "instantánea"/snapshot). * ''git status'': lista los ficheros que sufrieron algún cambio desde el último commit y lista de ficheros que no está siguiendo Git. ===== Resolución de errores ===== Si al añadir ficheros a STAGE nos aparecen las siguientes advertencias: warning: LF will be replaced by CRLF in archivo.txt. The file will have its original line endings in your working directory Se debe a que está configurado ''core.autocrlf = true''. Lo que hace esa opción es indicar si quieres que en el repositorio se guarden los saltos de línea de los ficheros en formato Unix (LF) pese a tener tus ficheros en tu entorno local con saltos de línea al formato Windows (CRLF). Al hacer commmit, tus ficheros se transformarán automáticamente a LF, y cuando hagas checkout de un fichero, se convertirá automáticamente a CRLF Podemos solucionarlo con: git config core.autocrlf false ===== Recursos ===== * [[https://gist.github.com/miguelpantoja89/ab7bb98ed1415759acee2becc8c618d2|Git Básico]] (Gist de GitHub) * [[https://meteo.unican.es/trac/wiki/versionControl/git|GIT]] * [[https://rogerdudler.github.io/git-guide/|git - the simple guide]] * [[https://www.conventionalcommits.org/en/v1.0.0/|Conventional Commits]]