Notxor tiene un blog

Defenestrando la vida


Cómo estoy utilizando git

Git es uno de mis demonios

Dada mi torpeza habitual git es uno de esas herramientas que se me atraviesan. Y además, no es por falta de uso o de empeño. De hecho lo utilizo con bastante asiduidad en los proyectos que voy emprendiendo y que se convierten en vaporware la mayoría. Cuando creo que tengo dominada la bestia meto el cuezo en algún momento y git me lo devuelve (doblado). Como muchas veces tengo que estar mirando mis chuletas de cómo usarlo (y algunas veces no las tengo a mano) por eso, voy a ponerlo en limpio en este artículo.

Hace tiempo que utilizo sistemas de control de versiones, desde el viejuno cvs o su heredero svn, hasta que apareció git y se quedó en mi ordenador.

Creación de un repositorio para uso personal

Para muchos proyectos de los que empiezo se podría utilizar algún repositorio como hithub, que proporcionan espacio de almacenamiento para el código y para una página web sencilla, que no necesite bases de datos ni mucha infraestructura detrás.

Sin embargo, como la mayoría de mis proyectos son tan sólo tentativas o son personales, como este blog, yo tengo la tendencia a crear mi repositorio en «mi nube». Tengo montado un nextcloud de uso personal y lo que hago es montarlo todo en esa nube que además me sirve de «copia de seguridad» del repositorio en caso de desastre.

Lo primero que hay que hacer es crear lo que llamo repositorio remoto y una vez creado lo que hago es crear el que llamo repositorio local en mi directorio de proyectos clonando el remoto.

Por ejemplo:

~/nube> mkdir proyecto-nuevo.git
~/nube> cd proyecto-nuevo.git
~/nube/proyecto-nuevo.git> git init --bare

Lo primero que he hecho es crear un directorio proyecto-nuevo.git que de momento es una carpeta normal y corriente. Entro en ella y le digo a git que inicie un repositorio. La opción --bare le dice a git que ese repositorio no se utilizará como copia local sino como copia remota. Es decir, no voy a trabajar con este repositorio directamente sino que lo utilizaré como almacén remoto y en él no se guardarán los ficheros de trabajo directamente sino la base de datos (por llamar a como guarda git la información de una manera sencilla) donde se guardan los datos. Por defecto, git llamará a este repositorio origin, como origen de todo.

Puede parecer un enredo mantener un repositorio remoto en un directorio al que puedes acceder como de forma local, pero se trata de mantener la filosofía de red. Luego, esto que se almacena en la nube, es accesible desde cualquier equipo que conecte a la misma. Y por ello me gusta pensar en esto como remoto.

Pero para trabajar, necesito un repositorio local que sí pueda modificar, y eso lo hago con:

~/proyectos> git clone ~/nube/proyecto-nuevo.git
Clonando en 'proyecto-nuevo'...
warning: Pareces haber clonado un repositorio sin contenido.
hecho.

Ese comando clona el repositorio remoto y crea el directorio proyecto-nuevo en mi carpeta de proyectos. Eso sí, avisa de que el repositorio que estoy clonando está vacío, algo que voy a arreglar añadiendo contenido al mismo, así que ese aviso no me preocupa. Es el momento oportuno para añadir, por ejemplo, un fichero README que describa un poco de qué va el proyecto.

~/proyectos/proyecto-nuevo> echo "Ejemplo de repositorio para explicar cómo utilizo _git_." > README.md

~/proyectos/proyecto-nuevo> git status
En la rama master

No hay commits todavia

Archivos sin seguimiento:
  (usa "git add <archivo>..." para incluirlo a lo que se será confirmado)

        README.md

no hay nada agregado a la confirmación pero hay archivos sin seguimiento presentes (usa "git add" para hacerles seguimiento)

git status es el bastón del ciego con ella voy dando golpecitos en la acera de git para no tropezar. En este caso me dice que estoy en la rama master (luego hablaré sobre las ramas), que no tiene commits todavía, que hay un archivo README.md que está en el lugar pero sin seguimiento (o sin sincronizar) y aconseja utilizar el comando add para añadirlo al repositorio... Pues nada, voy a hacerle caso:

