$ gitinit
hint: Using 'master' as the name for the initial branch. This default branch namehint: is subject to change. To configure the initial branch name to use in allhint: of your new repositories, which will suppress this warning, call:hint:hint: git config --global init.defaultBranch <name>hint:hint: Names commonly chosen instead of 'master' are 'main', 'trunk' andhint: 'development'. The just-created branch can be renamed via this command:hint:hint: git branch -m <name>Initialized empty Git repository in /home/fraya/Dylan/curso-de-git/.git/
Esto creará un directorio .git para almacenar nuestro repositorio
local.
Nota
Tradicionalmente a la rama inicial de un proyecto se la llamaba
master, pero esta palabra en inglés tiene connotaciones
esclavistas ya que también significa amo, en la relación
amo/esclavo.
Las nuevas tendencias en USA pretenden cambiar las palabras sin
cambiar sus actos, pensando que al desaparecer la palabra «amo»
desaparecen los esclavos. Por ello git nos da una pista por si
queremos cambiar la rama inicial y poner main de nombre.
Podemos ser politicamente correctos y cambiarlo para todos los
nuevos repositorios, evitando este mensaje de advertencia
git config --global init.defaultBranch main
siempre que seamos conscientes de que sigue habiendo esclavitud.
$ gitstatus
On branch mainNo commits yetUntracked files: (use "git add <file>..." to include in what will be committed) _packages/ curso-de-git-app-library.dylan curso-de-git-app.dylan curso-de-git-app.lid curso-de-git.dylan curso-de-git.lid dylan-package.json library.dylan registry/ tests/nothing added to commit but untracked files present (use "git add" to track)
Estamos en la rama (branch) main y no tenemos ninguna
confirmación (commits) aún. Tenemos una lista de ficheros sin
seguimiento (untracked) y podemos añadirlos usando gitadd.
Sin embargo dentro de los ficheros untracked hay ficheros que no
necesitamos en nuestro repositorio.
Directorios que no queremos incluir en el repositorio¶
$ gitstatus
On branch mainNo commits yetUntracked files: (use "git add <file>..." to include in what will be committed) _packages/ curso-de-git-app-library.dylan curso-de-git-app.dylan curso-de-git-app.lid curso-de-git.dylan curso-de-git.lid dylan-package.json library.dylan registry/ tests/
Los directorios _packages y registry son creados por la
herramienta dylan. Estos datos pueden ser creados de nuevo usando
el comando dylanupdate en este directorio cuando lo
necesitemos. Por lo tanto no deben estar dentro de nuestro
repositorio. Para evitar que estos ficheros entren dentro por error,
crearemos un fichero llamado .gitignore con una lista de ficheros
y directorios que no queremos que git incluya ni que aparezcan en los
comandos.
$ gitstatus
On branch mainNo commits yetUntracked files: (use "git add <file>..." to include in what will be committed) .gitignore curso-de-git-app-library.dylan curso-de-git-app.dylan curso-de-git-app.lid curso-de-git.dylan curso-de-git.lid dylan-package.json library.dylan tests/nothing added to commit but untracked files present (use "git add" to track)
Vemos que los directorios _packages y registry ya no aparecen
entre los ficheros sin seguimiento. De la misma manera desaparecerían
cualquier fichero terminado en ~ (copia de seguridad en Unix) o
.bak (copia de seguridad en Windows) en cualquier
subdirectorio (ya que no empieza la línea por / en
.gitignore).
Ya estamos preparados para añadir la aplicación. Podemos añadir los
ficheros uno a uno, gitadd.gitignore por ejemplo o añadirlos
todos a la vez, gitadd.
Añadimos todos los ficheros al área de preparación o staged¶
$ gitstatus
On branch mainNo commits yetChanges to be committed: (use "git rm --cached <file>..." to unstage) new file: .gitignore new file: curso-de-git-app-library.dylan new file: curso-de-git-app.dylan new file: curso-de-git-app.lid new file: curso-de-git.dylan new file: curso-de-git.lid new file: dylan-package.json new file: library.dylan new file: tests/curso-de-git-test-suite.dylan new file: tests/curso-de-git-test-suite.lid new file: tests/library.dylan
Nos muestra que tenemos una lista de cambios en seguimiento preparados
para ser confirmados (committed). También nos muestra que podemos
sacarlos del área de seguimento (unstage) mediante el comando gitrm--cached<file>....
No se trata de tu primera comunión ni de la confirmación de la Iglesia
sino de aceptar los ficheros en seguimiento y guardarlos en el
repositorio. Sin más dilación:
$ gitcommit-m"Revisión inicial"
Confirmamos con commit y escribimos un mensaje de confirmación
(-m) en la misma línea (más adelante veremos como hacerlo más
correcto).
Vamos a compilar el programa y ejecutar las pruebas:
$ dylanbuild--all
Saldrá mucho texto y unas advertencias que no tiene importancia. Una
vez terminado saldrá algo parecido a esto:
...Opened project curso-de-git (/home/fraya/Dylan/curso-de-git/curso-de-git.lid)Build of 'curso-de-git' completed[========================================] Building targets: dll within /hom...
Los programas ejecutables se encuentran en _build/bin. Ejecutemos
las pruebas:
$ _build/bin/curso-de-git-test-suite
Suite curso-de-git-test-suite:Test test-greeting:Test test-greeting PASSED in 0.000048s and 8KiBTest test-$greeting:Test test-$greeting PASSED in 0.000032s and 2KiBSuite curso-de-git-test-suite PASSED in 0.000080s and 10KiBRan 2 assertionsRan 2 testsPASSED in 0.000080 seconds
Ejecutemos ahora el programa:
$ _build/bin/curso-de-git-app
Hello world!
Nos gustaría que el mensaje fuera distinto de Hello world! y que
dijese ¡Hola mundo! u ¡Hola mamá!. Vamos a cambiar el código para
que al pasarle un parámetro nos devuelva un Hola personalizado.
Primero voy a cambiar las pruebas con lo que quiero que salga:
1Module:curso-de-git-test-suite 2 3definetesttest-$greeting() 4assert-equal("Hola",$greeting); 5endtest; 6 7definetesttest-greeting() 8assert-equal("¡Hola mundo!",greeting("mundo")); 9endtest;1011// Use `_build/bin/curso-de-git-test-suite --help` to see options.12run-test-application()
En la línea 4, quiero asegurarme que la constante $greeting que
contenía Hello World! se vaya a cambiar por Hola.
En la línea 8, quiero que cuando le pase el parámetro mundo a la
función greeting me devuelva ¡Hola mundo!.
Realizo los cambios, compilo con dylanbuild-a o dylanbuild--all.
$ dylanbuild-a
Open Dylan 2023.1Opened project curso-de-git-app (/home/fraya/Dylan/curso-de-git/curso-de-git-app.lid)Build of 'curso-de-git-app' completed[==================================== ] Building targets: exe within /hom...Open Dylan 2023.1Opened project curso-de-git-test-suite (/home/fraya/Dylan/curso-de-git/tests/curso-de-git-test-suite.lid)/home/fraya/Dylan/curso-de-git/tests/curso-de-git-test-suite.dylan:8.33-50:Serious warning - Too many arguments in call to method greeting () => (s :: <string>) - 1 supplied, 0 expected. ----------------- assert-equal("¡Hola mundo!", greeting("mundo")); -----------------Build of 'curso-de-git-test-suite' completed[================================= ] Building targets: exe within /hom...Build of "curso-de-git-test-suite" failed with exit status 3.
El compilador se pone serio y nos avisa con un Serious warning de
que tenemos demasiados argumentos para llamar al método greeting,
que esperaba 0 y le hemos pasado 1. En la siguiente línea nos muestra
donde ha visto el código sospechoso.
Como un día que vas a cenar y se presenta alguien sin avisar, el
método greeting tiene un invitado que no esperaba. ¿Qué pasará si
ejecutamos las pruebas?
1$ _build/bin/curso-de-git-test-suite
2Suite curso-de-git-test-suite: 3 Test test-$greeting: 4 FAILED: "Hola" = $greeting 5 want: "Hola" 6 got: "Hello world!" 7 detail: sizes differ (4 and 12); element 1 is the first mismatch 8 Test test-$greeting FAILED in 0.000245s and 23KiB 9 Test test-greeting:10 CRASHED: "\<C2>\<A1>Hola mundo!" = greeting("mundo")11 Error evaluating assert-equal expressions: Attempted to call {<simple-method>: ??? () => (<string>)} with 1 arguments12 Test test-greeting FAILED in 0.000230s and 25KiB13Suite curso-de-git-test-suite FAILED in 0.000475s and 47KiB14 FAILED: curso-de-git-test-suite15 FAILED: test-$greeting16 FAILED: "Hola" = $greeting17 want: "Hola"18 got: "Hello world!"19 detail: sizes differ (4 and 12); element 1 is the first mismatch20 FAILED: test-greeting21 CRASHED: "\<C2>\<A1>Hola mundo!" = greeting("mundo")22 Error evaluating assert-equal expressions: Attempted to call {<simple-method>: ??? () => (<string>)} with 1 arguments2324Ran 2 assertions25Ran 2 tests: 2 failed26FAILED in 0.000475 seconds
En la línea 3 vemos que la primera prueba ha fallado, queríamos
(want) que saliera Hola, pero tuvimos (got) Hello world!.
En la línea 9 nuestro segunda prueba también ha fallado, esta vez con
el programa roto (crashed). El programa ha preferido suicidarse
antes que ejecutar algo que podría haber estropeado el ordenador.
Nota
Cuando un programa hace un crash, se mata el mismo y suele
escribir un volcado de la memoria en el momento de la ejecución
para que el programador haga la autopsia y pueda saber que
pasó. Este volcado se conoce como core dump. El volcado del
fallo y el lugar donde se guarda dependen de tu sistema
operativo. ¡Ya tienes deberes, averígualo!.
Vamos a arreglarlo, primero cambiaremos la constante $greeting
para que contenga Hola en lugar de Hello world!.
Primero en la línea 7 pasamos a greeting el parámetro mensaje,
que pasa de no esperar a ninguno () a esperar uno (mensaje).
En la línea 8 unimos todas las cadenas para conseguir el ¡Hola
<mensaje>!.
Grabamos y compilamos el código:
$ dylanbuild-a
Open Dylan 2023.1Opened project curso-de-git-app (/home/fraya/Dylan/curso-de-git/curso-de-git-app.lid)/home/fraya/Dylan/curso-de-git/curso-de-git-app.dylan:5.22-32:Serious warning - Too few arguments in call to methodgreeting (mensaje :: <object>) => (s :: <string>) -0 supplied, 1 expected. ---------- format-out("%s\n", greeting()); ----------Build of 'curso-de-git-app' completed[==================================== ] Building targets: exe within /hom
Claro, hemos arreglado las pruebas pero no el programa principal, que
sigue esperando una función greeting sin parámetros.
Module:curso-de-git-appdefinefunctionmain(name::<string>,arguments::<vector>)format-out("%s\n",greeting(arguments[0]));exit-application(0);endfunction;// Calling our main function (which could have any name) should be the last// thing we domain(application-name(),application-arguments());
Le pasamos a greeting el primer argumento que le pase el usuario
al programa ejecutable (arguments[0]).
Grabamos, compilamos (ya no se muestran errores) y probamos.
$ _build/bin/curso-de-git-test-suite
Suite curso-de-git-test-suite: Test test-greeting: Test test-greeting PASSED in 0.000066s and 8KiB Test test-$greeting: Test test-$greeting PASSED in 0.000009s and 2KiBSuite curso-de-git-test-suite PASSED in 0.000075s and 10KiBRan 2 assertionsRan 2 testsPASSED in 0.000075 seconds
Dan ganas de saludar a todo el mundo. Tranquila. Vamos a ver el
repositorio.
$ gitstatus
On branch mainChanges not staged for commit: (use "git add <file>..." to update what will be committed) (use "git restore <file>..." to discard changes in working directory) modified: curso-de-git-app.dylan modified: curso-de-git.dylan modified: tests/curso-de-git-test-suite.dylan Untracked files: (use "git add <file>..." to include in what will be committed) _build/ no changes added to commit (use "git add" and/or "git commit -a")
Por un lado vemos que tenemos ficheros modificados (modified) que
deberíamos añadir al stage. Por otro que el compilador ha creado el
directorio _build/ y que no tiene seguimiento.
Module:curso-de-git-appdefinefunctionmain(name::<string>,arguments::<vector>)letmensaje=if(arguments.size<1)"mundo"elsearguments[0]end;format-out("%s\n",greeting(mensaje));exit-application(0);endfunction;// Calling our main function (which could have any name) should be the last// thing we domain(application-name(),application-arguments());
Esta vez añadimos los cambios a la fase de staging, pero sin
confirmarlos (commit).
$ gitaddcurso-de-git-app.dylan
Volvemos a modificar el programa para indicar con un comentario lo que
hemos hecho:
Module:curso-de-git-appdefinefunctionmain(name::<string>,arguments::<vector>)// Si no hay argumentos poner mensaje por defectoletmensaje=if(arguments.size<1)"mundo"elsearguments[0]end;format-out("%s\n",greeting(mensaje));exit-application(0);endfunction;// Calling our main function (which could have any name) should be the last// thing we domain(application-name(),application-arguments());
Veamos el estado del repositorio:
Estado del repositorio con un fichero en stage y otro en workdir¶
$ gitstatus
On branch mainChanges to be committed: (use "git restore --staged <file>..." to unstage) modified: curso-de-git-app.dylanChanges not staged for commit: (use "git add <file>..." to update what will be committed) (use "git restore <file>..." to discard changes in working directory) modified: curso-de-git-app.dylan
Podemos ver como aparecen el archivo curso-de-git-app.dylan dos
veces. El primero está preparado para ser confirmado y está almacenado
en la zona de staging. El segundo indica que el archivo
curso-de-git-app.dylan está modificado otra vez en la zona de
trabajo (workdir).
Advertencia
Si volvieramos a hacer un gitaddcurso-de-git-app.dylan
sobreescribiríamos los cambios previos que había en la zona de
staging.
$ gitcommit-m"Se añade un parámetro por defecto"[master 47e9e6f] Se añade un parámetro por defecto1 file changed, 5 insertions(+)$ gitstatus
On branch mainChanges not staged for commit: (use "git add <file>..." to update what will be committed) (use "git restore <file>..." to discard changes in working directory) modified: curso-de-git-app.dylanno changes added to commit (use "git add" and/or "git commit -a")$ gitadd.
$ gitstatus
On branch mainChanges to be committed: (use "git restore --staged <file>..." to unstage) modified: curso-de-git-app.dylan
Author: Fernando Raya <f..@gmail.com>Date: Sat May 18 17:02:01 2024 +0200 Añade comentario para parámetro por defectocommit 47e9e6f5ba7aa85e63d17c31e244f020a93ec8bbAuthor: Fernando Raya <f..@gmail.com>Date: Sat May 18 17:00:59 2024 +0200 Se añade un parámetro por defectocommit 73c695b8c026eb0f7c5ff3bf0ad6215dee359953Author: Fernando Raya <f..@gmail.com>Date: Sat May 18 14:01:28 2024 +0200 Parametrizar el mensaje de saludocommit 81f67ea62cc92abc6e39a9daaae9d207165def31Author: Fernando Raya <f..@gmail.com>Date: Sat May 18 11:02:01 2024 +0200 Revisión inicial
También es posible ver versiones abreviadas o limitadas, dependiendo
de los parámetros:
$ gitlog--oneline
2ee18ff (HEAD -> main) Añade comentario para parámetro por defecto47e9e6f Se añade un parámetro por defecto73c695b Parametrizar el mensaje de saludo81f67ea Revisión inicial
Una versión muy útil de git log es la siguiente, pues nos permite
ver en que lugares está main y HEAD, entre otras cosas:
git log --pretty=format:'%h %ad | %s%d [%an]' --grap--date=short* 2ee18ff 2024-05-18 | Añade comentario para parámetro por defecto (HEAD -> main) [Fernando Raya]* 47e9e6f 2024-05-18 | Se añade un parámetro por defecto [Fernando Raya]* 73c695b 2024-05-18 | Parametrizar el mensaje de saludo [Fernando Raya]* 81f67ea 2024-05-18 | Revisión inicial [Fernando Raya]
Cada cambio es etiquetado por un hash, para poder regresar a ese
momento del estado del proyecto se usa la orden gitcheckout.
$ gitcheckout81f67ea
Note: checking out '81f67ea'.You are in 'detached HEAD' state. You can look around, make experimentalchanges and commit them, and you can discard any commits you make in thisstate without impacting any branches by performing another checkout.If you want to create a new branch to retain commits you create, you maydo so (now or later) by using -b with the checkout command again. Example: git checkout -b new_branch_nameHEAD is now at 81f67ea... Revisión inicial
El aviso que nos sale nos indica que estamos en un estado donde no
trabajamos en ninguna rama concreta. Eso significa que los cambios que
hagamos podrían «perderse» porque si no son guardados en una nueva
rama, en principio no podríamos volver a recuperarlos. Hay que pensar
que Git es como un árbol donde un nodo tiene información de su nodo
padre, no de sus nodos hijos, con lo que siempre necesitaríamos
información de dónde se encuentran los nodos finales o de otra manera
no podríamos acceder a ellos.
Para poder recuperar versiones concretas en la historia del
repositorio, podemos etiquetarlas, lo cual es más facil que usar un
hash. Para eso usaremos la orden gittag.
$ gittagv1
Ahora vamos a etiquetar la versión inmediatamente anterior como
v1-beta. Para ello podemos usar los modificadores ^ o ~ que
nos llevarán a un ancestro determinado. Las siguientes dos órdenes son
equivalentes:
$ gitcheckoutv1^
$ gitcheckoutv1~1
Si ejecutamos la orden sin parámetros nos mostrará todas las etiquetas
existentes.
$ gittag
v1
Y para verlas en el historial:
$ gitlog--oneline
b804719 (HEAD -> main, tag: v1) Añade comentario para parámetro por defecto9f41f0b Se añade un parámetro por defectocf2b658 Parametrizar el mensaje de saludo56b0ec5 Revisión inicial
Ya hemos visto antes que podemos ignorar ciertos ficheros para que no
entren en nuestro repositorio. La orden gitadd. o gitaddnombre_directorio es muy cómoda, ya que nos permite añadir todos los
archivos del proyecto o todos los contenidos en un directorio y sus
subdirectorios. Es mucho más rápido que tener que ir añadiéndolos uno
por uno. El problema es que, si no se tiene cuidado, se puede terminar
por añadir archivos innecesarios o con información sensible.
Por lo general se debe evitar añadir archivos que se hayan generado
como producto de la compilación del proyecto, los que generen los
entornos de desarrollo (archivos de configuración y temporales) y
aquellos que contentan información sensible, como contraseñas o tokens
de autenticación. Por ejemplo, en un proyecto de C/C++, los archivos
objeto no deben incluirse, solo los que contengan código fuente y los
make que los generen.
Para indicarle a git que debe ignorar un archivo, se puede crear un
fichero llamado .gitignore, bien en la raíz del proyecto o en los
subdirectorios que queramos. Dicho fichero puede contener patrones,
uno en cada línea, que especiquen qué archivos deben ignorarse. El
formato es el siguiente:
dir1/ # ignora todo lo que contenga el directorio dir1
!dir1/info.txt # El operador ! excluye del ignore a dir1/info.txt (sí se guardaría)
dir2/*.txt # ignora todos los archivos txt que hay en el directorio dir2
dir3/**/*.txt # ignora todos los archivos txt que hay en el dir3 y sus subdirectorios
*.o # ignora todos los archivos con extensión .o en todos los directorio
Cada tipo de proyecto genera sus ficheros temporales, así que para
cada proyecto hay un .gitignore apropiado. Existen repositorios
que ya tienen creadas plantillas. Podéis encontrar uno en
https://github.com/github/gitignore