git.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH] Don't ignore write failure from git-diff, git-log, etc.
@ 2007-05-26 11:45 Jim Meyering
  2007-05-26 16:18 ` Linus Torvalds
  0 siblings, 1 reply; 29+ messages in thread
From: Jim Meyering @ 2007-05-26 11:45 UTC (permalink / raw)
  To: git

Currently, when git-diff writes to a full device or gets an I/O error,
it fails to detect the write error:

    $ git-diff |wc -c
    3984
    $ git-diff > /dev/full && echo ignored write failure
    ignored write failure

git-log does the same thing:

    $ git-log -n1 > /dev/full && echo ignored write failure
    ignored write failure

Each git command should report such a failure.
Some already do, but with the patch below, they all do, and we
won't have to rely on code in each command's implementation to
perform the right incantation.

    $ ./git-log -n1 > /dev/full
    fatal: write failure on standard output: No space left on device
    [Exit 128]
    $ ./git-diff > /dev/full
    fatal: write failure on standard output: No space left on device
    [Exit 128]

You can demonstrate this with git's own --version output, too:
(but git --help detects the failure without this patch)

    $ ./git --version > /dev/full
    fatal: write failure on standard output: No space left on device
    [Exit 128]

Note that the fcntl test (for whether the fileno may be closed) is
required in order to avoid EBADF upon closing an already-closed stdout,
as would happen for each git command that already closes stdout; I think
update-index was the one I noticed in the failure of t5400, before I
added that test.

Signed-off-by: Jim Meyering <jim@meyering.net>
---
 git.c |   11 ++++++++++-
 1 files changed, 10 insertions(+), 1 deletions(-)

diff --git a/git.c b/git.c
index 29b55a1..a7d6515 100644
--- a/git.c
+++ b/git.c
@@ -308,6 +308,7 @@ static void handle_internal_command(int argc, const char **argv, char **envp)
 	for (i = 0; i < ARRAY_SIZE(commands); i++) {
 		struct cmd_struct *p = commands+i;
 		const char *prefix;
+		int status;
 		if (strcmp(p->cmd, cmd))
 			continue;

@@ -321,7 +322,15 @@ static void handle_internal_command(int argc, const char **argv, char **envp)
 			die("%s must be run in a work tree", cmd);
 		trace_argv_printf(argv, argc, "trace: built-in: git");

-		exit(p->fn(argc, argv, prefix));
+		status = p->fn(argc, argv, prefix);
+
+		/* Close stdout if necessary, and diagnose any failure.  */
+		if (0 <= fcntl(fileno (stdout), F_GETFD)
+		    && (ferror(stdout) || fclose(stdout)))
+			die("write failure on standard output: %s",
+			    strerror(errno));
+
+		exit(status);
 	}
 }

--
1.5.2.73.g18bece

^ permalink raw reply related	[flat|nested] 29+ messages in thread

* Re: [PATCH] Don't ignore write failure from git-diff, git-log, etc.
  2007-05-26 11:45 [PATCH] Don't ignore write failure from git-diff, git-log, etc Jim Meyering
@ 2007-05-26 16:18 ` Linus Torvalds
  2007-05-26 17:27   ` Junio C Hamano
  2007-05-27  9:16   ` Jim Meyering
  0 siblings, 2 replies; 29+ messages in thread
From: Linus Torvalds @ 2007-05-26 16:18 UTC (permalink / raw)
  To: Jim Meyering; +Cc: git



On Sat, 26 May 2007, Jim Meyering wrote:
>
> Each git command should report such a failure.
> Some already do, but with the patch below, they all do, and we
> won't have to rely on code in each command's implementation to
> perform the right incantation.

The patch is wrong.

Some write errors are expected and GOOD.

For example, EPIPE should not be reported. It's normal. The user got 
bored. It might be hidden by the SIGPIPE killing us, but regardless, 
reporting it for the normal log/diff thing is just not correct. EPIPE 
isn't an error, it's a "ok, nobody is listening any more".

Also, PLEASE don't do this:

> +		if (0 <= fcntl(fileno (stdout), F_GETFD)

That's totally unreadable to any normal human.

You don't say "if zero is smaller or equal to X". You say "if X is larger 
than or equal to zero". Stop messing with peoples minds, dammit!

Anybody who thinks that code like this causes fewer errors is just fooling 
himself. It causes *more* bugs, because people have a harder time reading 
it.

Maybe you and Junio have taught yourself bad manners, but you're a tiny 
tiny part of humanity or the development community. Junio can do it just 
because while he's just a single person, he's a big part of the git coding 
base, but anybody else who does it should just be shot.

			Linus

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH] Don't ignore write failure from git-diff, git-log, etc.
  2007-05-26 16:18 ` Linus Torvalds
@ 2007-05-26 17:27   ` Junio C Hamano
  2007-05-27  3:03     ` Nicolas Pitre
  2007-05-27  9:16   ` Jim Meyering
  1 sibling, 1 reply; 29+ messages in thread
From: Junio C Hamano @ 2007-05-26 17:27 UTC (permalink / raw)
  To: Linus Torvalds; +Cc: Jim Meyering, git

Linus Torvalds <torvalds@linux-foundation.org> writes:

> Also, PLEASE don't do this:
>
>> +		if (0 <= fcntl(fileno (stdout), F_GETFD)
>
> That's totally unreadable to any normal human.
>
> You don't say "if zero is smaller or equal to X". You say "if X is larger 
> than or equal to zero". Stop messing with peoples minds, dammit!
>
> Anybody who thinks that code like this causes fewer errors is just fooling 
> himself. It causes *more* bugs, because people have a harder time reading 
> it.
>
> Maybe you and Junio have taught yourself bad manners, but you're a tiny 
> tiny part of humanity or the development community. Junio can do it just 
> because while he's just a single person, he's a big part of the git coding 
> base, but anybody else who does it should just be shot.

Whew, that is a blast from the past.

cf. http://thread.gmane.org/gmane.comp.version-control.git/3903/focus=3906

 (1) Maybe Jim was just being nice, trying to make the code look
     like surrounding code;

 (2) Maybe Jim and the person I learned the style from worked
     together for a long time and they picked it up from the
     same source;

 (3) Maybe I am not alone, and it is not native language -
     mother tongue issue as some suspected in the quoted thread.

In any case, I think my recent code have much less "textual
order should reflect actual order" convention than before,
because I have been forcing myself to say aloud "if X is larger"
or "if X is smaller" before writing my comparisons, in order to
match the "peoples minds" expectation you mentioned above.

This initially slowed me down and made my head hurt quite a bit,
and sometimes it still does.

Once you learn to _visualize_ the ordering relationship in "X op
Y" by relying on "op" being always < or <=, you will get the
"number line" pop in your head whenever you see a comparision
expression, without even having to think about it, and you "see"
X and Y on the number line:

        ... -2        -1         0         1         2  ...  
    ---------+---------+---------+---------+---------+---------
    true:                        0   <=  fcntl(...)


        ... -2        -1         0         1         2  ...  
    ---------+---------+---------+---------+---------+---------
    false:    (0 <= fcntl(...))

What the comparison is doing comes naturally to you, without
even having to translate it back to human language "X is larger
(or smaller) than this constant".  The ordering is right there,
in front of your eyes, before you vocalize it.

In a sense, just like it is hard to go back from git to CVS (or
it is hard to go back to not knowing the power of the index), it
is very hard to go back once you learn to do this.

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH] Don't ignore write failure from git-diff, git-log, etc.
  2007-05-26 17:27   ` Junio C Hamano
@ 2007-05-27  3:03     ` Nicolas Pitre
  0 siblings, 0 replies; 29+ messages in thread
From: Nicolas Pitre @ 2007-05-27  3:03 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Linus Torvalds, Jim Meyering, git

On Sat, 26 May 2007, Junio C Hamano wrote:

> Once you learn to _visualize_ the ordering relationship in "X op
> Y" by relying on "op" being always < or <=, you will get the
> "number line" pop in your head whenever you see a comparision
> expression, without even having to think about it, and you "see"
> X and Y on the number line:
> 
>         ... -2        -1         0         1         2  ...  
>     ---------+---------+---------+---------+---------+---------
>     true:                        0   <=  fcntl(...)
> 
> 
>         ... -2        -1         0         1         2  ...  
>     ---------+---------+---------+---------+---------+---------
>     false:    (0 <= fcntl(...))
> 
> What the comparison is doing comes naturally to you, without
> even having to translate it back to human language "X is larger
> (or smaller) than this constant".  The ordering is right there,
> in front of your eyes, before you vocalize it.

Well... it probably depends on how your brain is wired up.

I completely agree with your reasoning.  It _should_ indeed be natural 
and more obvious to always put things in increasing order.

BUT it is not how my brain is connected, and after many attempts I just 
cannot work efficiently with your method.  It simply doesn't come out 
logical for me and I have to spend an unusual amount of time on every 
occasion I encounter this structure to really get it.  To me it always 
looks backward.

And I suspect the majority of people who just cannot train their brain 
with the arguably superior representation are many, probably the 
majority. It appears to be the case for Linus.  It is definitely the 
case for me.


Nicolas, who apologizes for his defective brain.

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH] Don't ignore write failure from git-diff, git-log, etc.
  2007-05-26 16:18 ` Linus Torvalds
  2007-05-26 17:27   ` Junio C Hamano
@ 2007-05-27  9:16   ` Jim Meyering
  2007-05-27 16:17     ` Linus Torvalds
  1 sibling, 1 reply; 29+ messages in thread
From: Jim Meyering @ 2007-05-27  9:16 UTC (permalink / raw)
  To: Linus Torvalds; +Cc: git

Linus Torvalds <torvalds@linux-foundation.org> wrote:
> On Sat, 26 May 2007, Jim Meyering wrote:
>>
>> Each git command should report such a failure.
>> Some already do, but with the patch below, they all do, and we
>> won't have to rely on code in each command's implementation to
>> perform the right incantation.
>
> The patch is wrong.

What you should have said is that the patch is fine in principle, since
it does fix a pretty serious bug (important tools ignoring ENOSPC),
but you'd prefer that it continue to ignore EPIPE.

With a name like yours, being more positive would go a long way toward
encouraging (or rather *not discouraging*) contributions.

> Some write errors are expected and GOOD.
>
> For example, EPIPE should not be reported. It's normal. The user got
> bored. It might be hidden by the SIGPIPE killing us, but regardless,
> reporting it for the normal log/diff thing is just not correct. EPIPE
> isn't an error, it's a "ok, nobody is listening any more".

I have to disagree.  There may be precedent for hiding EPIPE errors,
but that is not the norm among command line tools wrt piped stdout.

First of all, one has to work just to get such an error.  These days,
most people use a shell that doesn't ignore, handle or block SIGPIPE, so
direct use of a program like git-log or git-diff gets the signal directly,
and there's no EPIPE error.  E.g., try "cat", and you see it gets SIGPIPE
(141=128+SIGPIPE(13)):

    $ seq 90000|(cat; echo $? >&2) | head -1 > /dev/null
    141

However, you can tweak your shell to handle/ignore SIGPIPE.  Also,
some porcelain scripts do ignore SIGPIPE.  Then, a program run in that
environment does see EPIPE.  Consider how a few other non-interactive
programs work when one of their stdout-writing syscalls fails with EPIPE:

Try GNU diff and sed:
[now, using shorter ":" in place of more realistic head -1]

    $ (trap '' PIPE; seq 90000|diff - /dev/null;echo $? >&2)| :
    diff: standard output: Broken pipe
    2
    $ (trap '' PIPE; seq 90000|sed s/a/b/; echo $? 1>&2)| :
    /bin/sed: couldn't write 5 items to stdout: Broken pipe
    seq: write error: Broken pipe
    4

