Fundamentos de Git (parte 2)
Clonar, Inicializar y Guardar CambiosPuedes obtener un proyecto Git de 2 maneras. La primera es tomar un proyecto o directorio existente e inicializarlo con Git. La segunda es clonar un repositorio existente en Git desde otro servidor.
git init
Esto crea un subdirectorio .git/, el cual contiene todos los archivos necesarios del repositorio.
Todavia no hay nada en tu proyecto que este bajo seguimiento.
Para empezar a controlar versiones de archivos existentes, tenemos que comenzar el seguimiento de esos archivos y hace una confirmacion inicial.
Esto se consigue con unos pocos comandos git add para especificar que archivos queremos controlar, seguidos de un git commit para confirmar los cambios:
git add *.c
git add LICENSE
git commit -m 'initial project version'
Veremos lo que hacen estos comandos mas adelante. Por ahora, tenemos un repositorio de Git con archivos bajo seguimiento y una confirmacion inicial.
Para obtener una copia de un repositorio Git existente, el comando que necesitas es git clone.
Al ejecutar el comando, cada version de cada archivo de la historia del proyecto es descargada por defecto.
De hecho, si el disco de tu servidor se corrompe, puedes usar cualquiera de los clones en cualquiera de los clientes para devolver el servidor
al estado en el que estaba cuando fue clonado.
Podemos clonar un repositorio con git clone [url].
Por ejemplo si quisieramos clonar la libreria de Git llamada libgit2 podemos hacer algo asi:
git clone https://github.com/libgit2/libgit2
Esto crea un directorio llamado libgit2/, inicializa un directorio .git/ en su interior, descarga toda la informacion de ese repositorio y saca una copia de trabajo de la ultima version.
Si quisieramos clonarlo con otro nombre que no sea libgit2, podemos especificarlo asi:
git clone https://github.com/libgit2/libgit2 nombre
Ya tenemos un repositorio Git y un checkout o copia de trabajo de los archivos de dicho proyecto. El siguiente paso es realizar algunos cambios y confirmar instantaneas (snapshots) de esos cambios en el repositorio cada vez que el proyecto alcance un estado que queramos conservar.
Recordemos que los archivos que tienen seguimiento en Git pueden tener 3 estados: committed (Confirmado), modified (Modificado) y staged (Preparado).
Ademas de archivos con seguimiento podemos tener archivos que son nuevos para Git y que no tienen seguimiento, aqui es donde se dividen en 2 estados mas:
Cuando clonas por primera vez un repositorio, todos los archivos van a estar rastreados (tracked) y sin modificar (unmodified).
Mientras editas archivos, Git los ve como modificados, porque fueron cambiados desde su ultimo commit. Luego preparas estos archivos modificados y finalmente confirmas todos los cambios preparados, y repites el ciclo.
Aqui Unmodified y Modified son archivos rasteados (tracked)
La herramienta principal para determinar que archivos estan en que estado es el comando git status.
Si ejecutas este comando inmediatamente despues de clonar un repositorio, deberias ver algo como esto:
Esto significa que tienes un directorio de trabajo limpio, osea que no hay archivos rastreados y modificados. Ademas, Git no encuentra archivos sin rastrear, de lo contrario aparecerian listados ahi. Finalmente, el comando te indica en cual rama estas y te informa que no ha variado respecto a la misma rama en el servidor.
Supongamos que añades un nuevo archivo a tu proyecto, un simple README.
Si el archivo no existia antes y ejecutar git status, veras el archivo sin rastrear de la siguiente manera:
Git no incluira en tu proximo commit a los archivos sin rastrear a menos que se lo indiques explicitamente.
Para comenzar a rastrear un archivo debes usar el commando git add. Para comenzar a rastrar el archivo README, puede ejecutar lo siguiente:
git add README
Ahora si vuelves a ver el estado del proyecto, veras que el archivo README esta siendo rastreado y esta preparado para ser confirmado:
Si confirmamos en este punto, se guardara en el historial la version del archivo correspondiente al instante en que ejecutaste git add.
Anteriormente cuando ejecutaste git init, ejecutaste luego git add [files], lo cual indico el rastreo de archivos en tu directorio.
El comando git add puede recibir tanto una ruta de archivo como de un directorio. Si es un directorio, el comando añade recursivamente los archivos que estan dentro de el.
Vamos a cambiar un archivo que este rastreado. Si cambias el archivo rastrado CONTRIBUTING.md y luego ejecutas el commando git status, veras algo como esto:
La cabecera “Changes not staged for commit”, significa que existe un archivo rastreado que ha sido modificado en el directorio de trabajo pero que aun no esta preparado.
Para prepararlo, ejecutas el comando git add:
Ambos archivos estan preparados y formaran parte de tu proxima confirmacion.
Ahora supongamos que recuerdas que debes hacer un pequeño cambio en CONTRIBUTING.md antes de confirmarlo.
Modificas el archivo y ahora estas listo para confirmar. Sin embargo, ejecutemos git status una vez mas:
¿¿Comoooo??…
Ahora CONTRIBUTING.md aparece como preparado y como no preparado
Resulta que Git prepara un archivo de acuerdo al estaod que tenia cuando ejecutas el comando git add.
Si confirmas ahora, se confirmara la version de CONTRIBUTING.md que tenias la ultima vez que ejecutaste git add
y no la version que ves ahora en tu directorio de trbaajo al ejecutar git status.
Si modificas un archivo luego de ejecutar git add, deberas ejecutar git add de nuevo para preparar la ultima version del archivo:
Si bien es cierto que la salida de git status es bastante explicita, tambien es verdad que es muy extensa.
Existe un comando para una version mas corta del estado de los cambios.
Ejecutando git status -s o git status --short obtendremos algo como esto:
Las letras que indican el estado de los archivos se compone de 2 columnas:
Cada letra tiene su significado, la M indica Modificado, la A indica Nuevo Archivo, y los ?? significa No Rastreado.
A veces, trendras algun tipo de archivo que no quieres que Git añada automaticamente o mas aun, que ni siquiera quieras que aparezca como no rastreado.
En estos casos, puedes crear un archivo llamado .gitignore que liste patrones a considerar. Aqui un ejemplo:
Se pueden definir archivos o carpetas o patrones de archivos y carpetas utilizando expresiones regulares.
Si el comando git status te resulta muy impreciso. Puedes ver exactamente que esta pasando con el comando git diff.
Un ejemplo de git status vs git diff:
Tambien podemos ver que hemos preparado hasta ahora, con git diff --staged o git diff --cached (son el mismo comando):
Ahora que el area de preparacion (staging) esta como queremos, podemos confirmar los cambios. La forma sencilla de confirmar es:
git commit
Al ejecutar, arrancara el editor que configuramos usando git config --global core.editor <editor>:
# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
#
# On branch master
# Changes to be committed:
# modified: CONTRIBUTING.md
# new file: docs.txt
# modified: main.c
#
# Changes not staged for commit:
# modified: CONTRIBUTING.md
# modified: README
#
# Untracked files:
# LICENSE
#
# ------------------------ >8 ------------------------
# Do not modify or remove the line above.
# Everything below it will be ignored.
Tambien podemos indicar el mensaje de confirmacion directamente en el comando, esta es la forma convencional de realizar commits ya que es mas rapida:
git commit -m "feat: nueva funcionalidad!"
Tambien podemos agregarle la opcion -a para que Git prepare automaticamente todos los archivos rastreados antes de confirmarlos,
ahorrandote el paso de git add:
git commit -am "fix: terrible bug de aliasing"
Para eliminar archivos de Git, debes eliminarlos de los archivos rastreados (del area de preparacion) y luego confirmar.
Para eso existe el comando git rm, que ademas elimina el archivo de tu directorio de trabajo de manera que no aparezca la proxima vez como un archivo no rastreado.
Tambien es posible solo quitarlos del area de preparacion (staging) sin eliminarlos del disco, usando:
git rm --cached <archivo>
En general los comandos de git que aceptan nombres de archivo, tambien aceptan patrones
glob, ej:git rm log/\*.txt
A diferencia de muchos VCSs, Git no rastrea explicitamente los cambios de nombre en archivos. Si renombras un archivo en Git,
no se guardara ningun metadato que indique que renombraste el archivo. Sin embargo, Git es bastante listo como para detectar estos cambios luego que los has hehco.
Por esto, resulta confuso que Git tenga un comando mv. Si quieres renombrar un archivo en Git, puedes ejecutar algo como:
git mv file_old file_new
Tambien si luego ejecutas git status, veras que Git lo considera como un renombramiento, ejemplo git mv README.md README:
git status
On branch master
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
renamed: README.md -> README
Fundamentos de Git (parte 3)
Historial, Deshacer, Remotos y AliasLa herramienta mas basica y potente para ver el historial es git log.
Para los siguientes ejemplos se usara el repositorio simplegit.
git clone https://github.com/schacon/simplegit-progit
Cuando ejecutes git log sobre este repositorio, deberias ver una salida similar a esta:
El comando git log proporciona una gran cantidad de opcoines para mostrarte exactamente lo que buscas.
-p -2Una de las opciones mas utiles es -p, que muestra las diferencias en cada confirmacion.
Tambien se puede usar -2 para limitar a las ultimas 2 entradas del historial.
--statSi quieres ver estadisticas de cada confirmacion puedes usar la opcion --stat.
--prettyEsta opcion es realmente util, porque modifica el formato de la salida.
Tienes unos cuantos estilos disponibles, la sub-opcion oneline imprime cada confirmacion en una unica linea.
--pretty=formatLa opcion mas interesante es format, que te permite especificar tu propio formato.
Por ejemplo usando %h, %an, %ar y %s:
git log --pretty=format| Opción | Descripción de la salida |
|---|---|
%H | Hash de la confirmación |
%h | Hash de la confirmación abreviado |
%T | Hash del árbol |
%t | Hash del árbol abreviado |
%P | Hashes de las confirmaciones padre |
%p | Hashes de las confirmaciones padre abreviados |
%an | Nombre del autor |
%ae | Dirección de correo del autor |
%ad | Fecha de autoría (el formato respeta la opción --date) |
%ar | Fecha de autoría, relativa |
%cn | Nombre del confirmador |
%ce | Dirección de correo del confirmador |
%cd | Fecha de confirmación |
%cr | Fecha de confirmación, relativa |
%s | Asunto |
--graphLas sub-opciones oneline y format son especialmente utiles combinadas con la opcion --graph.
Esta añade un mini grafico ASCII mostrando tu historial de ramificaciones y uniones (branches & merges)
git log| Opción | Descripción |
|---|---|
-p | Muestra el parche introducido en cada confirmación. |
--stat | Muestra estadísticas sobre los archivos modificados en cada confirmación. |
--shortstat | Muestra solamente la línea de resumen de la opción --stat. |
--name-only | Muestra la lista de archivos afectados. |
--name-status | Muestra la lista de archivos afectados, indicando además si fueron añadidos, modificados o eliminados. |
--abbrev-commit | Muestra solamente los primeros caracteres de la suma SHA-1, en vez de los 40 caracteres de que se compone. |
--relative-date | Muestra la fecha en formato relativo (por ejemplo, “2 weeks ago” (“hace 2 semanas”)) en lugar del formato completo. |
--graph | Muestra un gráfico ASCII con la historia de ramificaciones y uniones. |
--pretty | Muestra las confirmaciones usando un formato alternativo. Posibles opciones son oneline, short, full, fuller y format (mediante el cual puedes especificar tu propio formato). |
Ademas de formatear la salida, existen una seria de opciones para limitarla.
Por ejemplo ya vimos la opcion -2 que proviene de la opcion -<n> que limita la salida a n entradas.
Las opciones temporales --since (desde) y --until (hasta) son muy utiles.
Por ejemplo, este comando lista todas las confirmaciones hechas durante las ultimas 2 semanas:
git log --since=2.weeks
Este comando acepta muchos formatos. Puedes indicar exactamente
"2008-01-15"o de manera relativa como"2 years 1 day 3 minutes ago"
-SLa ultima opcion realmente util para filtrar la salida de git log es -S,
la cual recibe un string y solo muestra confirmaciones que cambiaron el codigo añadiendo o eliminando el string indicado.
Por ejemplo si queremos encontrar la ultima confirmacion que modifico una referencia a una funcion especifica, ejecutamos:
git log -Sfunction_name
git log| Opción | Descripción |
|---|---|
-(n) | Muestra solamente las últimas n confirmaciones |
--since, --after | Muestra aquellas confirmaciones hechas después de la fecha especificada. |
--until, --before | Muestra aquellas confirmaciones hechas antes de la fecha especificada. |
--author | Muestra sólo aquellas confirmaciones cuyo autor coincide con la cadena especificada. |
--committer | Muestra sólo aquellas confirmaciones cuyo confirmador coincide con la cadena especificada. |
-S | Muestra sólo aquellas confirmaciones que añaden o eliminen código que corresponda con la cadena especificada. |
En cualquier momento puede que quieras deshacer algo.
Una de las acciones mas comunes a deshacer es cuando confirmas un cambio antes de tiempo y olvidas agregar algun archivo,
Si quieres rehacer la confirmacion, puedes reconfirmar con la opcion --amend.
git commit --amend
Este comando utiliza tu area de preparacion (staging) para la confirmacion. Si no has hecho cambios desde tu ultima confirmacion, entonces la instantanea (snapshot) lucira exactamente igual y lo unico que cambiaras sera el mensaje de confirmacion.
Se lanzara el mismo editor de confirmacion, pero veras que ya incluye el mensaje de tu confirmacion anterior. Edita el mensaje y se sobreecribira tu confirmacion anterior.
Por ejemplo, si confirmas y luego te das cuenta que olvidaste preparar los cambios de un archivo que querias incluir en esta confirmacion, puedes hacer lo siguiente:
git commit -m 'initial commit'
git add forgotten_file
git commit --amend
Al final terminaras con una sola confimarcion, la segunda confirmacion reemplaza el resultado de la primera.
Supongamos que has cambiado 2 archivos y que quieres confirmarlos como dos cambios separados,
pero accidentalmente has escrito git add * y has preparado ambos. ¿Como puedes sacar del area de preparacion uno de ellos?
Veamos que dice git status:
El propio git status te indica como hacerlo:
git restore --staged <archivo>
El archivo vuelve al estado modified pero ya no esta preparado.
¿Que pasa si te das cuenta que no quieres mantener los cambios del archivo CONTRIBUTING.md?
¿Como puedes revertirlo facilmente a como estaba en la ultima confirmacion?
git status tambien te lo indica:
git restore CONTRIBUTING.md
Importante:
git restore <archivo>es un comando destructivo. Cualquier cambio que hayas hecho al archivo desaparecera — se sobreescribira con la ultima version confirmada. Nunca uses este comando a menos que estes absolutamente seguro de que no quieres los cambios.
Para colaborar necesitas saber como gestionar repositorios remotos. Un remoto es una version de tu proyecto hospedada en internet o en otra red.
git remote # lista los nombres
git remote -v # lista nombres + URLs
git remote add origin https://github.com/usuario/repo.git
git fetch origin # descarga cambios sin integrarlos a tu rama
git pull # descarga + integra automaticamente (fetch + merge)
La diferencia entre
fetchypulles importante:fetchsolo descarga, tu decides cuando integrar.pulllo hace automaticamente.
git push origin main
Si alguien mas hizo push antes que tu, deberas integrar su trabajo primero con
git pull.
Nota: En la clase 7 veremos remotos en profundidad: tracking branches,
git remote rename,git remote remove,push --force-with-leasey mas.
Git no deduce automaticamente tu comando si lo tecleas parcialmente.
Puedes establecer facilmente un alias para cada comando mediante git config.
git config --global alias.co checkout
git config --global alias.br branch
git config --global alias.ci commit
git config --global alias.st status
Esto significa que, por ejemplo, en lugar de teclear git commit, solo necesitas teclear git ci.
# Ver el historial como grafo visual
git config --global alias.lg "log --oneline --graph --all --decorate"
# Ver el ultimo commit
git config --global alias.last "log -1 HEAD"
# Sacar del staging (version moderna)
git config --global alias.unstage "restore --staged"
Git simplemente sustituye el nuevo comando por lo que hayas puesto en el alias. Usaremos
git lgconstantemente a partir de la clase 3 para visualizar el DAG.
2.1 a 2.8 (excepto 2.6, junto con un poco de 2.5 y 2.7)