~/proyectos/proyecto-nuevo> git add README.md 
~/proyectos/proyecto-nuevo> git status
En la rama master

No hay commits todavia

Cambios a ser confirmados:
  (usa "git rm --cached <archivo>..." para sacar del area de stage)

        nuevo archivo:  README.md

Me vuelve a repetir que estoy en la rama master que todavía no hay commits y que tiene en la lista para añadir el fichero README.md, que me puedo arrepentir con el comando rm. Pero como en mi caso lo quiero confirmar para hacer el primer commit del repositorio, hago lo siguiente.

~/proyectos/proyecto-nuevo> git commit -m "Primer commit del repositorio añadiendo el README.md."
[master(commit-raiz) f1f91e9] Primer commit del repositorio añadiendo el README.md.
 1 file changed, 1 insertion(+)
 create mode 100644 README.md
~/proyectos/proyecto-nuevo> git status
En la rama master
tu rama esta basada en 'origin/master', pero upstream ha desaparecido.
  (usa "git branch --unset-upstream" para arreglar)

nada para hacer comit, el arbol de trabajo esta limpio
~/proyectos/proyecto-nuevo> git push
Contando objetos: 3, listo.
Delta compression using up to 4 threads.
Comprimiendo objetos: 100% (2/2), listo.
Escribiendo objetos: 100% (3/3), 306 bytes | 306.00 KiB/s, listo.
Total 3 (delta 0), reused 0 (delta 0)
To /home/notxor/nube/proyecto-nuevo.git
 * [new branch]      master -> master
~/proyectos/proyecto-nuevo> git status
En la rama master
Tu rama está actualizada con 'origin/master'.

nada para hacer comit, el arbol de trabajo esta limpio

En todo el código anterior he hecho un commit y un push. El commit lo pienso como añadirlo a la lista de cambios y el push como subirlo al repositorio remoto. Y siempre utilizo entre comando y comando el status, porque es lo que me garantiza en qué estado está el repositorio. Ya he dicho que git es mi bestia negra pero no por fallos de git sino por precipitación mía. Así que utilizo mi bastón de ciego y si encuentro algún bordillo lo arreglo (si sé) antes de tropezar.

Hasta aquí, la creación de un repositorio básico.

Mantenimiento de ramas

Para mantener este blog, estoy utilizando dos ramas del mismo repositorio. Los ficheros html generados, los css, etc. los guardo en la rama master. Sin embargo, los ficheros de texto que creo los guardo en una rama source. En esa rama están todos los ficheros .org pero ninguno de los generados y me mantienen la lógica del sitio de forma que no se mezclen unos ficheros con otros.

Nuestro repositorio de ejemplo, sólo tiene una rama. Lo podemos confirmar así:

~/proyectos/proyecto-nuevo> git branch
* master

Crear ramas

Bueno, vamos a crear otra rama por ejemplo, para hacer pruebas:

~/proyectos/proyecto-nuevo> git branch prueba
~/proyectos/proyecto-nuevo> git branch
* master
  prueba

Ya tengo dos ramas: master y prueba. Además me marca con el * cuál es la rama activa. Ahora cambio a la rama que he creado para hacer pruebas. En este caso no se utiliza el comando branch sino el checkout.

~/proyectos/proyecto-nuevo> git checkout prueba 
Cambiado a rama 'prueba'
~/proyectos/proyecto-nuevo> git branch
  master
* prueba

Ya tengo una rama y me cambiado a ella ahora hago los cambios que necesito y el proceso de commit y push de nuevo. Al tener dos ramas y dos repositorios, tengo que contar con lo que yo llamo los diales. En este caso 2 ramas por 2 repositorios me dan 4 diales:

  • origin/master → (la rama master del remoto)
  • master → (la rama master del local)
  • origin/prueba → (la rama prueba del remoto... bueno, esta aún no la tengo pero hay que contar con ella. Ahora lo cuento.)
  • prueba → (la rama prueba del local)
