Skip to content

Commit da8fb6b

Browse files
sunshinecogitster
authored andcommitted
worktree: send "chatty" messages to stderr
The order in which the stdout and stderr streams are flushed is not guaranteed to be the same across platforms or `libc` implementations. This lack of determinism can lead to anomalous and potentially confusing output if normal (stdout) output is flushed after error (stderr) output. For instance, the following output which clearly indicates a failure due to a fatal error: % git worktree add ../foo bar Preparing worktree (checking out 'bar') fatal: 'bar' is already checked out at '.../wherever' has been reported[1] on Microsoft Windows to appear as: % git worktree add ../foo bar fatal: 'bar' is already checked out at '.../wherever' Preparing worktree (checking out 'bar') which may confuse the reader into thinking that the command somehow recovered and ran to completion despite the error. This problem crops up because the "chatty" status message "Preparing worktree" is sent to stdout, whereas the "fatal" error message is sent to stderr. One way to fix this would be to flush stdout manually before git-worktree reports any errors to stderr. However, common practice in Git is for "chatty" messages to be sent to stderr. Therefore, a more appropriate fix is to adjust git-worktree to conform to that practice by sending its "chatty" messages to stderr rather than stdout as is currently the case. There may be concern that relocating messages from stdout to stderr could break existing tooling, however, these messages are already internationalized, thus are unstable. And, indeed, the "Preparing worktree" message has already been the subject of somewhat significant changes in 2c27002 (worktree: improve message when creating a new worktree, 2018-04-24). Moreover, there is existing precedent, such as 68b939b (clone: send diagnostic messages to stderr, 2013-09-18) which likewise relocated "chatty" messages from stdout to stderr for git-clone. [1]: https://lore.kernel.org/git/CA+34VNLj6VB1kCkA=MfM7TZR+6HgqNi5-UaziAoCXacSVkch4A@mail.gmail.com/T/ Reported-by: Baruch Burstein <[email protected]> Signed-off-by: Eric Sunshine <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent e9d7761 commit da8fb6b

File tree

4 files changed

+27
-33
lines changed

4 files changed

+27
-33
lines changed

