diff mbox series

[v2,1/4] worktree: refactor infer_backlink() to use *strbuf

Message ID 20241006060017.171788-2-cdwhite3@pm.me (mailing list archive)
State New
Headers show
Series Link worktrees with relative paths | expand

Commit Message

Caleb White Oct. 6, 2024, 6 a.m. UTC
This refactors the `infer_backlink` function to return an integer
result and use a pre-allocated `strbuf` for the inferred backlink
path, replacing the previous `char*` return type.

This lays the groundwork for the next patch, which needs the
resultant backlink as a `strbuf`. There was no need to go from
`strbuf -> char* -> strbuf` again. This change also aligns the
function's signature with other `strbuf`-based functions.

Signed-off-by: Caleb White <cdwhite3@pm.me>
---
 worktree.c | 26 ++++++++++++++------------
 1 file changed, 14 insertions(+), 12 deletions(-)

 	if (strbuf_read_file(&actual, gitfile, 0) < 0)
@@ -658,17 +657,16 @@ static char *infer_backlink(const char *gitfile)
 	id++; /* advance past '/' to point at <id> */
 	if (!*id)
 		goto error;
-	strbuf_git_common_path(&inferred, the_repository, "worktrees/%s", id);
-	if (!is_directory(inferred.buf))
+	strbuf_git_common_path(inferred, the_repository, "worktrees/%s", id);
+	if (!is_directory(inferred->buf))
 		goto error;
 
 	strbuf_release(&actual);
-	return strbuf_detach(&inferred, NULL);
+	return 0;
 
 error:
 	strbuf_release(&actual);
-	strbuf_release(&inferred);
-	return NULL;
+	return 1;
 }
 
 /*
@@ -680,9 +678,10 @@ void repair_worktree_at_path(const char *path,
 {
 	struct strbuf dotgit = STRBUF_INIT;
 	struct strbuf realdotgit = STRBUF_INIT;
+	struct strbuf backlink = STRBUF_INIT;
 	struct strbuf gitd
ir = STRBUF_INIT;
 	struct strbuf olddotgit = STRBUF_INIT;
-	char *backlink = NULL;
+	char *git_contents = NULL;
 	const char *repair = NULL;
 	int err;
 
@@ -698,21 +697,23 @@ void repair_worktree_at_path(const char *path,
 		goto done;
 	}
 
-	backlink = xstrdup_or_null(read_gitfile_gently(realdotgit.buf, &err));
+	git_contents = xstrdup_or_null(read_gitfile_gently(realdotgit.buf, &err));
 	if (err == READ_GITFILE_ERR_NOT_A_FILE) {
 		fn(1, realdotgit.buf, _("unable to locate repository; .git is not a file"), cb_data);
 		goto done;
 	} else if (err == READ_GITFILE_ERR_NOT_A_REPO) {
-		if (!(backlink = infer_backlink(realdotgit.buf))) {
+		if (infer_backlink(&backlink, realdotgit.buf)) {
 			fn(1, realdotgit.buf, _("unable to locate repository; .git file does not reference a repository"), cb_data);
 			goto done;
 		}
 	} else if (err) {
 		fn(1, realdotgit.buf, _("unable to locate repository; .git file broken"), cb_data);
 		goto done;
+	} else if (git_conte
nts) {
+		strbuf_addstr(&backlink, git_contents);
 	}
 
-	strbuf_addf(&gitdir, "%s/gitdir", backlink);
+	strbuf_addf(&gitdir, "%s/gitdir", backlink.buf);
 	if (strbuf_read_file(&olddotgit, gitdir.buf, 0) < 0)
 		repair = _("gitdir unreadable");
 	else {
@@ -726,8 +727,9 @@ void repair_worktree_at_path(const char *path,
 		write_file(gitdir.buf, "%s", realdotgit.buf);
 	}
 done:
-	free(backlink);
+	free(git_contents);
 	strbuf_release(&olddotgit);
+	strbuf_release(&backlink);
 	strbuf_release(&gitdir);
 	strbuf_release(&realdotgit);
 	strbuf_release(&dotgit);

Comments

shejialuo Oct. 6, 2024, 3:09 p.m. UTC | #1
On Sun, Oct 06, 2024 at 06:00:57AM +0000, Caleb White wrote:
> This refactors the `infer_backlink` function to return an integer
> result and use a pre-allocated `strbuf` for the inferred backlink
> path, replacing the previous `char*` return type.
> 
> This lays the groundwork for the next patch, which needs the
> resultant backlink as a `strbuf`. There was no need to go from
> `strbuf -> char* -> strbuf` again. This change also aligns the
> function's signature with other `strbuf`-based functions.
> 

I think we should first say why we need to add the change in the commit
message which means we should express our motivation in the first. It's
wired to say "I have done something" and then talk about the motivation
why we do this.

> Signed-off-by: Caleb White <cdwhite3@pm.me>
> ---
>  worktree.c | 26 ++++++++++++++------------
>  1 file changed, 14 insertions(+), 12 deletions(-)
> 
> diff --git a/worktree.c b/worktree.c
> index 0f032cc..c6d2ede 100644
> --- a/worktree.c
> +++ b/worktree.c
> @@ -642,10 +642,9 @@ static int is_main_worktree_path(const char *path)
>   * be able to infer the gitdir by manually reading /path/to/worktree/.git,
>   * extracting the <id>, and checking if <repo>/worktrees/<id> exists.
>   */
> -static char *infer_backlink(const char *gitfile)
> +static int infer_backlink(st
> ruct strbuf *inferred, const char *gitfile)