Try tee (from GNU coreutils):

    $ (trap '' PIPE; seq 90000|tee /dev/null; echo $? >&2) | :
    tee: standard output: Broken pipe
    tee: write error
    1

sort, tac, cut, fold, od, head, tail, tr, uniq, etc. all work the same
way, if you're using the coreutils.  But perhaps that's not fair, since
I maintain the coreutils.  And there is some variance among how other-
vendor versions of those tools work.  E.g., Solaris 10's /bin/cat
diagnoses the error, but neither /bin/sort nor /bin/diff do.

As for version control tools, monotone does what I'd expect in
this situation: "mtn diff" reports the failure and exits nonzero.

svn and cvs also report the error, although they both exit successfully
in spite of that.  I tried both log and diff commands for each tool.
cvs catches the signal, svn doesn't.

mercurial and darcs totally ignore the write error and SIGPIPE,
so there is no way to determine from stderr or exit code whether
their writes complete normally.

For any tool whose output might be piped to another, the pipe-writing
tool should exit nonzero for any write error.  Otherwise, its exit code
ends up being a lie, pretending success but, in effect, covering up for
a failure.  In general, I've found that papering over syscall failures
makes higher-level problems harder to diagnose.

Do you really want git-log to continue to do this?

    $ (trap '' PIPE; git-log; echo $? >&2 ) | :
    0

With my patch, it does this:

    $ (trap '' PIPE; ./git-log; echo $? >&2 ) | :
    fatal: write failure on standard output: Broken pipe
    128

> Also, PLEASE don't do this:
>
>> +		if (0 <= fcntl(fileno (stdout), F_GETFD)

Since Junio is making an effort to "conform",
I too will make the effort when contributing to git.

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH] Don't ignore write failure from git-diff, git-log, etc.
  2007-05-27  9:16   ` Jim Meyering
@ 2007-05-27 16:17     ` Linus Torvalds
  2007-05-28 13:14       ` Jim Meyering
  0 siblings, 1 reply; 29+ messages in thread
From: Linus Torvalds @ 2007-05-27 16:17 UTC (permalink / raw)
  To: Jim Meyering; +Cc: git



On Sun, 27 May 2007, Jim Meyering wrote:
> 
> I have to disagree.  There may be precedent for hiding EPIPE errors,
> but that is not the norm among command line tools wrt piped stdout.

.. and this is a PROBLEM. Which is why I think your patch was really 
wrong.

I don't know how many people remember all the _stupid_ problems we had 
exactly because many versions of bash are crap, crap, crap, and people 
(including you) don't realize that EPIPE is _different_ from other write 
errors.

Just do a google search for

	"broken pipe" bash

and not only will you see a lot of complaints, but the #5 entry is a 
complaint for a git issue that we had tons of problems with. See for 
example

	http://www.gelato.unsw.edu.au/archives/git/0504/2602.html

The reason? Some _idiotic_ versions of bash don't have DONT_REPORT_SIGPIPE 
on by default. 

So I do get upset when people then make the same error with git.

> Do you really want git-log to continue to do this?
> 
>     $ (trap '' PIPE; git-log; echo $? >&2 ) | :
>     0
> 
> With my patch, it does this:
> 
>     $ (trap '' PIPE; ./git-log; echo $? >&2 ) | :
>     fatal: write failure on standard output: Broken pipe
>     128

That error return is fine. The annoying error report, however, is NOT.

For _exactly_ the same reason that a bash that doesn't have 
DONT_REPORT_SIGPIPE enabled is a piece of crap.

And your arguments that "others do it wrong, so we can too" is so broken 
as to be really really sad. If you cannot see the serious problem with 
that argument, I don't know what to tell you.

Try this:

	trap '' PIPE; ./git-log | head

and dammit, if you get an error message from that, your program is BROKEN.

And if you cannot understand that, then I don't even know what to say.

But _exiting_ is fine. It's the bogus error reporting that isn't. The 
above command like should NOT cause the user to have to skip stderr - 
because no error happened!

(Whether the error code is 0 or some error, I dunno. I'd argue that if you 
ignore SIGPIPE, you'd probably also want to do "exit(0)" for EPIPE, but 
it's not nearly as annoying as writing bogus error messages to stderr.

			Linus

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH] Don't ignore write failure from git-diff, git-log, etc.
  2007-05-27 16:17     ` Linus Torvalds
@ 2007-05-28 13:14       ` Jim Meyering
  2007-05-28 15:46         ` Marco Roeland
                           ` (2 more replies)
  0 siblings, 3 replies; 29+ messages in thread
From: Jim Meyering @ 2007-05-28 13:14 UTC (permalink / raw)
  To: Linus Torvalds; +Cc: git

Linus Torvalds <torvalds@linux-foundation.org> wrote:
> On Sun, 27 May 2007, Jim Meyering wrote:
>>
>> I have to disagree.  There may be precedent for hiding EPIPE errors,
>> but that is not the norm among command line tools wrt piped stdout.
>
> .. and this is a PROBLEM. Which is why I think your patch was really
> wrong.
>
> I don't know how many people remember all the _stupid_ problems we had
> exactly because many versions of bash are crap, crap, crap, and people
> (including you) don't realize that EPIPE is _different_ from other write
> errors.
>
> Just do a google search for
>
> 	"broken pipe" bash
>
> and not only will you see a lot of complaints, but the #5 entry is a
> complaint for a git issue that we had tons of problems with. See for
> example
>
> 	http://www.gelato.unsw.edu.au/archives/git/0504/2602.html

Whether bash should print a diagnostic when it kills a process with
SIGPIPE is _different_ from whether the writing process should diagnose
its own write failure arising from a handled SIGPIPE.

I suspect that git's special treatment of EPIPE was a shoot-the-messenger
reaction to the work-around (trap '' PIPE) required to avoid diagnostics
from porcelain being interpreted by what would now be a 2-year-old
version of bash.  It is time to remove that work-around, because it
can obscure real errors, and removing it will be largely unnoticed.

...
>> Do you really want git-log to continue to do this?
>>
>>     $ (trap '' PIPE; git-log; echo $? >&2 ) | :
>>     0
>>
>> With my patch, it does this:
>>
>>     $ (trap '' PIPE; ./git-log; echo $? >&2 ) | :
>>     fatal: write failure on standard output: Broken pipe
>>     128
>
> That error return is fine.  The annoying error report, however, is NOT.

That error message serves to indicate a REAL FLAW, some of the time.
In such cases, the diagnostic is likely to be welcome, not annoying.
Which is more important: avoiding annoyance in some now-very-unusual
circumstances, or allowing robust porcelain to diagnose real errors?

> And your arguments that "others do it wrong, so we can too" is so broken
> as to be really really sad. If you cannot see the serious problem with
> that argument, I don't know what to tell you.

Questionable debate tactic: misrepresent an opponent's argument with a
statement that is obviously foolish, then proceed to argue that anyone
who doesn't recognise the silliness of the restated argument leaves
you speechless.

My argument is "If so many other tools do it RIGHT,
why can't git do it right, too?".

> Try this:
>
> 	trap '' PIPE; ./git-log | head
>
> and dammit, if you get an error message from that, your program is BROKEN.
>
> And if you cannot understand that, then I don't even know what to say.

Of course error messages are annoying when your short-pipe-read is
_deliberate_ (tho, most real uses of git tools will actually get no
message to be annoyed about[*]), but what if there really *is* a mistake?
Try this:

    # You want to force git to ignore the error.
    $ trap '' PIPE; git-rev-list HEAD | sync
    $

    # I want to diagnose the error (the exit 128 is from zsh, bash gets 0):
    $ trap '' PIPE; git-rev-list HEAD | sync
    fatal: write failure on standard output: Broken pipe
    [Exit 128]

The use of "sync" above is obviously a mistake.
With the existing git tools, most write-to-stdout errors are ignored,
including EPIPE, so this erroneous command completes successfully, just as
it would when writing to a full or corrupt disk.  Even if you use bash's
"set -o pipefail" option, there is no indication of the failure.  With my
patch, write errors are reported, even EPIPE, so that in this case, the
user of the above will get an error message.  And with bash's pipefail
option, the git-rev-list write failure can propagate "out" to the script.

Since using "sync" is contrived, for a little more realism, imagine the
SHA1 strings are being written to a FIFO, and you don't have access to
the program running on the other side.  The sender should have a way to
detect when the other end closes the pipe prematurely.  Exempting EPIPE,
it CANNOT detect failure:

    $ mkfifo F && head -1 F > /dev/null & sleep 1
    $ trap '' PIPE; git-rev-list HEAD > F && echo bad: ignored write failure
    bad: ignored write failure

Handling EPIPE like other write errors, it CAN detect failure:

    $ mkfifo F && head -1 F > /dev/null & sleep 1
    $ trap '' PIPE; ./git-rev-list HEAD > F || echo ok
    fatal: write failure on standard output: Broken pipe
    ok

> But _exiting_ is fine. It's the bogus error reporting that isn't. The
> above command like should NOT cause the user to have to skip stderr -
> because no error happened!

Don't worry about the diagnostic.  It probably won't show up much at all
[*] these days, since most shells now let SIGPIPE kill the writer (silently).
And if the message does appear once in a while, there are cleaner ways
to suppress it than hamstringing all of the git plumbing for everyone.

In fact, in this vein, the existing EPIPE exceptions in write_or_die.c
and merge-recursive.c should be removed.  Here's a revised patch to do
that, too.  Junio, if you see fit to accept any part of this, I'll be
happy to write test case additions demonstrating before/after improvements.

========================================================================
Subject: [PATCH] Don't ignore write failure from git-diff, git-log, etc.

Currently, when git-diff writes to a full device or gets an I/O error,
it fails to detect the write error:

    $ git-diff |wc -c
    3984
    $ git-diff > /dev/full && echo ignored write failure
    ignored write failure

git-log does the same thing:

    $ git-log -n1 > /dev/full && echo ignored write failure
    ignored write failure

Each and every git command should report such a failure.
Some already do, but with the patch below, they all do, and we
won't have to rely on code in each command's implementation to
perform the right incantation.

    $ ./git-log -n1 > /dev/full
    fatal: write failure on standard output: No space left on device
    [Exit 128]
    $ ./git-diff > /dev/full
    fatal: write failure on standard output: No space left on device
    [Exit 128]

You can demonstrate this with git's own --version output, too:
(but git --help detects the failure without this patch)

    $ ./git --version > /dev/full
    fatal: write failure on standard output: No space left on device
    [Exit 128]

Note that the fcntl test (for whether the fileno may be closed) is
required in order to avoid EBADF upon closing an already-closed stdout,
as would happen for each git command that already closes stdout; I think
update-index was the one I noticed in the failure of t5400, before I
added that test.

Also, to be consistent, don't ignore EPIPE write failures.

Signed-off-by: Jim Meyering <jim@meyering.net>
---
 git.c             |   11 ++++++++++-
 merge-recursive.c |    3 ---
 write_or_die.c    |    4 ----
 3 files changed, 10 insertions(+), 8 deletions(-)

diff --git a/git.c b/git.c
index 29b55a1..e176ab4 100644
--- a/git.c
+++ b/git.c
@@ -308,6 +308,7 @@ static void handle_internal_command(int argc, const char **argv, char **envp)
 	for (i = 0; i < ARRAY_SIZE(commands); i++) {
 		struct cmd_struct *p = commands+i;
 		const char *prefix;
+		int status;
 		if (strcmp(p->cmd, cmd))
 			continue;

@@ -321,7 +322,15 @@ static void handle_internal_command(int argc, const char **argv, char **envp)
 			die("%s must be run in a work tree", cmd);
 		trace_argv_printf(argv, argc, "trace: built-in: git");

-		exit(p->fn(argc, argv, prefix));
+		status = p->fn(argc, argv, prefix);
+
+		/* Close stdout if necessary, and diagnose any failure.  */
+		if (fcntl(fileno (stdout), F_GETFD) >= 0)
+		    && (ferror(stdout) || fclose(stdout)))
+			die("write failure on standard output: %s",
+			    strerror(errno));
+
+		exit(status);
 	}
 }

diff --git a/merge-recursive.c b/merge-recursive.c
index 8f72b2c..bfa4335 100644
--- a/merge-recursive.c
+++ b/merge-recursive.c
@@ -523,9 +523,6 @@ static void flush_buffer(int fd, const char *buf, unsigned long size)
 	while (size > 0) {
 		long ret = write_in_full(fd, buf, size);
 		if (ret < 0) {
-			/* Ignore epipe */
-			if (errno == EPIPE)
-				break;
 			die("merge-recursive: %s", strerror(errno));
 		} else if (!ret) {
 			die("merge-recursive: disk full?");
diff --git a/write_or_die.c b/write_or_die.c
index 5c4bc85..fadfcaa 100644
--- a/write_or_die.c
+++ b/write_or_die.c
@@ -41,8 +41,6 @@ int write_in_full(int fd, const void *buf, size_t count)
 void write_or_die(int fd, const void *buf, size_t count)
 {
 	if (write_in_full(fd, buf, count) < 0) {
-		if (errno == EPIPE)
-			exit(0);
 		die("write error (%s)", strerror(errno));
 	}
 }
@@ -50,8 +48,6 @@ void write_or_die(int fd, const void *buf, size_t count)
 int write_or_whine_pipe(int fd, const void *buf, size_t count, const char *msg)
 {
 	if (write_in_full(fd, buf, count) < 0) {
-		if (errno == EPIPE)
-			exit(0);
 		fprintf(stderr, "%s: write error (%s)\n",
 			msg, strerror(errno));
 		return 0;

^ permalink raw reply related	[flat|nested] 29+ messages in thread

* Re: [PATCH] Don't ignore write failure from git-diff, git-log, etc.
  2007-05-28 13:14       ` Jim Meyering
