Author: Megver83 Category: GNU/Linux Date: 2017-07-08 10:02 Image: 2018/04/git-diff.png Lang: en Save_as: make-patches-with-git/index.html URL: make-patches-with-git/ Slug: crear-parches-con-git Tags: git, diff, patch Title: Make patches with Git
Many times it happens, especially when working in code development, that we modify software (eg something as simple as a script or several files from the source code of a program) and we want to share that modification or just save it to have that "differentiation" in the form of a plain text file for later application when the program on which we are based is updated. Well that's the role patches play.
Wikipedia says the following about patches:
A patch is a set of changes to a computer program or its supporting data designed to update, fix, or improve it. This includes fixing security vulnerabilities and other bugs, with such patches usually being called bugfixes or bug fixes, and improving the functionality, usability or performance.
Well there are several methods to create patches, the most used
are diff
and git diff
. In this tutorial the use of git diff
will be taught, as it is more complete.
This is a very important step, that most of the tutorials skip, why will be explained later.
If you look closely, in Git, every time a commit is made a
patch is created, and when it shows a modified file the
command diff --git a/path/to/file/modified.sh b/path/to/file/modified.sh
where modified.sh
is, in this case, a script that was modified (.❛ ᴗ ❛.)
So, to modify our script, text or source code, we must first
create the directory a
and b
:::bash
$ mkdir a b
In directory a
we will put the unmodified file or files,
and in directory b
the modified one.
Run:
$ git diff --no-prefix --no-index --no-renames --binary a b > patched.patch
You have your patch ready. Simple, right? Well, now is the time to try it.
Once we have our patch as a .diff
or .patch
file (although in general any
extension can be used), we will apply it with patch
or git apply
depending on the case.
Plain text only: If your patch only modifies plain text, such as scripts, C/C ++ source files, Python, Pascal, Javascript, PHP, HMTL, etc. then we will use this command:
:::bash
$ patch -p1 -i /ruta/del/parche.diff
With binary files: That is, things like already compiled executable programs, PNG images, JPEG, Gif, etc. other than plain text. In general you will be able to identify when a binary is patched when in patch it says something like "GIT binary patch". In this case we will apply the patch as follows:
:::bash
$ git apply -v /ruta/del/parche.diff
Now, going back to what I said earlier about why this is important,
it is because in many guides, wikis, etc. I have found that instead
of creating these directories, they create a file (eg) script.sh
and script.sh.new
and then based on that they run
diff -u scripts.sh script.sh.new
.
It turns out that there are two problems in this:
By doing that, in the patch instead of saying something like
diff --options a/path/to/file/modified.sh b/path/to/file/modified.sh
says (in this case) diff --options script.sh script.sh.new
,
but it turns out that you want to patch b/script.sh
,
not script.sh.new
(because inside b/
are the modified files).
If diff
is used, when it detects a file that didn't originally exist in a/
(surely because you created one in b/
), it won't add it in the patch,
and if you deleted one inside the original tree, it won't remove that file either.
diff
cannot patch binaries.
To better understand it, I will exemplify each case with two examples. In the first one, I will create the files that I put as an example (worth the redundancy) and I will use 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"
Now we will do what most internet tutorials tell you to do:
:::bash
$ diff -u script.sh script.sh.new
And it looks like this:
:::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"
Everything apparently fine, but now let's apply that patch
$ 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:
It fails being that I am in the same directory as script.sh{.new}
,
so this is fixed using the create directories a/
and b/
hack.
However, this does not turn out point 2 and 3. Let's go for it.
Suppose we have this inside a/
and b/
:
a: script.sh
b: binary_file.bin script.sh
Okay, now let's make the patch with diff:
$ diff -ur a b
:::diff
Only in b: binary_file.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"
And what is said in point 2 is true, it does not put the new file,
it tells you "Only in b" or if there is a file that is in a/
but not in b/
(that is to say, surely you removed it from your fork), you will
get the message "Only in a" instead of deleting or creating it.
If we apply this patch it will only affect the plain text files,
and even if it did its job and created this new file it would not
work because binary_file.bin
is a binary, which is not supported
by diff
but it is supported by git
which leads us to third point.
See what happens if I use git
instead of diff
:
$ git diff --no-prefix --no-index --no-renames --binary a b
:::diff
diff --git b/binary_file.bin b/binary_file.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"
Now I did consider the non-existent binary file in a/
but tangible in b/
.
Note that in this particular case, as I explained earlier, when dealing with
binary files that only git supports (see the message "GIT binary patch") you
must use git apply
. But I recommend using it only when it is mandatory,
not always (in general, not many binaries are used in software that is
100% free, unless they are cases such as firmware for the kernel or
precompiled libraries, but free software blobbeado It usually has proprietary
binaries in its code, although just because it's binary doesn't necessarily
mean it's proprietary.)
If you have doubts about the use of diff
and git diff
or patch and git apply
,
remember that you can leave them in the comments, as well as read their manpages
and consult their web pages for more information.