git@vger.kernel.org mailing list mirror (one of many)
 help / color / mirror / code / Atom feed
From: Junio C Hamano <gitster@pobox.com>
To: Elijah Newren <newren@gmail.com>
Cc: "Phil Hord" <phil.hord@gmail.com>, Git <git@vger.kernel.org>,
	"Martin Ågren" <martin.agren@gmail.com>
Subject: Re: [PATCH v2] use delete_refs when deleting tags or branches
Date: Fri, 15 Jan 2021 14:04:56 -0800	[thread overview]
Message-ID: <xmqqmtx9rh4n.fsf@gitster.c.googlers.com> (raw)
In-Reply-To: <CABPp-BEUPH5Yc08uDehAXNQ5-3fJ9YeW0xscVBR45hniDe+HEg@mail.gmail.com> (Elijah Newren's message of "Fri, 15 Jan 2021 10:43:07 -0800")

Elijah Newren <newren@gmail.com> writes:

>>>                 /* For subsequent UI messages */
>>>                 remote_branch = 1;
>>>                 allowed_interpret = INTERPRET_BRANCH_REMOTE;
>>> -
>>> +               refname_pos = 13;
>>>                 force = 1;
>>>                 break;
>>>         case FILTER_REFS_BRANCHES:
>>>                 fmt = "refs/heads/%s";
>>>                 allowed_interpret = INTERPRET_BRANCH_LOCAL;
>>> +               refname_pos = 11;
>>>                 break;

Hmph, I can tell 11 is strlen("refs/heads/") and 13 is probably for
"refs/remotes/", but can't we do better?

		switch (kinds) {
		case ...:
			fmt = "refs/remotes/%s";
			...
		}
		refname_skip_bytes = strcspn(fmt, "%");

or something like that?

I think refname_pos is a misleading variable name.  The refname is
the whole thing, so it would always be 0.  What we are looking for
is the position at which we find human readable "branch name"
begins, or, the number of bytes to skip to get there from the
beginning (the latter is where 'refname_skip_bytes' come from).

I am also OK with 'branch_name_pos'.

>>> +       delete_refs(NULL, &refs_to_delete, REF_NO_DEREF);

Earlier in a separate message, I wondered where the branches that
are not merged are rejected; it seems that the code reuses the
original code that jumps to the end of the loop and refrains from
placing such a branch to the refs_to_delete list, so from the point
of view of "correctness", the patch would not make it "all or none",
which is good.

The order in which the failures are reported will change, though.

In the original code, we got errors in the order we iterate through
the branches.  With the patch, we'll see all the "not merged and can
not delete" first from A to Z, and then "we tried to delete but this
exists after that" in a separate batch from A to Z (we won't report
for the same ref twice).  I think it probably is OK and not worth
refactoring the error reporting mechanism of check_branch_commit()
just in order to restore the order of the error messages, but I am
pointing it out just for completeness.

