diff mbox series

[4/6] fetch: report errors when backfilling tags fails

Message ID 54fdee845bea7f67f46817417f8e5a504bd39665.1644565025.git.ps@pks.im (mailing list archive)
State Superseded
Headers show
Series fetch: improve atomicity of `--atomic` flag | expand

Commit Message

Patrick Steinhardt Feb. 11, 2022, 7:46 a.m. UTC
When the backfilling of tags fails we do not report this error to the
caller, but only report it implicitly at a later point when reporting
updated references. This leaves callers unable to act upon the
information of whether the backfilling succeeded or not.

Refactor the function to return an error code and pass it up the
callstack. This causes us to correctly propagate the error back to the
user of git-fetch(1).

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 builtin/fetch.c      | 29 +++++++++++++++++++++--------
 t/t5503-tagfollow.sh |  4 +---
 2 files changed, 22 insertions(+), 11 deletions(-)

Comments

Christian Couder Feb. 15, 2022, 7:52 a.m. UTC | #1
On Fri, Feb 11, 2022 at 9:03 PM Patrick Steinhardt <ps@pks.im> wrote:
>
> When the backfilling of tags fails we do not report this error to the
> caller, but only report it implicitly at a later point when reporting
> updated references.

Probably stupid question: are we sure that it's a bug and not a feature?

> This leaves callers unable to act upon the
> information of whether the backfilling succeeded or not.
>
> Refactor the function to return an error code and pass it up the
> callstack. This causes us to correctly propagate the error back to the
> user of git-fetch(1).

Even if it would have been the right behavior when backfilling tags
was implemented to return an error when backfilling tags fails, I
think it's interesting to ask ourselves if this change could be seen
as a regression by some users.
Jonathan Tan Feb. 16, 2022, 11:35 p.m. UTC | #2
Patrick Steinhardt <ps@pks.im> writes:
> @@ -1628,8 +1630,19 @@ static int do_fetch(struct transport *transport,
>  		struct ref *tags_ref_map = NULL, **tail = &tags_ref_map;
>  
>  		find_non_local_tags(remote_refs, &tags_ref_map, &tail);
> -		if (tags_ref_map)
> -			backfill_tags(transport, tags_ref_map, &fetch_head, worktrees);
> +		if (tags_ref_map) {
> +			/*
> +			 * If backfilling tags succeeds we used to not return
> +			 * an error code to the user at all. Instead, we
> +			 * silently swallowed that error and updated the local
> +			 * state of the repository. We now notify the user of
> +			 * any such errors, but we continue to make sure that
> +			 * FETCH_HEAD and the upstream branch are configured as
> +			 * expected.
> +			 */
> +			if (backfill_tags(transport, tags_ref_map, &fetch_head, worktrees))
> +				retcode = 1;

I think s/succeeds/fails/

Having said that, I don't think that we need to describe the past here.
Notifying the user if something has failed is expected and doesn't
require an explanation about how we didn't do that in the past.

Everything up to this patch looks good to me.
Elijah Newren Feb. 17, 2022, 1:31 a.m. UTC | #3
On Fri, Feb 11, 2022 at 12:03 PM Patrick Steinhardt <ps@pks.im> wrote:
>
> When the backfilling of tags fails we do not report this error to the
> caller, but only report it implicitly at a later point when reporting
> updated references. This leaves callers unable to act upon the
> information of whether the backfilling succeeded or not.
>
> Refactor the function to return an error code and pass it up the
> callstack. This causes us to correctly propagate the error back to the
> user of git-fetch(1).
>
> Signed-off-by: Patrick Steinhardt <ps@pks.im>
> ---
>  builtin/fetch.c      | 29 +++++++++++++++++++++--------
>  t/t5503-tagfollow.sh |  4 +---
>  2 files changed, 22 insertions(+), 11 deletions(-)
>
> diff --git a/builtin/fetch.c b/builtin/fetch.c
> index 627847e2f8..1eda0b68ff 100644
> --- a/builtin/fetch.c
> +++ b/builtin/fetch.c
> @@ -1495,12 +1495,12 @@ static struct transport *prepare_transport(struct remote *remote, int deepen)
>         return transport;
>  }
>
> -static void backfill_tags(struct transport *transport,
> -                         struct ref *ref_map,
> -                         struct fetch_head *fetch_head,
> -                         struct worktree **worktrees)
> +static int backfill_tags(struct transport *transport,
> +                        struct ref *ref_map,
> +                        struct fetch_head *fetch_head,
> +                        struct worktree **worktrees)
>  {
> -       int cannot_reuse;
> +       int retcode, cannot_reuse;
>
>         /*
>          * Once we have set TRANS_OPT_DEEPEN_SINCE, we can't unset it
> @@ -1519,12 +1519,14 @@ static void backfill_tags(struct transport *transport,
>         transport_set_option(transport, TRANS_OPT_FOLLOWTAGS, NULL);
>         transport_set_option(transport, TRANS_OPT_DEPTH, "0");
>         transport_set_option(transport, TRANS_OPT_DEEPEN_RELATIVE, NULL);
> -       fetch_and_consume_refs(transport, ref_map, fetch_head, worktrees);
> +       retcode = fetch_and_consume_refs(transport, ref_map, fetch_head, worktrees);
>
>         if (gsecondary) {
>                 transport_disconnect(gsecondary);
>                 gsecondary = NULL;
>         }
> +
> +       return retcode;
>  }
>
>  static int do_fetch(struct transport *transport,
> @@ -1628,8 +1630,19 @@ static int do_fetch(struct transport *transport,
>                 struct ref *tags_ref_map = NULL, **tail = &tags_ref_map;
>
>                 find_non_local_tags(remote_refs, &tags_ref_map, &tail);
> -               if (tags_ref_map)
> -                       backfill_tags(transport, tags_ref_map, &fetch_head, worktrees);
> +               if (tags_ref_map) {
> +                       /*
> +                        * If backfilling tags succeeds we used to not return
> +                        * an error code to the user at all. Instead, we
> +                        * silently swallowed that error and updated the local
> +                        * state of the repository. We now notify the user of
> +                        * any such errors, but we continue to make sure that
> +                        * FETCH_HEAD and the upstream branch are configured as
> +                        * expected.
> +                        */

nit: I'd prefer to see code comments explain what we currently do, and
move history lessons to the commit message.


> +                       if (backfill_tags(transport, tags_ref_map, &fetch_head, worktrees))
> +                               retcode = 1;
> +               }
>
>                 free_refs(tags_ref_map);
>         }
> diff --git a/t/t5503-tagfollow.sh b/t/t5503-tagfollow.sh
> index 888305ad4d..549f908b90 100755
> --- a/t/t5503-tagfollow.sh
> +++ b/t/t5503-tagfollow.sh
> @@ -237,9 +237,7 @@ test_expect_success 'backfill failure causes command to fail' '
>                 done
>         EOF
>
> -       # Even though we fail to create refs/tags/tag1 the below command
> -       # unexpectedly succeeds.
> -       git -C clone5 fetch .. $B:refs/heads/something &&
> +       test_must_fail git -C clone5 fetch .. $B:refs/heads/something &&
>         test $B = $(git -C clone5 rev-parse --verify refs/heads/something) &&
>         test $S = $(git -C clone5 rev-parse --verify tag2) &&
>         test_must_fail git -C clone5 rev-parse --verify tag1
> --
> 2.35.1
>
Patrick Steinhardt Feb. 17, 2022, 11:27 a.m. UTC | #4
On Tue, Feb 15, 2022 at 08:52:14AM +0100, Christian Couder wrote:
> On Fri, Feb 11, 2022 at 9:03 PM Patrick Steinhardt <ps@pks.im> wrote:
> >
> > When the backfilling of tags fails we do not report this error to the
> > caller, but only report it implicitly at a later point when reporting
> > updated references.
> 
> Probably stupid question: are we sure that it's a bug and not a feature?

