miércoles, 28 de agosto de 2013

Primeros pasos con git (parte 1)

Lo prometido es deuda, así que vamos a empezar a hacer cosas con git. En este post vamos a crear un repositorio "central" en algún lado, clonarlo en otro, y luego modificarlo y publicar los cambios en el primero, para que después un tercero pueda obtener también esos cambios. En realidad este post no es más que una excusa para documentar en algún lado lo básico de mi forma de trabajar con git para la próxima vez que empiece un proyecto, porque ya voy armando una receta y me la suelo olvidar.

Antes de empezar aclaro que considero a un repositorio como "central", al de sourceforge si estamos hablando de alguno de mis proyectos públicos, o a un repositorio en una carpeta especial de mi disco si estamos hablando de uno privado, pero acuérdense que es solo una idea mía, a git le da igual cualquiera. Luego, en otro lugar, en una carpeta de trabajo, hago los cambios y los subo al "central". Más tarde si alguien más quiere ver que hice se baja los cambios, o si yo mismo quiero seguir en otra pc, me los bajo también.

Lo primero es instalar git. Si sos usuario de GNU/Linux puede que ya lo tengas. Si no lo tenes buscalo en el gestor de paquetes de tu distro e instalalo (en Ubuntu y parecidos: "sudo apt-get install git"). A partir de ahí, podes abrir una consola/terminal cualquiera y empezar a escupir comandos git. Si sos usuario de Windows, descargá el instalador desde aquí, dale "aceptar" "siguiente" "siguiente" "siguiente" sin leer como todo buen Windowsero, y después buscá en el menú inicio el que dice "Git Bash" para abrir la consola adecuada.

Hay una sola idea más que comentar antes de tirar lineas de comandos. Hay dos tipos de repositorios en git, los que tienen una copia "andando" (working copy) del proyecto, y los que no. Es decir, los primeros tienen al proyecto en su estado actual, más todos los archivos de git con la historia. Los segundos, solo tienen la historia (de ahí se podrá "deducir" luego el estado actual). En general, si vamos a poner un repositorio como central, para que nadie trabaje directamente ahí, hacemos uno del segundo tipo, que se llaman repositorios "bare".

Entonces, en algún lado (carpeta que quieran) y con algún usuario escribimos en la consola:
    git init --bare prueba.git
Esto le dice a git que arme un repositorio nuevo de tipo "bare" con el nombre (creando la nueva carpeta) "prueba.git". Listo, tenemos nuestro repositorio perfectamente vació esperando.

Ahora, en otro lado y si quieren con otro usuario, hacemos la clonación. Cada usuario de git debería tener algunos datos mínimos para "firmar" sus cambios por ejemplo. Para que git no nos ande recordando a cada rato que no cargamos esos datos, podemos escribir lo siguiente:
    git config --global user.name "cucaracha"
    git config --global user.email "cucaracha@racing.com"
Con esto le estamos diciendo que use ese nombre de usuario e email por defecto para todo lo que hagamos con git. Más tarde podemos cambiarlo en algún proyecto en particular ejecutando lo mismo en el directorio del proyecto, pero sacando el "--global".

Algo interesante de git es que se comunica por ssh, así que para autenticarnos si usamos otro usuario/otra pc, se utiliza el mismo mecanismo para loguearnos a una terminal por ssh. Por mismo mecanismo me refiero a las mismas direcciones y mismas contraseñas. Por ejemplo, si el repositorio de recién lo creé en el home de un usuario llamado "boo" en la misma pc, puedo clonarlo con otro usuario haciendo:
    git clone boo@127.0.0.1:~/prueba.git
Clonarlo significa obtener una copia para empezar a trabajar sobre ella. En este caso, la copia será normal, no de tipo "bare". Haremos cambios en esa copia, y luego le pasaremos los cambios al "central" para que todos los vean.

Ahora, tendríamos que crear algunos archivos para poner el repositorio para que no esté tan vacío. Por ejemplo:
    echo "Hola mundo" > hola.txt
    git add hola.txt
    git commit -m 'primer commit'
La primer línea crea un archivo, la segunda le dice a git que lo vaya preparando para poner en el repositorio, y la tercera lo pone. ¿Por qué en dos partes? Porque si tenemos muchos archivos nuevos o cambiados, con la primera elegimos cuales queremos "empaquetar" (poner en lo que se llama la stagin area) para luego guardar todo eso como un solo gran cambio (commit). El "-m 'algo'" es para agregar un mensaje describiendo rápidamente de qué se trata el cambio, para que al revisar la historia del proyecto tengamos idea de qué fue pasando. El commit hace que el cambio se agregue a  nuestro repositorio clonado, pero todavía no se envía al central (esto nos permite trabajar offline, cosa que otros sistemas no).

