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.

## First step: create directories
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.

## Step two: create patch
Run:

```bash
$ git diff --no-prefix --no-index --no-renames --binary a b > patched.patch
```

+ --no-prefix: Do not show any source or destination prefix.
+ --no-index: It is used to compare the two paths given in the file system.
+ --no-remanes: Turn off file renaming detection.
+ --binary: Create a binary diff that can be applied with git apply.

You have your patch ready. Simple, right? Well, now is the time to try it.

## Step three: apply patch
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.

1. 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

2. 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

## The issue with diff and not making directories a and b
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

```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:

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:

```bash
$ 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`:

```bash
$ 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.
