Author: Megver83 Category: GNU/Linux Date: 2017-07-08 10:02 Image: 2018/04/git-diff.png Lang: es Slug: crear-parches-con-git Tags: git, diff, patch Title: Crear parches con Git
Muchas veces pasa, especialmente cuando se trabaja en desarrollo de código, que modificamos software (por ej. algo tan simple como un script o varios archivos del código fuente de un programa) y queremos compartir esa modificación o solamente guardarla para tener esa "diferenciación" en la forma de un archivo de texto plano para más tarde aplicarla cuando el programa en el que nos basamos se actualice. Pues esa es la función que cumplen los parches.
En Wikipedia dice lo siguiente sobre los parches:
En informática, un parche consta de cambios que se aplican a un programa, para corregir errores, agregarle funcionalidad, actualizarlo, etc.
Pues hay varios métodos para crear parches, los más usados son diff
y git diff
.
En este tutorial se enseñará el uso de git diff
, por ser más completo.
Este es un paso muy importante, que la mayoría de los tutoriales omiten, más adelante se explicará por qué.
Si se fijan bien, en Git, cada vez que se hace un commit se crea un parche,
y cuando muestra un archivo modificado aparece el comando
diff --git a/ruta/al/archivo/modificado.sh b/ruta/al/archivo/modificado.sh
donde modificado.sh
es, en este caso, un script que fue modificado (.❛ ᴗ ❛.)
Entonces, para modificar nuestro script, texto o código fuente primero hay que
crear el directorio a
y b
:::bash
$ mkdir a b
En el directorio a
pondremos el o los archivos sin modificar, y en el
directorio b
el modificado.
Ejecuta:
$ git diff --no-prefix --no-index --no-renames --binary a b > parche.patch
Ya tienen listo su parche. Sencillo ¿no?. Pues bien, ahora es la hora de probarlo.
Una vez tenemos nuestro parche como archivo .diff
o .patch
(aunque en general
se puede usar cualquier extensión), lo aplicaremos con patch
o git apply
dependiendo del caso.
Solo texto plano: Si su parche únicamente modifica texto plano, como scripts, archivos de código fuente en C/C++, Python, Pascal, Javascript, PHP, HMTL, etc. entonces usaremos este comando:
:::bash
$ patch -p1 -i /ruta/del/parche.diff
Con archivos binarios: Es decir, cosas como programas ejecutables ya compilados, imágenes PNG, JPEG, Gif, etc. que no sean texto plano. En general podrás identificar cuando se parcha un binario cuando en parche dice algo como "GIT binary patch". En este caso aplicaremos el parche de la siguiente manera:
:::bash
$ git apply -v /ruta/del/parche.diff
Ahora, regresando a lo que decía anteriormente sobre por qué esto es importante,
se debe a que en muchas guías, wikis, etc. he encontrado que en vez de crear estos
directorios, crean un archivo (por ej.) script.sh
y script.sh.new
y luego en base
a eso ejecutan diff -u scripts.sh script.sh.new
.
Resulta que hay dos problemas en esto:
diff --opciones a/ruta/al/archivo/modificado.sh b/ruta/al/archivo/modificado.sh
dice (en este caso) diff --opciones script.sh script.sh.new
, pero resulta que tu
quieres parchar b/script.sh
, no script.sh.new
(porque dentro de b/
están los
archivos modificados).diff
, cuando se detecte un archivo que no existía originalmente en a/
(seguramente porque creaste uno en b/
), no lo va a agregar en el parche, y si
eliminaste uno dentro del árbol original, tampoco quitará dicho archivo.diff
no puede hacer parches de binarios.Para que se entienda mejor, voy a ejemplificar cada caso con dos ejemplos. En el primero, crearé los archivos que puse de ejemplo (valga la redundancia) y usaré diff:
script.sh:
:::bash
#!/bin/bash
echo "Hello world"
script.sh.new:
:::sh
#!/bin/sh
echo "Hello world"
echo "This is a patched file :D"
Ahora haremos lo que la mayoría de tutoriales de internet te dicen que hagas:
:::bash
$ diff -u script.sh script.sh.new
Y me queda así:
:::diff
--- script.sh 2018-03-16 15:52:49.887087539 -0300
+++ script.sh.new 2018-03-16 15:53:02.490420209 -0300
@@ -1,2 +1,3 @@
-#!/bin/bash
+#!/bin/sh
echo "Hello world"
+echo "This is a patched file :D"
Todo aparentemente bien, pero ahora apliquemos dicho parche
$ diff -u script.sh script.sh.new | patch -p1 -i /dev/stdin
:::diff
can't find file to patch at input line 3
Perhaps you used the wrong -p or --strip option?
The text leading up to this was:
--------------------------
|--- script.sh 2018-03-16 15:52:49.887087539 -0300
|+++ script.sh.new 2018-03-16 15:53:02.490420209 -0300
--------------------------
File to patch:
Falla siendo que estoy en el mismo directorio que script.sh{.new}
, de modo que
esto se corrige usando el hack de crear los directorios a/
y b/
.
Sin embargo, esto no resulve el punto 2 y 3. Vamos a por ello.
Supongamos que tenemos esto dentro de a/
y b/
:
a: script.sh
b: archivo_binario.bin script.sh
Bien, ahora hagamos el parche con diff:
$ diff -ur a b
:::diff
Sólo en b: archivo_binario.bin
diff -ur a/script.sh b/script.sh
--- a/script.sh 2018-03-16 15:37:27.513802777 -0300
+++ b/script.sh 2018-03-16 15:41:17.717123987 -0300
@@ -1,2 +1,3 @@
-#!/bin/bash
+#!/bin/sh
echo "Hello world"
+echo "This is a patched file :D"
Y se cumple lo que decía en el punto 2, no te pone el archivo nuevo,
te dice "Sólo en b" o si hay un fichero que está en a/
pero no en b/
(es decir, seguro que lo eliminaste de tu fork), te saldrá el mensaje
"Sólo en a" en vez de eliminarlo o crearlo. Si aplicamos este parche
solo afectará a los archivos de texto plano, y aunque hiciera bien
su trabajo y creara este nuevo archivo no funcionaría porque
archivo_binario.bin
es un binario, el cual no está soportado por diff
pero sí por git
lo cual nos lleva al tercer punto.
Mira lo que pasa si uso git
en vez de diff
:
$ git diff --no-prefix --no-index --no-renames --binary a b
:::diff
diff --git b/archivo_binario.bin b/archivo_binario.bin
new file mode 100644
index 0000000000000000000000000000000000000000..1ce3c1c596d7a7f400b0cc89bda5a41eed2780c5
GIT binary patch
literal 73
pcmd-HXHZUIU{c}EWl|AfLZWk+R0P|Ad@#)bSHb~R0-{lr003gr3L5|b
literal 0
HcmV?d00001
diff --git a/script.sh b/script.sh
index da049c4..3d351f5 100644
--- a/script.sh
+++ b/script.sh
@@ -1,2 +1,3 @@
-#!/bin/bash
+#!/bin/sh
echo "Hello world"
+echo "This is a patched file :D"
Ahora sí me consideró el archivo binario inexistente en a/
pero tangible en b/
.
Noten que en este caso particular, como ya expliqué anteriormente, al tratar con
archivos binarios que solo git soporta (vean el mensaje "GIT binary patch") se
debe usar git apply
obligatoriamente. Pero les recomiendo usarlo solo cuando sea
obligatorio, no siempre (en general no se usan muchos binarios en el software que
es 100% libre, a no ser que se traten de casos como firmware para el kernel o
librerías precompiladas, pero el software libre blobbeado suele tener binarios
privativos en su código, aunque el hecho de que sea binario no significa que sea
necesariamente privativo).
Si tienes dudas al respecto sobre el uso de diff
y git diff
o patch
y git apply
recuerda que puedes dejarlas en los comentarios, así como también leer sus manpages y
consultar sus páginas web para más información.