Git (escencial)
En este artículo se muestra el manejo básico del sistema de versionamiento de código git, también algunos tips para aprovechar sus funcionalidades.
Sistema de versionamiento
Un sistema de control de versionamiento de código (VCS en inglés) es un sistema que permite mantener versiones de un trabajo que se desarrolla, por ejemplo al escribir código a medida que se avanza en un programa puede darse el caso de necesitar que los archivos vuelvan a un estado anterior al actual. Los sitemas de versionamiento de código son las herramientas indicadas para ello, así si hemos hecho cambios problemáticos en nuestro código podemos cambiar el código a una versión anterior que no tenía problemas (o al menos no tantos ;) ).
Los sistemas de versionamiento no se limitan a código, sirven también para cualquier archivo.
Existen los CVCS (sistemas de versionamiento centralizado tales como svn) y los DVCS (sistemas de control de versionamiento distribuido como git y bazaar), sólo veremos git por lo que los CVCS no se abarcarán en este artículo.
Empezar a usar git
Requerimientos
Una pc con git instalado y una shell para introducir comandos, si no se tiene instalado git hay que instalarlo en un sistema basado en Debian bastaría con
apt-get install git
Como trabaja git
Lo primero es entender como git considera los cambios en los archivos.
Snapshots y no cambios en los archivos
Git hace "snapshots" en los archivos (es como tomar imágenes de un archivo para ver como luce), a medida que se va haciendo commits git hace snapshots de los archivos considerados y almacena una referencia a ese snapshot. Pero al hacer un nuevo commit git sólo almacena los snapshots que han cambiado.
Un ejemplo claro de esto es cuando uno inicia git, se hará un snapshot incial de por ejemplo un arhcivo. Luego se modifica ese archivo por ejemplo borrando una letra y luego volverla a escribir en el mismo lugar y luego guardar el archivo, para el sistema operativo u otros sistemas de versionamiento el archivo cambió pero en realidad el archivo luce exactamente igual.
Para git este archivo no cambió en lo absoluto y por eso al hacer de nuevo un snapshot git no registrará ningún cambió en el archivo por que los snapshots de ambos lucen iguales.
En cambio si el archivo cambió en al menos una letra los snapshots lucirán distintos y para git este cambio debe ser tomado en cuenta.
Es virtualmente imposible que git no note un cambio en los archivos bajo el principio mencionado arriba. Esto por que git usa algoritmos de checksum en este caso sha-1 hash
Tres estados
Git hace que los archivos estén en uno de tres estados y estos son:
Modificado
El archivo has sido modificado ero no ha sido marcado para el siguiente commit.
Marcado para commit (staged)
Es cuando un archivo modificado ha sido marcado para que en el siguiente commit se tome un snapshot de este y se almacene en la base de datos del commit hecho. Cuando un archivo modificado ha sido marcado para ir en el siguiente commit se dice que ese archivo esta en el archivo conocido como índice'ostaging area.
Comiteado (commited)
Significa que el archivo esta apropiadamente almacenado en la base de datos correspondiente al commit hecho. Esto sucede sólo con los archivos tomados en cuenta y luego se hace el uso de git commit
trabajo local
La gran ventaja de git es que no necesita conexion a internet para trabajar con el, por lo que la mayoría de los trabajaos lo hacemos en nuestra pc y en privado. Eso no significa que no se pueda trabajar colaborativamente mediante git, ya está diseñado para eso. Entonces git es una buena opción para trabajar proyectos en conjunto con otras personas a través de internet pero se puede usar git para trabajar individualmente y es una gran herramienta de versionamiento.
Setup
Necesitamos hacer algunos ajustes si es la primera vez que usamos git en nuestra pc. Git guarda las configuraciones globales en la carpeta personal de cada usuario en; ~/.gitconfig
. Git también permite tener configuraciones locales es decir configuraciones distintas a las globales por repositorio donde el archivo se encuentra en el directorio principal del repositorio y dentro la carpeta .git entonces; .git/config
Indentificarse
Para identificar al autor de cada commit git necesita que se coloque una dirección de correo electrónico y el nombre, a continuación haremos una configuración global para el usuario acutal:
git config --global user.name "Nombre del usuario"
git config --global user.email "correo@algo.com"
Este nombre de usuario correo electrónico aparecerán en cada commit que se haga y sirven para contactar al autor del commit.
Para hacer las configuraciones locales basta git config --local
usando las mismas opciones que en global. Para esto la shell debe estar en el directorio del repositorio git en el que se quiere hacer los cambios y estas configuraciones locales se superpondrán a las configuraciones globales.
Editor por defecto
Adicionalmente se puede configurar el editor usado por defecto para escribir los mensajes al hacer los commits, por ejemplo para que el editor sea 'gedit'.
git config --global core.editor gedit
Crear repositorio git
Si queremos hacer que git colabore con el versionamiento de cualquier trabajo aquí simples pasos:
- Abrir una shell (terminal o consola)
- Dirigir la shell al direcotrio principal donde se encuentra el trabajo (cd hasta estar en el directorio donde esta nuestro trabajo)
- Una vez en el direcotorio principal escribir en la shell:
git init
Se creará un repostorio git inicial, luego se puede ver que se ha creado una carpeta con el nombre .git
que es donde se guarda el repositorio git.
Añadir archivos al seguimiento
Una vez se ha creado el repositorio git ya se pueden marcar archivos para se comiteados el comando para esto es git add
. Por ejemplo para agregar al índice un archivo llamado README
se hace con:
git add README
Ahora para agregar al índice todos los archivos que terminen en .c
git add *.c
git soporta expresiones regulares por lo que se pueden usar los identificadores *, [], (), ? y otros.
Ver seguimiento
El comando git status
es el indicado
Hay que notar que los archivos añadidos con git add ahora están marcados para ser parte del siguiente commit y si su contenido cambia git lo notará y podremos visualizarlo con:
git status
Ahora veamos un ejemplo donde se tiene un proyecto con tres archivos main.c README libs.h
, supongamos que hemos ejecutado:
git add *
Por lo que se han marcado todos los archivos y subcarpetas dentro de este directorio. Ahora al hacer git status
aparece:
Cambios para hacer commit:
(use «git rm --cached <archivo>...« para eliminar stage)
new file: README
new file: libs.h
new file: main.c
Esto nos muestra que se han agregado ciertos archivos para el siguiente commit. Ahora supongamos que hemos modificado el archivo main.c
y hemos creado otro llamado func1.c
, al hacer git status
aparece:
En la rama master
Commit inicial
Cambios para hacer commit:
(use «git rm --cached <archivo>...« para eliminar stage)
new file: README
new file: libs.h
new file: main.c
Cambios no preparados para el commit:
(use «git add <archivo>...» para actualizar lo que se ejecutará)
(use «git checkout -- <archivo>...« para descartar cambios en le directorio de trabajo)
modificado: main.c
Archivos sin seguimiento:
(use «git add <archivo>...» para incluir lo que se ha de ejecutar)
func1.c
El archivo main.c
aparece como modificado y git nos sugiere que agreguemos func1.c
usando git add
.
Si ejecutamos nuevamente git add *
todos los arhcivos serán marcados para ser parte del siguiente commit pero podemos marcar los archivos que necesitemos y usar git status
para ver la lista de los archivos con seguimiento.
Quitar archivos del seguimiento
Es natural cometer errores al agregar archivos al índice, por ejemplo siguiendo el ejemplo anterior se desea quitar el archivo README
así no se tomará en cuenta al hacer el siguiente commit. Afortunadamente podemos hacerlo con:
git rm --cached README
Que quitará del seguimiento al archivo README, noten que la opción --cached se usa y es para indicar que 'se quite del seguimiento solamente, con el siguiente ejemplo se puede apreciar esto.
Veamos un caso particular con el ejemplo anterior, se ha añadido por error un archivo img.png
y al hacer git status aparece:
Cambios para hacer commit:
(use «git rm --cached <archivo>...« para eliminar stage)
new file: func1.c
new file: img.png
new file: libs.h
new file: main.c
Archivos sin seguimiento:
(use «git add <archivo>...» para incluir lo que se ha de ejecutar)
README
Si hacemos
git rm *.png
Se desmarcarán los .png que estén actualmente en el seguimiento pero esto nos devolverá un error con:
error: the following file has changes staged in the index:
img.png
(use --cached to keep the file, or -f to force removal)
Que nos dice que img.png
se encuentra marcado y que usemos la opción --cached
para quitarlo del índice de seguimiento o la opción -f
. Si se usa -f
git forzará la eliminación del índice y del disco duro. Por lo que realmente eliminaremos el archivo. Por eso la opción --cached
elimina solamente del índice de seguimiento y protege el archivo original.
Ahora supongamos que hemos eliminado el archivo func1.c
nosotros mismos al hacer git status aparecerá:
deleted: func1.c
Que dice que se ha eliminado del disco ese archivo, para quitarlo del seguimiento podemos usar
git rm func1.c
Que desmarcará el archivo eliminado y no entrará en el siguiente commit. Ahora al hacer git status
ese archivo no aparecerá más por que ha sido eliminado del disco y también del seguimiento.
El archivo .gitignore
Hacer un commit
Una vez tenemos marcados todos los archivos que se necesiten se puede hacer un commit que es tomar snapshots de todos los archivos marcados y guardarlos en la base de datos del repositorio git, entonces basta con:
git commit
Al hacerlo se abrirá el editor especificado como por defecto para que escribamos un mensaje que describa los cambios hechos al trabajo en el commit actual. Las líneas que contengan #en el mensaje serán ignoradas. Al terminar de escribir el mensaje, guardarlo y salir del editor que se abrió el commit estará hecho. Podemos ver los distintos commits que se han hecho en el trabajo usando:
git log
Este un ejemplo del mensaje que muestra con git log
.
commit 6b3603354b48f5ef3f5b69f2140c8b66caabd8dc
Author: Juan Perez <juan@algo.com>
Date: Tue Jan 13 15:07:38 2015 -0400
Commit inicial
Este es el mensaje que aparecerá y nos muestra los cambios que se han hecho.
Es buena idea describir los cambios de manera concisa para tener una buena referncia
de los cambios realizados en este commit.
Se abre una pantalla de la que se puede salir con q y no muestra:
commit 6b3603354b48f5ef3f5b69f2140c8b66caabd8dc
- donde 6b3603354b48f5ef3f5b69f2140c8b66caabd8dc es la cadena que identifica al commit, esta cadena es única por commit y no hay riesgo de que se confunda con otro. A partir de ahora para referirnos a ese commit en particular se usará esa cadena.
Author: Juan Perez <juan@algo.com>
muestra el autor y email de la persona que realizó el commit.
Date: Tue Jan 13 15:07:38 2015 -0400
La fecha y hora del commit.
> Luego se muestra el mensaje que se escribió indicando los cambios que representa el commit.
> Por cada commit se mostrará la misma estructura que la anterior y una vez hecho un commit los archivos incluídos en este están dentro el repositorio de versiones de git, esto quiere decir que son como puntos de retorno para deshacer cambios en los archivos. Por eso es buena idea hacer commits cada que se hagan cambios significativos o se desee hacer un respaldo de los cambios realizados desde un commit a otro.
Reparar commit
Ver diferencias
Deshacer cambios (volver a un estado anterior)
Trabajando en ramas
Las ramas ("branches" en inglés) sirven para poder crear un repositorio paralelo al que estemos usando y poder trabajar en él sin necesidad de afectar al original.
Por ejemplo, digamos que creamos un "branch" llamado "bugFixing" (reparación de bugs) a partir de "master" (la rama principal), esto para que una parte del equipo siga creando nuevas características en el branch "master" y para que otra parte del equipo trabaje en la reparación de "bugs" en la rama "bugFixing". Al final de este proceso ambas ramas deben mezclarse ("merge") y volver a ser una sola.
Ver todas las ramas
$ git branch
Cambiar a una rama
$ git checkout <nombre_de_la_rama>
Crear una rama en tu entorno local
$ git branch <nombre_de_la_rama>
Subir la rama al servidor
$ git push origin <nombre_de_la_rama>
Eliminar la rama de tu entorno local
$ git branch -D <nombre_de_la_rama>
Eliminar la rama en el servidor
$ git push origin --delete <nombre_de_la_rama>
Clonar repositorio git
Trabajo colaborativo
Tips
Listar todos los archivos eliminados en el repositorio
Si quieres listar todos los archivos que han sido eliminados en el repositorio, esto te servirá:
$ git log --diff-filter=D --summary
Si es que no quieres toda la información acerca de el "commit" en el que el archivo fue eliminado, puedes adicionar un "grep":
$ git log --diff-filter=D --summary | grep delete
Ver los logs filtrados por autor
El siguiente formato sirve para ver los logs filtrados por autor en el siguiente formato: Hash del commit - Nombre del autor - Fecha relativa al autor - Asunto del commit
$ git log --pretty=format:"%h | %H - %an, %ar : %s" --author="[autor_a_filtrar]"
Buscar una línea de código en la historia
Este útil truco te ayudará a localizar una línea específica de código en todas los commits.
$ git log -S 'linea_de_codigo_a_buscar' --source --all