Diferencia

La versión actual de la página aún no ha sido revisada por colaboradores experimentados y puede diferir significativamente de la versión revisada el 14 de marzo de 2022; las comprobaciones requieren 2 ediciones .

En informática,  diff es una utilidad de comparación de archivos que muestra la diferencia entre dos archivos. Este programa imprime línea por línea los cambios realizados en el archivo (para archivos de texto). Las implementaciones modernas también admiten binarios . La salida de la utilidad se llama "diff" o, más comúnmente, un parche porque se puede aplicar con el programa de parches . La salida de otras utilidades de comparación de archivos también suele denominarse "diff".

Historia

La utilidad diff se desarrolló a principios de la década de 1970 para el sistema operativo Unix , que fue obra de AT&T Bell Labs , en Murray Hill, Nueva Jersey. La versión final, distribuida con Unix 5 en 1974, fue escrita íntegramente por Douglas McIlroy .

El trabajo de McIlroy fue precedido e influenciado por el programa de comparación GECOS de Steve Johnson y el programa de prueba de Mike Lesk. Proof también se originó en Unix y, como diff, realizó cambios línea por línea e incluso usó corchetes angulares (">" y "<") para representar inserciones y eliminaciones de líneas en la salida del programa. Sin embargo, las heurísticas utilizadas en estas primeras aplicaciones se consideraron poco fiables. La utilidad potencial de la herramienta de comparación provocó que McIlroy investigara y desarrollara una herramienta más robusta que pudiera usarse en una variedad de tareas pero que funcionaría bien dentro de las limitaciones de procesamiento y tamaño del hardware PDP-11. Su enfoque del problema fue el resultado de la colaboración con personas de Bell Labs, incluidos Alfred Aho, Elliot Pinson, Jeffrey Ullman y Harold S. Stone.

Algoritmo

El funcionamiento de diff se basa en encontrar la subsecuencia común más larga ( problema LCS) .  Por ejemplo, hay dos secuencias de elementos:

abcdfghjqz abcdefgijkrxyz

y necesita encontrar la secuencia más larga de elementos que se presenta en ambas secuencias en el mismo orden. Esto significa que es necesario encontrar una nueva secuencia, que se puede obtener de la primera secuencia eliminando algunos elementos o de la segunda secuencia eliminando otros elementos. En este caso, la secuencia será

abcdfgjz

Después de obtener la secuencia común más grande, solo queda un pequeño paso antes de obtener una salida similar a la diferencia:

ehikqrxy + - + + - + + +

Uso

diff se llama desde la línea de comandos con los nombres de dos archivos como argumentos: diff original new . El resultado del comando son los cambios que deben realizarse en el archivo de origen original para obtener el nuevo archivo nuevo. Si el original y el nuevo son directorios, la diferencia se aplicará automáticamente a todos los archivos que existan en ambos directorios. Todos los ejemplos de este artículo utilizan los siguientes dos archivos, original y new :

original:

Esta parte del documento permanecido sin cambios de versión a versión. si un no hay cambio en ella no debe mostrarse. De lo contrario no ayuda conclusión del óptimo producido cambios. Este párrafo contiene texto obsoleto. será eliminado pronto. Este documento necesita ser corrector ortográfico. Por otra parte, el error en una palabra - no el fin del mundo. Resto del párrafo no requiere cambios. El texto nuevo puede añadir al final del documento.

nuevo:

¡Esta es una nota importante! Por lo tanto, debería ser localizado al comienzo de este ¡documento! Esta parte del documento permanecido sin cambios de versión a versión. si un no hay cambio en ella no debe mostrarse. De lo contrario no ayuda conclusión del óptimo la cantidad de información. Este documento necesita ser corrector ortográfico. Por otra parte, el error en una palabra - no el fin del mundo. Resto del párrafo no requiere cambios. El texto nuevo puede añadir al final del documento. Este párrafo contiene adiciones importantes para este documento.

El nuevo comando diff original produce la siguiente salida diff normal :

0a1.6 > ¡Esta es una nota importante! > Por lo tanto, debe > estar ubicado > al principio de esto > documento! > 8.14c14 < volumen de producido < cambios. < < Este párrafo contiene < texto obsoleto. < Será eliminado < en un futuro próximo. --- > cantidad de información. 17c17 < hay que hacer --- > hay que hacerlo 24a25.28 > > Este párrafo contiene > adiciones importantes > para este documento.

En este formato de salida tradicional, a significa agregado (del inglés  add ), d significa eliminado , c significa cambiado . Las letras a, d o c están precedidas por los números de línea del archivo de origen, seguidos por los números de línea del archivo de destino. Cada línea que se ha agregado, eliminado o modificado está precedida por corchetes angulares .

De forma predeterminada, no se especifican los números de línea comunes a los archivos de origen y de destino. Las filas que se mueven se muestran como agregadas en su nueva ubicación y eliminadas de su ubicación anterior. [una]

Opciones

