threads / discuss / 19489

Git produces Unidiff - Does it really?

Subject: Git produces Unidiff - Does it really?

## tl;dr

3 messages between May 24, 2009 and May 25, 2009.

replies: 2people: 2as markdown or json

Jan Engelhardt· May 24, 2009, 23:15 UTC · lore
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· May 25, 2009, 03:27 UTC · re: Jan Engelhardt · lore

Re: Git produces Unidiff - Does it really?

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· May 25, 2009, 09:48 UTC · re: Junio C Hamano · lore

Re: Git produces Unidiff - Does it really?

On Monday 2009-05-25 05:27, Junio C Hamano wrote:
Show 28 quoted lines
>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

← back to recent threads