diff mbox series

[RFC,2/2] notes.c: fixed tip when target and append note are both empty

Message ID 20221013055654.39628-3-tenglong.tl@alibaba-inc.com (mailing list archive)
State New, archived
Headers show
Series notes.c: introduce "--no-blankline" option | expand

Commit Message

Teng Long Oct. 13, 2022, 5:56 a.m. UTC
From: Teng Long <dyroneteng@gmail.com>

When "git notes append <object>" is executed, if there is no note in
the given object and the appended note is empty, the command line
prompt will be as follows:

     "Removing note for object <object>"

Actually, this tip is not that accurate, because there is no note in
the original <object>, and it also does no remove work on the notes
reference, so let's fix this and give the correct tip.

Signed-off-by: Teng Long <dyroneteng@gmail.com>
---
 builtin/notes.c  | 13 +++++++++++--
 t/t3301-notes.sh |  3 ++-
 2 files changed, 13 insertions(+), 3 deletions(-)

Comments

Ævar Arnfjörð Bjarmason Oct. 13, 2022, 9:36 a.m. UTC | #1
On Thu, Oct 13 2022, Teng Long wrote:

> From: Teng Long <dyroneteng@gmail.com>
>
> When "git notes append <object>" is executed, if there is no note in
> the given object and the appended note is empty, the command line
> prompt will be as follows:
>
>      "Removing note for object <object>"
>
> Actually, this tip is not that accurate, because there is no note in
> the original <object>, and it also does no remove work on the notes
> reference, so let's fix this and give the correct tip.
>
> Signed-off-by: Teng Long <dyroneteng@gmail.com>
> ---
>  builtin/notes.c  | 13 +++++++++++--
>  t/t3301-notes.sh |  3 ++-
>  2 files changed, 13 insertions(+), 3 deletions(-)
>
> diff --git a/builtin/notes.c b/builtin/notes.c
> index 1ca0476a27..cc1e3aa2b6 100644
> --- a/builtin/notes.c
> +++ b/builtin/notes.c
> @@ -567,9 +567,10 @@ static int append_edit(int argc, const char **argv, const char *prefix)
>  	struct notes_tree *t;
>  	struct object_id object, new_note;
>  	const struct object_id *note;
> -	char *logmsg;
> +	char *logmsg = NULL;

Hrm, interesting that (at least my) gcc doesn't catch if we don't
NULL-initialize this, but -fanalyzer does (usually it's not needed for
such trivial cases0. Anyawy...

>  	const char * const *usage;
>  	struct note_data d = { 0, 0, NULL, STRBUF_INIT };
> +	struct note_data cp = { 0, 0, NULL, STRBUF_INIT };

This is probably better "fixed while at it" to set both to use "{ .buf =
STRBUF_INIT }", rather than copying the pre-C99 pattern.
Phillip Wood Oct. 13, 2022, 10:10 a.m. UTC | #2
On 13/10/2022 10:36, Ævar Arnfjörð Bjarmason wrote:
> 
> On Thu, Oct 13 2022, Teng Long wrote:
> 
>> From: Teng Long <dyroneteng@gmail.com>
>>
>> When "git notes append <object>" is executed, if there is no note in
>> the given object and the appended note is empty, the command line
>> prompt will be as follows:
>>
>>       "Removing note for object <object>"
>>
>> Actually, this tip is not that accurate, because there is no note in
>> the original <object>, and it also does no remove work on the notes
>> reference, so let's fix this and give the correct tip.
>>
>> Signed-off-by: Teng Long <dyroneteng@gmail.com>
>> ---
>>   builtin/notes.c  | 13 +++++++++++--
>>   t/t3301-notes.sh |  3 ++-
>>   2 files changed, 13 insertions(+), 3 deletions(-)
>>
>> diff --git a/builtin/notes.c b/builtin/notes.c
>> index 1ca0476a27..cc1e3aa2b6 100644
>> --- a/builtin/notes.c
>> +++ b/builtin/notes.c
>> @@ -567,9 +567,10 @@ static int append_edit(int argc, const char **argv, const char *prefix)
>>   	struct notes_tree *t;
>>   	struct object_id object, new_note;
>>   	const struct object_id *note;
>> -	char *logmsg;
>> +	char *logmsg = NULL;
> 
> Hrm, interesting that (at least my) gcc doesn't catch if we don't
> NULL-initialize this, but -fanalyzer does (usually it's not needed for
> such trivial cases0. Anyawy...

I don't think its written to if we take the 'else if' branch added by 
this patch so we need to initialize it for the free() at the end.

>>   	const char * const *usage;
>>   	struct note_data d = { 0, 0, NULL, STRBUF_INIT };
>> +	struct note_data cp = { 0, 0, NULL, STRBUF_INIT };
> 
> This is probably better "fixed while at it" to set both to use "{ .buf =
> STRBUF_INIT }", rather than copying the pre-C99 pattern.