@ 2007-05-28 15:46         ` Marco Roeland
  2007-05-28 18:19           ` Jim Meyering
  2007-05-28 16:32         ` Linus Torvalds
  2007-05-28 21:27         ` Junio C Hamano
  2 siblings, 1 reply; 29+ messages in thread
From: Marco Roeland @ 2007-05-28 15:46 UTC (permalink / raw)
  To: Jim Meyering; +Cc: Linus Torvalds, git

On monday May 28th 2007 at 15:14 Jim Meyering wrote:

> > I don't know how many people remember all the _stupid_ problems we had
> > exactly because many versions of bash are crap, crap, crap, and people
> > (including you) don't realize that EPIPE is _different_ from other write
> > errors.
> >
> > Just do a google search for
> >
> > 	"broken pipe" bash
> >
> > and not only will you see a lot of complaints, but the #5 entry is a
> > complaint for a git issue that we had tons of problems with. See for
> > example
> >
> > 	http://www.gelato.unsw.edu.au/archives/git/0504/2602.html
> 
> Whether bash should print a diagnostic when it kills a process with
> SIGPIPE is _different_ from whether the writing process should diagnose
> its own write failure arising from a handled SIGPIPE.
> 
> I suspect that git's special treatment of EPIPE was a shoot-the-messenger
> reaction to the work-around (trap '' PIPE) required to avoid diagnostics
> from porcelain being interpreted by what would now be a 2-year-old
> version of bash.  It is time to remove that work-around, because it
> can obscure real errors, and removing it will be largely unnoticed.

Good point. But also notice that when you are stuck with a shell that
does complain about SIGPIPE it is _really_ annoying!

On current Debian 'sid' with your patch this does not seem to be the
case.

The second chunk of your patch (to git.c) contains a small copy-paste
accident methinks:

> @@ -321,7 +322,15 @@ static void handle_internal_command(int argc, const char **argv, char **envp)
>  			die("%s must be run in a work tree", cmd);
>  		trace_argv_printf(argv, argc, "trace: built-in: git");
> 
> -		exit(p->fn(argc, argv, prefix));
> +		status = p->fn(argc, argv, prefix);
> +
> +		/* Close stdout if necessary, and diagnose any failure.  */
> +		if (fcntl(fileno (stdout), F_GETFD) >= 0)
> +		    && (ferror(stdout) || fclose(stdout)))
> +			die("write failure on standard output: %s",
> +			    strerror(errno));
> +
> +		exit(status);

The if statement with 'fcntl' is missing a brace, it should be:

+		if ((fcntl(fileno (stdout), F_GETFD) >= 0)
+		    && (ferror(stdout) || fclose(stdout)))

-- 
Marco Roeland

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH] Don't ignore write failure from git-diff, git-log, etc.
  2007-05-28 13:14       ` Jim Meyering
  2007-05-28 15:46         ` Marco Roeland
@ 2007-05-28 16:32         ` Linus Torvalds
  2007-05-28 20:04           ` Jim Meyering
  2007-05-28 21:27         ` Junio C Hamano
  2 siblings, 1 reply; 29+ messages in thread
From: Linus Torvalds @ 2007-05-28 16:32 UTC (permalink / raw)
  To: Jim Meyering; +Cc: git



On Mon, 28 May 2007, Jim Meyering wrote:
> 
> I suspect that git's special treatment of EPIPE was a shoot-the-messenger
> reaction to the work-around (trap '' PIPE) required to avoid diagnostics
> from porcelain being interpreted by what would now be a 2-year-old
> version of bash.

No. You don't seem to realize. That was the *default* behaviour of bash.

For all I know, it might _still_ be the default behaviour.

The only reason not everybody ever even noticed, was that most 
distributions were clueful enough to have figured out that it was broken, 
and configured bash separately. But "most" does not mean "all", and I had 
this problem on powerpc, and others had it on Debian, I htink  (might have 
been slackware). I think RH and SuSE had both fixed it explicitly.

> diff --git a/write_or_die.c b/write_or_die.c
> index 5c4bc85..fadfcaa 100644
> --- a/write_or_die.c
> +++ b/write_or_die.c
> @@ -41,8 +41,6 @@ int write_in_full(int fd, const void *buf, size_t count)
>  void write_or_die(int fd, const void *buf, size_t count)
>  {
>  	if (write_in_full(fd, buf, count) < 0) {
> -		if (errno == EPIPE)
> -			exit(0);
>  		die("write error (%s)", strerror(errno));

Nack. Nack. NACK.

I think this patch is fundamentally WRONG. This fragment is just a prime 
example of why the whole patch is crap. The old code was correct, and you 
broke it.

		Linus

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH] Don't ignore write failure from git-diff, git-log, etc.
  2007-05-28 15:46         ` Marco Roeland
@ 2007-05-28 18:19           ` Jim Meyering
  2007-05-28 19:05             ` Marco Roeland
  2007-05-28 20:44             ` Junio C Hamano
  0 siblings, 2 replies; 29+ messages in thread
From: Jim Meyering @ 2007-05-28 18:19 UTC (permalink / raw)
  To: Marco Roeland; +Cc: git

Marco Roeland <marco.roeland@xs4all.nl> wrote:
> The if statement with 'fcntl' is missing a brace, it should be:
>
> +		if ((fcntl(fileno (stdout), F_GETFD) >= 0)
> +		    && (ferror(stdout) || fclose(stdout)))

Thank you.
That was a stale patch (from before I compiled/tested, obviously).
I intended this:

		if (fcntl(fileno (stdout), F_GETFD) >= 0
		    && (ferror(stdout) || fclose(stdout)))

Here's the correct one:

Subject: [PATCH] Don't ignore write failure from git-diff, git-log, etc.

Currently, when git-diff writes to a full device or gets an I/O error,
it fails to detect the write error:

    $ git-diff |wc -c
    3984
    $ git-diff > /dev/full && echo ignored write failure
    ignored write failure

git-log does the same thing:

    $ git-log -n1 > /dev/full && echo ignored write failure
    ignored write failure

Each and every git command should report such a failure.
Some already do, but with the patch below, they all do, and we
won't have to rely on code in each command's implementation to
perform the right incantation.

    $ ./git-log -n1 > /dev/full
    fatal: write failure on standard output: No space left on device
    [Exit 128]
    $ ./git-diff > /dev/full
    fatal: write failure on standard output: No space left on device
    [Exit 128]

You can demonstrate this with git's own --version output, too:
(but git --help detects the failure without this patch)

    $ ./git --version > /dev/full
    fatal: write failure on standard output: No space left on device
    [Exit 128]

Note that the fcntl test (for whether the fileno may be closed) is
required in order to avoid EBADF upon closing an already-closed stdout,
as would happen for each git command that already closes stdout; I think
update-index was the one I noticed in the failure of t5400, before I
added that test.

Also, to be consistent, don't ignore EPIPE write failures.

Signed-off-by: Jim Meyering <jim@meyering.net>
---
 git.c             |   11 ++++++++++-
 merge-recursive.c |    3 ---
 write_or_die.c    |    4 ----
 3 files changed, 10 insertions(+), 8 deletions(-)

diff --git a/git.c b/git.c
index 29b55a1..7b45a73 100644
--- a/git.c
+++ b/git.c
@@ -308,6 +308,7 @@ static void handle_internal_command(int argc, const char **argv, char **envp)
 	for (i = 0; i < ARRAY_SIZE(commands); i++) {
 		struct cmd_struct *p = commands+i;
 		const char *prefix;
+		int status;
 		if (strcmp(p->cmd, cmd))
 			continue;

@@ -321,7 +322,15 @@ static void handle_internal_command(int argc, const char **argv, char **envp)
 			die("%s must be run in a work tree", cmd);
 		trace_argv_printf(argv, argc, "trace: built-in: git");

-		exit(p->fn(argc, argv, prefix));
+		status = p->fn(argc, argv, prefix);
+
+		/* Close stdout if necessary, and diagnose any failure.  */
+		if (fcntl(fileno (stdout), F_GETFD) >= 0
+		    && (ferror(stdout) || fclose(stdout)))
+			die("write failure on standard output: %s",
+			    strerror(errno));
+
+		exit(status);
 	}
 }

diff --git a/merge-recursive.c b/merge-recursive.c
index 8f72b2c..bfa4335 100644
--- a/merge-recursive.c
+++ b/merge-recursive.c
@@ -523,9 +523,6 @@ static void flush_buffer(int fd, const char *buf, unsigned long size)
 	while (size > 0) {
 		long ret = write_in_full(fd, buf, size);
 		if (ret < 0) {
-			/* Ignore epipe */
-			if (errno == EPIPE)
-				break;
 			die("merge-recursive: %s", strerror(errno));
 		} else if (!ret) {
 			die("merge-recursive: disk full?");
diff --git a/write_or_die.c b/write_or_die.c
index 5c4bc85..fadfcaa 100644
--- a/write_or_die.c
+++ b/write_or_die.c
@@ -41,8 +41,6 @@ int write_in_full(int fd, const void *buf, size_t count)
 void write_or_die(int fd, const void *buf, size_t count)
 {
 	if (write_in_full(fd, buf, count) < 0) {
-		if (errno == EPIPE)
-			exit(0);
 		die("write error (%s)", strerror(errno));
 	}
 }
@@ -50,8 +48,6 @@ void write_or_die(int fd, const void *buf, size_t count)
 int write_or_whine_pipe(int fd, const void *buf, size_t count, const char *msg)
 {
 	if (write_in_full(fd, buf, count) < 0) {
-		if (errno == EPIPE)
-			exit(0);
 		fprintf(stderr, "%s: write error (%s)\n",
 			msg, strerror(errno));
 		return 0;

^ permalink raw reply related	[flat|nested] 29+ messages in thread

* Re: [PATCH] Don't ignore write failure from git-diff, git-log, etc.
  2007-05-28 18:19           ` Jim Meyering
