En informática , la utilidad diff es una herramienta de comparación de datos que calcula y muestra las diferencias entre el contenido de los archivos. A diferencia de las nociones de distancia de edición utilizadas para otros fines, diff está orientado a líneas en lugar de a caracteres, pero es como la distancia de Levenshtein en el sentido de que intenta determinar el conjunto más pequeño de eliminaciones e inserciones para crear un archivo a partir del otro. La utilidad muestra los cambios en uno de varios formatos estándar, de modo que tanto los humanos como las computadoras puedan analizar los cambios y usarlos para aplicar parches .
Normalmente, diff se utiliza para mostrar los cambios entre dos versiones del mismo archivo. Las implementaciones modernas también admiten archivos binarios . [1] La salida se llama "diff" o parche , ya que la salida se puede aplicar con el parche del programa Unix . El resultado de utilidades de comparación de archivos similares también se denomina "diff"; Al igual que el uso de la palabra " grep " para describir el acto de búsqueda, la palabra diff se convirtió en un término genérico para calcular la diferencia de datos y sus resultados. [2] El estándar POSIX especifica el comportamiento de las utilidades "diff" y "patch" y sus formatos de archivo. [3]
diff se desarrolló a principios de la década de 1970 en el sistema operativo Unix, que estaba surgiendo en los laboratorios Bell en Murray Hill, Nueva Jersey. Fue parte de la quinta edición de Unix lanzada en 1974, [4] y fue escrito por Douglas McIlroy y James Hunt . Esta investigación fue publicada en un artículo de 1976 coescrito con James W. Hunt, quien desarrolló un prototipo inicial de diff . [5] El algoritmo que se describe en este artículo se conoció como algoritmo de Hunt-Szymanski .
El trabajo de McIlroy fue precedido e influenciado por el programa de comparación de Steve Johnson sobre GECOS y el programa de prueba de Mike Lesk . La prueba también se originó en Unix y, al igual que diff , produjo cambios línea por línea e incluso usó corchetes angulares (">" y "<") para presentar 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 una herramienta de diferencias impulsó a McIlroy a investigar y diseñar una herramienta más robusta que pudiera usarse en una variedad de tareas, pero que funcionara bien en las limitaciones de procesamiento y tamaño del hardware del 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.
En el contexto de Unix, el uso del editor de línea ed proporcionó a diff la capacidad natural de crear "scripts de edición" utilizables por máquina. Estos scripts de edición, cuando se guardan en un archivo, pueden, junto con el archivo original, reconstituirse mediante ed en el archivo modificado en su totalidad. Esto redujo en gran medida el almacenamiento secundario necesario para mantener múltiples versiones de un archivo. McIlroy consideró escribir un posprocesador para diff donde se pudieran diseñar e implementar una variedad de formatos de salida, pero encontró más frugal y sencillo que diff fuera responsable de generar la sintaxis y la entrada de orden inverso aceptada por el comando ed .
En 1984, Larry Wall creó una utilidad separada, patch , publicando su código fuente en los grupos de noticias mod.sources y net.sources . [6] [7] [8] Este programa modifica archivos usando la salida de diff y tiene la capacidad de hacer coincidir el contexto.
X/Open Portability Guide número 2 de 1987 incluye diferencias. El modo de contexto se agregó en POSIX.1-2001 (número 6). El modo unificado se agregó en POSIX.1-2008 (número 7). [9]
En los primeros años de diff , los usos comunes incluían comparar cambios en la fuente del código de software y el marcado de documentos técnicos, verificar la salida de depuración del programa, comparar listados de sistemas de archivos y analizar el código ensamblador de la computadora. El resultado destinado a ed estaba motivado para proporcionar compresión para una secuencia de modificaciones realizadas en un archivo. [ cita necesaria ] El Sistema de control de código fuente (SCCS) y su capacidad para archivar revisiones surgieron a fines de la década de 1970 como consecuencia del almacenamiento de scripts de edición desde diff .
El funcionamiento de diff se basa en resolver el problema de subsecuencia común más largo . [5]
En este problema, dadas dos secuencias de elementos:
a b c d f g h j q z
a b c d e f g i j krxy z
y queremos encontrar una secuencia más larga de elementos que esté presente en ambas secuencias originales en el mismo orden. Es decir, queremos encontrar una nueva secuencia que pueda obtenerse de la primera secuencia original eliminando algunos elementos y de la segunda secuencia original eliminando otros elementos. También queremos que esta secuencia sea lo más larga posible. En este caso lo es
abcdfgjz
A partir de una subsecuencia común más larga, solo hay un pequeño paso para obtener un resultado similar a una diferencia : si un elemento está ausente en la subsecuencia pero está presente en la primera secuencia original, debe haber sido eliminado (como lo indican las marcas '-', debajo ). Si está ausente en la subsecuencia pero presente en la segunda secuencia original, debe haber sido insertado (como lo indican las marcas '+').
ehiqkrxy+ - + - + + + +
El diff
comando se invoca desde la línea de comando, pasándole los nombres de dos archivos: . El resultado del comando representa los cambios necesarios para transformar el archivo original en el nuevo archivo.diff original new
Si original y nuevo son directorios, entonces se ejecutará diff en cada archivo que exista en ambos directorios. Una opción, descenderá recursivamente cualquier subdirectorio coincidente para comparar archivos entre directorios.-r
Cualquiera de los ejemplos del artículo utiliza los dos archivos siguientes, original y nuevo :
En este formato de salida tradicional,asignifica agregado ,dpara eliminado yCpara cambiado . Los números de línea del archivo original aparecen antesa/d/Cy después aparecen los del nuevo archivo. Los signos menor que y mayor que (al comienzo de las líneas que se agregan, eliminan o modifican) indican en qué archivo aparecen las líneas. Las líneas adicionales se agregan al archivo original para que aparezcan en el nuevo archivo. Las líneas de eliminación se eliminan del archivo original y faltan en el archivo nuevo.
De forma predeterminada, no se muestran las líneas comunes a ambos archivos. Las líneas que se han movido se muestran como agregadas en su nueva ubicación y como eliminadas de su ubicación anterior. [10] Sin embargo, algunas herramientas de diferenciación resaltan las líneas movidas.
Las versiones modernas de diff aún pueden generar un script ed-e
con la opción. El script de edición resultante para este ejemplo es el siguiente:
24a _Este párrafo contiene nuevas adiciones importantes a este documento..17 c consulte este documento. En.11,15 d 0 a ¡Este es un aviso importante ! ¡Por lo tanto, debería ubicarse al principio de este documento!.
Para transformar el contenido del archivo original en el contenido del archivo nuevo usando ed , debemos agregar dos líneas a este archivo diff, una línea que contenga un comando (escribir) y otra que contenga un comando (salir) (por ejemplo, por ). Aquí le dimos al archivo diff el nombre mydiff y la transformación se producirá cuando ejecutemos .w
q
printf "w\nq\n" >> mydiff
ed -s original < mydiff
La distribución Berkeley de Unix se propuso agregar el formato de contexto ( -c
) y la capacidad de recurrir a estructuras de directorios del sistema de archivos ( -r
), agregando esas características en 2.8 BSD, lanzado en julio de 1981. El formato de contexto de diff introducido en Berkeley ayudó con la distribución. parches para el código fuente que pueden haber sido modificados mínimamente.
En el formato de contexto, cualquier línea modificada se muestra junto con las líneas sin cambios antes y después. La inclusión de cualquier número de líneas sin cambios proporciona un contexto al parche. El contexto consta de líneas que no han cambiado entre los dos archivos y sirve como referencia para ubicar el lugar de las líneas en un archivo modificado y encontrar la ubicación prevista para que se aplique un cambio independientemente de si los números de línea aún corresponden. El formato de contexto introduce una mayor legibilidad para los humanos y confiabilidad al aplicar el parche, y una salida que se acepta como entrada para el programa del parche . Este comportamiento inteligente no es posible con la salida de diferenciación tradicional.
El usuario puede definir el número de líneas sin cambios que se muestran encima y debajo de un fragmento de cambio, incluso cero, pero tres líneas suelen ser el valor predeterminado. Si el contexto de las líneas sin cambios en un fragmento se superpone con un fragmento adyacente, entonces diff evitará duplicar las líneas sin cambios y fusionará los fragmentos en un solo fragmento.
Un " !
" representa un cambio entre líneas que corresponden en los dos archivos, mientras que un " +
" representa la adición de una línea y un " -
" la eliminación de una línea. Un espacio en blanco representa una línea sin cambios. Al comienzo del parche se encuentra la información del archivo, incluida la ruta completa y una marca de tiempo delimitada por un carácter de tabulación. Al comienzo de cada trozo están los números de línea que aplican para el cambio correspondiente en los archivos. Un rango de números que aparece entre conjuntos de tres asteriscos se aplica al archivo original, mientras que los conjuntos de tres guiones se aplican al archivo nuevo. Los rangos de fragmentos especifican los números de línea inicial y final en el archivo respectivo.
El comando diff -c original new
produce el siguiente resultado:
*** /ruta/a/la marca de tiempo original --- /ruta/a/la nueva marca de tiempo****************** 1,3 ****--- 1,9 ---- + ¡Este es un aviso + importante! ¡Por lo tanto , debería estar ubicado al principio de este documento! + Esta parte del documento se ha mantenido igual de una versión a otra.****************** 8,20 **** comprimir el tamaño de los cambios.- Este párrafo contiene - texto obsoleto. - Se eliminará en un futuro próximo. ¡Es importante deletrear ! consulte este documento. Por otro lado, una palabra mal escrita no es el fin del mundo.--- 14,21 ---- comprimir el tamaño de los cambios. ¡Es importante deletrear ! consulte este documento. Por otro lado, una palabra mal escrita no es el fin del mundo.****************** 22,24 ****--- 23,29 ---- este párrafo debe modificarse. Se pueden agregar cosas después. + + Este párrafo contiene + nuevas incorporaciones importantes + a este documento.
Nota: Aquí, la salida de la diferencia se muestra con colores para que sea más fácil de leer. La utilidad diff no produce resultados en color; su salida es texto plano . Sin embargo, muchas herramientas pueden mostrar el resultado con colores mediante el resaltado de sintaxis .
El formato unificado (o unidiff ) [11] [12] hereda las mejoras técnicas realizadas por el formato de contexto, pero produce una diferencia más pequeña con texto antiguo y nuevo presentado inmediatamente adyacente. El formato unificado generalmente se invoca usando la " -u
" opción de línea de comandos . Esta salida se utiliza a menudo como entrada para el programa de parche . Muchos proyectos solicitan específicamente que las "diferencias" se envíen en formato unificado, lo que hace que el formato de diferencias unificadas sea el formato más común para el intercambio entre desarrolladores de software.
Las diferencias de contexto unificadas fueron desarrolladas originalmente por Wayne Davison en agosto de 1990 (en unidiff que apareció en el Volumen 14 de comp.sources.misc). Richard Stallman agregó soporte para diferencias unificadas a la utilidad diff del Proyecto GNU un mes después, y la característica 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 las diferencias.
El formato comienza con el mismo encabezado de dos líneas que el formato de contexto, excepto que el archivo original está precedido por "---" y el nuevo archivo está precedido por "+++". A continuación hay uno o más fragmentos de cambios que contienen las diferencias de línea en el archivo. Las líneas contextuales sin cambios están precedidas por un carácter de espacio, las líneas de adición están precedidas por un signo más y las líneas de eliminación están precedidas por un signo menos .
Un fragmento comienza con información de rango y es seguido inmediatamente por las adiciones y eliminaciones de líneas y cualquier número de líneas contextuales. La información del rango está rodeada por signos dobles de arroba y combina en una sola línea lo que aparece en dos líneas en el formato de contexto (arriba). El formato de la línea de información de rango es el siguiente:
@@ -l,s +l,s @@ encabezado de sección opcional
La información del rango de fragmentos contiene dos rangos de fragmentos. El rango del fragmento del archivo original está precedido por un símbolo menos y el rango del nuevo archivo está precedido por un símbolo más. Cada rango de fragmento tiene el formato l,s donde l es el número de línea inicial y s es el número de líneas a las que se aplica el fragmento de cambio para cada archivo respectivo. En muchas versiones de GNU diff, cada rango puede omitir la coma y el valor final s , en cuyo caso s por defecto es 1. Tenga en cuenta que el único valor realmente interesante es el número de línea l del primer rango; todos los demás valores se pueden calcular a partir de la diferencia.
El rango de fragmentos del original debe ser la suma de todas las líneas de fragmentos contextuales y de eliminación (incluidas las modificadas). El rango de fragmentos para el nuevo archivo debe ser una suma de todas las líneas de fragmentos contextuales y adicionales (incluidas las modificadas). Si la información del tamaño del fragmento no se corresponde con el número de líneas del fragmento, entonces la diferencia podría considerarse inválida y rechazarse.
Opcionalmente, el rango del trozo puede ir seguido del encabezado de la sección o función de la que forma parte el trozo. Esto es principalmente útil para hacer que la diferencia sea más fácil de leer. Al crear una diferencia con GNU diff, el encabezado se identifica mediante la coincidencia de expresiones regulares . [13]
Si se modifica una línea, se representa como una eliminación y una adición. Dado que los fragmentos del archivo original y nuevo aparecen en el mismo fragmento, dichos cambios aparecerían uno al lado del otro. [14] Una ocurrencia de esto en el siguiente ejemplo es:
-Consulta este documento. En+consulta este documento. En
El comando diff -u original new
produce el siguiente resultado:
--- /ruta/a/marca de tiempo original +++ /ruta/a/nueva marca de tiempo @@ -1,3 +1,9 @@ +¡Éste es un +aviso importante ! Por lo tanto, debería estar ubicado al principio de este documento. + Esta parte del documento se ha mantenido igual desde la versión hasta @@ -8,13 +14,8 @@ comprime el tamaño de los cambios.-Este párrafo contiene -texto obsoleto. -Será eliminado en un futuro próximo. - Es importante la ortografía -revisa este documento. En +verificar este documento. Por otro lado, una palabra mal escrita no es el fin del mundo.@@ -22,3 +23,7 @@ este párrafo debe cambiarse. Se pueden agregar cosas después. + +Este párrafo contiene +nuevas incorporaciones importantes +a este documento.
Nota: Aquí, la salida de la diferencia se muestra con colores para que sea más fácil de leer. La utilidad diff no produce resultados en color; su salida es texto plano . Sin embargo, muchas herramientas pueden mostrar el resultado con colores mediante el resaltado de sintaxis .
Tenga en cuenta que para separar correctamente los nombres de los archivos de las marcas de tiempo, el delimitador entre ellos es un carácter de tabulación. Esto es invisible en la pantalla y se puede perder cuando se copian/pegan diferencias desde las pantallas de la consola/terminal.
Existen algunas modificaciones y extensiones a los formatos de diferencias que ciertos programas utilizan y entienden en ciertos contextos. Por ejemplo, algunos sistemas de control de revisiones , como Subversion, especifican un número de versión, "copia de trabajo" o cualquier otro comentario en lugar o además de una marca de tiempo en la sección del encabezado de la diferencia.
Algunas herramientas permiten fusionar diferencias de varios archivos diferentes en uno solo, utilizando un encabezado para cada archivo modificado que puede verse así:
Índice: ruta/al/archivo.cpp
No se maneja el caso especial de archivos que no terminan en una nueva línea. Ni la utilidad unidiff ni el estándar POSIX diff definen una forma de manejar este tipo de archivos. (De hecho, dichos archivos no son archivos de "texto" según las definiciones estrictas de POSIX. [15] ) GNU diff y git producen "\ Sin nueva línea al final del archivo" (o una versión traducida) como diagnóstico, pero este comportamiento no es portátil . [16] El parche GNU no parece manejar este caso, mientras que git-apply sí. [17]
El programa de parche no necesariamente reconoce la salida de diferencias específica de la implementación. Sin embargo, se sabe que el parche GNU reconoce parches de git y actúa de manera un poco diferente. [18]
Changes since 1975 include improvements to the core algorithm, the addition of useful features to the command, and the design of new output formats. The basic algorithm is described in the papers An O(ND) Difference Algorithm and its Variations by Eugene W. Myers[19]and in A File Comparison Program by Webb Miller and Myers.[20]The algorithm was independently discovered and described in Algorithms for Approximate String Matching, by Esko Ukkonen.[21]The first editions of the diff program were designed for line comparisons of text files expecting the newline character to delimit lines. By the 1980s, support for binary files resulted in a shift in the application's design and implementation.
GNU diff and diff3 are included in the diffutils package with other diff and patch related utilities.[22]
Postprocessors sdiff and diffmk render side-by-side diff listings and applied change marks to printed documents, respectively. Both were developed elsewhere in Bell Labs in or before 1981.[citation needed][discuss]
Diff3 compares one file against two other files by reconciling two diffs. It was originally conceived by Paul Jensen to reconcile changes made by two people editing a common source. It is also used by revision control systems, e.g. RCS, for merging.[23]
Emacs has Ediff for showing the changes a patch would provide in a user interface that combines interactive editing and merging capabilities for patch files.
Vim provides vimdiff to compare from two to eight files, with differences highlighted in color.[24] While historically invoking the diff program, modern vim uses git's fork of xdiff library (LibXDiff) code, providing improved speed and functionality.[25]
GNU Wdiff[26] is a front end to diff that shows the words or phrases that changed in a text document of written language even in the presence of word-wrapping or different column widths.
colordiff is a Perl wrapper for 'diff' and produces the same output but with colorization for added and deleted bits.[27] diff-so-fancy and diff-highlight are newer analogues.[28] "delta" is a Rust rewrite that highlights changes and the underlying code at the same time.[29]
Patchutils contiene herramientas que combinan, reorganizan, comparan y corrigen diferencias de contexto y diferencias unificadas. [30]
Las utilidades que comparan archivos fuente según su estructura sintáctica se han creado principalmente como herramientas de investigación para algunos lenguajes de programación; [31] [32] [33] algunos están disponibles como herramientas comerciales. [34] [35] Además, las herramientas gratuitas que realizan diferencias con reconocimiento de sintaxis incluyen:
spiff es una variante de diff que ignora las diferencias en los cálculos de punto flotante con errores de redondeo y espacios en blanco , los cuales generalmente son irrelevantes para la comparación del código fuente. Bellcore escribió la versión original. [41] [42] Un puerto HPUX es la versión pública más reciente. spiff no admite archivos binarios. spiff genera la salida estándar en formato diff estándar y acepta entradas en los lenguajes de programación C , Bourne Shell , Fortran , Modula-2 y Lisp . [43] [44] [41] [45] [42]
LibXDiff es una biblioteca LGPL que proporciona una interfaz para muchos algoritmos de 1998. Se implementó originalmente un algoritmo Myers mejorado con la huella digital de Rabin (a partir del lanzamiento final de 2008), [46] pero desde entonces la bifurcación de git y libgit2 ha ampliado el repositorio. con muchos propios. Un algoritmo llamado "histograma" generalmente se considera mucho mejor que el algoritmo original de Myers, tanto en velocidad como en calidad. [47] [48] Esta es la versión moderna de LibXDiff utilizada por Vim. [25]
En las diferencias de estilo git, el estado "antes" de cada parche se refiere al estado inicial antes de modificar cualquier archivo.
La forma más sencilla de empezar a editar en modo diff es con el comando "vimdiff".
Esto inicia Vim como de costumbre y, además, lo configura para ver las diferencias entre los argumentos.
Esto equivale a:
vimdiff file1 file2 [file3] [file4] [...file8]
vim -d file1 file2 [file3] [file4] [...file8]
De hecho, esto muestra que la diferencia de histograma supera ligeramente a Myers, mientras que la paciencia es mucho más lenta que los demás.