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 ramamaster
del remoto)master
→ (la ramamaster
del local)origin/prueba
→ (la ramaprueba
del remoto... bueno, esta aún no la tengo pero hay que contar con ella. Ahora lo cuento.)prueba
→ (la ramaprueba
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.