@ 2007-05-28 19:05             ` Marco Roeland
  2007-05-28 20:23               ` Jim Meyering
  2007-05-28 20:44             ` Junio C Hamano
  1 sibling, 1 reply; 29+ messages in thread
From: Marco Roeland @ 2007-05-28 19:05 UTC (permalink / raw)
  To: Jim Meyering; +Cc: git

On monday May 28th 2007 at 20:19 Jim Meyering wrote:

> ...
> 
> Also, to be consistent, don't ignore EPIPE write failures.

In practice I agree with someone else on this thread that EPIPE _is_
different. In a way the responsibility doesn't lie with the writer but
with the reader.

But just out of curiosity is there an easy way to test the EPIPE
behaviour? I cite a piece of the "changelog.Debian" file from the
Debian version of the bash shell. In Debian, as earlier in many other
distributions, the annoying EPIPE error was "fixed" in version 2.0.3-3
from 19 dec 1999.

========================================================================
* Define DONT_REPORT_SIGPIPE: We don't want to see `Broken pipe' messages
  when a job like `cat jobs.c | exit 1' is executed. Fixes part of #7047,
  #10259, #10433 and #10494. Comment from the upstream author: "The default
  bash behavior with respect to the exit status of a pipeline will not
  change.  Changing it as suggested in the discussion of #10494 would render
  bash incompatible with every other shell out there.". Closed these reports.

  -- Matthias Klose <doko@debian.org>  Sun, 19 Dec 1999 15:58:43 +0100
========================================================================

The mentioned "test-case" as used in "git log -n1 | exit 1" doesn't
produce an error in my Debian 'sid' bash, either with or without your
patch, so it doesn't seem to have any effect there? Whereas probably in
a "default" bash (don't know if upstream has changed it's mind already!)
with your patch (i.e. the EPIPE special casing removal) it will again
probably introduce these annoying (for interactive use) errors.

Thanks for your patch anyway, the "fcntl" diagnosis is a really useful
technique to know, and IMVHO also useful for git; although perhaps not
very portable for all platforms.
-- 
Marco Roeland

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH] Don't ignore write failure from git-diff, git-log, etc.
  2007-05-28 16:32         ` Linus Torvalds
@ 2007-05-28 20:04           ` Jim Meyering
  2007-05-29  3:01             ` Linus Torvalds
  0 siblings, 1 reply; 29+ messages in thread
From: Jim Meyering @ 2007-05-28 20:04 UTC (permalink / raw)
  To: Linus Torvalds; +Cc: git

Linus Torvalds <torvalds@linux-foundation.org> wrote:
> On Mon, 28 May 2007, Jim Meyering wrote:
>>
>> I suspect that git's special treatment of EPIPE was a shoot-the-messenger
>> reaction to the work-around (trap '' PIPE) required to avoid diagnostics
>> from porcelain being interpreted by what would now be a 2-year-old
>> version of bash.

Hi Linus!

Thanks for all the encouragement.

> No. You don't seem to realize. That was the *default* behaviour of bash.

Of course it was the default.  Because it was the default, it
provoked contortions like using `trap '' PIPE' in shell scripts,
which in turn provoked EPIPE diagnostics from git, which
prompted the EPIPE-ignoring changes in git plumbing.
And those changes can now OBSCURE REAL ERRORS, as I've shown.

Note: it was the SIGPIPE-ignoring work-around that caused the trouble.
The bash bug didn't cause trouble with git _directly_.

If anyone can find a mainstream distro (I didn't) on which my
patch causes trouble, please let us all know.

Bash changed its default first in bash-3.1-alpha1.
The next stable release was bash-3.1, in Dec 2005:

  r.  By default, the shell no longer reports processes dying from SIGPIPE.

It looks like most major distros had fixed it long before.
The latest stable release is bash-3.2 from October, 2006.

> For all I know, it might _still_ be the default behaviour.

It's not.  See above.
Easy to test: run this: seq 99999|head -1
if all you see is a single line with "1" on it, and an exit status of 0,
then there's no problem.

However, the version of bash you use is IRRELEVANT to the question of
EPIPE.  SIGPIPE has always been delivered by default.  The only difference
lay in whether bash _reported_ the delivery.  It's only the work-around
ignoring of SIGPIPE that used to provoke EPIPE "broken pipe" errors.
Now, all of the git porcelain shell code that did that appears to be gone,
probably converted to perl or C.

You can get an EPIPE diagnostic with my patch any time the affected
plumbing is invoked from an environment in which SIGPIPE is ignored.
That environment could be your shell (if you put "trap '' PIPE" in a
start-up file -- though no one does *that*), or porcelain that does that,
or the perlish $SIG{PIPE} = 'IGNORE'.  The following are the only parts
of git I've found that ignore SIGPIPE:

  git-archimport.perl
  git-cvsimport.perl
  git-svn.perl
  git-svnimport.perl

And nothing in cogito does.
So, now, I *really* don't see why there's any fuss about EPIPE.

> The only reason not everybody ever even noticed, was that most
> distributions were clueful enough to have figured out that it was broken,
> and configured bash separately. But "most" does not mean "all", and I had
> this problem on powerpc, and others had it on Debian, I htink  (might have
> been slackware). I think RH and SuSE had both fixed it explicitly.

Precisely.  That behavior in bash was so annoying that people were
motivated to fix it quickly.  But all of that was resolved long ago.

...
> Nack. Nack. NACK.
>
> I think this patch is fundamentally WRONG. This fragment is just a prime
> example of why the whole patch is crap. The old code was correct, and you
> broke it.

Um... maybe you've forgotten that this patch fixes a hole in the
"old code" (git.c).  Many git tools ignore write (ENOSPC) failures.
Compared to that aspect of the fix, I would have thought EPIPE-
handling would be a minor detail.  But now, the whole patch has
become "crap"?

Consider the EPIPE-related risks/choice:

  1) Continue to ignore EPIPE write failure: can obscure real errors.
       BTW, Linus, don't you agree?  You never commented on this point.

  2) Remove the EPIPE exclusion: *might* make git give a "broken pipe"
       diagnostic, if you run git in a SIGPIPE-ignoring environment.

#2 seems to be the lesser of two evils, considering that we can fix or
work around the occasional "broken pipe" error, but we can't work around
an unconditionally-ignored EPIPE.

Jim

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH] Don't ignore write failure from git-diff, git-log, etc.
  2007-05-28 19:05             ` Marco Roeland
@ 2007-05-28 20:23               ` Jim Meyering
  2007-05-28 23:41                 ` Petr Baudis
  0 siblings, 1 reply; 29+ messages in thread
From: Jim Meyering @ 2007-05-28 20:23 UTC (permalink / raw)
  To: Marco Roeland; +Cc: git

Marco Roeland <marco.roeland@xs4all.nl> wrote:
> On monday May 28th 2007 at 20:19 Jim Meyering wrote:
>> Also, to be consistent, don't ignore EPIPE write failures.
>
> In practice I agree with someone else on this thread that EPIPE _is_
> different. In a way the responsibility doesn't lie with the writer but
> with the reader.

Do you think it's ok for git-rev-list _not_ to diagnose an erroneous
command like this (i.e., to exit(0)):

    git-rev-list HEAD | sync

where "sync" could be any command that exits successfully
without reading any input?

Is it ok that it is currently *impossible* to diagnose that
failure by looking at exit codes?

> But just out of curiosity is there an easy way to test the EPIPE
> behaviour? I cite a piece of the "changelog.Debian" file from the

There are some examples here:
http://thread.gmane.org/gmane.comp.version-control.git/48469/focus=48617

...
> The mentioned "test-case" as used in "git log -n1 | exit 1" doesn't
> produce an error in my Debian 'sid' bash, either with or without your
> patch, so it doesn't seem to have any effect there? Whereas probably in
> a "default" bash (don't know if upstream has changed it's mind already!)
> with your patch (i.e. the EPIPE special casing removal) it will again
> probably introduce these annoying (for interactive use) errors.

As I just said in reply to Linus, the EPIPE handling difference
is independent of what version of bash you use.

> Thanks for your patch anyway, the "fcntl" diagnosis is a really useful
> technique to know, and IMVHO also useful for git; although perhaps not
> very portable for all platforms.

It appears to be portable enough.  fcntl/F_GETFD support is required
by POSIX, and has been around for ages.  FWIW, it's also used in git's
daemon.c and sha1_file.c.

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH] Don't ignore write failure from git-diff, git-log, etc.
  2007-05-28 18:19           ` Jim Meyering
  2007-05-28 19:05             ` Marco Roeland
@ 2007-05-28 20:44             ` Junio C Hamano
  2007-05-29 20:11               ` Jim Meyering
  1 sibling, 1 reply; 29+ messages in thread
From: Junio C Hamano @ 2007-05-28 20:44 UTC (permalink / raw)
  To: Jim Meyering; +Cc: Marco Roeland, git

Jim Meyering <jim@meyering.net> writes:

> Subject: [PATCH] Don't ignore write failure from git-diff, git-log, etc.
> ...
> You can demonstrate this with git's own --version output, too:
> (but git --help detects the failure without this patch)
>
>     $ ./git --version > /dev/full
>     fatal: write failure on standard output: No space left on device
>     [Exit 128]
>
> Note that the fcntl test (for whether the fileno may be closed) is
> required in order to avoid EBADF upon closing an already-closed stdout,
> as would happen for each git command that already closes stdout; I think
> update-index was the one I noticed in the failure of t5400, before I
> added that test.
>
> Also, to be consistent, don't ignore EPIPE write failures.
>
> Signed-off-by: Jim Meyering <jim@meyering.net>

I do not think anybody has much objection about the change to
handle_internal_command() in git.c you made.  Earlier we relied
on exit(3) to close still open filehandles (while ignoring
errors), and you made the close explicit in order to detect
errors.

But "to be consistent" above is not a very good justification.

In the presense of shells that give "annoying" behaviour (we
have to remember that not everybody have enough privilege,
expertise, or motivation to update /bin/sh or install their own
copy in $HOME/bin/sh), "EPIPE is different from other errors" is
a more practical point of view, I'd have to say.  IOW, it is not
clear if it is a good thing "to be consistent" to begin with.

I would have appreciated if this were two separate patches.  I
think the EPIPE one is an independent issue.  We could even make
it a configuration option to ignore EPIPE ;-)

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH] Don't ignore write failure from git-diff, git-log, etc.
  2007-05-28 13:14       ` Jim Meyering
  2007-05-28 15:46         ` Marco Roeland
  2007-05-28 16:32         ` Linus Torvalds
@ 2007-05-28 21:27         ` Junio C Hamano
  2007-05-29  3:47           ` Linus Torvalds
  2007-05-30 11:39           ` Jim Meyering
  2 siblings, 2 replies; 29+ messages in thread
From: Junio C Hamano @ 2007-05-28 21:27 UTC (permalink / raw)
  To: Jim Meyering; +Cc: Linus Torvalds, git