La mayoría de las implementaciones de diferencias se han mantenido aparentemente sin cambios desde 1975. Las modificaciones incluyen mejoras en el algoritmo principal, la adición de nuevas teclas de comando, nuevos formatos de salida. El algoritmo básico se describe en An O(ND) Difference Algorithm and its Variations de Eugene W. Myers [2] y A File Comparison Program de Webb Miller and Myers [3] . El algoritmo fue descubierto y descrito de forma independiente en Algorithms for Aproximate String Matching de E. Ukkonen [4] . Las primeras versiones del programa diff fueron diseñadas para comparar líneas de archivos de texto utilizando el carácter de nueva línea como separador de línea. En la década de 1980, la compatibilidad con archivos binarios provocó cambios en el funcionamiento y la implementación del programa.

Editar guión

El script de edición puede ser generado por versiones modernas de diff con la opción -e . El resultado de nuestro ejemplo se verá así:

24a Este párrafo contiene adiciones importantes para este documento. . 17c necesita ser . 8.14c la cantidad de información. . 0a ¡Esta es una nota importante! Por lo tanto, debería ser localizado al comienzo de este ¡documento! .

Para usar la secuencia de comandos resultante para convertir el archivo original al nuevo estado de archivo , debemos agregar dos líneas al final de la secuencia de comandos: una contiene el comando w (escribir), la otra - q (salir). Por ejemplo, así . Aquí hemos nombrado el archivo diff mydiff . La transformación se producirá cuando demos la orden . printf "w\nq\n" >> mydiffed -s original < mydiff

Formato de contexto

La versión 2.8 de BSD (lanzada en julio de 1981) introdujo el formato de contexto ( -c ) y la capacidad de recorrer recursivamente el árbol de directorios del sistema de archivos ( -r ).

En formato de contexto, las líneas modificadas se muestran junto con las líneas no afectadas antes y después del fragmento modificado. Insertar cualquier cantidad de líneas no afectadas proporciona contexto para el parche. El contexto , que consta de líneas no afectadas, sirve como referencia para determinar la ubicación del fragmento que se modifica en el archivo de destino, incluso si los números de línea de las líneas modificadas en los archivos de origen y de destino no coinciden. El formato de contexto es más legible para los humanos y más confiable cuando se aplica un parche, y la salida se toma como entrada para el programa de parche .

El usuario puede establecer el número de líneas no afectadas antes y después del fragmento modificado e incluso puede ser cero, pero por lo general el valor predeterminado es tres líneas. Si el contexto de las líneas no afectadas en un fragmento se superpone con un fragmento adyacente, entonces diff evitará copiar las líneas no afectadas y fusionará los fragmentos adyacentes en uno solo.

La salida del comando diff -c original new es:

*** /ruta/a/la ''marca de tiempo'' original --- /ruta/a/la nueva ''marca de tiempo'' *************** *** 1.3 **** --- 1.9 ---- + ¡Esta es una nota importante! + ¡Así que debería + ubicarse + al comienzo de este + documento! + Esta parte del documento permanecido sin cambios de versión a versión. si un *************** *** 5.20 **** no debe mostrarse. De lo contrario no ayuda conclusión del óptimo ! volumen producido ! cambios. ! ! Este párrafo contiene ! texto obsoleto. ! ¡Será eliminado ! pronto. Este documento ! necesita ser corrector ortográfico. Por otra parte, el error en una palabra - no el fin del mundo. --- 11.20 ---- no debe mostrarse. De lo contrario no ayuda conclusión del óptimo ! la cantidad de información. Este documento ! necesita ser corrector ortográfico. Por otra parte, el error en una palabra - no el fin del mundo. *************** *** 22.24 **** --- 22.28 ---- no requiere cambios. El texto nuevo puede añadir al final del documento. ++ Este párrafo contiene adiciones importantes + para este documento.

Formato universal

El formato universal (o unidiff ) incorpora las mejoras técnicas realizadas en el formato de contexto, pero hace que la diferencia entre el texto antiguo y el nuevo sea más concisa. El formato universal generalmente se invoca usando la opción de línea de comando " -u " . Esta salida se usa a menudo como un parche para los programas. Muchos proyectos solicitan específicamente que se les envíen "diffs" en un formato genérico, lo que hace que el formato genérico sea el intercambio más común entre los desarrolladores de software.

Las diferencias de contexto universales fueron desarrolladas por primera vez por Wayne Davison en agosto de 1990 ( unidiff aparece en el capítulo 14 de comp.sources.misc). Stallman agregó soporte de formato universal a la utilidad diff del Proyecto GNU un mes después, y esta funcionalidad debutó en GNU diff 1.15, lanzado en enero de 1991. Desde entonces, GNU diff ha generalizado el formato de contexto para permitir el formato arbitrario de diferencias.

Un archivo de formato genérico comienza con las mismas dos líneas que el formato de contexto, excepto que el archivo original comienza con " --- " y el nuevo archivo comienza con " +++ ". Van seguidos de uno o más fragmentos modificados que contienen cambios línea por línea en los archivos. Las líneas sin cambios comienzan con un espacio, las líneas agregadas comienzan con un signo más, las líneas eliminadas comienzan con un signo menos.

