FreeToGenerate.com

Escribe el archivo y comprueba qué hace git realmente con él. Medido, no copiado del manual.

Tu .gitattributes

Comprueba una ruta contra él

Relativa a la raíz del repositorio, con barras normales. Esta es la pregunta que el archivo no responde con solo leerlo: qué línea gana.

Quien clone tu repositorio tiene uno de estos tres ajustes, y tú no lo controlas. De eso va el panel de abajo.

Atributos para esta ruta

AtributoEstadoDe
textauto*

Qué hará git de verdad con los finales de línea

guardado en el repositorio
LF
escrito en su copia de trabajo
CRLF

Sin fijar. Cambia el ajuste de arriba y mira cómo se mueve este panel: es un ajuste de cada persona decidiendo tus archivos.

Esto está medido, no copiado del manual

La tabla de arriba sale de confirmar el mismo archivo con CRLF en veintiún repositorios de usar y tirar —todas las combinaciones de tres ajustes de core.autocrlf y siete líneas de atributos— y preguntarle después a git qué acabó realmente en el almacén de objetos y, tras borrar y restaurar el archivo, en la copia de trabajo.

El resultado principal es que sin un .gitattributes lo que guarda tu repositorio depende de quién hiciera el commit. El mismo archivo entra como CRLF con core.autocrlf=false y como LF con true o input, y nada en tu propia copia te dice cuál ocurrió. Añade cualquier atributo text y esa variación desaparece: los tres ajustes produjeron bytes idénticos.

Lo que la gente entiende mal es lo que text=auto no hace. Fija el repositorio y deja en paz la copia de trabajo: al volver a extraerlo, el mismo archivo llega como CRLF con dos de los tres ajustes y como LF con el tercero. Solo eol=lf o eol=crlf fijan los dos extremos. Y eol=crlf sigue guardando LF, porque es una instrucción sobre la extracción y no sobre el almacenamiento, lo que hace que parezca que no hace nada hasta que miras en el sitio correcto.

Los patrones son los de gitignore, con una excepción peligrosa

La coincidencia funciona como esperas de un .gitignore: un patrón con barra se mide desde el directorio de su propio .gitattributes, uno sin barra coincide con un nombre a cualquier profundidad, ** cruza directorios y también coincide con ninguno. La precedencia va por posición —gana la última línea que coincida y lo específico no cuenta nada— mientras que un .gitattributes más profundo en el árbol gana a uno más superficial, sea cual sea el orden en que git los lea.

La excepción es el signo de exclamación, y merece conocerse porque falla en silencio. En un .gitignore, un ! inicial niega el patrón. En un .gitattributes no hace absolutamente nada: una línea que empieza por ! sencillamente no está soportada, y git sigue con el valor anterior. Para quitar un atributo se pone un guion delante del atributo, no del patrón, y para devolverlo a sin especificar se pone allí el signo de exclamación.

Comportamiento medido contra el propio git; las sondas están en el repositorio. No se sube nada: la comparación ocurre en esta pestaña.

También disponible en: English · Português · Français · العربية

Generador de .gitattributes

Genera un archivo .gitattributes y comprueba cualquier ruta: qué línea gana y qué finales de línea guardará git y entregará a quien clone tu repositorio.

Qué es un archivo .gitattributes

Un archivo .gitattributes le dice a git cómo tratar rutas concretas. Vive en tu repositorio, se confirma como cualquier otra cosa, y decide qué archivos cuentan como texto, cuáles son binarios, cómo se comparan y se fusionan y —la parte por la que casi todo el mundo llega aquí— qué pasa con los finales de línea al entrar y al salir.

El formato es un patrón seguido de atributos, una regla por línea, exactamente la forma de un .gitignore. Esa familiaridad es la mayor parte del motivo por el que sale mal: los patrones son de verdad los de gitignore, pero una pieza de la sintaxis significa algo completamente distinto, y el atributo al que todo el mundo recurre hace menos de lo que su nombre sugiere.

Esta herramienta hace las dos mitades. Editas el archivo, le das una ruta y te dice qué línea gana, en qué estado queda cada atributo y qué guardará y extraerá git realmente, incluido para el ajuste de la máquina de otra persona que tú no controlas.

Cómo se usa

  1. Parte del archivo que se muestra. Es un punto de partida razonable: normalizar el texto, fijar los scripts de shell a LF y los .bat a CRLF, marcar los binarios de siempre y evitar que git compare un archivo de bloqueo. Edítalo directamente.
  2. Escribe una ruta para comprobar. Relativa a la raíz del repositorio. Cada atributo que coincide aparece con el patrón que lo aportó, así que puedes ver qué línea ha ganado de verdad y no cuál pretendías.
  3. Cambia su core.autocrlf. Esos tres botones representan el ajuste en la máquina de quien clone tu repositorio. Mira el panel de finales de línea mientras los pulsas: si se mueve, tus archivos dependen de un ajuste que no puedes ver.