builtin/worktree.c

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ static void delete_worktrees_dir_if_empty(void)
7272
static void prune_worktree(const char *id, const char *reason)
7373
{
7474
if (show_only || verbose)
75-
printf_ln(_("Removing %s/%s: %s"), "worktrees", id, reason);
75+
fprintf_ln(stderr, _("Removing %s/%s: %s"), "worktrees", id, reason);
7676
if (!show_only)
7777
delete_git_dir(id);
7878
}
@@ -418,24 +418,24 @@ static void print_preparing_worktree_line(int detach,
418418
if (force_new_branch) {
419419
struct commit *commit = lookup_commit_reference_by_name(new_branch);
420420
if (!commit)
421-
printf_ln(_("Preparing worktree (new branch '%s')"), new_branch);
421+
fprintf_ln(stderr, _("Preparing worktree (new branch '%s')"), new_branch);
422422
else
423-
printf_ln(_("Preparing worktree (resetting branch '%s'; was at %s)"),
423+
fprintf_ln(stderr, _("Preparing worktree (resetting branch '%s'; was at %s)"),
424424
new_branch,
425425
find_unique_abbrev(&commit->object.oid, DEFAULT_ABBREV));
426426
} else if (new_branch) {
427-
printf_ln(_("Preparing worktree (new branch '%s')"), new_branch);
427+
fprintf_ln(stderr, _("Preparing worktree (new branch '%s')"), new_branch);
428428
} else {
429429
struct strbuf s = STRBUF_INIT;
430430
if (!detach && !strbuf_check_branch_ref(&s, branch) &&
431431
ref_exists(s.buf))
432-
printf_ln(_("Preparing worktree (checking out '%s')"),
432+
fprintf_ln(stderr, _("Preparing worktree (checking out '%s')"),
433433
branch);
434434
else {
435435
struct commit *commit = lookup_commit_reference_by_name(branch);
436436
if (!commit)
437437
die(_("invalid reference: %s"), branch);
438-
printf_ln(_("Preparing worktree (detached HEAD %s)"),
438+
fprintf_ln(stderr, _("Preparing worktree (detached HEAD %s)"),
439439
find_unique_abbrev(&commit->object.oid, DEFAULT_ABBREV));
440440
}
441441
strbuf_release(&s);
@@ -1006,7 +1006,7 @@ static int remove_worktree(int ac, const char **av, const char *prefix)
10061006
static void report_repair(int iserr, const char *path, const char *msg, void *cb_data)
10071007
{
10081008
if (!iserr) {
1009-
printf_ln(_("repair: %s: %s"), msg, path);
1009+
fprintf_ln(stderr, _("repair: %s: %s"), msg, path);
10101010
} else {
10111011
int *exit_status = (int *)cb_data;
10121012
fprintf_ln(stderr, _("error: %s: %s"), msg, path);

t/t2401-worktree-prune.sh

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ test_expect_success 'worktree prune on normal repo' '
1919
test_expect_success 'prune files inside $GIT_DIR/worktrees' '
2020
mkdir .git/worktrees &&
2121
: >.git/worktrees/abc &&
22-
git worktree prune --verbose >actual &&
22+
git worktree prune --verbose 2>actual &&
2323
cat >expect <<EOF &&
2424
Removing worktrees/abc: not a valid directory
2525
EOF
@@ -34,7 +34,7 @@ test_expect_success 'prune directories without gitdir' '
3434
cat >expect <<EOF &&
3535
Removing worktrees/def: gitdir file does not exist
3636
EOF
37-
git worktree prune --verbose >actual &&
37+
git worktree prune --verbose 2>actual &&
3838
test_cmp expect actual &&
3939
! test -d .git/worktrees/def &&
4040
! test -d .git/worktrees
@@ -45,7 +45,7 @@ test_expect_success SANITY 'prune directories with unreadable gitdir' '
4545
: >.git/worktrees/def/def &&
4646
: >.git/worktrees/def/gitdir &&
4747
chmod u-r .git/worktrees/def/gitdir &&
48-
git worktree prune --verbose >actual &&
48+
git worktree prune --verbose 2>actual &&
4949
test_i18ngrep "Removing worktrees/def: unable to read gitdir file" actual &&
5050
! test -d .git/worktrees/def &&
5151
! test -d .git/worktrees
@@ -55,7 +55,7 @@ test_expect_success 'prune directories with invalid gitdir' '
5555
mkdir -p .git/worktrees/def/abc &&
5656
: >.git/worktrees/def/def &&
5757
: >.git/worktrees/def/gitdir &&
58-
git worktree prune --verbose >actual &&
58+
git worktree prune --verbose 2>actual &&
5959
test_i18ngrep "Removing worktrees/def: invalid gitdir file" actual &&
6060
! test -d .git/worktrees/def &&
6161
! test -d .git/worktrees
@@ -65,7 +65,7 @@ test_expect_success 'prune directories with gitdir pointing to nowhere' '
6565
mkdir -p .git/worktrees/def/abc &&
6666
: >.git/worktrees/def/def &&
6767
echo "$(pwd)"/nowhere >.git/worktrees/def/gitdir &&
68-
git worktree prune --verbose >actual &&
68+
git worktree prune --verbose 2>actual &&
6969
test_i18ngrep "Removing worktrees/def: gitdir file points to non-existent location" actual &&
7070
! test -d .git/worktrees/def &&
7171
! test -d .git/worktrees
@@ -101,7 +101,7 @@ test_expect_success 'prune duplicate (linked/linked)' '
101101
git worktree add --detach w2 &&
102102
sed "s/w2/w1/" .git/worktrees/w2/gitdir >.git/worktrees/w2/gitdir.new &&
103103
mv .git/worktrees/w2/gitdir.new .git/worktrees/w2/gitdir &&
104-
git worktree prune --verbose >actual &&
104+
git worktree prune --verbose 2>actual &&
105105
test_i18ngrep "duplicate entry" actual &&
106106
test -d .git/worktrees/w1 &&
107107
! test -d .git/worktrees/w2
@@ -114,7 +114,7 @@ test_expect_success 'prune duplicate (main/linked)' '
114114
git -C repo worktree add --detach ../wt &&
115115
rm -fr wt &&
116116
mv repo wt &&
117-
git -C wt worktree prune --verbose >actual &&
117+
git -C wt worktree prune --verbose 2>actual &&
118118
test_i18ngrep "duplicate entry" actual &&
119119
! test -d .git/worktrees/wt
120120
'

t/t2402-worktree-list.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ test_expect_success '"list" all worktrees with prunable consistent with "prune"'
134134
git worktree list >out &&
135135
grep "/prunable *[0-9a-f].* prunable$" out &&
136136
! grep "/unprunable *[0-9a-f].* unprunable$" out &&
137-
git worktree prune --verbose >out &&
137+
git worktree prune --verbose 2>out &&
138138
test_i18ngrep "^Removing worktrees/prunable" out &&
139139
test_i18ngrep ! "^Removing worktrees/unprunable" out
140140
'

t/t2406-worktree-repair.sh

Lines changed: 12 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,8 @@ test_corrupt_gitfile () {
4545
git worktree add --detach corrupt &&
4646
git -C corrupt rev-parse --absolute-git-dir >expect &&
4747
eval "$butcher" &&
48-
git -C "$repairdir" worktree repair >out 2>err &&
49-
test_i18ngrep "$problem" out &&
50-
test_must_be_empty err &&
48+
git -C "$repairdir" worktree repair 2>err &&
49+
test_i18ngrep "$problem" err &&
5150
git -C corrupt rev-parse --absolute-git-dir >actual &&
5251
test_cmp expect actual
5352
}
@@ -130,42 +129,38 @@ test_expect_success 'repair broken gitdir' '
130129
sed s,orig/\.git$,moved/.git, .git/worktrees/orig/gitdir >expect &&
131130
rm .git/worktrees/orig/gitdir &&
132131
mv orig moved &&
133-
git worktree repair moved >out 2>err &&
132+
git worktree repair moved 2>err &&
134133
test_cmp expect .git/worktrees/orig/gitdir &&
135-
test_i18ngrep "gitdir unreadable" out &&
136-
test_must_be_empty err
134+
test_i18ngrep "gitdir unreadable" err
137135
'
138136

139137
test_expect_success 'repair incorrect gitdir' '
140138
test_when_finished "rm -rf orig moved && git worktree prune" &&
141139
git worktree add --detach orig &&
142140
sed s,orig/\.git$,moved/.git, .git/worktrees/orig/gitdir >expect &&
143141
mv orig moved &&
144-
git worktree repair moved >out 2>err &&
142+
git worktree repair moved 2>err &&
145143
test_cmp expect .git/worktrees/orig/gitdir &&
146-
test_i18ngrep "gitdir incorrect" out &&
147-
test_must_be_empty err
144+
test_i18ngrep "gitdir incorrect" err
148145
'
149146

150147
test_expect_success 'repair gitdir (implicit) from linked worktree' '
151148
test_when_finished "rm -rf orig moved && git worktree prune" &&
152149
git worktree add --detach orig &&
153150
sed s,orig/\.git$,moved/.git, .git/worktrees/orig/gitdir >expect &&
154151
mv orig moved &&
155-
git -C moved worktree repair >out 2>err &&
152+
git -C moved worktree repair 2>err &&
156153
test_cmp expect .git/worktrees/orig/gitdir &&
157-
test_i18ngrep "gitdir incorrect" out &&
158-
test_must_be_empty err
154+
test_i18ngrep "gitdir incorrect" err
159155
'
160156

161157
test_expect_success 'unable to repair gitdir (implicit) from main worktree' '
162158
test_when_finished "rm -rf orig moved && git worktree prune" &&
163159
git worktree add --detach orig &&
164160
cat .git/worktrees/orig/gitdir >expect &&
165161
mv orig moved &&
166-
git worktree repair >out 2>err &&
162+
git worktree repair 2>err &&
167163
test_cmp expect .git/worktrees/orig/gitdir &&
168-
test_must_be_empty out &&
169164
test_must_be_empty err
170165
'
171166

@@ -178,12 +173,11 @@ test_expect_success 'repair multiple gitdir files' '
178173
sed s,orig2/\.git$,moved2/.git, .git/worktrees/orig2/gitdir >expect2 &&
179174
mv orig1 moved1 &&
180175
mv orig2 moved2 &&
181-
git worktree repair moved1 moved2 >out 2>err &&
176+
git worktree repair moved1 moved2 2>err &&
182177
test_cmp expect1 .git/worktrees/orig1/gitdir &&
183178
test_cmp expect2 .git/worktrees/orig2/gitdir &&
184-
test_i18ngrep "gitdir incorrect:.*orig1/gitdir$" out &&
185-
test_i18ngrep "gitdir incorrect:.*orig2/gitdir$" out &&
186-
test_must_be_empty err
179+
test_i18ngrep "gitdir incorrect:.*orig1/gitdir$" err &&
180+
test_i18ngrep "gitdir incorrect:.*orig2/gitdir$" err
187181
'
188182

189183
test_expect_success 'repair moved main and linked worktrees' '

0 commit comments

Comments
 (0)