# Git produces Unidiff - Does it really?

3 messages from 2009-05-24 to 2009-05-25. Participants: Jan Engelhardt, Junio C Hamano.
Thread: https://gitlist.dev/t/19489

## Jan Engelhardt, 2009-05-24 23:15

Subject: Git produces Unidiff - Does it really?
Message-ID: <alpine.LSU.2.00.0905250105410.22963@fbirervta.pbzchgretzou.qr>
URL: https://gitlist.dev/e/alpine.LSU.2.00.0905250105410.22963%40fbirervta.pbzchgretzou.qr

```
Hi,


so I noticed that the diff output generated by git tools, such as 
"git diff", "git log -p", etc. is not accepted by TortoiseSVN, but is 
accepted by patch(1). Now I wondered which program could be at fault.
Git produces:

	--- a/src/foo.c
	+++ b/src/foo.c
	@@ your hunk goes here... @@
	etc.

Random webpages[1,2] have a tendency wanting to tell me unidiff is

	--- a/src/foo.c	$TIMESTAMP
	+++ b/src/foo.c	$TIMESTAMP
	@@ hunks... @@

instead, i.e. with the timestamp.
Since the diff format is known to be sketchy (are there _any_ uptodate 
specs that are actually followed?), which tool(s) is/are to blame?

 * git for not producing unidiff(-with-a-timestamp)
 * TSVN for not accepting unidiff(-without-a-timestamp)
(* patch for accepting unidiff body together with a non-unidiff file 
   header)


Any insights?

Jan

```

## Junio C Hamano, 2009-05-25 03:27

Subject: Re: Git produces Unidiff - Does it really?
Message-ID: <7v4ov9zwtt.fsf@alter.siamese.dyndns.org>
URL: https://gitlist.dev/e/7v4ov9zwtt.fsf%40alter.siamese.dyndns.org
In-Reply-To: <alpine.LSU.2.00.0905250105410.22963@fbirervta.pbzchgretzou.qr>

```
Jan Engelhardt <jengelh@medozas.de> writes:

> Any insights?

We have "index ..." and other metainformation (most noticeable when
renames and mode changes are involved) before any hunk begins.  POSIX
unified context format of course would not know about them either.

In the context of git, file modification time often does not have a
defined meaning.  If you are comparing two arbitrary tree objects (or two
arbitrary blob objects), there is no concept of "timestamp of the preimage
and the postimage".  If you are comparing two commits, you _could_ show
the timestamps of the commits, but that is misleading and useless
information.  The paths being shown may not have been changed by the newer
commit being compared, but by some other commit in-between the two commits
(and even that is not guaranteed --- the two commits may not have any
ancestry relationship, and/or the clock of the committer who made one of
these commits may have had skews).

The enhancements and ommissions were chosen not to confuse tools that
people use to process patches.  The metainformation headers are safely
ignored by "patch", for example (of course "patch" does not know about
renames yet, but that can be fixed later).  Even though "patch" _can_
optionally set the timestamp of the affected files, nobody sane would do
so in the context of source code control (it will screw up "make").

The answer to your question depends on how anal you would want to be, but
no, we fundamentally cannot produce "unidiff -- _really_" in the "POSIX
unified context format" without losing useful information (renames and
modes) nor without adding information that is meaningless (timestamps).

But on the other hand, you can say what we produce is in unified context
format to the same extent that RCS and CVS produces unified when given -u
option (i.e. "rcsdiff -u", "cvs diff -u").

For example, "rcsdiff -u" shows something like:

    diff -u -r1.1 -r1.2
    --- Make        1995/01/08 20:50:24     1.1
    +++ Make        2001/10/04 07:14:29     1.2
    @@ -1,7 +1,11 @@
     #!/bin/sh
    -...

It has cruft after the timestamp, and the timestamp itself is not even in
the format POSIX expects to see (see

 http://www.opengroup.org/onlinepubs/9699919799/utilities/diff.html#tag_20_34_10_07

if you really care).

Is it in violation of POSIX unified context format?  Of course yes.  Is it
then not in unified context format at all?

For all practical purposes, if you (either you human or your tool) know
how to read unified context format, you can process what we (or RCS)
produce.

I used RCS as an example because it has been maintained by the same person
who maintains "GNU diff" and was one of the main player who pushed
"unified context" format to the updated POSIX.

```