~/proyectos/proyecto-nuevo> echo "Esto es el fichero de prueba 1." > archivo1.txt
~/proyectos/proyecto-nuevo> git status
En la rama prueba
Archivos sin seguimiento:
  (usa "git add <archivo>..." para incluirlo a lo que se será confirmado)

        archivo1.txt

no hay nada agregado a la confirmación pero hay archivos sin seguimiento presentes (usa "git add" para hacerles seguimiento)
~/proyectos/proyecto-nuevo> echo "Esto es el fichero de prueba 2." > archivo2.txt
~/proyectos/proyecto-nuevo> git status
En la rama prueba
Archivos sin seguimiento:
  (usa "git add <archivo>..." para incluirlo a lo que se será confirmado)

        archivo1.txt
        archivo2.txt

no hay nada agregado a la confirmación pero hay archivos sin seguimiento presentes (usa "git add" para hacerles seguimiento)
~/proyectos/proyecto-nuevo> git add archivo1.txt 
~/proyectos/proyecto-nuevo> git commit -m "Añadir el archivo1.txt a la rama de pruebas."
[prueba ee63815] Añadir el archivo1.txt a la rama de pruebas.
 1 file changed, 1 insertion(+)
 create mode 100644 archivo1.txt
~/proyectos/proyecto-nuevo> git add archivo2.txt
~/proyectos/proyecto-nuevo> git commit -m "Añadir el archivo2.txt a la rama de pruebas."
[prueba a60c91a] Añadir el archivo2.txt a la rama de pruebas.
 1 file changed, 1 insertion(+)
 create mode 100644 archivo2.txt
~/proyectos/proyecto-nuevo> git push
fatal: La rama actual prueba no tiene una rama upstream.
Para realizar un push de la rama actual y configurar el remoto como upstream, use

        git push --set-upstream origin prueba

Todo ha ido como se esperaba, excepto cuando hemos intentado hacer el push que se queja de que el repositorio remoto no tiene ninguna rama que se llame prueba y me sugiere que la cree con el comando correspondiente.

~/proyectos/proyecto-nuevo> git push --set-upstream origin prueba
Contando objetos: 6, listo.
Delta compression using up to 4 threads.
Comprimiendo objetos: 100% (4/4), listo.
Escribiendo objetos: 100% (6/6), 608 bytes | 608.00 KiB/s, listo.
Total 6 (delta 1), reused 0 (delta 0)
To /home/notxor/nube/proyecto-nuevo.git
 * [new branch]      prueba -> prueba
Rama 'prueba' configurada para hacer seguimiento a la rama remota 'prueba' de 'origin'.

Pues ya tengo la rama prueba tanto en local como en remoto. Ahora mismo tengo los siguientes ficheros en la rama.

~/proyectos/proyecto-nuevo> ls
archivo1.txt  archivo2.txt  README.md

Al cambiar de rama, también cambian los ficheros que hay en el directorio de trabajo.

~/proyectos/proyecto-nuevo> git checkout master 
Cambiado a rama 'master'
Tu rama está actualizada con 'origin/master'.
~/proyectos/proyecto-nuevo> ls
README.md

Han desaparecido los dos ficheros txt que están en el seguimiento de la rama prueba. Se puede ver más detalladamente el repositorio con el comando log, que mostrará una salida de texto con el mismo estilo que el comando less del sistema operativo. Por ejemplo:

git log --oneline

Muestra una salida del estilo:

a60c91a (HEAD -> prueba, origin/prueba) Añadir el archivo2.txt a la rama de pruebas.
ee63815 Añadir el archivo1.txt a la rama de pruebas.
f1f91e9 (origin/master, master) Primer commit del repositorio añadiendo el README.md.