Jim Meyering <jim@meyering.net> writes:

> Of course error messages are annoying when your short-pipe-read is
> _deliberate_ (tho, most real uses of git tools will actually get no
> message to be annoyed about[*]), but what if there really *is* a mistake?
> Try this:
>
>     # You want to force git to ignore the error.
>     $ trap '' PIPE; git-rev-list HEAD | sync
>     $

It is perfectly valid (although it is stupid) for a Porcelain
script to do this:

    latest_by_jim=$(git log --pretty=oneline --author='Jim' | head -n 1)
    case "$latest_by_jim" in
    '') echo "No commit by Jim" ;;
    *)  # do something interesting on the commit
        ;;;
    esac

In such a case, it is a bit too much for my taste to force the
script to redirect what comes out of fd 2 of the upstream of the
pipe, so that it can filter out only the "write error" message
but still show other kinds of error messages.  You could do so
by elaborate shell magic, perhaps like this:

        filter_pipe_error () {
                exec 3>&1
                (eval "$1" 2>&1 1>&3 | grep >&2 -v 'Broken pipe')
        }

	latest_by_jim=$(filter_pipe_error \
        	'git log --pretty=oneline --author='\''Jim'\'' | head -n 1'
	)

but what's the point?

I think something like this instead might be more palatable.

diff --git a/write_or_die.c b/write_or_die.c
index 5c4bc85..fadfcaa 100644
--- a/write_or_die.c
+++ b/write_or_die.c
@@ -41,8 +41,8 @@ int write_in_full(int fd, const void *buf, size_t count)
 void write_or_die(int fd, const void *buf, size_t count)
 {
 	if (write_in_full(fd, buf, count) < 0) {
-		if (errno == EPIPE)
-			exit(0);
- 		die("write error (%s)", strerror(errno));
+		if (errno != EPIPE)
+			die("write error (%s)", strerror(errno));
+		exit(1);
 	}
 }

^ permalink raw reply related	[flat|nested] 29+ messages in thread

* Re: [PATCH] Don't ignore write failure from git-diff, git-log, etc.
  2007-05-28 20:23               ` Jim Meyering
@ 2007-05-28 23:41                 ` Petr Baudis
  0 siblings, 0 replies; 29+ messages in thread
From: Petr Baudis @ 2007-05-28 23:41 UTC (permalink / raw)
  To: Jim Meyering; +Cc: Marco Roeland, git

  (I think that funnily enough, Linus is to a degree to the Git
community something like Al Viro and Chris Hellwig are to the Linux
kernel community. Don't get too derailed by his blunt^Whonest criticism,
which is however usually quite valid. ;-)

On Mon, May 28, 2007 at 10:23:20PM CEST, Jim Meyering wrote:
> Marco Roeland <marco.roeland@xs4all.nl> wrote:
> > On monday May 28th 2007 at 20:19 Jim Meyering wrote:
> >> Also, to be consistent, don't ignore EPIPE write failures.
> >
> > In practice I agree with someone else on this thread that EPIPE _is_
> > different. In a way the responsibility doesn't lie with the writer but
> > with the reader.
> 
> Do you think it's ok for git-rev-list _not_ to diagnose an erroneous
> command like this (i.e., to exit(0)):
> 
>     git-rev-list HEAD | sync
> 
> where "sync" could be any command that exits successfully
> without reading any input?
> 
> Is it ok that it is currently *impossible* to diagnose that
> failure by looking at exit codes?

  Actually, yes!

  Because there's no "failure" per se. The command we piped the output
into just decided that he isn't actually interested in any (for whatever
reason; it might decide dynamically based on some parameters etc.). I
can't think of why it could be considered a failure for git-rev-list if
its customer doesn't happily eat all the output it generates. It's the
customer's job to report any real trouble that happenned and might be
cause of the premature end (or maybe the premature end was totally
valid).

  Maybe it could expose some (IMHO contrived) error scenarios, but in
most cases I think it will end up just spitting out bogus error
messages. And what will people do? They won't bother to filter out this
particular one (which isn't even that easy if the strerror() is
localized, furthermore). They will just 2>/dev/null it. And cause the
*real* error messages go to the land of void as well. There's enough of
impossible-to-diagnose-error-conditions-because-stderr-goes-to-null
scripts in the land of UNIX already and this patch, while actually
well-meant to do the opposite, might well actually increase their number
because of this.

-- 
				Petr "Pasky" Baudis
Stuff: http://pasky.or.cz/
Ever try. Ever fail. No matter. // Try again. Fail again. Fail better.
		-- Samuel Beckett

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH] Don't ignore write failure from git-diff, git-log, etc.
  2007-05-28 20:04           ` Jim Meyering
@ 2007-05-29  3:01             ` Linus Torvalds
  2007-05-29 20:19               ` Jim Meyering
  0 siblings, 1 reply; 29+ messages in thread
From: Linus Torvalds @ 2007-05-29  3:01 UTC (permalink / raw)
  To: Jim Meyering; +Cc: git



On Mon, 28 May 2007, Jim Meyering wrote:
> 
> Um... maybe you've forgotten that this patch fixes a hole in the
> "old code" (git.c).  Many git tools ignore write (ENOSPC) failures.

Maybe you have not noticed, but my argument has ben about EPIPE.

> Compared to that aspect of the fix, I would have thought EPIPE-
> handling would be a minor detail.  But now, the whole patch has
> become "crap"?

About half the patch was _removing_ EPIPE stuff - not at all about the 
ENOSPC stuff you claim.

And the ENOSPC code could have added the same *correct* code that does the 
right thing for EPIPE.

>   1) Continue to ignore EPIPE write failure: can obscure real errors.
>        BTW, Linus, don't you agree?  You never commented on this point.

THAT'S THE ONLY THING I'VE BEEN COMMENTING ON!

They aren't "obscure real errors". EPIPE is neither obscure _nor_ an 
error.

The code-paths where you removed EPIPE handlign have two cases:
 - SIGPIPE happens: you made no change
 - SIGPIPE diesn't happen: you broke the code.

So remind me again, why the hell do you think your patch is so great and 
so important, considering that it broke real code, and made things worse?

And why don't you just admit that EPIPE is special, isn't an error, and 
shouldn't be complained about? If you get EPIPE on the write, it means 
"the other end didn't care". It does NOT mean "I should now do a really 
annoying message".

It's that simple. You seem to admit that SIGPIPE handling in bash should 
have been fixed, and that it was annoying to complain about it there. Why 
can't you just admit that it's annoyign and wrong to complain about the 
same thing when it's EPIPE?

			Linus

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH] Don't ignore write failure from git-diff, git-log, etc.
  2007-05-28 21:27         ` Junio C Hamano
@ 2007-05-29  3:47           ` Linus Torvalds
  2007-05-30 11:39           ` Jim Meyering
  1 sibling, 0 replies; 29+ messages in thread
From: Linus Torvalds @ 2007-05-29  3:47 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Jim Meyering, git



On Mon, 28 May 2007, Junio C Hamano wrote:
> 
> I think something like this instead might be more palatable.
> 
> diff --git a/write_or_die.c b/write_or_die.c
> index 5c4bc85..fadfcaa 100644
> --- a/write_or_die.c
> +++ b/write_or_die.c
> @@ -41,8 +41,8 @@ int write_in_full(int fd, const void *buf, size_t count)
>  void write_or_die(int fd, const void *buf, size_t count)
>  {
>  	if (write_in_full(fd, buf, count) < 0) {
> -		if (errno == EPIPE)
> -			exit(0);
> - 		die("write error (%s)", strerror(errno));
> +		if (errno != EPIPE)
> +			die("write error (%s)", strerror(errno));
> +		exit(1);
>  	}
>  }

It's certainly less annoying, but I don't actually think it's *correct*.

I think the current behaviour is simply _superior_.

Think about what happens whenever somebody does

	git cmd | head

and then you want to see whether the end result was successful or not.

What would the error code from the "git cmd" thing mean?

I claim that exiting with SUCCESS is actually the rigt thing to do for the 
git command in question. It did what it was asked for.

And more importantly, considering that a pipe is of indeterminate size, 
what happens if it actuall yonly had 15 lines to be printed out. Guess 
what? If it writes them fast enough, they'll go into the pipe, and "head" 
will exit only after the write, and we'll never even *know* that the last 
five lines were ignored, ie there won't be a EPIPE at all.

In other words, even Jim's example of

	git log | sync

will actually *succeed* even with Jim's patch, if the output fit in the 
kernel pipe buffer, and the "git log" command ran quickly enough that 
"sync" took longer (which is not at all unlikely).

So EPIPE really _is_ special: because when you write to a pipe, there's no 
guarantee that you'll get it at all, so whenever you get an EPIPE you 
should ask yourself:

 - what would I have done if the data had fit in the 64kB kernel buffer?

 - should I really return a different error message or complain just 
   because I just happened to notice that the reader went away _this_ 
   time, even if I might not notice it next time?

In other words, the "exit(0)" is actually _more_ consistent than 
"exit(1)", because exiting with an error message or with an error return 
is going to depend on luck and timing.

For example, I just did a

	strace git show | sync

on my kernel archive, and strace shows me that I had:

	...
	write(1, "commit c420bc9f09a0926b708c3edb2"..., 736) = 736
	exit_group(0)

the first three times I tried it, and then

	write(1, "commit c420bc9f09a0926b708c3edb2"..., 736) = -1 EPIPE (Broken pipe)
	--- SIGPIPE (Broken pipe) @ 0 (0) ---
	+++ killed by SIGPIPE +++

the fourth time.

What should we learn from this? 

 - a shell that talks about SIGPIPE is going to be universally *hated*, 
   because it's really a total crap-shoot. Even with something like 
   "sync", that doesn't read a single byte from the input, most of the 
   time I didn't actually get EPIPE/SIGPIPE at all!

 - by exactly the same token, considering "EPIPE" as anything else than a 
   "success" is always just going to lead you to random behaviour.

So what _should_ you do for EPIPE?

Here's what EPIPE _really_ means:

 - you might as well consider the write a success, but the reader isn't 
   actually interested, so rather than go on, you might as well stop 
   early.

