diff - diff

diff
Autor (es) original (es) Douglas McIlroy
( Laboratorios Bell de AT&T )
Desarrollador (es) Varios desarrolladores comerciales y de código abierto
Versión inicial Junio ​​de 1974 ; Hace 47 años ( 1974-06 )
Sistema operativo Unix , similar a Unix , V , Plan 9 , Inferno
Plataforma Multiplataforma
Escribe Mando

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 orientado 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 pueden analizar los cambios y usarlos para parchear .

Normalmente, diff se utiliza para mostrar los cambios entre dos versiones del mismo archivo. Las implementaciones modernas también admiten archivos binarios . La salida se llama "diff", o parche , ya que la salida se puede aplicar con el parche del programa Unix . La salida 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 los resultados de la misma. El estándar POSIX especifica el comportamiento de las utilidades "diff" y "patch" y sus formatos de archivo.

Historia

diff se desarrolló a principios de la década de 1970 en el sistema operativo Unix, que surgía de Bell Labs en Murray Hill, Nueva Jersey. La primera versión publicada se envió con la quinta edición de Unix en 1974 y fue escrita por Douglas McIlroy y James Hunt . Esta investigación se publicó en un artículo de 1976 coescrito con James W. Hunt, quien desarrolló un prototipo inicial de diff . El algoritmo descrito en este artículo se conoció como el algoritmo de Hunt-Szymanski .

El trabajo de McIlroy fue precedida e influenciado por Steve Johnson 's programa de comparación de GECOS y Mike Lesk ' s prueba de programa. Proof también se originó en Unix y, como 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. Las heurísticas utilizadas en estas primeras aplicaciones fueron, sin embargo, considera poco fiable. La utilidad potencial de una herramienta de diferencias provocó que McIlroy investigara y diseñara una herramienta más robusta que podría usarse en una variedad de tareas, pero que funciona bien en el procesamiento y las limitaciones de tamaño del hardware del PDP-11 . Su enfoque del problema fue el resultado de la colaboración tambié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 la máquina. Estos scripts de edición, cuando se guardan en un archivo, pueden, junto con el archivo original, ser reconstituidos por 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 postprocesador para diff en el que se pudieran diseñar e implementar una variedad de formatos de salida, pero encontró más frugal y más simple que diff sea ​​responsable de generar la sintaxis y la entrada de orden inverso aceptada por el comando ed .

A finales de 1984 Larry Wall creado una utilidad independiente, parche , la liberación de su código fuente en los mod.sources y net.sources grupos de noticias. Este programa generalizó y amplió la capacidad de modificar archivos con salida de diff .

Los modos en Emacs también permiten convertir el formato de parches e incluso editar parches de forma interactiva.

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 de ensamblaje de la computadora. La salida destinada a ed estaba motivada para proporcionar compresión para una secuencia de modificaciones realizadas en un archivo. 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 de diff .

Algoritmo

La operación de diff se basa en resolver el problema de subsecuencia común más largo .

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 k r x y 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 se pueda obtener 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

a b c d  f  g  j  z

Desde una subsecuencia común más larga, es solo un pequeño paso para obtener una salida similar a la de diff : 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 '-', a continuación ). Si está ausente en la subsecuencia pero presente en la segunda secuencia original, debe haber sido insertado (como lo indican las marcas '+').

e   h i   q   k r x y
+   - +   -   + + + +

Uso

El diffcomando se ejecuta desde la línea de comandos, pasándole los nombres de dos archivos: . La salida del comando representa los cambios necesarios para transformar el archivo original en el nuevo archivo. diff original new

Si original y new son directorios, se ejecutará diff en cada archivo que exista en ambos directorios. Una opción -r,, descenderá recursivamente cualquier subdirectorio coincidente para comparar archivos entre directorios.

Cualquiera de los ejemplos del artículo utiliza los dos archivos siguientes, original y nuevo :

En este formato de salida tradicional, asignifica agregado ,Dpara borrado yCpor cambiado . Los números de línea del archivo original aparecen antesa/D/Cy los del nuevo archivo aparecen después. Los signos menor que y mayor que (al comienzo de las líneas que se agregan, eliminan o cambian) 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 para que falten en el nuevo archivo.

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. Sin embargo, algunas herramientas de diferencias resaltan las líneas movidas.

Producción variaciones

Editar guion

Las versiones modernas de diff aún pueden generar un script ed con la -eopción. El script de edición resultante para este ejemplo es el siguiente:

24a

This paragraph contains
important new additions
to this document.
.
17c
check this document. On
.
11,15d
0a
This is an important
notice! It should
therefore be located at
the beginning of this
document!

.

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 wcomando (escribir) y otra que contenga un qcomando (salir) (por ejemplo, por ). Aquí le dimos al archivo de diferencias el nombre mydiff y la transformación ocurrirá cuando lo ejecutemos . printf "w\nq\n" >> mydiffed -s original < mydiff

Formato de contexto

La distribución de Berkeley de Unix se propuso agregar el formato de contexto ( -c) y la capacidad de recurrir a las estructuras de directorio 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 cambiado mínimamente.