El fragmento comienza con información de rango y es seguido inmediatamente por líneas agregadas, líneas eliminadas y cualquier número de líneas de contexto. La información de rango está rodeada por signos @ dobles y concatenada en una sola línea, a diferencia de dos líneas en ( formato de contexto ). La información de rango tiene el siguiente formato:

@@ -l,s +l,s @@ encabezado de sección opcional

La información de rango consta de dos partes. La parte del archivo original comienza con un signo menos y la parte del archivo nuevo comienza con un signo más. Cada parte tiene el formato l, s , donde l  es el número de la línea con la que comenzamos y s  es el número de líneas que se han cambiado en el fragmento actual para cada uno de los archivos, respectivamente (es decir, en el primer caso, esta es la suma de las líneas de salida que comienzan con un espacio y con un menos, en el segundo - líneas que comienzan con un espacio y con un más). En muchas versiones de GNU diff, la coma y la s final pueden omitirse de cada rango. En este caso, el valor predeterminado de s es 1. Tenga en cuenta que el único valor útil para l solo  es el número de fila del primer rango, los otros valores se pueden calcular a partir de la diferencia.

El fragmento de rango del archivo original debe ser la suma de todo el contexto y las líneas eliminadas (incluidas las modificadas) del fragmento. El fragmento de rango para el nuevo archivo debe incluir la suma de todo el contexto y las líneas agregadas (incluidas las modificadas) del fragmento.

Un fragmento de rango puede estar precedido por el encabezado de la sección o función de la que forma parte el fragmento. Esto suele ser útil para leer el fragmento en sí. Al crear una diferencia usando GNU, el encabezado de diferencia está determinado por la expresión regular [5] .

Si se ha cambiado una línea, se muestra tanto eliminada como añadida. Dado que las líneas eliminadas y añadidas están en fragmentos adyacentes, estas líneas se muestran una al lado de la otra [6] . Por ejemplo:

-verifique este documento. En +ver este documento. En

El comando diff -u original new producirá el siguiente resultado:

--- /ruta/hacia/la ''marca de tiempo'' original +++ /ruta/hacia/la nueva ''marca de tiempo'' @@ -1.3 +1.9 @@ +¡Esta es una nota importante! +Por lo tanto, debe +ubicarse al +comienzo de este +documento! + Esta parte del documento permanecido sin cambios de versión a versión. si un @@ -5.16 +11.10 @@ no debe mostrarse. De lo contrario no ayuda conclusión del óptimo - el volumen de cambios realizados. - -Este párrafo contiene texto obsoleto. -Se eliminará -en un futuro próximo. + cantidad de información. Este documento - debe hacerse + debe hacerse corrector ortográfico. Por otra parte, el error en una palabra - no el fin del mundo. @@ -22.3 +22.7 @@ no requiere cambios. El texto nuevo puede añadir al final del documento. ++ Este párrafo contiene +adiciones importantes +para este documento.

Tenga en cuenta que las pestañas se utilizan para separar correctamente los nombres de archivo de las marcas de tiempo. Esto es invisible en la pantalla y se puede perder al copiar/pegar desde la consola.

Hay varias modificaciones y extensiones a los formatos diff que varios programas usan y entienden. Por ejemplo, algunos sistemas de control de versiones , como Subversion , especifican el número de versión, la "copia de trabajo" o cualquier otro comentario además de la marca de tiempo en el encabezado de la diferencia.

Algunos programas le permiten crear diferencias para varios archivos diferentes y fusionarlos en uno, utilizando un encabezado para cada archivo modificado, que podría verse así:

Índice: ruta/al/archivo.cpp

No se admite el tipo especial de archivos que no terminan con una nueva línea. Ni la utilidad unidiff ni el estándar POSIX diff especifican cómo se manejan dichos archivos (de hecho, los archivos de este tipo no son "texto" en la definición POSIX [7] ).

El programa de parches no sabe nada acerca de la implementación de la salida especial del comando diff.

Véase también

Notas

  1. David MacKenzie, Paul Eggert y Richard Stallman. Comparación y fusión de archivos con GNU Diff y  Patch . — 1997.
  2. E. Myers. Un algoritmo de diferencia O(ND ) y sus variaciones   // Algorithmica : diario. - 1986. - vol. 1 , no. 2 . - P. 251-266 .
  3. Webb Miller y Eugene W. Myers. Un programa de comparación de archivos // Software: práctica y experiencia. - 1985. - T. 15 , N º 11 . - S. 1025-1040 .
  4. E. Ukkonen. Algoritmos para la coincidencia  aproximada de  cadenas // Información y computación : diario. - 1985. - vol. 64 . - pág. 100-118 .
  5. 2.2.3 Mostrando en qué secciones se encuentran las diferencias Archivado el 26 de mayo de 2013 en Wayback Machine , manual de GNU diffutils 
  6. Unified Diff Format Archivado el 5 de abril de 2013 en Wayback Machine por Guido van Rossum , 14 de junio de  2006
  7. http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_205 Archivado el 29 de abril de 2013 en Wayback Machine Sección 3.205 

Enlaces