git.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Junio C Hamano <gitster@pobox.com>
To: Patrick Steinhardt <ps@pks.im>
Cc: Jeff King <peff@peff.net>,  Han Jiang <jhcarl0814@gmail.com>,
	 Git Mailing List <git@vger.kernel.org>
Subject: Re: `git remote rename` does not work when `refs/remotes/server/HEAD` is unborn (when right after `git remote add -m`)
Date: Thu, 24 Jul 2025 11:38:35 -0700	[thread overview]
Message-ID: <xmqqbjp9h1sk.fsf@gitster.g> (raw)
In-Reply-To: <aIIvHxR8wXLTCgMW@pks.im> (Patrick Steinhardt's message of "Thu, 24 Jul 2025 15:03:27 +0200")

Patrick Steinhardt <ps@pks.im> writes:

> I've quickly hacked something together now, see the work-in-progress
> patch below. The patch does not yet handle reflogs, but that isn't too
> hard to implement.
>
> And these changes indeed speed up things by quite a lot: instead of
> hours it now takes 7 seconds :) I'll polish this patch series and will
> likely send it in tomorrow.

Nice.  Not just the "oops, we shouldn't assume symrefs always point
at an existing ref" fix, performance fix comes at the same time ;-)

> diff --git a/builtin/remote.c b/builtin/remote.c
> index 5dd6cbbaeed..072a70e6b45 100644
> --- a/builtin/remote.c
> +++ b/builtin/remote.c
> @@ -612,36 +612,53 @@ static int add_branch_for_removal(const char *refname,
>  struct rename_info {
>  	const char *old_name;
>  	const char *new_name;
> -	struct string_list *remote_branches;
>  	uint32_t symrefs_nr;
> +	struct ref_transaction *transaction;
> +	struct strbuf *err;
>  };

OK, as a place to hook the transaction into, rename_info is a
convenient place, as it already is passed around throughout the
relevant code paths.

> -static int read_remote_branches(const char *refname, const char *referent UNUSED,
> -				const struct object_id *oid UNUSED,
> -				int flags UNUSED, void *cb_data)
> +static int queue_one_rename(const char *refname, const char *referent,
> +			    const struct object_id *oid,
> +			    int flags, void *cb_data)
>  {
> +	struct strbuf new_refname = STRBUF_INIT, new_referent = STRBUF_INIT;
>  	struct rename_info *rename = cb_data;
> -	struct strbuf buf = STRBUF_INIT;
> -	struct string_list_item *item;
> -	int flag;
> -	const char *symref;
> -
> -	strbuf_addf(&buf, "refs/remotes/%s/", rename->old_name);
> -	if (starts_with(refname, buf.buf)) {
> -		item = string_list_append(rename->remote_branches, refname);
> -		symref = refs_resolve_ref_unsafe(get_main_ref_store(the_repository),
> -						 refname, RESOLVE_REF_READING,
> -						 NULL, &flag);
> -		if (symref && (flag & REF_ISSYMREF)) {
> -			item->util = xstrdup(symref);
> -			rename->symrefs_nr++;
> -		} else {
> -			item->util = NULL;
> -		}
> +	int error;
> +
> +	strbuf_addf(&new_refname, "refs/remotes/%s/", rename->old_name);
> +	if (!starts_with(refname, new_refname.buf)) {
> +		error = 0;
> +		goto out;
>  	}
> -	strbuf_release(&buf);
>  
> -	return 0;
> +	if (flags & REF_ISSYMREF) {
> +		strbuf_addstr(&new_referent, referent);
> +		strbuf_splice(&new_referent, strlen("refs/remotes/"), strlen(rename->old_name),
> +			      rename->new_name, strlen(rename->new_name));
> +		oid = NULL;
> +	}
> +
> +	error = ref_transaction_delete(rename->transaction, refname,
> +				       oid, referent, REF_NO_DEREF, NULL, rename->err);

Remove old ...

> +	if (error < 0)
> +		goto out;
> +
> +	strbuf_reset(&new_refname);
> +	strbuf_addstr(&new_refname, refname);
> +	strbuf_splice(&new_refname, strlen("refs/remotes/"), strlen(rename->old_name),
> +		      rename->new_name, strlen(rename->new_name));
> +
> +	error = ref_transaction_update(rename->transaction, new_refname.buf, oid,
> +				       null_oid(the_hash_algo), (flags & REF_ISSYMREF) ? new_referent.buf : NULL, NULL,
> +				       REF_SKIP_CREATE_REFLOG | REF_NO_DEREF | REF_SKIP_OID_VERIFICATION,
> +				       NULL, rename->err);

... and create new.  Would we be hit with the same "while renaming A
to A/B, there is a D/F conflict which the ref transaction does not
handle by itself" issue we saw recently here?

> +	rename.transaction = ref_store_transaction_begin(get_main_ref_store(the_repository),
> +							 0, &err);
> +	if (!rename.transaction)
> +		goto out;
>  
> +	result = refs_for_each_rawref(get_main_ref_store(the_repository),
> +				      queue_one_rename, &rename);
> +	if (result < 0)
> +		die(_("renaming references failed: %s"), rename.err->buf);
>  
> +	result = ref_transaction_commit(rename.transaction, &err);
> +	if (result < 0)
> +		die(_("committing renamed references failed: %s"), rename.err->buf);
>  
>  	handle_push_default(rename.old_name, rename.new_name);
>  
>  out:
> -	string_list_clear(&remote_branches, 1);
> +	ref_transaction_free(rename.transaction);

Very nice.

>  	strbuf_release(&old_remote_context);
>  	strbuf_release(&buf);
>  	strbuf_release(&buf2);
> -	strbuf_release(&buf3);
> +	strbuf_release(&err);
>  	return result;
>  }
>  

  reply	other threads:[~2025-07-24 18:38 UTC|newest]

Thread overview: 10+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-07-24  9:59 `git remote rename` does not work when `refs/remotes/server/HEAD` is unborn (when right after `git remote add -m`) Han Jiang
2025-07-24 10:45 ` Jeff King
2025-07-24 11:58   ` Patrick Steinhardt
2025-07-24 13:03     ` Patrick Steinhardt
2025-07-24 18:38       ` Junio C Hamano [this message]
2025-07-25  8:00         ` Patrick Steinhardt
2025-07-25 11:02       ` Jeff King
2025-07-25 11:13         ` Jeff King
2025-07-28  6:05           ` Patrick Steinhardt
2025-07-28  6:05         ` Patrick Steinhardt

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

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

  git send-email \
    --in-reply-to=xmqqbjp9h1sk.fsf@gitster.g \
    --to=gitster@pobox.com \
    --cc=git@vger.kernel.org \
    --cc=jhcarl0814@gmail.com \
    --cc=peff@peff.net \
    --cc=ps@pks.im \
    /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.
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 NNTP newsgroup(s).