We only seem to be using cp.buf.len so we can test check if the original 
note was empty so I think it would be better just to add

	int note_was_empty;

`	...

	note_was_empty = !d.buf.len

instead.

Best Wishes

Phillip
Ævar Arnfjörð Bjarmason Oct. 13, 2022, 10:23 a.m. UTC | #3
On Thu, Oct 13 2022, Phillip Wood wrote:

> On 13/10/2022 10:36, Ævar Arnfjörð Bjarmason wrote:
>> On Thu, Oct 13 2022, Teng Long wrote:
>> 
>>> From: Teng Long <dyroneteng@gmail.com>
>>>
>>> When "git notes append <object>" is executed, if there is no note in
>>> the given object and the appended note is empty, the command line
>>> prompt will be as follows:
>>>
>>>       "Removing note for object <object>"
>>>
>>> Actually, this tip is not that accurate, because there is no note in
>>> the original <object>, and it also does no remove work on the notes
>>> reference, so let's fix this and give the correct tip.
>>>
>>> Signed-off-by: Teng Long <dyroneteng@gmail.com>
>>> ---
>>>   builtin/notes.c  | 13 +++++++++++--
>>>   t/t3301-notes.sh |  3 ++-
>>>   2 files changed, 13 insertions(+), 3 deletions(-)
>>>
>>> diff --git a/builtin/notes.c b/builtin/notes.c
>>> index 1ca0476a27..cc1e3aa2b6 100644
>>> --- a/builtin/notes.c
>>> +++ b/builtin/notes.c
>>> @@ -567,9 +567,10 @@ static int append_edit(int argc, const char **argv, const char *prefix)
>>>   	struct notes_tree *t;
>>>   	struct object_id object, new_note;
>>>   	const struct object_id *note;
>>> -	char *logmsg;
>>> +	char *logmsg = NULL;
>> Hrm, interesting that (at least my) gcc doesn't catch if we don't
>> NULL-initialize this, but -fanalyzer does (usually it's not needed for
>> such trivial cases0. Anyawy...
>
> I don't think its written to if we take the 'else if' branch added by
> this patch so we need to initialize it for the free() at the end.

Yes, sorry about not being clear. It *does* need to be uninitialized, I
was just narrating my surprise at this not being a case where my
compiler caught it when I was locally testing this.

Then I remembered this is one of those cases where clang is slightly
better at analysis, gcc without -fanalyzer says nothing, but clang by
default will note (if you remove the NULL initialization here):

	builtin/notes.c:641:13: error: variable 'logmsg' is used uninitialized whenever 'if' condition is true [-Werror,-Wsometimes-uninitialized]
	        } else if (!cp.buf.len) {
	                   ^~~~~~~~~~~
	builtin/notes.c:653:7: note: uninitialized use occurs here
	        free(logmsg);
	             ^~~~~~
	builtin/notes.c:641:9: note: remove the 'if' if its condition is always false
	        } else if (!cp.buf.len) {
	               ^~~~~~~~~~~~~~~~~~
	builtin/notes.c:570:14: note: initialize the variable 'logmsg' to silence this warning
	        char *logmsg;
	                    ^
	                     = NULL

Anyway, nothing needs to be changed about the code here, sorry about the
distraction. I should have left it at something like "this NULL
initialization is needed".

>>>   	const char * const *usage;
>>>   	struct note_data d = { 0, 0, NULL, STRBUF_INIT };
>>> +	struct note_data cp = { 0, 0, NULL, STRBUF_INIT };
>> This is probably better "fixed while at it" to set both to use "{
>> .buf =
>> STRBUF_INIT }", rather than copying the pre-C99 pattern.
>
> We only seem to be using cp.buf.len so we can test check if the
> original note was empty so I think it would be better just to add
>
> 	int note_was_empty;
>
> `	...
>
> 	note_was_empty = !d.buf.len

