222 lines
7.7 KiB
Markdown
222 lines
7.7 KiB
Markdown
Author: Megver83
|
|
Category: GNU/Linux
|
|
Date: 2017-07-08 10:02
|
|
Image: 2018/04/git-diff.png
|
|
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.
|
|
|
|
## Primer paso: crear los directorios
|
|
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.
|
|
|
|
## Segundo paso: crea el parche
|
|
Ejecuta:
|
|
|
|
```bash
|
|
git diff --no-prefix --no-index --no-renames --binary a b > parche.patch
|
|
```
|
|
|
|
+ --no-prefix: No mostrar ningún prefijo de origen o destino.
|
|
+ --no-index: Se usa para comparar las dos rutas dadas en el sistema de archivos.
|
|
+ --no-remanes: Desactiva la detección de cambio de nombre de un archivo.
|
|
+ --binary: Crea un diff binario que puede ser aplicado con git apply.
|
|
|
|
Ya tienen listo su parche. Sencillo ¿no?. Pues bien, ahora es la hora de probarlo.
|
|
|
|
## Tercer paso: aplicar el parche
|
|
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.
|
|
|
|
1. 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
|
|
|
|
2. 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
|
|
|
|
## El problema con diff y no hacer directorios a y b
|
|
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:
|
|
|
|
|
|
+ Al hacer eso, en el parche en vez de decir algo como
|
|
`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).
|
|
+ Si se usa `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:**
|
|
|
|
```bash
|
|
#!/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
|
|
|
|
```bash
|
|
$ 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:
|
|
|
|
```bash
|
|
$ 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`:
|
|
|
|
```bash
|
|
$ 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.
|