Notice how I very carefull avoided the word "error" anywhere. Because it's 
really not an error. The reader already got everything it wanted. So EPIPE 
should generally be seen as an "early success" rather than as a "failure".

		Linus

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH] Don't ignore write failure from git-diff, git-log, etc.
  2007-05-28 20:44             ` Junio C Hamano
@ 2007-05-29 20:11               ` Jim Meyering
  2007-05-29 23:50                 ` Junio C Hamano
  0 siblings, 1 reply; 29+ messages in thread
From: Jim Meyering @ 2007-05-29 20:11 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Marco Roeland, git

Junio C Hamano <junkio@cox.net> wrote:
> Jim Meyering <jim@meyering.net> writes:
>
>> Subject: [PATCH] Don't ignore write failure from git-diff, git-log, etc.
>> ...
...
> I do not think anybody has much objection about the change to
> handle_internal_command() in git.c you made.  Earlier we relied
> on exit(3) to close still open filehandles (while ignoring
> errors), and you made the close explicit in order to detect
> errors.

Hi Junio,

> But "to be consistent" above is not a very good justification.

I know that well, now, especially after all of my fruitless
justification on this list.

> In the presense of shells that give "annoying" behaviour (we
> have to remember that not everybody have enough privilege,
> expertise, or motivation to update /bin/sh or install their own
> copy in $HOME/bin/sh), "EPIPE is different from other errors" is
> a more practical point of view, I'd have to say.  IOW, it is not
> clear if it is a good thing "to be consistent" to begin with.

In case you haven't seen it in the rest of this thread, the version of
bash you use does not change git's EPIPE-handling behavior.  Using stock
bash-3.0 or bash-2.05b, you will get "Broken pipe" messages from some
scripts, but those are from bash, when it delivers the SIGPIPE signal,
and not from any application like git.  The EPIPE-handling behavior can
come into play only when SIGPIPE is *ignored*.

> I would have appreciated if this were two separate patches.  I
> think the EPIPE one is an independent issue.  We could even make
> it a configuration option to ignore EPIPE ;-)

Ok.  Even though I'm still convinced that ignoring EPIPE is no longer
justified, I've hamstrung my patch to do what people here want.

Note that I've also changed it not to print strerror(errno) when the
ferror(stdout) test is triggered.  In that case, errno may well be
irrelevant.  The ugliness of this addition is pretty striking, compared
to what I'm used to.  FWIW, I would have liked to handle closing stdout
here with the same one-liner I use in coreutils: atexit (close_stdout);
but that requires autoconf/automake/gnulib infrastructure.

-----------------------------
From 42e3a6f676e9ae4e9640bc2ff36b7ab0b061a60e Mon Sep 17 00:00:00 2001
From: Jim Meyering <jim@meyering.net>
Date: Sat, 26 May 2007 13:43:07 +0200
Subject: [PATCH] Don't ignore write failure from git-diff, git-log, etc.

Currently, when git-diff writes to a full device or gets an I/O error,
it fails to detect the write error:

    $ git-diff |wc -c
    3984
    $ git-diff > /dev/full && echo ignored write failure
    ignored write failure

git-log does the same thing:

    $ git-log -n1 > /dev/full && echo ignored write failure
    ignored write failure

Each and every git command should report such a failure.
Some already do, but with the patch below, they all do, and we
won't have to rely on code in each command's implementation to
perform the right incantation.

    $ ./git-log -n1 > /dev/full
    fatal: write failure on standard output: No space left on device
    [Exit 128]
    $ ./git-diff > /dev/full
    fatal: write failure on standard output: No space left on device
    [Exit 128]

You can demonstrate this with git's own --version output, too:
(but git --help detects the failure without this patch)

    $ ./git --version > /dev/full
    fatal: write failure on standard output: No space left on device
    [Exit 128]

Note that the fcntl test (for whether the fileno may be closed) is
required in order to avoid EBADF upon closing an already-closed stdout,
as would happen for each git command that already closes stdout; I think
update-index was the one I noticed in the failure of t5400, before I
added that test.

Also, to be consistent with e.g., write_or_die, do not
diagnose EPIPE write failures.

Signed-off-by: Jim Meyering <jim@meyering.net>
---
 git.c |   19 ++++++++++++++++++-
 1 files changed, 18 insertions(+), 1 deletions(-)

diff --git a/git.c b/git.c
index 29b55a1..8258885 100644
--- a/git.c
+++ b/git.c
@@ -308,6 +308,7 @@ static void handle_internal_command(int argc, const char **argv, char **envp)
 	for (i = 0; i < ARRAY_SIZE(commands); i++) {
 		struct cmd_struct *p = commands+i;
 		const char *prefix;
+		int status;
 		if (strcmp(p->cmd, cmd))
 			continue;

@@ -321,7 +322,23 @@ static void handle_internal_command(int argc, const char **argv, char **envp)
 			die("%s must be run in a work tree", cmd);
 		trace_argv_printf(argv, argc, "trace: built-in: git");

-		exit(p->fn(argc, argv, prefix));
+		status = p->fn(argc, argv, prefix);
+
+		/* Close stdout if necessary, and diagnose any failure
+		   other than EPIPE.  */
+		if (fcntl(fileno (stdout), F_GETFD) >= 0) {
+			errno = 0;
+			if ((ferror(stdout) || fclose(stdout))
+			    && errno != EPIPE) {
+				if (errno == 0)
+					die("write failure on standard output");
+				else
+					die("write failure on standard output"
+					    ": %s", strerror(errno));
+			}
+		}
+
+		exit(status);
 	}
 }

--
1.5.2.73.g18bece

^ permalink raw reply related	[flat|nested] 29+ messages in thread

* Re: [PATCH] Don't ignore write failure from git-diff, git-log, etc.
  2007-05-29  3:01             ` Linus Torvalds
@ 2007-05-29 20:19               ` Jim Meyering
  2007-05-29 21:19                 ` Linus Torvalds
  0 siblings, 1 reply; 29+ messages in thread
From: Jim Meyering @ 2007-05-29 20:19 UTC (permalink / raw)
  To: Linus Torvalds; +Cc: git

Linus Torvalds <torvalds@linux-foundation.org> wrote:

> On Mon, 28 May 2007, Jim Meyering wrote:
>>
>> Um... maybe you've forgotten that this patch fixes a hole in the
>> "old code" (git.c).  Many git tools ignore write (ENOSPC) failures.
>
> Maybe you have not noticed, but my argument has ben about EPIPE.

Ha ha.  That's a good one.
The point was that even you must see that your
"[Jim's] WHOLE patch is crap" statement was wrong.

>>   1) Continue to ignore EPIPE write failure: can obscure real errors.
>>        BTW, Linus, don't you agree?  You never commented on this point.
>
> THAT'S THE ONLY THING I'VE BEEN COMMENTING ON!
>
> They aren't "obscure real errors". EPIPE is neither obscure _nor_ an
> error.

Reread #1, above.
"ignore EPIPE write failure: can obscure real errors" (using "obscure"
as a verb, not an adjective) means that ignoring EPIPE failure can cause
git commands to hide/mask/paper-over a real, conceptual error like this:

    # This is obviously bogus shell code:
    # "cat" with an argument like this doesn't read stdin
    git-rev-list HEAD | cat foo | ...

We're really on the head of a pin here, since the EPIPE test
makes a difference only when SIGPIPE is being ignored (unusual).
Note that ignoring SIGPIPE can cause gross inefficiencies or
even expose bugs.  E.g., On Solaris 10, this infloops:

  (trap '' PIPE; /bin/yes) |head -1

so there are good reasons *not* to ignore SIGPIPE.  Add to that the fact
that the condition provoking an EPIPE is not *that* common, and you
begin to see that even if EPIPE is a little different, it doesn't matter
enough to justify polluting every application file-close test with an
EPIPE exemption.

> The code-paths where you removed EPIPE handlign have two cases:
>  - SIGPIPE happens: you made no change
>  - SIGPIPE diesn't happen: you broke the code.
>
> So remind me again, why the hell do you think your patch is so great and
> so important,

Whoa!  I guess you had a bad day, yesterday.

I try to be humble, and certainly have not been crowing that this
tiny patch is "so great and so important."  However, I did defend it
when you claimed that the whole thing was crap.

> considering that it broke real code, and made things worse?

I believe it is an IMPROVEMENT to make such mistakes detectable (exit
nonzero), and that the risk of annoying users with EPIPE diagnostics is
minimal.  You seem to think there would be some outpouring of "broken
pipe" errors, but since so few Porcelains ignore SIGPIPE, I disagree.

However, it's an improvement only in the unusual event that SIGPIPE is
being ignored, which is also when an application may output the "broken
pipe" error.  IMHO, these conditions are rare enough (now) that there's
no point in making an exception for EPIPE everywhere.

> And why don't you just admit that EPIPE is special, isn't an error, and
> shouldn't be complained about? If you get EPIPE on the write, it means
> "the other end didn't care". It does NOT mean "I should now do a really
> annoying message".

Sure, EPIPE is special, but it is so unusual now that it's not
worth even the small added complexity to treat it specially in all
application code.

> It's that simple. You seem to admit that SIGPIPE handling in bash should
> have been fixed, and that it was annoying to complain about it there. Why

Yes. When bash-3.0 announced each process-killing-via-SIGPIPE with e.g.,
    /some/script: line 2: 31994 Broken pipe     seq 99999
that was a big deal because it affected many scripts.

> can't you just admit that it's annoyign and wrong to complain about the
> same thing when it's EPIPE?

If it happened a lot, it *would* be annoying, but that's just it:
it doesn't happen much at all anymore.

Also, no one is complaining about EPIPE diagnostics from any of
the GNU coreutils, and I take that as a good indication that there
is no problem.

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH] Don't ignore write failure from git-diff, git-log, etc.
  2007-05-29 20:19               ` Jim Meyering
@ 2007-05-29 21:19                 ` Linus Torvalds
  2007-05-30 12:25                   ` Jim Meyering
  0 siblings, 1 reply; 29+ messages in thread
From: Linus Torvalds @ 2007-05-29 21:19 UTC (permalink / raw)
  To: Jim Meyering; +Cc: git



On Tue, 29 May 2007, Jim Meyering wrote:
> >
> > Maybe you have not noticed, but my argument has ben about EPIPE.
> 
> Ha ha.  That's a good one.
> The point was that even you must see that your
> "[Jim's] WHOLE patch is crap" statement was wrong.

Ehh. That's a rather edited version of what I said, isn't it?

That's after I explicitly _quoted_ the part where you actively removed the 
code that said "EPIPE is right", and also after I had told you several 
times that you should consider EPIPE as a special case in your other part.

In other words, yes, EVERY SINGLE HUNK of your patch was wrong, and I had 
told you exactly why.

How wrong does a patch have to be to be "crap"? Maybe I have higher 
standards than you do (apparently so), but "every single hunk was wrong" 
should certainly be a damn good reason to consider _any_ patch crap, 
wouldn't you say?

And now you have trouble accepting that, even after you have sent out a 
fixed patch without the crap. Thanks for finally bothering to get the 
patch right, but I don't see why you have to try to make-believe that it 
was ever about anything but EPIPE.

So go back and read my emails. You'll see that in every single one I made 
it very clear that EPIPE was special. From the very first one (where I 
didn't call your patch crap, btw: I said it was wrong, and that some 
errors are expected and good, and I explicitly told you about EPIPE).

So what did you do? Instead of acknowledging that EPIPE was different, you 
actually *expanded* on that original patch, and made the other places 
where we _did_ handle EPIPE correctly, and made those places handle it 
_incorrectly_.

And then you expect me to be _polite_ about it? Grow up. I was polite 
before you started explicitly doing the reverse of what I told you you 
should do. At that point, your patch went from "meant well, but the patch 
was wrong" to "That's just obviously crap".

		Linus

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH] Don't ignore write failure from git-diff, git-log, etc.
  2007-05-29 20:11               ` Jim Meyering
@ 2007-05-29 23:50                 ` Junio C Hamano
  2007-05-30  7:12                   ` Jim Meyering
  0 siblings, 1 reply; 29+ messages in thread
From: Junio C Hamano @ 2007-05-29 23:50 UTC (permalink / raw)
  To: Jim Meyering; +Cc: Marco Roeland, git

Jim Meyering <jim@meyering.net> writes:

> ...
> Also, to be consistent with e.g., write_or_die, do not
> diagnose EPIPE write failures.
>
> Signed-off-by: Jim Meyering <jim@meyering.net>
> ---
>  git.c |   19 ++++++++++++++++++-
>  1 files changed, 18 insertions(+), 1 deletions(-)
>
> diff --git a/git.c b/git.c
> index 29b55a1..8258885 100644
> --- a/git.c
> +++ b/git.c
> @@ -308,6 +308,7 @@ static void handle_internal_command(int argc, const char **argv, char **envp)
>  	for (i = 0; i < ARRAY_SIZE(commands); i++) {
>  		struct cmd_struct *p = commands+i;
>  		const char *prefix;
> +		int status;
>  		if (strcmp(p->cmd, cmd))
>  			continue;
>
> @@ -321,7 +322,23 @@ static void handle_internal_command(int argc, const char **argv, char **envp)
>  			die("%s must be run in a work tree", cmd);
>  		trace_argv_printf(argv, argc, "trace: built-in: git");
>
> -		exit(p->fn(argc, argv, prefix));
> +		status = p->fn(argc, argv, prefix);
> +
> +		/* Close stdout if necessary, and diagnose any failure
> +		   other than EPIPE.  */
> +		if (fcntl(fileno (stdout), F_GETFD) >= 0) {
> +			errno = 0;
> +			if ((ferror(stdout) || fclose(stdout))
> +			    && errno != EPIPE) {
> +				if (errno == 0)
> +					die("write failure on standard output");
> +				else
> +					die("write failure on standard output"
> +					    ": %s", strerror(errno));
> +			}

This makes the final write failure trump the breakage p->fn()
already diagnosed, doesn't it?  Maybe if (fcntrl(...) >=0 )
should read if (!status && fcntrl(...) >= 0).

> +		}
> +
> +		exit(status);
>  	}
>  }
>
> --
> 1.5.2.73.g18bece

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH] Don't ignore write failure from git-diff, git-log, etc.
  2007-05-29 23:50                 ` Junio C Hamano
@ 2007-05-30  7:12                   ` Jim Meyering
  0 siblings, 0 replies; 29+ messages in thread
From: Jim Meyering @ 2007-05-30  7:12 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Marco Roeland, git

Junio C Hamano <junkio@cox.net> wrote:
>> -		exit(p->fn(argc, argv, prefix));
>> +		status = p->fn(argc, argv, prefix);
>> +
>> +		/* Close stdout if necessary, and diagnose any failure
>> +		   other than EPIPE.  */
>> +		if (fcntl(fileno (stdout), F_GETFD) >= 0) {
>> +			errno = 0;
>> +			if ((ferror(stdout) || fclose(stdout))
>> +			    && errno != EPIPE) {
>> +				if (errno == 0)
>> +					die("write failure on standard output");
>> +				else
>> +					die("write failure on standard output"
>> +					    ": %s", strerror(errno));
>> +			}
>
> This makes the final write failure trump the breakage p->fn()
> already diagnosed, doesn't it?

Yes.  Are there circumstances in which a nonzero status from
some cmd_* function would mean something so grave that you
wouldn't also want to know that standard output is incomplete
or corrupt (and possibly use a different exit status)?
So far, after a quick and incomplete survey, I haven't found any.

However, if some git command is documented to exit with
status N for some listed values of N, e.g.,
    1 A happened
    2 B happened
    3 any other failure
then the above choice of dying with "die" would be wrong.

E.g. git-diff's --exit-code comes close:

       --exit-code
           Make the program exit with codes similar to diff(1). That is, it
           exits with 1 if there were differences and 0 means no differences.

but doesn't say how it handles errors.

[ OT: Perhaps that documentation should be changed to look more like diff's,
  so that it says there is a different exit code for the third
  case (some failure):

    $ diff --help|tail -3|head -1
    Exit status is 0 if inputs are the same, 1 if different, 2 if trouble.
]

> Maybe if (fcntrl(...) >=0 )
> should read if (!status && fcntrl(...) >= 0).

No, because then something like git-diff's --exit-code could hide
a write error.

If you want to preserve the exit status, then it should be enough
to call set_die_routine with a function that will work just like
"die" but exit with a specified (status) value.

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH] Don't ignore write failure from git-diff, git-log, etc.
  2007-05-28 21:27         ` Junio C Hamano
  2007-05-29  3:47           ` Linus Torvalds
@ 2007-05-30 11:39           ` Jim Meyering
  2007-05-30 17:51             ` Junio C Hamano
  1 sibling, 1 reply; 29+ messages in thread
From: Jim Meyering @ 2007-05-30 11:39 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git

Junio C Hamano <junkio@cox.net> wrote:
> Jim Meyering <jim@meyering.net> writes:
>
>> Of course error messages are annoying when your short-pipe-read is
>> _deliberate_ (tho, most real uses of git tools will actually get no
>> message to be annoyed about[*]), but what if there really *is* a mistake?
>> Try this:
>>
>>     # You want to force git to ignore the error.
>>     $ trap '' PIPE; git-rev-list HEAD | sync
>>     $
>
> It is perfectly valid (although it is stupid) for a Porcelain
> script to do this:
>
>     latest_by_jim=$(git log --pretty=oneline --author='Jim' | head -n 1)
>     case "$latest_by_jim" in
>     '') echo "No commit by Jim" ;;
>     *)  # do something interesting on the commit
>         ;;;
>     esac

Hi Junio,

The above snippet (prepending a single #!/bin/bash line) doesn't provoke
an EPIPE diagnostic from my patched git.  In fact, even if you're using
an old, unpatched version of bash, it provokes *no* diagnostic at all.

To provoke a diagnostic (from bash, not git), using old unpatched bash,
you need a script doing output from a subshell, e.g.:

    #!/tmp/bash-3.0/bash
    for x in 1; do
      git-log
    done | head -1

With unpatched bash-3.0, it does this:

    commit 42e3a6f676e9ae4e9640bc2ff36b7ab0b061a60e
    /tmp/bp-demo: line 2: 24864 Broken pipe             git-log

It's only if you try to avoid the above and change your script to
ignore SIGPIPE do you finally get an offending EPIPE diagnostic:

    #!/tmp/bash-3.0/bash
    trap '' PIPE
    for x in 1; do
      ./git-log; echo $? 1>&2
    done | head -1

Here's its output, using my patch:

    commit 42e3a6f676e9ae4e9640bc2ff36b7ab0b061a60e
    fatal: write failure on standard output
    128

That trap has the nasty side effect of making the poorly written script
wait until "git-log" has completed (before, it was interrupted right
away), so it can take a lot longer.  With my patch, it also gives a
diagnostic, which might serve to inform someone that they should not
ignore SIGPIPE.

No porcelain (modulo [*]) in git proper or cogito ignores SIGPIPE,
so I don't see how EPIPE error diagnostics can be a problem.

[*] These scripts do ignore SIGPIPE, but either don't need to,
or can/should be fixed not to:

   git-archimport.perl
   git-cvsimport.perl
   git-svnimport.perl

And, yes, I'd be happy to fix them, if anyone is interested.

> In such a case, it is a bit too much for my taste to force the
> script to redirect what comes out of fd 2 of the upstream of the
> pipe, so that it can filter out only the "write error" message
> but still show other kinds of error messages.  You could do so
> by elaborate shell magic, perhaps like this:
>
>         filter_pipe_error () {
>                 exec 3>&1
>                 (eval "$1" 2>&1 1>&3 | grep >&2 -v 'Broken pipe')
>         }
>
> 	latest_by_jim=$(filter_pipe_error \
>         	'git log --pretty=oneline --author='\''Jim'\'' | head -n 1'
> 	)

I agree that would be extreme.  But it's not necessary, since the
'Broken pipe' diagnostic appears now only in contrived circumstances.

> but what's the point?
>
> I think something like this instead might be more palatable.

...[patch to make git/EPIPE exit nonzero, but with no diagnostic]

Thank you for taking the time to reply and to come up with a compromise.
At first I thought this would be a step in the right direction, but,
now that I understand how infrequently EPIPE actually comes into play,
I think it'd be better to avoid a half-measure fix, since that would
just perpetuate the idea that EPIPE is worth handling specially.

Jim

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH] Don't ignore write failure from git-diff, git-log, etc.
  2007-05-29 21:19                 ` Linus Torvalds
@ 2007-05-30 12:25                   ` Jim Meyering
  2007-05-30 15:40                     ` Linus Torvalds
  0 siblings, 1 reply; 29+ messages in thread
From: Jim Meyering @ 2007-05-30 12:25 UTC (permalink / raw)
  To: Linus Torvalds; +Cc: git

Linus Torvalds <torvalds@linux-foundation.org> wrote:

> On Tue, 29 May 2007, Jim Meyering wrote:
>> >
>> > Maybe you have not noticed, but my argument has ben about EPIPE.
>>
>> Ha ha.  That's a good one.
>> The point was that even you must see that your
>> "[Jim's] WHOLE patch is crap" statement was wrong.
>
> Ehh. That's a rather edited version of what I said, isn't it?

No.  I'm glad to see that perhaps even you are surprised by your words.
The only editing was to capitalize WHOLE.  Here's what you wrote:

    > I think this patch is fundamentally WRONG. This fragment is just a prime
    > example of why the whole patch is crap. The old code was correct, and you
    > broke it.

Umm... are the above three lines the only part of my message you're
prepared to talk about?  You haven't addressed any of the interesting
(technical) parts.

> That's after I explicitly _quoted_ the part where you actively removed the
> code that said "EPIPE is right", and also after I had told you several
> times that you should consider EPIPE as a special case in your other part.
>
> In other words, yes, EVERY SINGLE HUNK of your patch was wrong, and I had
> told you exactly why.

And I told you why I think my patch was on the right track.
i.e., why it now appears to be fine to treat EPIPE like any other error.

It is interesting to see that you've elided all of my arguments rather
than make an attempt to rebut them.  I'm trying to keep my side of this
discussion professional, so I'll ignore parts of what you've written
below.

> How wrong does a patch have to be to be "crap"? Maybe I have higher
> standards than you do...

> And now you have trouble accepting that, even after you have sent out a
> fixed patch without the crap. Thanks for finally bothering to get the
> patch right, but I don't see why you have to try to make-believe that it
> was ever about anything but EPIPE.

My original patch was about ENOSPC and EIO.  EPIPE was mostly incidental.
I don't care about EPIPE, and think it deserves no special treatment.
You appear to be obsessed with it now, perhaps because SIGPIPE-ignoring
porcelain (now long gone) once caused trouble.

> So go back and read my emails.

Did you read mine?  I explained why EPIPE is no longer a problem for
git, even if you're using stock bash-2.05b or bash-3.0.

> You'll see that in every single one I made
> it very clear that EPIPE was special.

No.  You merely *said* it was special.
You haven't demonstrated that it's special enough (and still common
enough) to pollute all code that tests for file-close failure.  I hear
that it *used to be* common enough to merit such treatment.  Now, it is
a much harder case to make.  But from what I've seen, you haven't even
tried to do that much.  Hmm... or maybe you did try to make the case,
and came up short.  Is that why you are resorting to hyperbole, and ad
hominem arguments?

> From the very first one (where I
> didn't call your patch crap, btw: I said it was wrong, and that some
> errors are expected and good, and I explicitly told you about EPIPE).
>
> So what did you do? Instead of acknowledging that EPIPE was different, you
> actually *expanded* on that original patch, and made the other places
> where we _did_ handle EPIPE correctly, and made those places handle it
> _incorrectly_.
>
> And then you expect me to be _polite_ about it? Grow up. I was polite
> before you started explicitly doing the reverse of what I told you you
> should do. At that point, your patch went from "meant well, but the patch
> was wrong" to "That's just obviously crap".

Let's see...  I dared to post code contrary to your unsubstantiated claim,
and therefore you describe that code as "obviously crap".

Just because you are Linus doesn't mean you can decree that
"EPIPE must be ignored" and make everyone take it on faith.

Can you substantiate your claim that my proposed changes cause trouble
*in practice*?  So far, all I've heard is FUD, and all of my explanations
of why EPIPE no longer matters seem to have been ignored.

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH] Don't ignore write failure from git-diff, git-log, etc.
  2007-05-30 12:25                   ` Jim Meyering