Antes de enviar los cambios al central, veamos qué haríamos si quisiéramos empezar a usar git con un proyecto ya en marcha. Pues bien, la primer parte, la de crear el repositorio bare sería igual, pero la segunda pasaría a ser esta (a ejecutar en la carpeta del proyecto):
    git init 
    git add *
    git commit -m 'primer commit'
    git remote add --track master origin boo@127.0.0.1:~/prueba.git
Veamos por partes. La primer linea dice que arme un repositorio en la carpeta actual (la del proyecto que teníamos). La segunda y la tercera dicen que agregue todo lo que ya tiene esa carpeta al repositorio. La cuarta le dice a git dónde va a tener que subir los cambios. Antes, cuando clonamos el repositorio, git asumió que los cambios se iban a subir por defecto al lugar de donde clonamos. Ahora, no sabe donde, así que hay que decirle. Se dice con "git remote add <direccion_ssh>", pero si no ponemos el "--track master" podremos subir fácilmente a ese lugar, pero tendremos problemas más tarde para descargar los cambios que hagan otros. Si ya lo hicieron por medio y no pusieron eso, les ahorro la búsqueda y les digo que se corrige con "git remote set-branches origin master".

Ahora, ya sea que cambiamos las cosas luego de clonarlo, o agregamos archivos nuevos, hay que enviarle los cambios al central. Para eso, hacemos:
    git push origin master
El "origin master" no hace falta si clonamos un repositorio que ya tenía cosas, solo es necesario cuando creamos uno de cero. Le estamos diciendo a cual de los posibles repositorios externos que hemos configurado antes (solo hay uno) enviar las cosas, y qué cosas (cual rama, hay solo una por ahora, la master, más adelante hablaremos de eso). Las próximas veces, con "git push" ya alcanza.

Supongamos ahora que hacemos lo que decía arriba, creamos un repositorio central, inicializamos otro con un proyecto, y le subimos esas cosas al central. Ahora cualquiera puede clonar del central lo que subimos con el "git clone ..." del principio. Cuando subamos nuevos cambios, ese otro usuario puede actualizar su copia clonada simplemente con:
    git pull

Y así, una vez puesto todo en marcha, con add, commit y push cualquiera con los permisos y las contraseñas adecuadas puede publicar cambios. Y con pull, cualquiera puede obtener esos cambios. Solo quedan algunas pocas aclaraciones prácticas sobre estas cosas básicas. En principio no podemos bajar cambios si modificamos los archivos luego del clone o del último pull, porque entonces los cambios que bajamos ya no se pueden aplicar directamente sobre lo que tenemos. Para deshacer (perder para siempre) los cambios, se puede usar el comando "git reset --hard". El reset deshace, y el "--hard" es el "para siempre", y todo vuelve a quedar como justo después del último clone o pull. Y ahí sí podemos hacer otro pull. Los mecanismos para combinar los cambios propios con los que bajamos (los hay, o también podemos hacer ramas) quedan para otro post porque por ahora ni yo los tengo del todo claros. Para ver la lista de commits (el historial de cambios), usamos "git log", o una versión más corta:
    git log --format=oneline

Otro detalle interesante, es que si por alguna razón cambia la dirección del repositorio central (por ejemplo, estamos en una LAN, las direcciones IP van por DHCP y entonces a veces al reiniciar cambian), podemos actualizarlas en el repositorio clonado con:
    git remote set-url origin boo@192.168.0.120:~/prueba.git

Y ahora sí finalmente, un último detalle del commit. Si cambiamos varios archivos,  y queremos poner todos juntos en un commit, en lugar de especificarlos uno por uno con add, podemos tan solo agregarle un "-a" antes del "-m" al "git commit...". Esto equivale a haber hecho un "add" para cada archivo que git ya conocía y cambió (pero si hay archivos nuevos, no queda otra que agregarlo con el "add"). Si queremos ver qué hay (cuales cambiaron, cuales son nuevos para git), ejecutamos "git status" y nos muestra las listas.

Y eso es todo por ahora. Sabemos crear un repositorio, y subir y bajar cosas ahí siempre y cuando no hagamos nada raro. Y en realidad esto es todo lo que uso normalmente de git, porque suele ser el único que hace cambios en mis proyectos, y entonces lo uso para llevar el registro y publicar los fuentes, no tanto para trabajar en grupo. Pero la potencia de git también pasa por los merge (las mezclas cuando varios hacen cambios), las ramas (a partir de un punto del proyecto, seguir en dos lineas separadas, pero usando un mismo repositorio, y tal vez más tarde combinarlas), la historia (volver versiones atrás para rastrear el origen de un bug por ejemplo), y tantas otras cosas que no he mencionado. Cuando tenga más firmes las ideas al respecto, seguiré con la parte 2.

Este post es continuación de Primeros pasos con git (parte 0).

No hay comentarios:

Publicar un comentario