This line is so strange. Why it generates a newline here?

>  {
>  	struct strbuf actual = STRBUF_INIT;
> -	struct strbuf inferred = STRBUF_INIT;
>  	const char *id;
>  
>  	if (strbuf_read_file(&actual, gitfile, 0) < 0)
> @@ -658,17 +657,16 @@ static char *infer_backlink(const char *gitfile)
>  	id++; /* advance past '/' to point at <id> */
>  	if (!*id)
>  		goto error;
> -	strbuf_git_common_path(&inferred, the_repository, "worktrees/%s", id);
> -	if (!is_directory(inferred.buf))
> +	strbuf_git_common_path(inferred, the_repository, "worktrees/%s", id);
> +	if (!is_directory(inferred->buf))
>  		goto error;
>  
>  	strbuf_release(&actual);
> -	return strbuf_detach(&inferred, NULL);
> +	return 0;
>  
>  error:
>  	strbuf_release(&actual);
> -	strbuf_release(&inferred);

Because we leave the life cycle of the "inferred" to be outside of this
function, we should not use "strbuf_release" to release the memory here.
Make sense.

> -	return NULL;
> +	return 1;
>  }
>  
>  /*
> @@ -680,9 +678,10 @@ void repair_worktree_at_path(const char *path,
>  {
>  	struct strbuf dotgit = STRBUF_INIT;
>  	struct strbuf realdotgit = STRBUF_INIT;
> +	struct strbuf backlink = STRBUF_INIT;

Here, we replace "char *backlink" to "struct strbuf backlink", we need
to align the changed "infer_backlink" function. That's OK.

>  	struct strbuf gitd
> ir = STRBUF_INIT;

Another strange newline here.

>  	struct strbuf olddotgit = STRBUF_INIT;
> -	char *backlink = NULL;
> +	char *git_contents = NULL;
>  	const char *repair = NULL;
>  	int err;
>  
> @@ -698,21 +697,23 @@ void repair_worktree_at_path(const char *path,
>  		goto done;
>  	}
>  
> -	backlink = xstrdup_or_null(read_gitfile_gently(realdotgit.buf, &err));
> +	git_contents = xstrdup_or_null(read_gitfile_gently(realdotgit.buf, &err));

So, we create a new variable "git_contents" here. I suspect this is a
poor design. In the original logic, we will do the following things for
"backlink".

  1. Call the "read_gitfile_gently" function. If it encounters error, it
     will return NULL and the "err" variable will be set to NON-zero.
  2. If the value of "err" is 0, we would simply execute the
     "strbuf_addstr(&gitdir, "%s/gitdir", backlink)".
  3. If not, and the "err" is "READ_GITFILE_ERR_NOT_A_REPO". We will
     call "infer_backlink" to set the "backlink".

Because now "backlink" is "struct strbuf", we cannot just assign it by
using "xstrdup_or_null(read_gitfile_gently(...))". So, we create a new
variable "git_contents" here.

And we will check whether "git_contents" is NULL to set the value for
the "backlink".

Why not simply do the following things here (I don't think
"git_contents" is a good name, however I am not familiar with the
worktree, I cannot give some advice here).

    const char *git_contents;
    git_contents = read_gitfile_gently(...);
    if (git_contents)
        strbuf_addstr(&backlink, git_contents);

Even more, we could enhance the logic here. If "git_contents" is not
NULL, there is no need for us to check the "err" variable.

>  	if (err == READ_GITFILE_ERR_NOT_A_FILE) {
>  		fn(1, realdotgit.buf, _("unable to locate repository; .git is not a file"), cb_data);
>  		goto done;
>  	} else if (err == READ_GITFILE_ERR_NOT_A_REPO) {
> -		if (!(backlink = infer_backlink(realdotgit.buf))) {
> +		if (infer_backlink(&backlink, realdotgit.buf)) {
>  			fn(1, realdotgit.buf, _("unable to locate repository; .git file does not reference a repository"), cb_data);
>  			goto done;
>  		}
>  	} else if (err) {
>  		fn(1, realdotgit.buf, _("unable to locate repository; .git file broken"), cb_data);
>  		goto done;
> +	} else if (git_conte
> nts) {

Another strange newline here. As I have talked about above, we should
not check "git_contents" here.

> +		strbuf_addstr(&backlink, git_contents);
>  	}
>  
> -	strbuf_addf(&gitdir, "%s/gitdir", backlink);
> +	strbuf_addf(&gitdir, "%s/gitdir", backlink.buf);
>  	if (strbuf_read_file(&olddotgit, gitdir.buf, 0) < 0)
>  		repair = _("gitdir unreadable");
>  	else {
> @@ -726,8 +727,9 @@ void repair_worktree_at_path(const char *path,
>  		write_file(gitdir.buf, "%s", realdotgit.buf);
>  	}
>  done:
> -	free(backlink);
> +	free(git_contents);
>  	strbuf_release(&olddotgit);
> +	strbuf_release(&backlink);
>  	strbuf_release(&gitdir);
>  	strbuf_release(&realdotgit);
>  	strbuf_release(&dotgit);
> -- 
> 2.46.2
> 

Thanks,
Jialuo
Kristoffer Haugsbakk Oct. 6, 2024, 3:13 p.m. UTC | #2
On Sun, Oct 6, 2024, at 17:09, shejialuo wrote:
> On Sun, Oct 06, 2024 at 06:00:57AM +0000, Caleb White wrote:
>> This refactors the `infer_backlink` function to return an integer
>> result and use a pre-allocated `strbuf` for the inferred backlink
>> path, replacing the previous `char*` return type.
>>
>> This lays the groundwork for the next patch, which needs the
>> resultant backlink as a `strbuf`. There was no need to go from
>> `strbuf -> char* -> strbuf` again. This change also aligns the
>> function's signature with other `strbuf`-based functions.
>>
>
> I think we should first say why we need to add the change in the commit
> message which means we should express our motivation in the first. It's
> wired to say "I have done something" and then talk about the motivation
> why we do this.
>
>> Signed-off-by: Caleb White <cdwhite3@pm.me>
>> ---
>>  worktree.c | 26 ++++++++++++++------------
>>  1 file changed, 14 insertions(+), 12 deletions(-)
>>
>> diff --git a/worktree.c b/worktree.c
>> index 0f032cc..c6d2ede 100644
>> --- a/worktree.c
>> +++ b/worktree.c
>> @@ -642,10 +642,9 @@ static int is_main_worktree_path(const char *path)
>>   * be able to infer the gitdir by manually reading /path/to/worktree/.git,
>>   * extracting the <id>, and checking if <repo>/worktrees/<id> exists.
>>   */
>> -static char *infer_backlink(const char *gitfile)
>> +static int infer_backlink(st
>> ruct strbuf *inferred, const char *gitfile)
>
> This line is so strange. Why it generates a newline here?