That's a good catch, and one I didn't catch on my read-through, i.e. in
general this seems like a "was the strbuf empty?" pattern, before we
re-append to it.

But playing with it further:
	
	diff --git a/builtin/notes.c b/builtin/notes.c
	index cc1e3aa2b6b..262bbffa375 100644
	--- a/builtin/notes.c
	+++ b/builtin/notes.c
	@@ -570,7 +570,6 @@ static int append_edit(int argc, const char **argv, const char *prefix)
	 	char *logmsg = NULL;
	 	const char * const *usage;
	 	struct note_data d = { 0, 0, NULL, STRBUF_INIT };
	-	struct note_data cp = { 0, 0, NULL, STRBUF_INIT };
	 	struct option options[] = {
	 		OPT_CALLBACK_F('m', "message", &d, N_("message"),
	 			N_("note contents as a string"), PARSE_OPT_NONEG,
	@@ -616,8 +615,6 @@ static int append_edit(int argc, const char **argv, const char *prefix)
	 
	 	prepare_note_data(&object, &d, edit && note ? note : NULL);
	 
	-	strbuf_addbuf(&cp.buf, &d.buf);
	-
	 	if (note && !edit) {
	 		/* Append buf to previous note contents */
	 		unsigned long size;
	@@ -638,21 +635,16 @@ static int append_edit(int argc, const char **argv, const char *prefix)
	 			BUG("combine_notes_overwrite failed");
	 		logmsg = xstrfmt("Notes added by 'git notes %s'", argv[0]);
	 		commit_notes(the_repository, t, logmsg);
	-	} else if (!cp.buf.len) {
	+	} else if (!d.buf.len) {
	 		fprintf(stderr,
	 			_("Both original and appended notes are empty in %s, do nothing\n"),
	 			oid_to_hex(&object));
	 	} else {
	-		fprintf(stderr, _("Removing note for object %s\n"),
	-			oid_to_hex(&object));
	-		remove_note(t, object.hash);
	-		logmsg = xstrfmt("Notes removed by 'git notes %s'", argv[0]);
	-		commit_notes(the_repository, t, logmsg);
	+		BUG("this is not reachable by any test now");
	 	}
	 
	 	free(logmsg);
	 	free_note_data(&d);
	-	free_note_data(&cp);
	 	free_notes(t);
	 	return 0;
	 }
	
This 2/2 makes that "else" test-unreachable, so whatever else we do here
we should start by making sure that by adding the "else if" we still
have test coverage for the "else".
Phillip Wood Oct. 15, 2022, 7:40 p.m. UTC | #4
Hi Ævar

On 13/10/2022 11:23, Ævar Arnfjörð Bjarmason wrote:
> 
> On Thu, Oct 13 2022, Phillip Wood wrote:
>>>> diff --git a/builtin/notes.c b/builtin/notes.c
>>>> index 1ca0476a27..cc1e3aa2b6 100644
>>>> --- a/builtin/notes.c
>>>> +++ b/builtin/notes.c
>>>> @@ -567,9 +567,10 @@ static int append_edit(int argc, const char **argv, const char *prefix)
>>>>    	struct notes_tree *t;
>>>>    	struct object_id object, new_note;
>>>>    	const struct object_id *note;
>>>> -	char *logmsg;
>>>> +	char *logmsg = NULL;
>>> Hrm, interesting that (at least my) gcc doesn't catch if we don't
>>> NULL-initialize this, but -fanalyzer does (usually it's not needed for
>>> such trivial cases0. Anyawy...
>>
>> I don't think its written to if we take the 'else if' branch added by
>> this patch so we need to initialize it for the free() at the end.
> 
> Yes, sorry about not being clear. It *does* need to be uninitialized, I
> was just narrating my surprise at this not being a case where my
> compiler caught it when I was locally testing this.

Ah I think I slightly misunderstood your comment - I agree it is 
surprising that the compiler didn't catch that.

> 	@@ -638,21 +635,16 @@ static int append_edit(int argc, const char **argv, const char *prefix)
> 	 			BUG("combine_notes_overwrite failed");
> 	 		logmsg = xstrfmt("Notes added by 'git notes %s'", argv[0]);
> 	 		commit_notes(the_repository, t, logmsg);
> 	-	} else if (!cp.buf.len) {
> 	+	} else if (!d.buf.len) {
> 	 		fprintf(stderr,
> 	 			_("Both original and appended notes are empty in %s, do nothing\n"),
> 	 			oid_to_hex(&object));
> 	 	} else {
> 	-		fprintf(stderr, _("Removing note for object %s\n"),
> 	-			oid_to_hex(&object));
> 	-		remove_note(t, object.hash);
> 	-		logmsg = xstrfmt("Notes removed by 'git notes %s'", argv[0]);
> 	-		commit_notes(the_repository, t, logmsg);
> 	+		BUG("this is not reachable by any test now");
> 	 	}
> 	
> This 2/2 makes that "else" test-unreachable, so whatever else we do here
> we should start by making sure that by adding the "else if" we still
> have test coverage for the "else".

Oh so we can just use d.buf.len directly - nicely spotted and kudos for 
checking the test coverage. Looking at the existing tests they are 
checking if an empty note is removed which suggests this patch is 
failing to distinguish between an existing empty note and no note. I 
think we probably need to be doing "else if (note && d.buf.len)" but 
I've not looked very closely.

Best Wishes

Phillip
Teng Long Oct. 18, 2022, 3:11 a.m. UTC | #5
"Ævar Arnfjörð Bjarmason" <avarab@gmail.com> writes:

> Hrm, interesting that (at least my) gcc doesn't catch if we don't
> NULL-initialize this, but -fanalyzer does (usually it's not needed for
> such trivial cases0. Anyawy...

On my local env the warnings shows , show I change the line (initialize with
NULL to "logmsg").

But it seems like different as the last time I built... However now "suggest
braces around initialization of subobject" appears, is it normal or we should
repair this?

builtin/merge-file.c:29:23: warning: suggest braces around initialization of subobject [-Wmissing-braces]
        mmfile_t mmfs[3] = { 0 };
                             ^
                             {}
builtin/merge-file.c:31:20: warning: suggest braces around initialization of subobject [-Wmissing-braces]
        xmparam_t xmp = { 0 };
                          ^
                          {}
2 warnings generated.
builtin/notes.c:641:13: warning: variable 'logmsg' is used uninitialized whenever 'if' condition is true [-Wsometimes-uninitialized]
        } else if (!cp.buf.len) {
                   ^~~~~~~~~~~
builtin/notes.c:653:7: note: uninitialized use occurs here
        free(logmsg);
             ^~~~~~
builtin/notes.c:641:9: note: remove the 'if' if its condition is always false
        } else if (!cp.buf.len) {
               ^~~~~~~~~~~~~~~~~~
builtin/notes.c:570:14: note: initialize the variable 'logmsg' to silence this warning
        char *logmsg;
                    ^
                     = NULL
1 warning generated.
builtin/submodule--helper.c:1749:56: warning: suggest braces around initialization of subobject [-Wmissing-braces]
        struct list_objects_filter_options filter_options = { 0 };
                                                              ^
                                                              {}
builtin/submodule--helper.c:2623:56: warning: suggest braces around initialization of subobject [-Wmissing-braces]
        struct list_objects_filter_options filter_options = { 0 };
                                                              ^
                                                              {}
builtin/unpack-objects.c:388:26: warning: suggest braces around initialization of subobject [-Wmissing-braces]
        git_zstream zstream = { 0 };
                                ^
                                {}


My gcc version:
   Apple clang version 11.0.0 (clang-1100.0.33.17)
   Target: x86_64-apple-darwin19.6.0
   Thread model: posix


> >  	const char * const *usage;
> >  	struct note_data d = { 0, 0, NULL, STRBUF_INIT };
> > +	struct note_data cp = { 0, 0, NULL, STRBUF_INIT };
>
> This is probably better "fixed while at it" to set both to use "{ .buf =
> STRBUF_INIT }", rather than copying the pre-C99 pattern.


Thanks for figuring this out, will fix both.
Teng Long Oct. 18, 2022, 3:25 a.m. UTC | #6
Phillip Wood <phillip.wood123@gmail.com> writes:

> I don't think its written to if we take the 'else if' branch added by
> this patch so we need to initialize it for the free() at the end.

Actually, I didn't get it totally (maybe because my English, sorry for that),
but indeed the 'else if' expose this problem out, so I think to initialize it
is needed.

Thanks.


> We only seem to be using cp.buf.len so we can test check if the original
> note was empty so I think it would be better just to add
>
> 	int note_was_empty;
>
> `	...
>
> 	note_was_empty = !d.buf.len
>
> instead.

Yes, it actually does as you describe above, your suggestion makes it look
better.

Thank you very much for your detailed review.
Teng Long Oct. 18, 2022, 8:08 a.m. UTC | #7
Phillip Wood <phillip.wood123@gmail.com> writes:

> I don't think its written to if we take the 'else if' branch added by
> this patch so we need to initialize it for the free() at the end.

Actually, I didn't get it totally (maybe because my English, sorry for that),
but indeed the 'else if' expose this problem out, so I think to initialize it
is needed.

Thanks.

> We only seem to be using cp.buf.len so we can test check if the original
> note was empty so I think it would be better just to add
>
> 	int note_was_empty;
>
> `	...
>
> 	note_was_empty = !d.buf.len
>
> instead.

Yes, it actually does as you describe above, your suggestion makes it look
better, will apply.

Thank you very much for your detailed review.
Ævar Arnfjörð Bjarmason Oct. 18, 2022, 9:23 a.m. UTC | #8
On Tue, Oct 18 2022, Teng Long wrote:

> "Ævar Arnfjörð Bjarmason" <avarab@gmail.com> writes:
>
>> Hrm, interesting that (at least my) gcc doesn't catch if we don't
>> NULL-initialize this, but -fanalyzer does (usually it's not needed for
>> such trivial cases0. Anyawy...
>
> On my local env the warnings shows , show I change the line (initialize with
> NULL to "logmsg").
>
> But it seems like different as the last time I built... However now "suggest
> braces around initialization of subobject" appears, is it normal or we should
> repair this?
>
> builtin/merge-file.c:29:23: warning: suggest braces around initialization of subobject [-Wmissing-braces]
>         mmfile_t mmfs[3] = { 0 };
>                              ^
>                              {}
> builtin/merge-file.c:31:20: warning: suggest braces around initialization of subobject [-Wmissing-braces]
>         xmparam_t xmp = { 0 };
>                           ^
>                           {}

The fix for this is in "next": 54795d37d9e (config.mak.dev: disable
suggest braces error on old clang versions, 2022-10-10)


> 2 warnings generated.
> builtin/notes.c:641:13: warning: variable 'logmsg' is used uninitialized whenever 'if' condition is true [-Wsometimes-uninitialized]
>         } else if (!cp.buf.len) {
>                    ^~~~~~~~~~~
> builtin/notes.c:653:7: note: uninitialized use occurs here
>         free(logmsg);
>              ^~~~~~
> builtin/notes.c:641:9: note: remove the 'if' if its condition is always false
>         } else if (!cp.buf.len) {
>                ^~~~~~~~~~~~~~~~~~
> builtin/notes.c:570:14: note: initialize the variable 'logmsg' to silence this warning
>         char *logmsg;
>                     ^
>                      = NULL
> 1 warning generated.

Yes, we should initialize it to NULL, so this is the expected
warning. Clang spots it.
diff mbox series

Patch

diff --git a/builtin/notes.c b/builtin/notes.c
index 1ca0476a27..cc1e3aa2b6 100644
--- a/builtin/notes.c
+++ b/builtin/notes.c
@@ -567,9 +567,10 @@  static int append_edit(int argc, const char **argv, const char *prefix)
 	struct notes_tree *t;
 	struct object_id object, new_note;
 	const struct object_id *note;
-	char *logmsg;
+	char *logmsg = NULL;
 	const char * const *usage;
 	struct note_data d = { 0, 0, NULL, STRBUF_INIT };
+	struct note_data cp = { 0, 0, NULL, STRBUF_INIT };
 	struct option options[] = {
 		OPT_CALLBACK_F('m', "message", &d, N_("message"),
 			N_("note contents as a string"), PARSE_OPT_NONEG,
@@ -615,6 +616,8 @@  static int append_edit(int argc, const char **argv, const char *prefix)
 
 	prepare_note_data(&object, &d, edit && note ? note : NULL);
 
+	strbuf_addbuf(&cp.buf, &d.buf);
+
 	if (note && !edit) {
 		/* Append buf to previous note contents */
 		unsigned long size;
@@ -634,16 +637,22 @@  static int append_edit(int argc, const char **argv, const char *prefix)
 		if (add_note(t, &object, &new_note, combine_notes_overwrite))
 			BUG("combine_notes_overwrite failed");
 		logmsg = xstrfmt("Notes added by 'git notes %s'", argv[0]);
+		commit_notes(the_repository, t, logmsg);
+	} else if (!cp.buf.len) {
+		fprintf(stderr,
+			_("Both original and appended notes are empty in %s, do nothing\n"),
+			oid_to_hex(&object));
 	} else {
 		fprintf(stderr, _("Removing note for object %s\n"),
 			oid_to_hex(&object));
 		remove_note(t, object.hash);
 		logmsg = xstrfmt("Notes removed by 'git notes %s'", argv[0]);
+		commit_notes(the_repository, t, logmsg);
 	}
-	commit_notes(the_repository, t, logmsg);
 
 	free(logmsg);
 	free_note_data(&d);
+	free_note_data(&cp);
 	free_notes(t);
 	return 0;
 }
diff --git a/t/t3301-notes.sh b/t/t3301-notes.sh
index 43ac3feb78..967e6bfb67 100755
--- a/t/t3301-notes.sh
+++ b/t/t3301-notes.sh
@@ -574,7 +574,8 @@  test_expect_success 'git notes append == add when there is no existing note' '
 test_expect_success 'appending empty string to non-existing note does not create note' '
 	git notes remove HEAD &&
 	test_must_fail git notes list HEAD &&
-	git notes append -m "" &&
+	git notes append -m "" >output 2>&1 &&
+	grep "Both original and appended notes are empty" output &&
 	test_must_fail git notes list HEAD
 '