En el formato de contexto, las líneas modificadas se muestran junto a las líneas sin cambios antes y después. La inclusión de cualquier número de líneas sin cambios proporciona un contexto para el parche. El contexto consiste en líneas que no han cambiado entre los dos archivos y sirven como referencia para ubicar el lugar de las líneas en un archivo modificado y encontrar la ubicación deseada para que se aplique un cambio independientemente de si los números de línea siguen correspondiendo. 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 al programa del parche . Este comportamiento inteligente no es posible con la salida diferencial tradicional.

El usuario puede definir el número de líneas sin cambios que se muestran arriba y debajo de un trozo de cambio , incluso cero, pero tres líneas suele ser el valor predeterminado. Si el contexto de las líneas sin cambios en un trozo se superpone con un trozo adyacente, entonces diff evitará duplicar las líneas sin cambios y fusionará los trozos en un solo trozo.

Un " !" representa un cambio entre las líneas que se corresponden en los dos archivos. Un " +" representa la adición de una línea, mientras que un espacio en blanco representa una línea sin cambios. Al comienzo del parche está 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 se aplican al 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 nuevo archivo. Los rangos de hunk especifican los números de línea inicial y final en el archivo respectivo.

El comando diff -c original newproduce la siguiente salida:

*** /path/to/original	timestamp
--- /path/to/new	timestamp
***************
*** 1,3 ****
--- 1,9 ----
+ This is an important
+ notice! It should
+ therefore be located at
+ the beginning of this
+ document!
+
  This part of the
  document has stayed the
  same from version to
***************
*** 8,20 ****
  compress the size of the
  changes.

- This paragraph contains
- text that is outdated.
- It will be deleted in the
- near future.

  It is important to spell
! check this dokument. On
  the other hand, a
  misspelled word isn't
  the end of the world.
--- 14,21 ----
  compress the size of the
  changes.

  It is important to spell
! check this document. On
  the other hand, a
  misspelled word isn't
  the end of the world.
***************
*** 22,24 ****
--- 23,29 ----
  this paragraph needs to
  be changed. Things can
  be added after it.
+
+ This paragraph contains
+ important new additions
+ to this document.

Nota: Aquí, la salida de diferencias se muestra con colores para facilitar la lectura. La utilidad diff no produce una salida en color; su salida es texto sin formato . Sin embargo, muchas herramientas pueden mostrar la salida con colores mediante el resaltado de sintaxis .

Formato unificado

El formato unificado (o unidiff ) hereda las mejoras técnicas realizadas por el formato de contexto, pero produce una diferencia más pequeña con texto antiguo y nuevo que se presentan inmediatamente adyacentes. El formato unificado se suele invocar mediante la opción de línea de " -u" comandos . Esta salida se utiliza a menudo como entrada al programa de parche . Muchos proyectos solicitan específicamente que los "diffs" se envíen en el formato unificado, lo que hace que el formato de diff unificado sea el formato más común para el intercambio entre desarrolladores de software.

Los diffs de contexto unificado fueron desarrollados originalmente por Wayne Davison en agosto de 1990 (en unidiff que apareció en el Volumen 14 de comp.sources.misc). Richard Stallman agregó soporte de diff unificado 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. GNU diff ha generalizado desde entonces el formato de contexto para permitir el formateo arbitrario de diffs.

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 trozos de cambio 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 trozo comienza con información de rango y es seguido inmediatamente por las adiciones de línea, eliminaciones de línea y cualquier número de líneas contextuales. La información de rango está rodeada por signos dobles 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 @@ optional section heading

La información de rango de hunk contiene dos rangos de hunk. El rango del trozo 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 hunk 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 hunk 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 hunk para el original debe ser la suma de todas las líneas de hunk contextuales y de eliminación (incluidas las cambiadas). El rango de hunk para el nuevo archivo debe ser una suma de todas las líneas de hunk contextuales y adicionales (incluidas las cambiadas). Si la información del tamaño del trozo no se corresponde con el número de líneas del trozo, 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 facilitar la lectura de las diferencias. Al crear un diff con GNU diff, el encabezado se identifica mediante la coincidencia de expresiones regulares .

Si se modifica una línea, se representa como una eliminación y una adición. Dado que los trozos del archivo original y el nuevo aparecen en el mismo trozo, dichos cambios aparecerían adyacentes entre sí. Una ocurrencia de esto en el ejemplo siguiente es:

-check this dokument. On
+check this document. On

El comando diff -u original newproduce la siguiente salida:

--- /path/to/original	timestamp
+++ /path/to/new	timestamp
@@ -1,3 +1,9 @@
+This is an important
+notice! It should
+therefore be located at
+the beginning of this
+document!
+
 This part of the
 document has stayed the
 same from version to
@@ -8,13 +14,8 @@
 compress the size of the
 changes.

-This paragraph contains
-text that is outdated.
-It will be deleted in the
-near future.
-
 It is important to spell