@ 2007-05-30 15:40                     ` Linus Torvalds
  2007-05-30 16:12                       ` Jim Meyering
  0 siblings, 1 reply; 29+ messages in thread
From: Linus Torvalds @ 2007-05-30 15:40 UTC (permalink / raw)
  To: Jim Meyering; +Cc: git



On Wed, 30 May 2007, Jim Meyering wrote:
> 
> No.  I'm glad to see that perhaps even you are surprised by your words.

If you thought I was a polite person and am surpised by calling things 
crap after the fact, I'm afraid you have a few shocking moments coming to 
you. My reply was fairly polite by my standards. My tag-line is often "On 
the internet, nobody can hear you being subtle".

So let me rephrase my message to you:

	I think your INTERMEDIATE PATCH WAS TOTAL AND UTTER CRAP.

	EVERY SINGLE HUNK WAS SH*T. 

	You expressly IGNORED my point that some errors aren't errors, and 
	MADE GIT WORSE.

Are we on the same page now? 

In contrast, your final patch was fine. The one where you finally fixed 
the issue that I complained about FROM THE VERY BEGINNING.

Comprende?

> The only editing was to capitalize WHOLE.  Here's what you wrote:
> 
>     > I think this patch is fundamentally WRONG. This fragment is just a prime
>     > example of why the whole patch is crap. The old code was correct, and you
>     > broke it.
> 
> Umm... are the above three lines the only part of my message you're
> prepared to talk about?  You haven't addressed any of the interesting
> (technical) parts.

Umm. Your final patch was a few trivial lines. I addressed all interesting 
technical parts IN MY ORIGINAL REPLY WHEN YOU FIRST POSTED IT.

Which you ignored (or rather, explicitly chose to disagree with, and 
added MORE crap to the patch).

Go away. I'm not interested in flaming you any more. The patch wasn't that 
interesting to begin with, and you have shown that you're more interested 
in being contrary than to actually fix the problems that were pointed out 
to you _immediately_ and without any flames. 

			Linus

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH] Don't ignore write failure from git-diff, git-log, etc.
  2007-05-30 15:40                     ` Linus Torvalds
@ 2007-05-30 16:12                       ` Jim Meyering
  0 siblings, 0 replies; 29+ messages in thread
From: Jim Meyering @ 2007-05-30 16:12 UTC (permalink / raw)
  To: Linus Torvalds; +Cc: git

Linus Torvalds <torvalds@linux-foundation.org> wrote:
[...big flame!...]

Wow!  This is like the scene from the Wizard of Oz:
I've just raised the curtain and seen who's behind it.

Jim

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH] Don't ignore write failure from git-diff, git-log, etc.
  2007-05-30 11:39           ` Jim Meyering
@ 2007-05-30 17:51             ` Junio C Hamano
  2007-05-30 19:43               ` Jim Meyering
  0 siblings, 1 reply; 29+ messages in thread
From: Junio C Hamano @ 2007-05-30 17:51 UTC (permalink / raw)
  To: Jim Meyering; +Cc: git

Jim Meyering <jim@meyering.net> writes:

> Junio C Hamano <junkio@cox.net> wrote:
>> Jim Meyering <jim@meyering.net> writes:
>>
>>> Of course error messages are annoying when your short-pipe-read is
>>> _deliberate_ (tho, most real uses of git tools will actually get no
>>> message to be annoyed about[*]), but what if there really *is* a mistake?
>>> Try this:
>>>
>>>     # You want to force git to ignore the error.
>>>     $ trap '' PIPE; git-rev-list HEAD | sync
>>>     $
>>
>> It is perfectly valid (although it is stupid) for a Porcelain
>> script to do this:
>>
>>     latest_by_jim=$(git log --pretty=oneline --author='Jim' | head -n 1)
>>     case "$latest_by_jim" in
>>     '') echo "No commit by Jim" ;;
>>     *)  # do something interesting on the commit
>>         ;;;
>>     esac
>
> Hi Junio,
>
> The above snippet (prepending a single #!/bin/bash line) doesn't provoke
> an EPIPE diagnostic from my patched git.  In fact, even if you're using
> an old, unpatched version of bash, it provokes *no* diagnostic at all.

> To provoke a diagnostic (from bash, not git), using old unpatched bash,
> you need a script doing output from a subshell, e.g.:
>
>     #!/tmp/bash-3.0/bash
>     for x in 1; do
>       git-log
>     done | head -1

I haven't thought it through, but isn't the above example only
talking about the "Broken pipe" message?  Surely, you would get
that message from older Bash if you have a shell loop on the
upstream side of the pipe no matter what we (the command that is
run by the shell loop) do, and trap is needed to squelch it.

But I do not see how this pipeline, where git-rev-list produces
more than what fits in the in-kernel pipe buffer:

	"git-rev-list a lot of data | head -n 1"

would not catch EPIPE and say "Broken Pipe" with your patch.
Especially if the downstream is sufficiently slow (say, replace
it with "(sleep 10 && head -n 1)", perhaps), wouldn't the
upstream produce enough without being read, gets stuck on write,
and when the downstream exits, it would notice its write(2)
failed with EPIPE, wouldn't it?

Maybe you are talking about your updated patch?

> ...[patch to make git/EPIPE exit nonzero, but with no diagnostic]
>
> Thank you for taking the time to reply and to come up with a compromise.
> At first I thought this would be a step in the right direction, but,
> now that I understand how infrequently EPIPE actually comes into play,
> I think it'd be better to avoid a half-measure fix, since that would
> just perpetuate the idea that EPIPE is worth handling specially.

After having read what Linus said about how the "fixed" one
would behave differently, depending on the amount of data we
produce before the consumer says "I've seen enough" and
depending on the amount of data that would fit in the in-kernel
pipe buffer, I no longer think the compromise patch you mention
above is improvement anymore.

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH] Don't ignore write failure from git-diff, git-log, etc.
  2007-05-30 17:51             ` Junio C Hamano
@ 2007-05-30 19:43               ` Jim Meyering
  0 siblings, 0 replies; 29+ messages in thread
From: Jim Meyering @ 2007-05-30 19:43 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git

Junio C Hamano <junkio@cox.net> wrote:
> Jim Meyering <jim@meyering.net> writes:
>> Junio C Hamano <junkio@cox.net> wrote:
>>> Jim Meyering <jim@meyering.net> writes:
>>>
>>>> Of course error messages are annoying when your short-pipe-read is
>>>> _deliberate_ (tho, most real uses of git tools will actually get no
>>>> message to be annoyed about[*]), but what if there really *is* a mistake?
>>>> Try this:
>>>>
>>>>     # You want to force git to ignore the error.
>>>>     $ trap '' PIPE; git-rev-list HEAD | sync
>>>>     $
>>>
>>> It is perfectly valid (although it is stupid) for a Porcelain
>>> script to do this:
>>>
>>>     latest_by_jim=$(git log --pretty=oneline --author='Jim' | head -n 1)
>>>     case "$latest_by_jim" in
>>>     '') echo "No commit by Jim" ;;
>>>     *)  # do something interesting on the commit
>>>         ;;;
>>>     esac
>>
>> Hi Junio,
>>
>> The above snippet (prepending a single #!/bin/bash line) doesn't provoke
>> an EPIPE diagnostic from my patched git.  In fact, even if you're using
>> an old, unpatched version of bash, it provokes *no* diagnostic at all.
>
>> To provoke a diagnostic (from bash, not git), using old unpatched bash,
>> you need a script doing output from a subshell, e.g.:
>>
>>     #!/tmp/bash-3.0/bash
>>     for x in 1; do
>>       git-log
>>     done | head -1
>
> I haven't thought it through, but isn't the above example only
> talking about the "Broken pipe" message?  Surely, you would get

I'm not sure which "Broken pipe" message you mean.

There are two types of "Broken pipe" messages.
There's the old, verbose one from bash that includes script line-number,
pid, and killed command name.  Old, unpatched versions of bash
print that message whenever bash kills a process with SIGPIPE.

Then there's the application (EPIPE) one, that can be printed
by a writing application like git,cat,seq,etc. only when SIGPIPE
stops a write but doesn't kill the writer.
In that case, the write syscall fails with errno==EPIPE,
and if it's diagnosed by the application, you get e.g.,

  seq: write error: Broken pipe

Since the script above is not ignoring SIGPIPE, the git-log process
is killed by bash-3.0, and that old version of bash announces the killing
with the verbose message:

  /t/bp-demo: line 2: 14474 Broken pipe             git-log

Any other patched or newer version of bash will print the
single requested line on stdout and nothing on stderr.

> that message from older Bash if you have a shell loop on the
> upstream side of the pipe no matter what we (the command that is
> run by the shell loop) do, and trap is needed to squelch it.

Right.
That's why no one is using such broken shells anymore.
And why no porcelain tools incur the penalty of ignoring
SIGQUIT anymore either.

> But I do not see how this pipeline, where git-rev-list produces
> more than what fits in the in-kernel pipe buffer:
>
> 	"git-rev-list a lot of data | head -n 1"
>
> would not catch EPIPE and say "Broken Pipe" with your patch.

I haven't debugged the old bash to see why that first one
fails to provoke a broken pipe message (from bash).
Unless you add a line like "trap '' PIPE" before it, bash kills
the writer with SIGPIPE, and so my patch is irrelevant,
because the failing write syscall never returns.

> Especially if the downstream is sufficiently slow (say, replace
> it with "(sleep 10 && head -n 1)", perhaps), wouldn't the
> upstream produce enough without being read, gets stuck on write,
> and when the downstream exits, it would notice its write(2)
> failed with EPIPE, wouldn't it?

Are you presuming SIGPIPE is ignored?

> Maybe you are talking about your updated patch?

I was talking about the initial patch, or the one that
also removed the errno == EPIPE tests.

^ permalink raw reply	[flat|nested] 29+ messages in thread

end of thread, other threads:[~2007-05-30 19:44 UTC | newest]

Thread overview: 29+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2007-05-26 11:45 [PATCH] Don't ignore write failure from git-diff, git-log, etc Jim Meyering
2007-05-26 16:18 ` Linus Torvalds
2007-05-26 17:27   ` Junio C Hamano
2007-05-27  3:03     ` Nicolas Pitre
2007-05-27  9:16   ` Jim Meyering
2007-05-27 16:17     ` Linus Torvalds
2007-05-28 13:14       ` Jim Meyering
2007-05-28 15:46         ` Marco Roeland
2007-05-28 18:19           ` Jim Meyering
2007-05-28 19:05             ` Marco Roeland
2007-05-28 20:23               ` Jim Meyering
2007-05-28 23:41                 ` Petr Baudis
2007-05-28 20:44             ` Junio C Hamano
2007-05-29 20:11               ` Jim Meyering
2007-05-29 23:50                 ` Junio C Hamano
2007-05-30  7:12                   ` Jim Meyering
2007-05-28 16:32         ` Linus Torvalds
2007-05-28 20:04           ` Jim Meyering
2007-05-29  3:01             ` Linus Torvalds
2007-05-29 20:19               ` Jim Meyering
2007-05-29 21:19                 ` Linus Torvalds
2007-05-30 12:25                   ` Jim Meyering
2007-05-30 15:40                     ` Linus Torvalds
2007-05-30 16:12                       ` Jim Meyering
2007-05-28 21:27         ` Junio C Hamano
2007-05-29  3:47           ` Linus Torvalds
2007-05-30 11:39           ` Jim Meyering
2007-05-30 17:51             ` Junio C Hamano
2007-05-30 19:43               ` Jim Meyering

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).