The patches got corrupted by something.  See the emails from Eric
Sunshine.

This resubmit didn’t fix the issue.

https://lore.kernel.org/git/CAPig+cTB-sA-g4cdhfEjWwY1mnbWJ41e=bOCNwp=Y8JKvpmpRg@mail.gmail.com/
Eric Sunshine Oct. 6, 2024, 6:16 p.m. UTC | #3
On Sun, Oct 6, 2024 at 2:01 AM Caleb White <cdwhite3@pm.me> wrote:
> This refactors the `infer_backlink` function to return an integer
> result and use a pre-allocated `strbuf` for the inferred backlink
> path, replacing the previous `char*` return type.
>
> This lays the groundwork for the next patch, which needs the
> resultant backlink as a `strbuf`. There was no need to go from
> `strbuf -> char* -> strbuf` again. This change also aligns the
> function's signature with other `strbuf`-based functions.

Regarding aligning the signature with other strbuf-based functions...

> Signed-off-by: Caleb White <cdwhite3@pm.me>
> ---
> diff --git a/worktree.c b/worktree.c
> @@ -642,10 +642,9 @@ static int is_main_worktree_path(const char *path)
> -static char *infer_backlink(const char *gitfile)
> +static int infer_backlink(struct strbuf *inferred, const char *gitfile)

... it's subjective, but I find that placing the strbuf as the first
argument makes the purpose of the function less clear. The direct
purpose of all (or the majority of) functions in strbuf.h is to
operate on strbufs, thus it makes sense for the strbuf to be the first
argument (just like `this` is the invisible first argument of instance
methods in C++ which operate on an instance of the class). However,
infer_backlink()'s purpose isn't to operate on a strbuf; the fact that
the original signature neither accepted nor returned a strbuf bears
that out. The really important input to this function is `gitfile`,
thus it makes sense for this argument to come first. The strbuf which
this patch makes it use is a mere implementation detail (a
micro-optimization) which doesn't otherwise change the function's
original purpose, thus it belongs in a less prominent position in the
argument list.

