On Wed, Jul 06, 2022 at 03:01:54PM -0700, Junio C Hamano wrote: > > I am kind of surprised that the code still behaves differently > > between empty and non-empty cases. Given the earlier decision above > > to consistently use the remote's HEAD, I would have expected that > > setting HEAD to point at an non-existent ref would be done at one > > place, instead of being done for empty and non-empty clone > > separately. We'll find out why while reading the patch, I think. > > OK, that is because we have if/else on the number of refs mapped via > the refspec by wanted_peer_refs(), and setup_unborn_head is done > independently in each of these if/else arms. Right. I was hoping to avoid disturbing the logic too much, because I didn't want to introduce new bugs. But I took a stab at it and it doesn't seem too bad: diff --git a/builtin/clone.c b/builtin/clone.c index aa0729f62d..7b270d436a 100644 --- a/builtin/clone.c +++ b/builtin/clone.c @@ -1290,32 +1290,28 @@ int cmd_clone(int argc, const char **argv, const char *prefix) remote_head = find_ref_by_name(refs, "HEAD"); remote_head_points_at = guess_remote_head(remote_head, mapped_refs, 0); - - if (option_branch) { - our_head_points_at = - find_remote_branch(mapped_refs, option_branch); - - if (!our_head_points_at) - die(_("Remote branch %s not found in upstream %s"), - option_branch, remote_name); - } else { - our_head_points_at = remote_head_points_at; - if (!our_head_points_at) - setup_unborn_head(transport_ls_refs_options.unborn_head_target, - reflog_msg.buf); - } + } else { + remote_head_points_at = NULL; + remote_head = NULL; } - else { - if (option_branch) + + if (option_branch) { + /* this is a noop if mapped_refs is NULL, but should be OK */ + our_head_points_at = find_remote_branch(mapped_refs, option_branch); + if (!our_head_points_at) die(_("Remote branch %s not found in upstream %s"), - option_branch, remote_name); + option_branch, remote_name); + } else if (remote_head_points_at) { + our_head_points_at = remote_head_points_at; + } else { + if (!mapped_refs) { + warning(_("You appear to have cloned an empty repository.")); + /* could do this even in mapped_refs case, but we'd + * want to issue a warning ourselves then */ + option_no_checkout = 1; + } - warning(_("You appear to have cloned an empty repository.")); our_head_points_at = NULL; - remote_head_points_at = NULL; - remote_head = NULL; - option_no_checkout = 1; - setup_unborn_head(transport_ls_refs_options.unborn_head_target, reflog_msg.buf); } In fact, I think it may make things more clear. We avoid the duplicate die() condition for option_branch, and we now have only one call to setup_unborn_head(). So we could drop patch 2 and just keep it inline here. > The following rewrite with the same behaviour may be a bit easier to > follow. > [...] > - } else { > + } else if (remote_head_points_at) { > our_head_points_at = remote_head_points_at; > - if (!our_head_points_at) > - setup_unborn_head(transport_ls_refs_options.unborn_head_target, > - reflog_msg.buf); > + } else { > + our_head_points_at = NULL; > + setup_unborn_head(transport_ls_refs_options.unborn_head_target, > + reflog_msg.buf); > } Heh, I actually wrote it that way initially, but then realized it collapsed to the more terse version. I don't mind doing it either way, but maybe it's worth the rewrite I showed above. If so, do you prefer to go straight there in patch 3 (and drop patch 2, keeping the unborn setup inline), or do you prefer to see it on top? Normally I'd suggest the former, but I worry that doing it all in one patch means it's reorganizing the code _and_ changing the behavior all at once, which is harder to reason about. And I don't see an easy way to reorganize the code without changing the behavior. -Peff