-check this dokument. On
+check this document. On
 the other hand, a
 misspelled word isn't
 the end of the world.
@@ -22,3 +23,7 @@
 this paragraph needs to
 be changed. Things can
 be added after it.
+
+This paragraph contains
+important new additions
+to this document.

Nota: Aquí, la salida de diferencias se muestra con colores para facilitar la lectura. La utilidad diff no produce una salida en color; su salida es texto sin formato . Sin embargo, muchas herramientas pueden mostrar la salida con colores mediante el resaltado de sintaxis .

Tenga en cuenta que para separar correctamente los nombres de archivo 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 las diferencias se copian / pegan desde las pantallas de la consola / terminal.

Existen algunas modificaciones y extensiones a los formatos diff que son usados ​​y entendidos por ciertos programas y en ciertos contextos. Por ejemplo, algunos sistemas de control de revisiones , como Subversion, especifican un número de versión, una "copia de trabajo" o cualquier otro comentario en lugar de o además de una marca de tiempo en la sección de encabezado de las diferencias.

Algunas herramientas permiten que las diferencias de varios archivos diferentes se fusionen en uno, utilizando un encabezado para cada archivo modificado que puede verse así:

Index: path/to/file.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 diff POSIX definen una forma de manejar este tipo de archivos. (De hecho, estos archivos no son archivos de "texto" según las definiciones POSIX estrictas). El programa de parche no es consciente ni siquiera de una salida de diferencia específica de la implementación.

Implementaciones y programas relacionados

Los cambios desde 1975 incluyen mejoras en el algoritmo central, la adición de funciones útiles al comando y el diseño de nuevos formatos de salida. El algoritmo básico se describe en los artículos An O (ND) Difference Algorithm and its Variations de Eugene W. Myers y en A File Comparison Program de Webb Miller y Myers. El algoritmo fue descubierto y descrito de forma independiente en Algorithms for Approximate String Matching , por Esko Ukkonen . Las primeras ediciones del programa diff fueron diseñadas para comparaciones de líneas de archivos de texto esperando que el carácter de nueva línea delimite las líneas. En la década de 1980, la compatibilidad con archivos binarios dio como resultado un cambio en el diseño y la implementación de la aplicación.

GNU diff y diff3 se incluyen en el paquete diffutils con otras utilidades relacionadas con diff y patch . Hoy en día también existe un paquete patchutils que puede combinar, reorganizar, comparar y corregir diferencias de contexto y diferencias unificadas.

Formateadores y front-end

Los posprocesadores sdiff y diffmk muestran listas de diferencias una al lado de la otra y aplican marcas de cambio a los documentos impresos, respectivamente. Ambos se desarrollaron en otros lugares de Bell Labs en 1981 o antes.

Diff3 compara un archivo con otros dos archivos conciliando dos diferencias. Originalmente fue concebido por Paul Jensen para conciliar los cambios realizados por dos personas que editan una fuente común. También lo utilizan los sistemas de control de revisiones, por ejemplo , RCS , para la fusión .

Emacs tiene a Ediff para mostrar los cambios que proporcionaría un parche en una interfaz de usuario que combina capacidades interactivas de edición y fusión para archivos de parche.

Vim proporciona vimdiff para comparar de dos a ocho archivos, con las diferencias resaltadas en color. Si bien históricamente invocar el programa diff, vim moderna utiliza git 's tenedor de la biblioteca xdiff (LibXDiff) código que proporciona mayor velocidad y funcionalidad.

GNU Wdiff es una interfaz para diff que muestra las palabras o frases que cambiaron en un documento de texto del lenguaje escrito incluso en presencia de un ajuste de palabras o de diferentes anchos de columna.

colordiff es un contenedor de Perl para 'diff' y produce la misma salida pero con un bonito resaltado de 'sintaxis'.

Derivados algorítmicos

Las utilidades que comparan archivos fuente por su estructura sintáctica se han construido principalmente como herramientas de investigación para algunos lenguajes de programación; algunos están disponibles como herramientas comerciales. Además, las herramientas gratuitas que realizan diferencias basadas en la sintaxis incluyen:

  • C ++: zograscope, basado en AST.
  • HTML: Daisydiff, html-diff.
  • XML: xmldiffpatch de Microsoft y xmldiffmerge para IBM.
  • JavaScript : astii (basado en AST).
  • Multi-idioma: Pretty Diff (código de formato y luego diff)

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. Un puerto HPUX es la versión pública más actual. spiff no admite archivos binarios. salidas spiff a la salida estándar en formato diff estándar y acepta entradas en la C , Bourne shell , Fortran , Modula-2 y Lisp lenguajes de programación .

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), pero la bifurcación de git y libgit2 ha expandido el repositorio con muchos de su propio. Un algoritmo llamado "histograma" generalmente se considera mucho mejor que el algoritmo de Myers original, tanto en velocidad como en calidad. Esta es la versión moderna de LibXDiff utilizada por Vim.

Ver también

Otras herramientas gratuitas de comparación de archivos

Referencias

Otras lecturas

enlaces externos