HEAD me marca dónde estoy, es el dial que muestra cuál es mi rama. En este caso en la rama prueba del repositorio local y además me marca que tengo sincronizada también la rama remota correspondiente. Ambas ramas prueba están en la posición (o commit) a60c91a, mientras que ambas ramas master se encuentran sincronizadas en la posición f1f91e9.

Si todavía quiero más detalles, quito el --oneline y si quiero algo más gráfico añado el comando --graph.

Mezclar ramas

Para mezclar ramas primero hay que moverse a la rama en la que vas a recibir los cambios hechos en la otra.

~/proyectos/proyecto-nuevo> git checkout master                                                        
Cambiado a rama 'master'                                                                                             
Tu rama está actualizada con 'origin/master'.

Al cambiar de rama avisa de que la tenemos sincronizada con el servidor remoto. Ese mensaje es importante, sobre todo cuando trabajo en un repositorio compartido en el que colaboran varias personas. Es importante que se compruebe y se actualice el repositorio local con los cambios que haya en el remoto antes de subir los cambios locales.

En este caso y una vez comprobado todo el estado del repositorio, mezclo las dos ramas con el comando merge.

~/proyectos/proyecto-nuevo> git merge prueba
Actualizando f1f91e9..a60c91a
Fast-forward
 archivo1.txt | 1 +
 archivo2.txt | 1 +
 2 files changed, 2 insertions(+)
 create mode 100644 archivo1.txt
 create mode 100644 archivo2.txt
~/proyectos/proyecto-nuevo> ls
archivo1.txt  archivo2.txt  README.md
~/proyectos/proyecto-nuevo> git branch
* master
  prueba

Como se ve, ha añadido el par de ficheros txt a la rama master, como muestra el listado del directorio. Al mirar en qué rama estoy, efectivamente el respositorio tiene activa la rama master. Al principio, cuando empezaba a utilizar git, esperaba que desapareciera la rama al mezclarla, sin embargo, git mantiene la rama prueba lo que ha ocurrido es que se han mezclado y ahora mismo las dos ramas tiene el mismo estado, como podemos apreciar con un log.

~/proyectos/proyecto-nuevo> git log --oneline

a60c91a (HEAD -> master, origin/prueba, prueba) Añadir el archivo2.txt a la rama de pruebas.
ee63815 Añadir el archivo1.txt a la rama de pruebas.
f1f91e9 (origin/master) Primer commit del repositorio añadiendo el README.md.

En la línea que comienza como a60c91a vemos que la rama en la que estamos, HEAD -> master, la remota origin/prueba y la local prueba están apuntando al mismo estado o commit. Sin embargo, la rama remota origin/master se encuentra en el commit f1f91e9 todavía.

Para terminar la mezcla hay que sincronizar con el remoto. Los cambios están hechos en el local, como se ve si le pregunto el estatus.

~/proyectos/proyecto-nuevo> git status
En la rama master
Tu rama está adelantada a 'origin/master' por 2 confirmaciones.
  (usa "git push" para publicar tus confirmaciones locales)

nada para hacer comit, el arbol de trabajo esta limpio
~/proyectos/proyecto-nuevo> git push
Total 0 (delta 0), reused 0 (delta 0)
To /home/notxor/nube/proyecto-nuevo.git
   f1f91e9..a60c91a  master -> master

Y si lo compruebo ahora, el resultado es:

~/proyectos/proyecto-nuevo> git log --oneline

a60c91a (HEAD -> master, origin/prueba, origin/master, prueba) Añadir el archivo2.txt a la rama de pruebas.
ee63815 Añadir el archivo1.txt a la rama de pruebas.
f1f91e9 Primer commit del repositorio añadiendo el README.md.

Y ya se aprecian los cuatro diales en el mismo commit.

Y como me está saliendo un chorizo muy largo de post, dejo el resto para otro día.


Comentarios

Debido a algunos ataques mailintencionados a través de la herramienta de comentarios, se ha decidido activar un filtro antispam y guardar las direcciones IP con el único objeto de añadir a la lista de bloqueos las que correspondan a spam y otras actividades maliciosas.

Disculpen las molestias.