Good question, and I don't have a definitive answer for it. But to me it
very much smells like a bug: if I ask for a fetch and the fetch fails to
populate some of the data I have asked for, then I want to get a
notification on that failure.

> > This leaves callers unable to act upon the
> > information of whether the backfilling succeeded or not.
> >
> > Refactor the function to return an error code and pass it up the
> > callstack. This causes us to correctly propagate the error back to the
> > user of git-fetch(1).
> 
> Even if it would have been the right behavior when backfilling tags
> was implemented to return an error when backfilling tags fails, I
> think it's interesting to ask ourselves if this change could be seen
> as a regression by some users.

Yeah, it's not all that clear-cut because auto-following of tags is a
bit obscure. But our default behaviour is to fetch tags pointing into
the history, and if a user didn't want that they should've passed
`--no-tags` to git-fetch(1). So conversely, we should assume that a user
is asking for auto-filling of tags if we're not told otherwise, which
also means that it is a failure if this fails.

At least that's my take, but I'm happy to hear arguments against my
viewpoint.

Patrick
Patrick Steinhardt Feb. 17, 2022, 12:47 p.m. UTC | #5
On Thu, Feb 17, 2022 at 12:27:15PM +0100, Patrick Steinhardt wrote:
> On Tue, Feb 15, 2022 at 08:52:14AM +0100, Christian Couder wrote:
> > On Fri, Feb 11, 2022 at 9:03 PM Patrick Steinhardt <ps@pks.im> wrote:
> > >
> > > When the backfilling of tags fails we do not report this error to the
> > > caller, but only report it implicitly at a later point when reporting
> > > updated references.
> > 
> > Probably stupid question: are we sure that it's a bug and not a feature?
> 
> Good question, and I don't have a definitive answer for it. But to me it
> very much smells like a bug: if I ask for a fetch and the fetch fails to
> populate some of the data I have asked for, then I want to get a
> notification on that failure.
> 
> > > This leaves callers unable to act upon the
> > > information of whether the backfilling succeeded or not.
> > >
> > > Refactor the function to return an error code and pass it up the
> > > callstack. This causes us to correctly propagate the error back to the
> > > user of git-fetch(1).
> > 
> > Even if it would have been the right behavior when backfilling tags
> > was implemented to return an error when backfilling tags fails, I
> > think it's interesting to ask ourselves if this change could be seen
> > as a regression by some users.
> 
> Yeah, it's not all that clear-cut because auto-following of tags is a
> bit obscure. But our default behaviour is to fetch tags pointing into
> the history, and if a user didn't want that they should've passed
> `--no-tags` to git-fetch(1). So conversely, we should assume that a user
> is asking for auto-filling of tags if we're not told otherwise, which
> also means that it is a failure if this fails.
> 
> At least that's my take, but I'm happy to hear arguments against my
> viewpoint.
> 
> Patrick