>>> +       for_each_string_list_item(item, &refs_to_delete) {
>>> +               char * describe_ref = item->util;
>>> +               char * name = item->string;

Style.  The asterisk sticks to the identifier, i.e.

		char *name = item->string;

>>> +               if (ref_exists(name))
>>> +                       ret = 1;
>
> Now it sets the return code if the branch still exists after trying to
> delete.  I thought that was subtly different...but I tried doing a
> branch deletion of a non-existent branch since I thought that would be
> the only difference -- however, that errors out earlier in the
> codepath before even getting to the stage of deleting refs.  So I
> think these are effectively the same.

It checks if the branch exists after it tried to delete.  A branch
may exist because delete_refs() failed to delete, or it may exist
because somebody created it anew after delete_refs() returned.  The
original reported if a deletion was successful or not by checking
the status returned by delete_ref().

We may want to introduce a variant of delete_refs() that reports
which ones it failed to delete to the caller if we really want to
report the errors properly.  I dunno.

>>> diff --git a/builtin/tag.c b/builtin/tag.c
>>> ...
>>> +static int collect_tags(const char *name, const char *ref,
>>> +                       const struct object_id *oid, void *cb_data)
>>>  {
>>> -       if (delete_ref(NULL, ref, oid, 0))
>>> -               return 1;
>
> This used to return 1 if it failed to delete a ref.
>
>>> -       printf(_("Deleted tag '%s' (was %s)\n"), name,
>>> -              find_unique_abbrev(oid, DEFAULT_ABBREV));
>>> +       struct string_list *ref_list = cb_data;
>>> +
>>> +       string_list_append(ref_list, ref);
>>> +       ref_list->items[ref_list->nr - 1].util = oiddup(oid);
>>>         return 0;
>
> Now it unconditionally returns 0.

But this is just "collect what to delete" phase, and the return
value is to be consumed by for_each_tag_name() as the callback, no?

I do not see a problem here.

>>>  }
>>>
>>> +static int delete_tags(const char **argv)
>>> +{
>>> +       int result;
>>> +       struct string_list refs_to_delete = STRING_LIST_INIT_DUP;
>>> +       struct string_list_item *item;
>>> +
>>> +       result = for_each_tag_name(argv, collect_tags, (void *)&refs_to_delete);
>>> +       delete_refs(NULL, &refs_to_delete, REF_NO_DEREF);
>
> You now only look at the result of collecting the tags, and ignore the
> result of trying to delete them...

That is a bigger problem, and is shared by the branch side, too.

I do think delete_refs() API is misdesigned in that it is not
equipped to report what happened to individual refs.  It only
says "we saw an error" when there is even a single ref that
encounters a trouble.

If we fix that, I suspect that this patch will become much simpler.

Another thing that I think is a misdesign of delete_refs() actually
can work for us here.  The list of refnames to be deleted is passed
as a "struct string_list" (which is not the most natural way to pass
bunch of strings, which is why I consider it a misdesign), and for
each ref, there is a place for the delete_refs() and its internal
helpers to store some piece of info, i.e. string_list_item.util.

Not even compile tested, but we could stuff NULL/non-NULL to the
.util member so that we can report per-ref status from the API,
perhaps like this:

diff --git i/refs/files-backend.c w/refs/files-backend.c
index 04e85e7002..bdc17f55ef 100644
--- i/refs/files-backend.c
+++ w/refs/files-backend.c
@@ -1224,8 +1224,12 @@ static int files_delete_refs(struct ref_store *ref_store, const char *msg,
 	for (i = 0; i < refnames->nr; i++) {
 		const char *refname = refnames->items[i].string;
 
-		if (refs_delete_ref(&refs->base, msg, refname, NULL, flags))
-			result |= error(_("could not remove reference %s"), refname);
+		if (!refs_delete_ref(&refs->base, msg, refname, NULL, flags)) {
+			refnames->items[i].util = NULL;
+			continue;
+		}
+		result |= error(_("could not remove reference %s"), refname);
+		refnames->items[i].util = "error";
 	}
 
 	strbuf_release(&err);
diff --git i/refs/packed-backend.c w/refs/packed-backend.c
index b912f2505f..a5b5071511 100644
--- i/refs/packed-backend.c
+++ w/refs/packed-backend.c
@@ -1541,7 +1541,10 @@ static int packed_delete_refs(struct ref_store *ref_store, const char *msg,
 					   flags, msg, &err)) {
 			warning(_("could not delete reference %s: %s"),
 				item->string, err.buf);
+			item->util = "error";
 			strbuf_reset(&err);
+		} else {
+			item->util = NULL;
 		}
 	}
 

  parent reply	other threads:[~2021-01-15 22:08 UTC|newest]

Thread overview: 5+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2019-08-14  2:49 [PATCH v2] use delete_refs when deleting tags or branches Phil Hord
     [not found] ` <CABURp0rtkmo7MSSCVrdNXT0UzV9XqV_kXOGkC23C+_vMENNJUg@mail.gmail.com>
2021-01-15 18:43   ` Elijah Newren
2021-01-15 20:06     ` Junio C Hamano
2021-01-15 22:04     ` Junio C Hamano [this message]
2021-01-16  2:27     ` Phil Hord

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: http://vger.kernel.org/majordomo-info.html

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=xmqqmtx9rh4n.fsf@gitster.c.googlers.com \
    --to=gitster@pobox.com \
    --cc=git@vger.kernel.org \
    --cc=martin.agren@gmail.com \
    --cc=newren@gmail.com \
    --cc=phil.hord@gmail.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

	https://80x24.org/mirrors/git.git

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 read-only IMAP folder(s) and NNTP newsgroup(s).