## Jan Engelhardt, 2009-05-25 09:48

Subject: Re: Git produces Unidiff - Does it really?
Message-ID: <alpine.LSU.2.00.0905251117340.27529@fbirervta.pbzchgretzou.qr>
URL: https://gitlist.dev/e/alpine.LSU.2.00.0905251117340.27529%40fbirervta.pbzchgretzou.qr
In-Reply-To: <7v4ov9zwtt.fsf@alter.siamese.dyndns.org>

```

On Monday 2009-05-25 05:27, Junio C Hamano wrote:
>Jan Engelhardt writes:
>
>> Any insights?
>
>[...]
>For example, "rcsdiff -u" shows something like:
>
>    diff -u -r1.1 -r1.2
>    --- Make        1995/01/08 20:50:24     1.1
>    +++ Make        2001/10/04 07:14:29     1.2
>    @@ -1,7 +1,11 @@
>     #!/bin/sh
>    -...
>It has cruft after the timestamp, and the timestamp itself is not even in
>the format POSIX expects to see (see
> http://www.opengroup.org/onlinepubs/9699919799/utilities/diff.html#tag_20_34_10_07
>if you really care).
>
>Is it in violation of POSIX unified context format?  Of course yes.  Is it
>then not in unified context format at all?
>
>For all practical purposes, if you (either you human or your tool) know
>how to read unified context format, you can process what we (or RCS)
>produce.
>I used RCS as an example because it has been maintained by the same person
>who maintains "GNU diff" and was one of the main player who pushed
>"unified context" format to the updated POSIX.
>

Here is a random patch; not that I am fond of it, but it
speaks in code at least.
Tear it apar! :)


parent 9619ff14159ab3401636b9883a715b0f20b051df (v1.6.3.1-152-g9619ff1)
commit 9d73395d1c08d2fc4e1b740c2d37d8daed7cb3e6
Author: Jan Engelhardt <jengelh@medozas.de>
Date:   Mon May 25 11:40:34 2009 +0200

diff: produce TSVN-acceptable diff

Once TortoiseSVNMerge sees a \t after the filename in a --- line,
it accepts a patch. (Not that t
So let's produce patches with said \t, and a $ so the \t does not
get chopped off by incompetent tools or overlooked otherwise.
---
 diff.c |    4 ++--
 1 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/diff.c b/diff.c
index f06876b..55cb974 100644
--- a/diff.c
+++ b/diff.c
@@ -324,7 +324,7 @@ static void emit_rewrite_diff(const char *name_a,
 	lc_a = count_lines(data_one, size_one);
 	lc_b = count_lines(data_two, size_two);
 	fprintf(o->file,
-		"%s--- %s%s%s\n%s+++ %s%s%s\n%s@@ -",
+		"%s--- %s%s%s\t$\n%s+++ %s%s%s\n%s@@ -",
 		metainfo, a_name.buf, name_a_tab, reset,
 		metainfo, b_name.buf, name_b_tab, reset, fraginfo);
 	print_line_count(o->file, lc_a);
@@ -637,7 +637,7 @@ static void fn_out_consume(void *priv, char *line, unsigned long len)
 		name_a_tab = strchr(ecbdata->label_path[0], ' ') ? "\t" : "";
 		name_b_tab = strchr(ecbdata->label_path[1], ' ') ? "\t" : "";
 
-		fprintf(ecbdata->file, "%s--- %s%s%s\n",
+		fprintf(ecbdata->file, "%s--- %s%s%s\t$\n",
 			meta, ecbdata->label_path[0], reset, name_a_tab);
 		fprintf(ecbdata->file, "%s+++ %s%s%s\n",
 			meta, ecbdata->label_path[1], reset, name_b_tab);
-- 
# Created with git-export-patch

```