I just noticed that we have in fact landed a change in the exact same
spirit on `main` via c9e04d905e (fetch --prune: exit with error if
pruning fails, 2022-01-31). So there is precedent that we fix up these
missing error codes, and that gives me more confidence that doing the
same fixup for the tag-backfill is the correct thing to do.

Patrick
diff mbox series

Patch

diff --git a/builtin/fetch.c b/builtin/fetch.c
index 627847e2f8..1eda0b68ff 100644
--- a/builtin/fetch.c
+++ b/builtin/fetch.c
@@ -1495,12 +1495,12 @@  static struct transport *prepare_transport(struct remote *remote, int deepen)
 	return transport;
 }
 
-static void backfill_tags(struct transport *transport,
-			  struct ref *ref_map,
-			  struct fetch_head *fetch_head,
-			  struct worktree **worktrees)
+static int backfill_tags(struct transport *transport,
+			 struct ref *ref_map,
+			 struct fetch_head *fetch_head,
+			 struct worktree **worktrees)
 {
-	int cannot_reuse;
+	int retcode, cannot_reuse;
 
 	/*
 	 * Once we have set TRANS_OPT_DEEPEN_SINCE, we can't unset it
@@ -1519,12 +1519,14 @@  static void backfill_tags(struct transport *transport,
 	transport_set_option(transport, TRANS_OPT_FOLLOWTAGS, NULL);
 	transport_set_option(transport, TRANS_OPT_DEPTH, "0");
 	transport_set_option(transport, TRANS_OPT_DEEPEN_RELATIVE, NULL);
-	fetch_and_consume_refs(transport, ref_map, fetch_head, worktrees);
+	retcode = fetch_and_consume_refs(transport, ref_map, fetch_head, worktrees);
 
 	if (gsecondary) {
 		transport_disconnect(gsecondary);
 		gsecondary = NULL;
 	}
+
+	return retcode;
 }
 
 static int do_fetch(struct transport *transport,
@@ -1628,8 +1630,19 @@  static int do_fetch(struct transport *transport,
 		struct ref *tags_ref_map = NULL, **tail = &tags_ref_map;
 
 		find_non_local_tags(remote_refs, &tags_ref_map, &tail);
-		if (tags_ref_map)
-			backfill_tags(transport, tags_ref_map, &fetch_head, worktrees);
+		if (tags_ref_map) {
+			/*
+			 * If backfilling tags succeeds we used to not return
+			 * an error code to the user at all. Instead, we
+			 * silently swallowed that error and updated the local
+			 * state of the repository. We now notify the user of
+			 * any such errors, but we continue to make sure that
+			 * FETCH_HEAD and the upstream branch are configured as
+			 * expected.
+			 */
+			if (backfill_tags(transport, tags_ref_map, &fetch_head, worktrees))
+				retcode = 1;
+		}
 
 		free_refs(tags_ref_map);
 	}
diff --git a/t/t5503-tagfollow.sh b/t/t5503-tagfollow.sh
index 888305ad4d..549f908b90 100755
--- a/t/t5503-tagfollow.sh
+++ b/t/t5503-tagfollow.sh
@@ -237,9 +237,7 @@  test_expect_success 'backfill failure causes command to fail' '
 		done
 	EOF
 
-	# Even though we fail to create refs/tags/tag1 the below command
-	# unexpectedly succeeds.
-	git -C clone5 fetch .. $B:refs/heads/something &&
+	test_must_fail git -C clone5 fetch .. $B:refs/heads/something &&
 	test $B = $(git -C clone5 rev-parse --verify refs/heads/something) &&
 	test $S = $(git -C clone5 rev-parse --verify tag2) &&
 	test_must_fail git -C clone5 rev-parse --verify tag1