gitdiffgit-diffgit-patch

What differences are in the patches/files created by diff and git diff?


I wonder what differences are in the format of the files/patches created by diff and git-diff.
I know in diff there are 3 (the "normal", the "compact" -c one and the "unified" -u one).

However there may be some differences and in some situations you cannot freely exchange git diffand diff. So:

  1. On what does it depend whether you can use git diffand diff for the same file?
  2. And what are the differences between the formats?
  3. If you cannot exchange the commands (see 1.) how can you convert the files into the other format so that you can use them with the other command?
  4. If you can exchange the commands (see 1.): Is it even recommend to do so?
  5. Are there any other notable differences in the files created by the two commands?

Solution

    1. On what does it depend whether you can use git diff and diff for the same file?

    Simply if the file is in a git repo working tree, then you would be able to use git diff to show changes for that file (against the same file as referenced by the git repo, like the index or blob objects).
    This differs from 'diff', which compares files (meaning you need two files, not just one as in git diff when used in a git repo)

    As hvd points out in the comments:

    You can use git diff outside any work tree and pass it two files.
    So you can use git diff in pretty much any situation you can use diff.
    The reverse is not true

     git diff --color-words --no-index file1 file2
    

    1. And what are the differences between the formats?

    git diff can emulate any diff format (unified, raw, ...).
    It has git-specific format as well (--summary, --stat, ...)

    See also:

    A git diff will include a git header, with a "similarity index".
    The hunks displays for each chunk of diffs is very similar to a diff -u.


    1. If you cannot exchange the commands (see 1.) how can you convert the files into the other format so that you can use them with the other command?

    You can convert a git diff in a raw format, or patch with raw: --patch-with-raw.
    The reverse is possible: you can apply a diff to a git repo.


    1. If you can exchange the commands (see 1.): Is it even recommend to do so?

    It is if you don't have git installed (see the previous example)


    1. Are there any other notable differences in the files created by the two commands?

    No: the result of applying a patch generated by a diff or a git diff should be the same.


    Warning: there is a bug fixed with Git 2.42 (Q3 2023): the "-s" (silent, squelch) option of the "diff" family of commands did not interact with other options that specify the output format well.
    This has been cleaned up so that it will clear all the formatting options given before.

    See commit 9d484b9 (05 May 2023) by Junio C Hamano (gitster).
    (Merged by Junio C Hamano -- gitster -- in commit 6901ffe, 13 Jun 2023)

    diff: fix interaction between the "-s" option and other options

    Sergey Organov noticed and reported "--patch --no-patch --raw" behaves differently from just "--raw".
    It turns out that there are a few interesting bugs in the implementation and documentation.

    • First, the documentation for "--no-patch" was unclear that it could be read to mean "--no-patch" countermands an earlier "--patch" but not other things.
      The intention of "--no-patch" ever since it was introduced at d09cd15 ("diff: allow --no-patch as synonym for -s", 2013-07-16, Git v1.8.4-rc0 -- merge) was to serve as a synonym for "-s", so "--raw --patch --no-patch should have produced no output, but it can be (mis)read to allow showing only "--raw" output.

    • Then the interaction between "-s" and other format options were poorly implemented.
      Modern versions of Git uses one bit each to represent formatting options like "--patch", "--stat" in a single output_format word, but for historical reasons, "-s" also is represented as another bit in the same word.
      This allows two interesting bugs to happen, and we have both X-<.

      1. After setting a format bit, then setting NO_OUTPUT with "-s", the code to process another "--<format>" option drops the NO_OUTPUT bit to allow output to be shown again.
        However, the code to handle "-s" only set NO_OUTPUT without unsetting format bits set earlier, so the earlier format bit got revealed upon seeing the second "--<format>" option. This is the problem Sergey observed.

      2. After setting NO_OUTPUT with "-s", code to process "--<format>" option can forget to unset NO_OUTPUT, leaving the command still silent.

    It is tempting to change the meaning of "--no-patch" to mean "disable only the patch format output", and reimplement "-s" as "not showing anything", but it would be an end-user visible change in behavior.
    Let's fix the interactions of these bits to first make "-s" work as intended.

    The fix is conceptually very simple.

    • Whenever we set DIFF_FORMAT_FOO because we saw the "--foo" option (e.g. DIFF_FORMAT_RAW is set when the "--raw" option is given), we make sure we drop DIFF_FORMAT_NO_OUTPUT.
      We forgot to do so in some of the options and caused (2) above.
    • When processing "-s" option, we should not just set DIFF_FORMAT_NO_OUTPUT bit, but clear other DIFF_FORMAT_* bits. We didn't do so and retained format bits set by options previously seen, causing (1) above.

    It is even more tempting to lose NO_OUTPUT bit and instead take output_format word being 0 as its replacement, but that would break the mechanism "git show"(man) uses to default to --patch output, where the distinction between telling the command to be silent with "-s" and having no output format specified on the command line matters, and an explicit output format given on the command line should not be "combined" with the default "--patch" format.

    So, while we cannot lose the NO_OUTPUT bit, as a follow-up work, we may want to replace it with OPTION_GIVEN bit, and:

    • make "--patch", "--raw", etc.
      set DIFF_FORMAT_$format bit and DIFF_FORMAT_OPTION_GIVEN bit on for each format.
      "--no-raw", etc.
      will set off DIFF_FORMAT_$format bit but still record the fact that we saw an option from the command line by setting DIFF_FORMAT_OPTION_GIVEN bit.

    • make "-s" (and its synonym "--no-patch") clear all other bits and set only the DIFF_FORMAT_OPTION_GIVEN bit on.

    which I suspect would make the code much cleaner without breaking any end-user expectations.

    Once that is in place, transitioning "--no-patch" to mean the counterpart of "--patch", just like "--no-raw" only defeats an earlier "--raw", would be quite simple at the code level.
    The social cost of migrating the end-user expectations might be too great for it to be worth, but at least the "GIVEN" bit clean-up alone may be worth it.

    diff-options now includes in its man page:

    Suppress all output from the diff machinery.

    Useful for commands like git show that show the patch by default to squelch their output, or to cancel the effect of options like --patch, --stat earlier on the command line in an alias.