Qué hacen de verdad los finales de línea, medido

Esta parte está medida, no leída. El mismo archivo con CRLF se confirmó en veintiún repositorios de usar y tirar —todas las combinaciones de tres ajustes de core.autocrlf y siete líneas de atributos distintas— y después se le preguntó a git qué había en el almacén de objetos y, tras borrar y restaurar el archivo, qué había en la copia de trabajo.

Sin un .gitattributes, lo que guarda tu repositorio depende de quién confirmara. El mismo archivo entra como CRLF con core.autocrlf=false y como LF con true o input. Nada en tu propia copia te dice cuál ocurrió, y por eso esto aparece como un pull request lleno de archivos enteros cambiados en vez de como un error. Añade cualquier atributo text y esa variación se desvanece: los tres ajustes produjeron bytes idénticos.

La sorpresa es lo que text=auto no hace. Fija el repositorio y deja en paz la copia de trabajo: al volver a extraerlo, el mismo archivo llega como CRLF con dos de los tres ajustes y como LF con el tercero. Así que un equipo que añade una sola línea de text=auto ha dejado su historia consistente y no ha conseguido que sus copias de trabajo coincidan. Solo eol=lf o eol=crlf fijan los dos extremos.

Y eol=crlf guarda LF. Es una instrucción sobre la extracción, no sobre el almacenamiento, así que si inspeccionas el blob encontrarás LF y concluirás que la línea no hizo nada. Sí hizo: simplemente lo hizo en el otro extremo.

Los patrones son los de gitignore, con una excepción que falla en silencio

La coincidencia se comporta como sugiere esa forma familiar. Un patrón con barra se mide desde el directorio de su propio .gitattributes; uno sin barra coincide con un nombre a cualquier profundidad por debajo. El doble asterisco cruza directorios y —medido, porque es fácil suponer lo contrario— también coincide con ningún directorio, así que sub/**/*.txt cubre sub/a.txt igual que sub/deep/a.txt. Las clases de caracteres funcionan.

La precedencia va por posición, no por lo específico: gana la última línea que coincida, y que un patrón sea más preciso no cuenta nada. Es la misma regla que tiene EditorConfig, y la misma que la gente entiende al revés en los dos. Entre archivos, un .gitattributes más profundo en el árbol gana a uno más superficial, sea cual sea el orden en que git los lea.

La excepción es el signo de exclamación. En un .gitignore, un ! inicial niega el patrón. En un .gitattributes no está soportado en absoluto: una línea que empieza por ! no hace nada, y el valor anterior sigue en vigor. No hay error ni aviso. Para quitar un atributo se pone un guion delante del atributo y no del patrón, y para devolverlo a sin especificar se pone allí el signo de exclamación.

Otra cosa que conviene saber, porque es la línea más común de cualquier archivo real: binary no es un atributo sino una macro. Se expande a desactivar text, diff y merge a la vez, y por eso marcar una ruta como binary sobrescribe correctamente un text=auto anterior en lugar de convivir con él.

Limitaciones honestas

Esto modela las reglas, no tu repositorio. Los proyectos reales pueden tener varios .gitattributes a distintas profundidades, y git lee además .git/info/attributes y un archivo global fuera del repositorio, ninguno de los cuales se confirma ni tiene nadie más. Aquí se modela el archivo confirmado y las reglas dentro de él, que es donde casi siempre está la parte confusa.

El panel de finales de línea predice qué hace git con un archivo de texto que ahora mismo tiene CRLF, porque ese es el caso con el que llega la gente. Un archivo que ya es solo LF, o uno con finales mezclados, se comporta de formas que el panel no intenta dibujar.

Cambiar un .gitattributes no cambia los archivos ya confirmados. Git aplica las reglas nuevas la próxima vez que un archivo pasa por él, así que un repositorio que venía guardando CRLF sigue guardando CRLF hasta que algo reescriba esos archivos. Para eso existe la opción de renormalizar, y produce exactamente el diff enorme que esperarías, y por eso conviene hacerlo a propósito y no descubrirlo después.

¿Por qué es gratis?

Funciona en tu navegador. Comparar una ruta con un patrón no cuesta nada en tu propia máquina, así que no hay servidor que pagar ni cuenta que crear.

Nada de lo que escribes se sube, se guarda ni se registra. Los nombres de archivo y la estructura de directorios dicen bastante sobre una base de código privada, y la forma fiable de mantener eso en privado es no enviarlo a ninguna parte.