-
1. Inicio - Sobre el Control de Versiones
-
2. Fundamentos de Git
-
3. Ramificaciones en Git
-
4. Git en el Servidor
- 4.1 Los Protocolos
- 4.2 Configurando Git en un servidor
- 4.3 Generando tu clave pública SSH
- 4.4 Configurando el servidor
- 4.5 El demonio Git
- 4.6 HTTP Inteligente
- 4.7 GitWeb
- 4.8 GitLab
- 4.9 Git en un alojamiento externo
- 4.10 Resumen
-
5. Git en entornos distribuidos
-
6. GitHub
-
7. Herramientas de Git
- 7.1 Revisión por selección
- 7.2 Organización interactiva
- 7.3 Guardado rápido y Limpieza
- 7.4 Firmando tu trabajo
- 7.5 Buscando
- 7.6 Reescribiendo la Historia
- 7.7 Reiniciar Desmitificado
- 7.8 Fusión Avanzada
- 7.9 Rerere
- 7.10 Haciendo debug con Git
- 7.11 Submódulos
- 7.12 Agrupaciones
- 7.13 Replace
- 7.14 Almacenamiento de credenciales
- 7.15 Resumen
-
8. Personalización de Git
-
9. Git y Otros Sistemas
- 9.1 Git como Cliente
- 9.2 Migración a Git
- 9.3 Resumen
-
10. Los entresijos internos de Git
-
A1. Apéndice A: Git en otros entornos
- A1.1 Interfaces gráficas
- A1.2 Git en Visual Studio
- A1.3 Git en Eclipse
- A1.4 Git con Bash
- A1.5 Git en Zsh
- A1.6 Git en Powershell
- A1.7 Resumen
-
A2. Apéndice B: Integrando Git en tus Aplicaciones
- A2.1 Git mediante Línea de Comandos
- A2.2 Libgit2
- A2.3 JGit
-
A3. Apéndice C: Comandos de Git
- A3.1 Configuración
- A3.2 Obtener y Crear Proyectos
- A3.3 Seguimiento Básico
- A3.4 Ramificar y Fusionar
- A3.5 Compartir y Actualizar Proyectos
- A3.6 Inspección y Comparación
- A3.7 Depuración
- A3.8 Parcheo
- A3.9 Correo Electrónico
- A3.10 Sistemas Externos
- A3.11 Administración
- A3.12 Comandos de Fontanería
8.2 Personalización de Git - Git Attributes
Git Attributes
Algunos de los ajustes que hemos visto, pueden ser especificados para un
camino (path) concreto, de tal forma que Git los aplicará únicamente para
una carpeta o para un grupo de archivos determinado. Estos ajustes específicos
relacionados con un camino, se denominan atributos en Git. Y se pueden fijar,
bien mediante un archivo .gitattribute
en uno de los directorios de tu
proyecto (normalmente en la raíz del proyecto), o bien mediante el archivo
git/info/attributes
en el caso de no querer guardar el archivo de atributos
dentro de tu proyecto.
Por medio de los atributos, puedes hacer cosas tales como indicar diferentes estrategias de fusión para archivos o carpetas concretas de tu proyecto, decirle a Git cómo comparar archivos no textuales, o indicar a Git que filtre ciertos contenidos antes de guardarlos o de extraerlos del repositorio Git. En esta sección, aprenderás acerca de algunos atributos que puedes asignar a ciertos caminos (paths) dentro de tu proyecto Git, viendo algunos ejemplos de cómo utilizar sus funcionalidades de manera práctica.
Archivos binarios
Un buen truco donde utilizar los atributos Git es para indicarle cuáles de los archivos son binarios (en los casos en que Git no podría llegar a determinarlo por sí mismo), dándole a Git instrucciones especiales sobre cómo tratar estos archivos. Por ejemplo, algunos archivos de texto se generan automáticamente y no tiene sentido compararlos; mientras que algunos archivos binarios sí que pueden ser comparados. Vamos a ver cómo indicar a Git cuál es cuál.
Identificación de archivos binarios
Algunos archivos aparentan ser textuales, pero a efectos prácticos merece más
la pena tratarlos como binarios. Por ejemplo, los proyectos Xcode en un Mac
contienen un archivo terminado en .pbxproj
. Este archivo es básicamente una
base de datos JSON (datos Javascript en formato de texto plano), escrita
directamente por el IDE para almacenar aspectos tales como tus ajustes de
compilación. Aunque técnicamente es un archivo de texto (ya que su contenido
lo forman caracteres UTF-8). Realmente nunca lo tratarás como tal, porque en
realidad es una base de datos ligera (y no puedes fusionar sus contenidos si
dos personas lo cambian, porque las comparaciones no son de utilidad). Éstos
son archivos destinados a ser tratados de forma automatizada. Y es preferible
tratarlos como si fueran archivos binarios.
Para indicar a Git que trate todos los archivos pbxproj
como binarios,
puedes añadir esta línea a tu archivo .gitattributes
:
*.pbxproj binary
A partir de ahora, Git no intentará convertir ni corregir problemas CRLF en
los finales de línea; ni intentará hacer comparaciones ni mostar diferencias
de este archivo cuando lances comandos git show
o git diff
en tu proyecto.
Comparación entre archivos binarios
Puedes utilizar los atributos Git para comparar archivos binarios. Se consigue diciéndole a Git la forma de convertir los datos binarios en texto, consiguiendo así que puedan ser comparados con la herramienta habitual de comparación textual.
En primer lugar, utilizarás esta técnica para resolver uno de los problemas
más engorrosos conocidos por la humanidad: el control de versiones en
documentos Word. Todo el mundo conoce el hecho de que Word es el editor más
horroroso de cuantos hay; pero, desgraciadamente, todo el mundo lo usa. Si
deseas controlar versiones en documentos Word, puedes añadirlos a un
repositorio Git e ir realizando confirmaciones de cambio (commit) cada vez.
Pero, ¿qué ganas con ello?. Si lanzas un comando git diff
, lo único que
verás será algo tal como:
$ git diff
diff --git a/chapter1.docx b/chapter1.docx
index 88839c4..4afcb7c 100644
Binary files a/chapter1.docx and b/chapter1.docx differ
No puedes comparar directamente dos versiones, a no ser que extraigas ambas
y las compares manualmente, ¿no?. Pero resulta que puedes hacerlo bastante
mejor utilizando los atributos Git. Poniendo lo siguiente en tu archivo
.gitattributes
:
*.docx diff=word
Así le decimos a Git que sobre cualquier archivo coincidente con el patrón
indicado, (.docx), ha de utilizar el filtro “word” cuando intente hacer
una comparación con él. ¿Qué es el filtro “word”? Tienes que configurarlo
tú mismo. Por ejemplo, puedes configurar Git para que utilice el programa
docx2txt
para convertir los documentos Word en archivos de texto plano,
archivos sobre los que poder realizar comparaciones sin problemas.
En primer lugar, necesitas instalar docx2txt
, obteniéndolo de:
http://docx2txt.sourceforge.net. Sigue las instrucciones del archivo
INSTALL
e instálalo en algún sitio donde se pueda ejecutar desde la shell.
A continuación, escribe un script que sirva para convertir el texto al formato
que Git necesita, utilizando docx2txt:
#!/bin/bash
docx2txt.pl $1 -
No olvides poner los permisos de ejecución al script (chmod a+x
). Finalmente,
configura Git para usar el script:
$ git config diff.word.textconv docx2txt
Ahora Git ya sabrá que si intentas comparar dos archivos, y cualquiera de ellos
finaliza en .docx
, lo hará a través del filtro “word”, que se define con el
programa docx2txt
. Esto provoca la creación de versiones de texto de los
archivos Word antes de intentar compararlos.
Por ejemplo, el capítulo 1 de este libro se convirtió a Word y se envió al
repositorio Git. Cuando añadimos posteriormente un nuevo párrafo, el git diff
muestra lo siguiente:
$ git diff
diff --git a/chapter1.docx b/chapter1.docx
index 0b013ca..ba25db5 100644
--- a/chapter1.docx
+++ b/chapter1.docx
@@ -2,6 +2,7 @@
This chapter will be about getting started with Git. We will begin at the beginning by explaining some background on version control tools, then move on to how to get Git running on your system and finally how to get it setup to start working with. At the end of this chapter you should understand why Git is around, why you should use it and you should be all setup to do so.
1.1. About Version Control
What is "version control", and why should you care? Version control is a system that records changes to a file or set of files over time so that you can recall specific versions later. For the examples in this book you will use software source code as the files being version controlled, though in reality you can do this with nearly any type of file on a computer.
+Testing: 1, 2, 3.
If you are a graphic or web designer and want to keep every version of an image or layout (which you would most certainly want to), a Version Control System (VCS) is a very wise thing to use. It allows you to revert files back to a previous state, revert the entire project back to a previous state, compare changes over time, see who last modified something that might be causing a problem, who introduced an issue and when, and more. Using a VCS also generally means that if you screw things up or lose files, you can easily recover. In addition, you get all this for very little overhead.
1.1.1. Local Version Control Systems
Many people's version-control method of choice is to copy files into another directory (perhaps a time-stamped directory, if they're clever). This approach is very common because it is so simple, but it is also incredibly error prone. It is easy to forget which directory you're in and accidentally write to the wrong file or copy over files you don't mean to.
Git nos muestra que se añadió la línea “Testing: 1, 2, 3.”, lo cual es correcto. No es perfecto (los cambios de formato, por ejemplo, no los mostrará) pero sirve en la mayoría de los casos.
Otro problema donde puede ser útil esta técnica, es en la comparación de
imágenes. Un camino puede ser pasar los archivos JPEG a través de un filtro
para extraer su información EXIF (los metadatos que se graban dentro de la
mayoría de los formatos gráficos). Si te descargas e instalas el programa
exiftool
, puedes utilizarlo para convertir tus imágenes a textos
(metadatos), de tal forma que el comando “diff” podrá, al menos, mostrarte algo útil de
cualquier cambio que se produzca:
$ echo '*.png diff=exif' >> .gitattributes
$ git config diff.exif.textconv exiftool
Si reemplazas cualquier imagen en el proyecto y ejecutas git diff
, verás
algo como:
diff --git a/image.png b/image.png
index 88839c4..4afcb7c 100644
--- a/image.png
+++ b/image.png
@@ -1,12 +1,12 @@
ExifTool Version Number : 7.74
-File Size : 70 kB
-File Modification Date/Time : 2009:04:21 07:02:45-07:00
+File Size : 94 kB
+File Modification Date/Time : 2009:04:21 07:02:43-07:00
File Type : PNG
MIME Type : image/png
-Image Width : 1058
-Image Height : 889
+Image Width : 1056
+Image Height : 827
Bit Depth : 8
Color Type : RGB with Alpha
Aquí se ve claramente que ha cambiado el tamaño del archivo y las dimensiones de la imagen.
Expansión de palabras clave
Algunos usuarios de sistemas SVN o CVS, echan de menos el disponer de expansiones de palabras clave al estilo de las que dichos sistemas tienen. El principal problema para hacerlo en Git reside en la imposibilidad de modificar los archivos con información relativa a la confirmación de cambios (commit). Debido a que Git calcula sus sumas de comprobación antes de las confirmaciones. De todas formas, es posible inyectar textos en un archivo cuando lo extraemos del repositorio (checkout) y quitarlos de nuevo antes de devolverlo al repositorio (commit). Los atributos Git admiten dos maneras de realizarlo.
La primera, es inyectando automáticamente la suma de comprobación SHA-1 de un gran objeto binario (blob) en un campo $Id$ dentro del archivo. Si colocas este atributo en un archivo o conjunto de archivos, Git lo sustituirá por la suma de comprobación SHA-1 la próxima vez que lo/s extraiga/s. Es importante destacar que no se trata de la suma SHA de la confirmación de cambios (commit), sino del propio objeto binario (blob):
$ echo '*.txt ident' >> .gitattributes
$ echo '$Id$' > test.txt
La próxima vez que extraigas el archivo, Git le habrá inyectado el SHA-1 del objeto binario (blob):
$ rm test.txt
$ git checkout -- test.txt
$ cat test.txt
$Id: 42812b7653c7b88933f8a9d6cad0ca16714b9bb3 $
Pero esto tiene un uso bastante limitado. Si has utilizado alguna vez las sustituciones de CVS o de Subversion, sabrás que pueden incluir una marca de fecha (la suma de comprobación SHA no es igual de útil, ya que, por ser bastante aleatoria, es imposible deducir si una suma SHA es anterior o posterior a otra).
Aunque resulta que también puedes escribir tus propios filtros para realizar sustituciones en los archivos al guardar o recuperar (commit/checkout). Se trata de los filtros “clean” y “smudge”. En el archivo ‘.gitattibutes’ puedes indicar filtros para carpetas o archivos determinados y luego preparar tus propios scripts para procesarlos justo antes de confirmar cambios en ellos (“clean”, ver Ejecución de filtro “smudge” en el checkout.), o justo antes de recuperarlos (“smudge”, ver Ejecución de filtro “clean” antes de confirmar el cambio.). Estos filtros pueden utilizarse para realizar todo tipo de acciones útiles.
El mensaje de confirmación para esta funcionalidad nos da un ejemplo simple: el
de pasar todo tu código fuente C por el programa indent
antes de almacenarlo.
Puedes hacerlo poniendo los atributos adecuados en tu archivo
.gitattributes
, para filtrar los archivos *.c a través de “indent”:
*.c filter=indent
E indicando después que el filtro “indent” actuará al manchar (smudge) y al limpiar (clean):
$ git config --global filter.indent.clean indent
$ git config --global filter.indent.smudge cat
En este ejemplo, cuando confirmes cambios (commit) en archivos con extensión
*.c
, Git los pasará previamente a través del programa indent
antes de
confirmarlos, y los pasará a través del programa cat
antes de extraerlos
de vuelta al disco. El programa cat
es básicamente transparente: de él salen
los mismos datos que entran. El efecto final de esta combinación es el de
filtrar todo el código fuente C a través de indent
antes de confirmar
cambios en él.
Otro ejemplo interesante es el de poder conseguir una expansión de la clave
$Date$
del estilo de RCS. Para hacerlo, necesitas un pequeño script que
coja el nombre de un archivo, localice la fecha de la última confirmación
de cambios en el proyecto, e inserte dicha información en el archivo. Este
podria ser un pequeño script Ruby para hacerlo:
#! /usr/bin/env ruby
data = STDIN.read
last_date = `git log --pretty=format:"%ad" -1`
puts data.gsub('$Date$', '$Date: ' + last_date.to_s + '$')
Simplemente, utiliza el comando git log
para obtener la fecha de la
última confirmación de cambios, y sustituye con ella todas las cadenas
$Date$
que encuentre en el flujo de entrada “stdin”; imprimiendo luego los
resultados. Debería de ser sencillo de implementarlo en cualquier otro
lenguaje que domines.
Puedes llamar expanddate
a este archivo y ponerlo en el path de
ejecución. Tras ello, has de poner un filtro en Git (podemos llamarle
dater
), e indicarle que use el filtro expanddate
para manchar (smudge)
los archivos al extraerlos (checkout). Puedes utilizar una expresión Perl
para limpiarlos (clean) al almacenarlos (commit):
$ git config filter.dater.smudge expand_date
$ git config filter.dater.clean 'perl -pe "s/\\\$Date[^\\\$]*\\\$/\\\$Date\\\$/"'
Esta expresión Perl extrae cualquier cosa que vea dentro de una cadena
$Date$
, para devolverla a como era en un principio. Una vez preparado el
filtro, puedes comprobar su funcionamiento preparando un archivo que contenga
la clave $Date$
e indicando a Git cual es el atributo para reconocer ese
tipo de archivo:
$ echo '# $Date$' > date_test.txt
$ echo 'date*.txt filter=dater' >> .gitattributes
Al confirmar cambios (commit) y luego extraer (checkout) el archivo de vuelta, verás la clave sustituida:
$ git add date_test.txt .gitattributes
$ git commit -m "Testing date expansion in Git"
$ rm date_test.txt
$ git checkout date_test.txt
$ cat date_test.txt
# $Date: Tue Apr 21 07:26:52 2009 -0700$
Esta es una muestra de lo poderosa que puede resultar esta técnica para
aplicaciones personalizadas. No obstante, debes de ser cuidadoso, ya que el
archivo .gitattibutes
se almacena y se transmite junto con el proyecto;
pero no así el propio filtro, (en este caso, dater
), sin el cual no puede
funcionar. Cuando diseñes este tipo de filtros, han de estar pensados para
que el proyecto continúe funcionando correctamente incluso cuando fallen.
Exportación del repositorio
Los atributos de Git permiten realizar algunas cosas interesantes cuando exportas un archivo de tu proyecto.
export-ignore
Puedes indicar a Git que ignore y no exporte ciertos archivos o carpetas
cuando genera un archivo de almacenamiento. Cuando tienes alguna carpeta
o archivo que no deseas incluir en tus registros, pero quieras tener
controlado en tu proyecto, puedes marcarlos a través del atributo
export-ignore
.
Por ejemplo, supongamos que tienes algunos archivos de pruebas en la
carpeta test/
, y que no tiene sentido incluirlos en los archivos comprimidos
(tarball) al exportar tu proyecto. Puedes añadir la siguiente línea al archivo
de atributos de Git:
test/ export-ignore
A partir de ese momento, cada vez que lances el comando git archive
para
crear un archivo comprimido de tu proyecto, esa carpeta no se incluirá en él.
export-subst
Otra cosa que puedes realizar sobre tus archivos es algún tipo de sustitución
simple de claves. Git te permite poner la cadena $Format:$
en cualquier
archivo, con cualquiera de las claves de formateo de --pretty=format
que vimos
en el capítulo 2. Por ejemplo, si deseas incluir un archivo llamado
LAST COMMIT
en tu proyecto, y poner en él automáticamente la fecha de la última
confirmación de cambios cada vez que lances el comando git archive
:
$ echo 'Last commit date: $Format:%cd$' > LAST_COMMIT
$ echo "LAST_COMMIT export-subst" >> .gitattributes
$ git add LAST_COMMIT .gitattributes
$ git commit -am 'adding LAST_COMMIT file for archives'
Cuando lances la orden git archive
, lo que la gente verá en ese archivo
cuando lo abra será:
$ cat LAST_COMMIT
Last commit date: $Format:Tue Apr 21 08:38:48 2009 -0700$
Estrategias de fusión (merge)
También puedes utilizar los atributos Git para indicar distintas estrategias de fusión para archivos específicos de tu proyecto. Una opción muy útil es la que nos permite indicar a Git que no intente fusionar ciertos archivos concretos cuando tengan conflictos, manteniendo en su lugar tus archivos sobre los de cualquier otro.
Puede ser interesante si una rama de tu proyecto es divergente o esta
especializada, pero deseas seguir siendo capaz de fusionar cambios de vuelta
desde ella, e ignorar ciertos archivos. Digamos que tienes un archivo de datos
denominado database.xml
, distinto en las dos ramas, y que deseas fusionar en
la otra rama sin perturbarlo. Puedes ajustar un atributo tal como:
database.xml merge=ours
Y luego definir una estrategia ours
con:
$ git config --global merge.ours.driver true
Si fusionas en la otra rama, en lugar de tener conflictos de fusión con el
archivo database.xml
, verás algo como:
$ git merge topic
Auto-merging database.xml
Merge made by recursive.
Y el archivo database.xml
permanecerá inalterado en cualquiera que fuera la
versión que tenias originalmente.