Subject: Re* [PATCH v2] clone: Allow combining --bare and --origin

From: Junio C Hamano <[email-masked]>
Date: Sat, 07 Aug 2021 15:08:02 -0700

Junio C Hamano <[email-masked]> writes:

>>> It is somewhat unfortunate that we do not say what the name of the
>>> "origin" is anywhere in the resulting configuration file.  The only
>>> way to tell that "--origin somewhere" was used is to notice that there
>>> is only one remote and its name is "somewhere".
>> ...
> But we'd end up treating them the same.  And something like
> remote.originName would help that.  Otherwise, we'd end up sending
> this message:
>
>     Even if we give "--bare --origin yourfavouritename" to you now,
>     unlike how 'origin' is treated in the default case, in the
>     resulting repository, 'yourfavouritename' is not special at all.
>
> Some people may want to treat yourfavouritename is not special at
> all, while some people may want to treat yourfavouritename truly as
> a replacement for 'origin' that is the default.  The message we
> would be sending is that we'd ignore the latter folks.

So, let's illustrate one of the things that is needed after the good
first step to allow --bare --origin=yourfavouritename used together.

There may be other things that needs fixing, of course, but we need
to start from somewhere.

---- >8 -------- >8 -------- >8 -------- >8 -------- >8 -------- >8 ----
Subject: [PATCH] remote: fall back on the sole remote when unspecified

Historically, we used hardcoded "origin" as the fallback default for
commands that take a remote (e.g. "git fetch") when the user did not
tell us otherwise.  Since the "--origin=name" option was taught to
"git clone", however, we may not have a remote whose name is
"origin" at all.

Which means that the name given to "git clone --origin" does not
truly replace the hardcoded "origin". An example of such limitation
is that "git fetch" (no other parameters) would fetch happily from
the "origin" repository, but in a repository cloned with the custom
name using "--origin=name", "git fetch" would not fetch from anywhere
and instead fail.

We can fix this by noticing that the repository has one and only one
remote defined, and use that as a replacement for the hardcoded
"origin".

Signed-off-by: Junio C Hamano <[email-masked]>
---

This matters for automation for those who want to use --origin
option.  Imagine you have multiple bare clones and you wanted to use
custom names for 'origin'.  And you want a cron job that goes over
these repositories and run "git fetch" from their upstream before
you come in for work, so that these bare clones can be used as
close-by mirrors of their upstream projects.

Unfortunately, that would not work.  If these repositories use
their own nicknames for their upstream that are not "origin",

	for repo in a b c
	do
		git -C $repo fetch
	done

would just fail.  Of course, you can somehow out-of-band know the
origin's name for each repo, e.g.

	for repoorigin in a:xyzzy b:frotz c:nitfol
	do
		repo=${repoorigin%:*}
                origin=${repoorigin#*:}
		git -C $repo fetch $origin
	done

but that is solving a problem that arises only because we are not
treating the name given to "git clone --origin=name" as a true
replacement for the default "origin".

 remote.c             | 10 +++++++++-
 t/t5512-ls-remote.sh | 10 ++++++++--
 2 files changed, 17 insertions(+), 3 deletions(-)

diff --git c/remote.c w/remote.c
index dfb863d808..8a2fd1ccc9 100644
--- c/remote.c
+++ w/remote.c
@@ -39,6 +39,8 @@ static int remotes_alloc;
 static int remotes_nr;
 static struct hashmap remotes_hash;
 
+static const char *default_remote_name;
+
 static struct branch **branches;
 static int branches_alloc;
 static int branches_nr;
@@ -460,6 +462,12 @@ static void read_config(void)
 	}
 	git_config(handle_config, NULL);
 	alias_all_urls();
+	if (remotes_nr == 1 &&
+	    remotes[0]->configured_in_repo &&
+	    remotes[0]->url)
+		default_remote_name = remotes[0]->name;
+	else
+		default_remote_name = "origin";
 }
 
 static int valid_remote_nick(const char *name)
@@ -483,7 +491,7 @@ const char *remote_for_branch(struct branch *branch, int *explicit)
 	}
 	if (explicit)
 		*explicit = 0;
-	return "origin";
+	return default_remote_name;
 }
 
 const char *pushremote_for_branch(struct branch *branch, int *explicit)
diff --git c/t/t5512-ls-remote.sh w/t/t5512-ls-remote.sh
index f53f58895a..aa6f14e8fd 100755
--- c/t/t5512-ls-remote.sh
+++ w/t/t5512-ls-remote.sh
@@ -83,8 +83,14 @@ test_expect_success 'ls-remote --sort="-refname" --tags self' '
 	test_cmp expect actual
 '
 
-test_expect_success 'dies when no remote specified and no default remotes found' '
-	test_must_fail git ls-remote
+test_expect_success 'ls-remote falls back to the only remote' '
+	generate_references \
+		refs/tags/mark1.2 \
+		refs/tags/mark1.10 \
+		refs/tags/mark1.1 \
+		refs/tags/mark >expect &&
+	git ls-remote --sort="-refname" --tags >actual &&
+	test_cmp expect actual
 '
 
 test_expect_success 'use "origin" when no remote specified' '