> @@ -658,17 +657,16 @@ static char *infer_backlink(const char *gitfile)
> -       strbuf_git_common_path(&inferred, the_repository, "worktrees/%s", id);
> -       if (!is_directory(inferred.buf))
> +       strbuf_git_common_path(inferred, the_repository, "worktrees/%s", id);
> +       if (!is_directory(inferred->buf))
>                 goto error;
>         strbuf_release(&actual);
> -       return strbuf_detach(&inferred, NULL);
> +       return 0;

On success, we now return zero...

>  error:
>         strbuf_release(&actual);
> -       strbuf_release(&inferred);
> -       return NULL;
> +       return 1;

... and on failure we return 1. To avoid confusing readers who are
familiar with project idioms, it would probably be better to instead
follow the convention used in most of the rest of the project (and in
Unix system calls in general) of returning -1.

However...

> @@ -698,21 +697,23 @@ void repair_worktree_at_path(const char *path,
> -               if (!(backlink = infer_backlink(realdotgit.buf))) {
> +               if (infer_backlink(&backlink, realdotgit.buf)) {
>                         fn(1, realdotgit.buf, _("unable to locate repository; .git file does not reference a repository"), cb_data);
>                         goto done;
>                 }

... this code now becomes more than a little confusing to read. It
says "if infer_backlink then signal error", which sounds rather
backward and leaves the reader scratching his or her head. ("Why
signal error if the function succeeded?"). Hence, infer_backlink()
should probably return 1 on success and 0 on failure, which will make
this code read more idiomatically:

    if (!infer_backlink(realdotgit.buf, &backlink)) {
        ...signal error...
Eric Sunshine Oct. 6, 2024, 6:41 p.m. UTC | #4
On Sun, Oct 6, 2024 at 11:09 AM shejialuo <shejialuo@gmail.com> wrote:
> On Sun, Oct 06, 2024 at 06:00:57AM +0000, Caleb White wrote:
> > -     backlink = xstrdup_or_null(read_gitfile_gently(realdotgit.buf, &err));
> > +     git_contents = xstrdup_or_null(read_gitfile_gently(realdotgit.buf, &err));
>
> So, we create a new variable "git_contents" here. I suspect this is a
> poor design. In the original logic, we will do the following things for
> "backlink".
>
>   1. Call the "read_gitfile_gently" function. If it encounters error, it
>      will return NULL and the "err" variable will be set to NON-zero.
>   2. If the value of "err" is 0, we would simply execute the
>      "strbuf_addstr(&gitdir, "%s/gitdir", backlink)".
>   3. If not, and the "err" is "READ_GITFILE_ERR_NOT_A_REPO". We will
>      call "infer_backlink" to set the "backlink".
>
> Because now "backlink" is "struct strbuf", we cannot just assign it by
> using "xstrdup_or_null(read_gitfile_gently(...))". So, we create a new
> variable "git_contents" here.
>
> And we will check whether "git_contents" is NULL to set the value for
> the "backlink".

Thanks for thinking through this logic. I have a few additional comments...

> Why not simply do the following things here (I don't think
> "git_contents" is a good name, however I am not familiar with the
> worktree, I cannot give some advice here).

I found the name "git_contents" clear enough and understood its
purpose at-a-glance, so I think it's a reasonably good choice. A
slightly better name might be "gitfile_contents" or perhaps
"dotgit_contents" for consistency with other similarly-named variables
in this function.

>     const char *git_contents;
>     git_contents = read_gitfile_gently(...);
>     if (git_contents)
>         strbuf_addstr(&backlink, git_contents);
>
> Even more, we could enhance the logic here.

Upon reading this patch, I had a similar thought about this, that it
would be more reflective of the original code to set "backlink" early
here rather than waiting until the end of the if-else-cascade.
However, upon reflection, I don't mind that setting "backlink" is
delayed until after the if-else-chain, though I think it deserves at
least one change which I will explain below.

> If "git_contents" is not
> NULL, there is no need for us to check the "err" variable.

I'm not sure we would want to change this; the existing logic seems
clear enough.

> >       if (err == READ_GITFILE_ERR_NOT_A_FILE) {
> >               fn(1, realdotgit.buf, _("unable to locate repository; .git is not a file"), cb_data);
> >               goto done;
> >       } else if (err == READ_GITFILE_ERR_NOT_A_REPO) {
> > -             if (!(backlink = infer_backlink(realdotgit.buf))) {
> > +             if (infer_backlink(&backlink, realdotgit.buf)) {
> >                       fn(1, realdotgit.buf, _("unable to locate repository; .git file does not reference a repository"), cb_data);
> >                       goto done;
> >               }
> >       } else if (err) {
> >               fn(1, realdotgit.buf, _("unable to locate repository; .git file broken"), cb_data);
> >               goto done;
> > +     } else if (git_contents) {
> > +             strbuf_addstr(&backlink, git_contents);
> >       }

It certainly makes sense to check whether "git_contents" is NULL
before trying to copy it into the "backlink" strbuf, however, if
"git_contents" is NULL here, then what does that mean? What does it
mean to leave "backlink" empty? The only way (presumably) we get this
far is if read_gitfile_gently() succeeded, so (presumably)
"git_contents" should not be NULL. Thus, rather than conditionally
copying into "backlink", we should instead indicate clearly via BUG()
that it should be impossible for "git_contents" to be NULL. So, rather
than making this part of the existing if-else-cascade, we should do
this as a standalone `if`:

    if (!git_contents)
        BUG(...);
    strbuf_addstr(&backlink, git_contents);
Caleb White Oct. 6, 2024, 11:47 p.m. UTC | #5
On Sunday, October 6th, 2024 at 10:09, shejialuo <shejialuo@gmail.com> wrote:
> I think we should first say why we need to add the change in the commit
> message which means we should express our motivation in the first. It's
> wired to say "I have done something" and then talk about the motivation
> why we do this.

I can reword this commit message.

> Because we leave the life cycle of the "inferred" to be outside of this
> function, we should not use "strbuf_release" to release the memory here.
> Make sense.

Yes, however, I just realized that I should likely reset the strbuf when it
enters the function (or before it is written to) which is similar to what
the relative_path() function does.

> So, we create a new variable "git_contents" here. I suspect this is a
> poor design. In the original logic, we will do the following things for
> "backlink".
>
> 1. Call the "read_gitfile_gently" function. If it encounters error, it
> will return NULL and the "err" variable will be set to NON-zero.
> 2. If the value of "err" is 0, we would simply execute the
> "strbuf_addstr(&gitdir, "%s/gitdir", backlink)".
> 3. If not, and the "err" is "READ_GITFILE_ERR_NOT_A_REPO". We will
> call "infer_backlink" to set the "backlink".
>
> Because now "backlink" is "struct strbuf", we cannot just assign it by
> using "xstrdup_or_null(read_gitfile_gently(...))". So, we create a new
> variable "git_contents" here.
>
> And we will check whether "git_contents" is NULL to set the value for
> the "backlink".
>
> Why not simply do the following things here (I don't think
> "git_contents" is a good name, however I am not familiar with the
> worktree, I cannot give some advice here).
>
> const char *git_contents;
> git_contents = read_gitfile_gently(...);
> if (git_contents)
> strbuf_addstr(&backlink, git_contents);
>
> Even more, we could enhance the logic here. If "git_contents" is not
> NULL, there is no need for us to check the "err" variable.

I was trying to not make huge changes to the logic flow, but I suppose I
could revisit this if desired. I can likely move the `if(git_contents)`
to the start instead of being at the end. I was not aware that if an err
occurred that the function returned NULL, I thought that perhaps there was
the possibility of git_contents being filled with something and an err still
occurring.
Caleb White Oct. 7, 2024, 2:26 a.m. UTC | #6
On Sunday, October 6th, 2024 at 13:41, Eric Sunshine <sunshine@sunshineco.com> wrote:
> I found the name "git_contents" clear enough and understood its
> purpose at-a-glance, so I think it's a reasonably good choice. A
> slightly better name might be "gitfile_contents" or perhaps
> "dotgit_contents" for consistency with other similarly-named variables
> in this function.

I will rename to `dotgit_contents`.

> It certainly makes sense to check whether "git_contents" is NULL
> before trying to copy it into the "backlink" strbuf, however, if
> "git_contents" is NULL here, then what does that mean? What does it
> mean to leave "backlink" empty? The only way (presumably) we get this
> far is if read_gitfile_gently() succeeded, so (presumably)
> "git_contents" should not be NULL. Thus, rather than conditionally
> copying into "backlink", we should instead indicate clearly via BUG()
> that it should be impossible for "git_contents" to be NULL. So, rather
> than making this part of the existing if-else-cascade, we should do
> this as a standalone `if`:
> 

> if (!git_contents)
> BUG(...);
> strbuf_addstr(&backlink, git_contents);

We can't use BUG because this is handled as part of the err
conditions. The contents can be NULL and `backlink` could be
filled with the inferred backlink. I moved this to the top
and I think it reads better.
Caleb White Oct. 7, 2024, 2:42 a.m. UTC | #7
On Sunday, October 6th, 2024 at 13:16, Eric Sunshine <sunshine@sunshineco.com> wrote:
> ... it's subjective, but I find that placing the strbuf as the first
> argument makes the purpose of the function less clear. The direct
> purpose of all (or the majority of) functions in strbuf.h is to
> operate on strbufs, thus it makes sense for the strbuf to be the first
> argument (just like `this` is the invisible first argument of instance
> methods in C++ which operate on an instance of the class). However,
> infer_backlink()'s purpose isn't to operate on a strbuf; the fact that
> the original signature neither accepted nor returned a strbuf bears
> that out. The really important input to this function is `gitfile`,
> thus it makes sense for this argument to come first. The strbuf which
> this patch makes it use is a mere implementation detail (a
> micro-optimization) which doesn't otherwise change the function's
> original purpose, thus it belongs in a less prominent position in the
> argument list.

I can reverse the arguments.

> ... this code now becomes more than a little confusing to read. It
> says "if infer_backlink then signal error", which sounds rather
> backward and leaves the reader scratching his or her head. ("Why
> signal error if the function succeeded?"). Hence, infer_backlink()
> should probably return 1 on success and 0 on failure, which will make
> this code read more idiomatically:
> 

> if (!infer_backlink(realdotgit.buf, &backlink)) {
> ...signal error...

This was my first thought, however, on unix it is fairly standard
to return 0 if successful and a non-zero int if there's an error.
I don't mind updating, but I want to follow what makes the most
sense and would be most expected.
Eric Sunshine Oct. 7, 2024, 3:26 a.m. UTC | #8
On Sun, Oct 6, 2024 at 10:42 PM Caleb White <cdwhite3@pm.me> wrote:
> On Sunday, October 6th, 2024 at 13:16, Eric Sunshine <sunshine@sunshineco.com> wrote:
> > ... this code now becomes more than a little confusing to read. It
> > says "if infer_backlink then signal error", which sounds rather
> > backward and leaves the reader scratching his or her head. ("Why
> > signal error if the function succeeded?"). Hence, infer_backlink()
> > should probably return 1 on success and 0 on failure, which will make
> > this code read more idiomatically:
> >
> > if (!infer_backlink(realdotgit.buf, &backlink)) {
> > ...signal error...
>
> This was my first thought, however, on unix it is fairly standard
> to return 0 if successful and a non-zero int if there's an error.
> I don't mind updating, but I want to follow what makes the most
> sense and would be most expected.

I mentioned it because it made my reading hiccup, but I don't feel too
strongly about it one way or the other considering that this is an
internal function.
Caleb White Oct. 7, 2024, 3:28 a.m. UTC | #9
On Sunday, October 6th, 2024 at 22:26, Eric Sunshine <sunshine@sunshineco.com> wrote:
> I mentioned it because it made my reading hiccup, but I don't feel too
> strongly about it one way or the other considering that this is an
> internal function.

I reversed the flags :thumsup:
shejialuo Oct. 7, 2024, 3:56 a.m. UTC | #10
On Sun, Oct 06, 2024 at 02:41:22PM -0400, Eric Sunshine wrote:

[snip]

> > Why not simply do the following things here (I don't think
> > "git_contents" is a good name, however I am not familiar with the
> > worktree, I cannot give some advice here).
> 
> I found the name "git_contents" clear enough and understood its
> purpose at-a-glance, so I think it's a reasonably good choice. A
> slightly better name might be "gitfile_contents" or perhaps
> "dotgit_contents" for consistency with other similarly-named variables
> in this function.
> 

Thanks, I don't know the context here.

> > If "git_contents" is not
> > NULL, there is no need for us to check the "err" variable.
> 
> I'm not sure we would want to change this; the existing logic seems
> clear enough.
> 

The reason why I don't think we need to check the "err" variable is that
the "git_contents" and "err" is relevant. If "git_contents" is not NULL,
the "err" must be zero unless there are bugs in "read_gitfile_gently".
So, if we already check "git_contents", why do we need to check again
for "err"?

But, I agree with you that the existing logic is clear enough. I just
explain further what I mean here.

Thanks,
Jialuo
Caleb White Oct. 7, 2024, 4:01 a.m. UTC | #11
On Sunday, October 6th, 2024 at 22:56, shejialuo <shejialuo@gmail.com> wrote:
> The reason why I don't think we need to check the "err" variable is that
> the "git_contents" and "err" is relevant. If "git_contents" is not NULL,
> the "err" must be zero unless there are bugs in "read_gitfile_gently".
> So, if we already check "git_contents", why do we need to check again
> for "err"?

There are two other error conditions we check, and one of them we try
to find the inferred backlink (so it is not a failure path):

```
} else if (err == READ_GITFILE_ERR_NOT_A_FILE) {
	fn(1, realdotgit.buf, _("unable to locate repository; .git is not a file"), cb_data);
	goto done;
} else if (err == READ_GITFILE_ERR_NOT_A_REPO) {
	if (!infer_backlink(realdotgit.buf, &backlink)) {
		fn(1, realdotgit.buf, _("unable to locate repository; .git file does not reference a repository"), cb_data);
		goto done;
	}
} else if (err) {
	fn(1, realdotgit.buf, _("unable to locate repository; .git file broken"), cb_data);
	goto done;
}

```
shejialuo Oct. 7, 2024, 4:12 a.m. UTC | #12
On Mon, Oct 07, 2024 at 02:26:51AM +0000, Caleb White wrote:
> On Sunday, October 6th, 2024 at 13:41, Eric Sunshine <sunshine@sunshineco.com> wrote:
> > I found the name "git_contents" clear enough and understood its
> > purpose at-a-glance, so I think it's a reasonably good choice. A
> > slightly better name might be "gitfile_contents" or perhaps
> > "dotgit_contents" for consistency with other similarly-named variables
> > in this function.
> 
> I will rename to `dotgit_contents`.
> 
> > It certainly makes sense to check whether "git_contents" is NULL
> > before trying to copy it into the "backlink" strbuf, however, if
> > "git_contents" is NULL here, then what does that mean? What does it
> > mean to leave "backlink" empty? The only way (presumably) we get this
> > far is if read_gitfile_gently() succeeded, so (presumably)
> > "git_contents" should not be NULL. Thus, rather than conditionally
> > copying into "backlink", we should instead indicate clearly via BUG()
> > that it should be impossible for "git_contents" to be NULL. So, rather
> > than making this part of the existing if-else-cascade, we should do
> > this as a standalone `if`:
> > 
> 
> > if (!git_contents)
> > BUG(...);
> > strbuf_addstr(&backlink, git_contents);

I agree with the idea that Eric proposed. It's truly we should raise a
bug here. That's would be much more clear.

> We can't use BUG because this is handled as part of the err
> conditions. The contents can be NULL and `backlink` could be
> filled with the inferred backlink. I moved this to the top
> and I think it reads better.

That's not correct. It is true that the contents can be NULL and
`backlink` could be filled with the infer_backlink. But do you notice
that if `backlink` was filled with the infer_backlink, it will jump
to the "done" label.

What Erie means is the following:

    git_contents = read_gitfile_gently(...);
    if (err) {
        if (...) {

        } else if (err == RAED_GITFILE_ERR_NOT_A_REPO) {
            // handle backlink
            goto done;

        }
    }

    if (!git_contents)
        BUG(...);
    strbuf_addstr(&backlink, git_contents);

    done:
        // cleanup operations

Thanks,
Jialuo
Caleb White Oct. 7, 2024, 4:19 a.m. UTC | #13
On Sunday, October 6th, 2024 at 23:12, shejialuo <shejialuo@gmail.com> wrote:
> That's not correct. It is true that the contents can be NULL and
> `backlink` could be filled with the infer_backlink. But do you notice
> that if `backlink` was filled with the infer_backlink, it will jump
> to the "done" label.

This is not correct, if backlink is filled with `infer_backlink` it
continues on with the processing. I have made this more clear:

    dotgit_contents = xstrdup_or_null(read_gitfile_gently(realdotgit.buf, &err));
    if (dotgit_contents) {
	if (is_absolute_path(dotgit_contents)) {
	    strbuf_addstr(&backlink, dotgit_contents);
        } else {
	    strbuf_addbuf(&backlink, &realdotgit);
	    strbuf_strip_suffix(&backlink, ".git");
	    strbuf_addstr(&backlink, dotgit_contents);
        }
    } else if (err == READ_GITFILE_ERR_NOT_A_FILE) {
	fn(1, realdotgit.buf, _("unable to locate repository; .git is not a file"), cb_data);
	goto done;
    } else if (err == READ_GITFILE_ERR_NOT_A_REPO) {
	if (!infer_backlink(realdotgit.buf, &backlink)) {
	    fn(1, realdotgit.buf, _("unable to locate repository; .git file does not reference a repository"), cb_data);
	    goto done;
        }
    } else if (err) {
        fn(1, realdotgit.buf, _("unable to locate repository; .git file broken"), cb_data);
        goto done;
    }
shejialuo Oct. 7, 2024, 4:19 a.m. UTC | #14
On Mon, Oct 07, 2024 at 04:01:43AM +0000, Caleb White wrote:
> On Sunday, October 6th, 2024 at 22:56, shejialuo <shejialuo@gmail.com> wrote:
> > The reason why I don't think we need to check the "err" variable is that
> > the "git_contents" and "err" is relevant. If "git_contents" is not NULL,
> > the "err" must be zero unless there are bugs in "read_gitfile_gently".
> > So, if we already check "git_contents", why do we need to check again
> > for "err"?
> 
> There are two other error conditions we check, and one of them we try
> to find the inferred backlink (so it is not a failure path):
> 
> ```
> } else if (err == READ_GITFILE_ERR_NOT_A_FILE) {
> 	fn(1, realdotgit.buf, _("unable to locate repository; .git is not a file"), cb_data);
> 	goto done;
> } else if (err == READ_GITFILE_ERR_NOT_A_REPO) {
> 	if (!infer_backlink(realdotgit.buf, &backlink)) {
> 		fn(1, realdotgit.buf, _("unable to locate repository; .git file does not reference a repository"), cb_data);
> 		goto done;
> 	}
> } else if (err) {
> 	fn(1, realdotgit.buf, _("unable to locate repository; .git file broken"), cb_data);
> 	goto done;
> }
> 
> ```

I am sorry that my words make you not clear here. I want to express that
if "git_contents" is not NULL and there is no need for us to check
"err". This means we could use the following flows:

    if (git_contents && !err) {
        ...
    } else if (err == xxx) {
        ...
    }

However, from my perspective, the way proposed by Eric where we could
use "BUG" is more robust. Because the current method assumes that
"read_gitfile_gently" works as we want.

Thanks,
Jialuo
shejialuo Oct. 7, 2024, 4:28 a.m. UTC | #15
On Mon, Oct 07, 2024 at 04:19:22AM +0000, Caleb White wrote:
> On Sunday, October 6th, 2024 at 23:12, shejialuo <shejialuo@gmail.com> wrote:
> > That's not correct. It is true that the contents can be NULL and
> > `backlink` could be filled with the infer_backlink. But do you notice
> > that if `backlink` was filled with the infer_backlink, it will jump
> > to the "done" label.
> 
> This is not correct, if backlink is filled with `infer_backlink` it
> continues on with the processing. I have made this more clear:
> 
>     dotgit_contents = xstrdup_or_null(read_gitfile_gently(realdotgit.buf, &err));
>     if (dotgit_contents) {
> 	if (is_absolute_path(dotgit_contents)) {
> 	    strbuf_addstr(&backlink, dotgit_contents);
>         } else {
> 	    strbuf_addbuf(&backlink, &realdotgit);
> 	    strbuf_strip_suffix(&backlink, ".git");
> 	    strbuf_addstr(&backlink, dotgit_contents);
>         }
>     } else if (err == READ_GITFILE_ERR_NOT_A_FILE) {
> 	fn(1, realdotgit.buf, _("unable to locate repository; .git is not a file"), cb_data);
> 	goto done;
>     } else if (err == READ_GITFILE_ERR_NOT_A_REPO) {
> 	if (!infer_backlink(realdotgit.buf, &backlink)) {
> 	    fn(1, realdotgit.buf, _("unable to locate repository; .git file does not reference a repository"), cb_data);
> 	    goto done;
>         }
>     } else if (err) {
>         fn(1, realdotgit.buf, _("unable to locate repository; .git file broken"), cb_data);
>         goto done;
>     }
> 
> 

Thanks, you are correct here. We cannot use "BUG" here. And I think
currently this is OK.
Caleb White Oct. 7, 2024, 4:31 a.m. UTC | #16
On Sunday, October 6th, 2024 at 23:28, shejialuo <shejialuo@gmail.com> wrote:
> > } else if (err) {
> > fn(1, realdotgit.buf, _("unable to locate repository; .git file broken"), cb_data);
> > goto done;
> > }
> 

> 

> Thanks, you are correct here. We cannot use "BUG" here. And I think
> currently this is OK.

Granted, that last `else if` could just become an `else` statement now,
I'll make that change.
diff mbox series

Patch

diff --git a/worktree.c b/worktree.c
index 0f032cc..c6d2ede 100644
--- a/worktree.c
+++ b/worktree.c
@@ -642,10 +642,9 @@  static int is_main_worktree_path(const char *path)
  * be able to infer the gitdir by manually reading /path/to/worktree/.git,
  * extracting the <id>, and checking if <repo>/worktrees/<id> exists.
  */
-static char *infer_backlink(const char *gitfile)
+static int infer_backlink(st
ruct strbuf *inferred, const char *gitfile)
 {
 	struct strbuf actual = STRBUF_INIT;
-	struct strbuf inferred = STRBUF_INIT;
 	const char *id;