{"thread":{"id":"62200","subject":"[PATCH] builtin/clone: teach git-clone(1) the --ref= argument","startedAt":"2024-09-27T08:54:58Z","lastAt":"2024-09-27T20:10:49Z","messageCount":5,"participants":["Toon Claes","Junio C Hamano","brian m. carlson","Kristoffer Haugsbakk"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"503605","messageId":"20240927085438.1010431-1-toon@iotcl.com","threadId":"62200","inReplyTo":null,"subject":"[PATCH] builtin/clone: teach git-clone(1) the --ref= argument","fromName":"Toon Claes","fromEmail":"toon@iotcl.com","sentAt":"2024-09-27T08:54:38Z","receivedAt":"2024-09-27T08:54:58Z","isPatch":true,"sender":{"key":"toon@iotcl.com","avatar":"https://avatars.githubusercontent.com/u/121621?v=4"},"body":"Add option `--ref` to git-clone(1). This enables the user to clone and\ncheckout the given named reference. It's pretty similar to --branch and\nwhile --branch takes a branch name or tag name, it doesn't take a fully\nqualified reference. This allows the user to clone a reference that\ndoesn't start with refs/heads or refs/tags. This can be useful when the\nserver uses custom references.\n\nAllow the user to clone a certain ref, similar to --branch.\n\nSigned-off-by: Toon Claes <toon@iotcl.com>\n---\n Documentation/git-clone.txt |  9 ++++-\n builtin/clone.c             | 67 ++++++++++++++++++++++++++++++++-----\n t/t5612-clone-refspec.sh    | 35 +++++++++++++++++++\n 3 files changed, 102 insertions(+), 9 deletions(-)\n\ndiff --git a/Documentation/git-clone.txt b/Documentation/git-clone.txt\nindex 8e925db7e9..7f75454aae 100644\n--- a/Documentation/git-clone.txt\n+++ b/Documentation/git-clone.txt\n@@ -215,6 +215,13 @@ objects from the source repository into a pack in the cloned repository.\n \t`--branch` can also take tags and detaches the HEAD at that commit\n \tin the resulting repository.\n\n+`--ref` _<name>_::\n+\tThis detaches HEAD and makes it point to the commit where the _<name>_\n+\treference is pointing to. In a non-bare repository, this is the ref that\n+\twill be checked out.\n+\tCan be used in conjunction with `--single-branch` and `--no-tags` to\n+\tclone only the given ref. Cannot be combined with `--branch`.\n+\n `-u` _<upload-pack>_::\n `--upload-pack` _<upload-pack>_::\n \tWhen given, and the repository to clone from is accessed\n@@ -259,7 +266,7 @@ corresponding `--mirror` and `--no-tags` options instead.\n\n `--`[`no-`]`single-branch`::\n \tClone only the history leading to the tip of a single branch,\n-\teither specified by the `--branch` option or the primary\n+\teither specified by the `--branch` or `--ref` option or the primary\n \tbranch remote's `HEAD` points at.\n \tFurther fetches into the resulting repository will only update the\n \tremote-tracking branch for the branch this option was used for the\ndiff --git a/builtin/clone.c b/builtin/clone.c\nindex e77339c847..384923703d 100644\n--- a/builtin/clone.c\n+++ b/builtin/clone.c\n@@ -69,6 +69,7 @@ static char *option_template, *option_depth, *option_since;\n static char *option_origin = NULL;\n static char *remote_name = NULL;\n static char *option_branch = NULL;\n+static char *option_ref = NULL;\n static struct string_list option_not = STRING_LIST_INIT_NODUP;\n static const char *real_git_dir;\n static const char *ref_format;\n@@ -141,6 +142,8 @@ static struct option builtin_clone_options[] = {\n \t\t   N_(\"use <name> instead of 'origin' to track upstream\")),\n \tOPT_STRING('b', \"branch\", &option_branch, N_(\"branch\"),\n \t\t   N_(\"checkout <branch> instead of the remote's HEAD\")),\n+\tOPT_STRING(0, \"ref\", &option_ref, N_(\"ref\"),\n+\t\t   N_(\"checkout <ref> (detached) instead of the remote's HEAD\")),\n \tOPT_STRING('u', \"upload-pack\", &option_upload_pack, N_(\"path\"),\n \t\t   N_(\"path to git-upload-pack on the remote\")),\n \tOPT_STRING(0, \"depth\", &option_depth, N_(\"depth\"),\n@@ -531,32 +534,64 @@ static struct ref *wanted_peer_refs(const struct ref *refs,\n \tif (option_single_branch) {\n \t\tstruct ref *remote_head = NULL;\n\n-\t\tif (!option_branch)\n+\t\tif (!option_branch && !option_ref)\n \t\t\tremote_head = guess_remote_head(head, refs, 0);\n \t\telse {\n \t\t\tfree_one_ref(head);\n \t\t\tlocal_refs = head = NULL;\n \t\t\ttail = &local_refs;\n-\t\t\tremote_head = copy_ref(find_remote_branch(refs, option_branch));\n+\t\t\tif (option_branch)\n+\t\t\t\tremote_head = copy_ref(find_remote_branch(refs, option_branch));\n+\t\t\telse\n+\t\t\t\tremote_head = copy_ref(find_ref_by_name(refs, option_ref));\n \t\t}\n\n \t\tif (!remote_head && option_branch)\n \t\t\twarning(_(\"Could not find remote branch %s to clone.\"),\n \t\t\t\toption_branch);\n+\t\telse if (!remote_head && option_ref)\n+\t\t\twarning(_(\"Could not find remote ref %s to clone.\"),\n+\t\t\t\toption_ref);\n \t\telse {\n \t\t\tint i;\n \t\t\tfor (i = 0; i < refspec->nr; i++)\n \t\t\t\tget_fetch_map(remote_head, &refspec->items[i],\n \t\t\t\t\t      &tail, 0);\n\n-\t\t\t/* if --branch=tag, pull the requested tag explicitly */\n-\t\t\tget_fetch_map(remote_head, &tag_refspec, &tail, 0);\n+\t\t\tif (option_ref) {\n+\t\t\t\tstruct strbuf spec = STRBUF_INIT;\n+\t\t\t\tstruct refspec_item ref_refspec;\n+\n+\t\t\t\tstrbuf_addf(&spec, \"%s:%s\", option_ref, option_ref);\n+\t\t\t\trefspec_item_init(&ref_refspec, spec.buf, 0);\n+\n+\t\t\t\tget_fetch_map(remote_head, &ref_refspec, &tail, 0);\n+\n+\t\t\t\trefspec_item_clear(&ref_refspec);\n+\t\t\t\tstrbuf_release(&spec);\n+\t\t\t} else {\n+\t\t\t\t/* if --branch=tag, pull the requested tag explicitly */\n+\t\t\t\tget_fetch_map(remote_head, &tag_refspec, &tail, 0);\n+\t\t\t}\n \t\t}\n \t\tfree_refs(remote_head);\n \t} else {\n \t\tint i;\n \t\tfor (i = 0; i < refspec->nr; i++)\n \t\t\tget_fetch_map(refs, &refspec->items[i], &tail, 0);\n+\n+\t\tif (option_ref) {\n+\t\t\tstruct strbuf spec = STRBUF_INIT;\n+\t\t\tstruct refspec_item ref_refspec;\n+\n+\t\t\tstrbuf_addf(&spec, \"%s:%s\", option_ref, option_ref);\n+\t\t\trefspec_item_init(&ref_refspec, spec.buf, 0);\n+\n+\t\t\tget_fetch_map(refs, &ref_refspec, &tail, 0);\n+\n+\t\t\trefspec_item_clear(&ref_refspec);\n+\t\t\tstrbuf_release(&spec);\n+\t\t}\n \t}\n\n \tif (!option_mirror && !option_single_branch && !option_no_tags)\n@@ -684,10 +719,15 @@ static void update_head(const struct ref *our, const struct ref *remote,\n \t} else if (our) {\n \t\tstruct commit *c = lookup_commit_reference(the_repository,\n \t\t\t\t\t\t\t   &our->old_oid);\n-\t\t/* --branch specifies a non-branch (i.e. tags), detach HEAD */\n-\t\trefs_update_ref(get_main_ref_store(the_repository), msg,\n-\t\t\t\t\"HEAD\", &c->object.oid, NULL, REF_NO_DEREF,\n-\t\t\t\tUPDATE_REFS_DIE_ON_ERR);\n+\t\tif (c)\n+\t\t\t/* --branch specifies a non-branch (i.e. tags), detach HEAD */\n+\t\t\trefs_update_ref(get_main_ref_store(the_repository), msg,\n+\t\t\t\t\t\"HEAD\", &c->object.oid, NULL, REF_NO_DEREF,\n+\t\t\t\t\tUPDATE_REFS_DIE_ON_ERR);\n+\t\telse\n+\t\t\trefs_update_ref(get_main_ref_store(the_repository), msg,\n+\t\t\t\t\t\"HEAD\", &our->old_oid, NULL, REF_NO_DEREF,\n+\t\t\t\t\tUPDATE_REFS_DIE_ON_ERR);\n \t} else if (remote) {\n \t\t/*\n \t\t * We know remote HEAD points to a non-branch, or\n@@ -898,6 +938,9 @@ static void write_refspec_config(const char *src_ref_prefix,\n \t\t\t\telse\n \t\t\t\t\tstrbuf_addf(&value, \"+%s:%s%s\", our_head_points_at->name,\n \t\t\t\t\t\tbranch_top->buf, option_branch);\n+\t\t\t} else if (option_ref) {\n+\t\t\t\tstrbuf_addf(&value, \"+%s:%s\", our_head_points_at->name,\n+\t\t\t\t\tour_head_points_at->name);\n \t\t\t} else if (remote_head_points_at) {\n \t\t\t\tconst char *head = remote_head_points_at->name;\n \t\t\t\tif (!skip_prefix(head, \"refs/heads/\", &head))\n@@ -1383,6 +1426,9 @@ int cmd_clone(int argc,\n \tif (option_branch)\n \t\texpand_ref_prefix(&transport_ls_refs_options.ref_prefixes,\n \t\t\t\t  option_branch);\n+\tif (option_ref)\n+\t\tstrvec_push(&transport_ls_refs_options.ref_prefixes,\n+\t\t\t    option_ref);\n \tif (!option_no_tags)\n \t\tstrvec_push(&transport_ls_refs_options.ref_prefixes,\n \t\t\t    \"refs/tags/\");\n@@ -1468,6 +1514,11 @@ int cmd_clone(int argc,\n \t\tif (!our_head_points_at)\n \t\t\tdie(_(\"Remote branch %s not found in upstream %s\"),\n \t\t\t    option_branch, remote_name);\n+\t} else if (option_ref) {\n+\t\tour_head_points_at = find_ref_by_name(mapped_refs, option_ref);\n+\t\tif (!our_head_points_at)\n+\t\t\tdie(_(\"Remote ref %s not found in upstream %s\"),\n+\t\t\t    option_ref, remote_name);\n \t} else if (remote_head_points_at) {\n \t\tour_head_points_at = remote_head_points_at;\n \t} else if (remote_head) {\ndiff --git a/t/t5612-clone-refspec.sh b/t/t5612-clone-refspec.sh\nindex 72762de977..51452bdd6f 100755\n--- a/t/t5612-clone-refspec.sh\n+++ b/t/t5612-clone-refspec.sh\n@@ -17,6 +17,7 @@ test_expect_success 'setup' '\n \tgit tag two &&\n \techo three >file &&\n \tgit commit -a -m three &&\n+\tgit update-ref refs/some/three HEAD &&\n \tgit checkout -b side &&\n \techo four >file &&\n \tgit commit -a -m four &&\n@@ -236,4 +237,38 @@ test_expect_success '--single-branch with detached' '\n \ttest_must_be_empty actual\n '\n\n+test_expect_success 'with --ref' '\n+\tgit clone --ref=refs/some/three . dir_ref &&\n+        git -C dir_ref for-each-ref refs > refs &&\n+        sed -e \"/HEAD$/d\" \\\n+\t    -e \"s|/remotes/origin/|/heads/|\" refs >actual &&\n+\tgit for-each-ref refs >expect &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'with --ref and --no-tags' '\n+\tgit clone --ref=refs/some/three --no-tags . dir_ref_notags &&\n+        git -C dir_ref_notags for-each-ref refs > refs &&\n+        sed -e \"/HEAD$/d\" \\\n+\t    -e \"s|/remotes/origin/|/heads/|\" refs >actual &&\n+\tgit for-each-ref refs/heads >expect &&\n+\tgit for-each-ref refs/some >>expect &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success '--single-branch with --ref' '\n+\tgit clone --single-branch --ref=refs/some/three . dir_single_ref &&\n+        git -C dir_single_ref for-each-ref refs > actual &&\n+\tgit for-each-ref refs/some >expect &&\n+\tgit for-each-ref refs/tags >>expect &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success '--single-branch with --ref and --no-tags' '\n+\tgit clone --single-branch --ref=refs/some/three --no-tags . dir_single_ref_notags &&\n+        git -C dir_single_ref_notags for-each-ref refs > actual &&\n+\tgit for-each-ref refs/some >expect &&\n+\ttest_cmp expect actual\n+'\n+\n test_done\n--\n2.46.0\n"},{"id":"503619","messageId":"xmqqikuggaz5.fsf@gitster.g","threadId":"62200","inReplyTo":"20240927085438.1010431-1-toon@iotcl.com","subject":"Re: [PATCH] builtin/clone: teach git-clone(1) the --ref= argument","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2024-09-27T19:20:14Z","receivedAt":"2024-09-27T19:20:17Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Toon Claes <toon@iotcl.com> writes:\n\n> Add option `--ref` to git-clone(1). This enables the user to clone and\n> checkout the given named reference.\n\nDefine \"checkout the reference\".  A ref that is outside of\n\"refs/heads/\" MUST NOT be pointed by HEAD, so giving a tag with this\noption should result in an initial working tree on a detached HEAD.\n\nIf it is what the new option does (which is similar to the way how\nwe handle a tag when it was given with --branch), we should be more\nexplicit that the ref is *not* checked out, but a deteached HEAD is\ncreated to point at the commit referenced.  I think the documentation\npart of this patch does a much better job at it.\n\n> It's pretty similar to --branch and\n> while --branch takes a branch name or tag name, it doesn't take a fully\n> qualified reference. This allows the user to clone a reference that\n> doesn't start with refs/heads or refs/tags. This can be useful when the\n> server uses custom references.\n\n\"when the server uses custom references\" is a rather weak\nexplanation.\n\nThe answer to \"Doctor, it hurts when I turn my elbow in this\nunnatural direction\" is usually \"Well, do not do it then\".  The\nanswer to \"Doctor, I cannot use the --branch option because I use\nnon branches to keep track of some histories\" should be the same.\nWhy do you want to turn your elbow in an unnatural angle in the\nfirst place?\n\nStepping one level higher.\n\nThe usual way to compose a log message of this project is to\n\n - Give an observation on how the current system work in the present\n   tense (so no need to say \"Currently X is Y\", just \"X is Y\"), and\n   discuss what you perceive as a problem in it.\n\n - Propose a solution (optional---often, problem description\n   trivially leads to an obvious solution in reader's minds).\n\n - Give commands to the codebase to \"become like so\".\n\nin this order.  The first paragraph that describes what the commit\nwants to do is better moved down to the last.  Justify why you want\nto turn your elbow in a way other people do not usually do (or why\nyou want unusual hierarchies on the server side), explain that it\nhurts (or say --branches is limited to branches and tags), and then\nfinally give a solution.\n\n> +`--ref` _<name>_::\n> +\tThis detaches HEAD and makes it point to the commit where the _<name>_\n> +\treference is pointing to. In a non-bare repository, this is the ref that\n> +\twill be checked out.\n> +\tCan be used in conjunction with `--single-branch` and `--no-tags` to\n> +\tclone only the given ref. Cannot be combined with `--branch`.\n\nOK, so it is an error to give both --branch and --ref at the same\ntime?  \n\nIf you say \"git clone --branch foo --branch bar ...\", wouldn't the\nusual last-one-wins rule apply?  Shouldn't this work the same way,\nmeaning \"git clone --branch foo --ref refs/some/one ...\"  would\nbehave just like \"git clone --ref refs/some/one ...\", without\ntreating the \"foo\" branch any specially?\n\nWhat if --ref is not pointing at a commit-ish?  If I in any\nrepository with non-empty history checked out do this:\n\n\t$ git update-ref refs/some/one HEAD:\n\ncan you clone from this repository with\n\n\t$ git clone --ref=refs/some/one $URL\n\nand what happens at the end?\n\n> diff --git a/builtin/clone.c b/builtin/clone.c\n> index e77339c847..384923703d 100644\n> --- a/builtin/clone.c\n> +++ b/builtin/clone.c\n> @@ -69,6 +69,7 @@ static char *option_template, *option_depth, *option_since;\n>  static char *option_origin = NULL;\n>  static char *remote_name = NULL;\n>  static char *option_branch = NULL;\n> +static char *option_ref = NULL;\n\nIf you want to make \"With --branch and --ref, the last one wins\",\nthis would not be sufficient.\n\n> @@ -141,6 +142,8 @@ static struct option builtin_clone_options[] = {\n>  \t\t   N_(\"use <name> instead of 'origin' to track upstream\")),\n>  \tOPT_STRING('b', \"branch\", &option_branch, N_(\"branch\"),\n>  \t\t   N_(\"checkout <branch> instead of the remote's HEAD\")),\n> +\tOPT_STRING(0, \"ref\", &option_ref, N_(\"ref\"),\n> +\t\t   N_(\"checkout <ref> (detached) instead of the remote's HEAD\")),\n\nDitto.\n\n> @@ -531,32 +534,64 @@ static struct ref *wanted_peer_refs(const struct ref *refs,\n>  \tif (option_single_branch) {\n>  \t\tstruct ref *remote_head = NULL;\n>\n> -\t\tif (!option_branch)\n> +\t\tif (!option_branch && !option_ref)\n>  \t\t\tremote_head = guess_remote_head(head, refs, 0);\n>  \t\telse {\n>  \t\t\tfree_one_ref(head);\n>  \t\t\tlocal_refs = head = NULL;\n>  \t\t\ttail = &local_refs;\n> -\t\t\tremote_head = copy_ref(find_remote_branch(refs, option_branch));\n> +\t\t\tif (option_branch)\n> +\t\t\t\tremote_head = copy_ref(find_remote_branch(refs, option_branch));\n> +\t\t\telse\n> +\t\t\t\tremote_head = copy_ref(find_ref_by_name(refs, option_ref));\n\nThis makes --ref ignored when --branch is given.  Intended?\n\n> diff --git a/t/t5612-clone-refspec.sh b/t/t5612-clone-refspec.sh\n> index 72762de977..51452bdd6f 100755\n> --- a/t/t5612-clone-refspec.sh\n> +++ b/t/t5612-clone-refspec.sh\n> @@ -17,6 +17,7 @@ test_expect_success 'setup' '\n>  \tgit tag two &&\n>  \techo three >file &&\n>  \tgit commit -a -m three &&\n> +\tgit update-ref refs/some/three HEAD &&\n>  \tgit checkout -b side &&\n>  \techo four >file &&\n>  \tgit commit -a -m four &&\n> @@ -236,4 +237,38 @@ test_expect_success '--single-branch with detached' '\n>  \ttest_must_be_empty actual\n>  '\n>\n> +test_expect_success 'with --ref' '\n> +\tgit clone --ref=refs/some/three . dir_ref &&\n> +        git -C dir_ref for-each-ref refs > refs &&\n\nLose SP between \">\" and \"refs\".\n\n> +        sed -e \"/HEAD$/d\" \\\n> +\t    -e \"s|/remotes/origin/|/heads/|\" refs >actual &&\n> +\tgit for-each-ref refs >expect &&\n> +\ttest_cmp expect actual\n> +'\n> +\n> +test_expect_success 'with --ref and --no-tags' '\n> +\tgit clone --ref=refs/some/three --no-tags . dir_ref_notags &&\n> +        git -C dir_ref_notags for-each-ref refs > refs &&\n> +        sed -e \"/HEAD$/d\" \\\n> +\t    -e \"s|/remotes/origin/|/heads/|\" refs >actual &&\n> +\tgit for-each-ref refs/heads >expect &&\n> +\tgit for-each-ref refs/some >>expect &&\n> +\ttest_cmp expect actual\n> +'\n> +\n> +test_expect_success '--single-branch with --ref' '\n> +\tgit clone --single-branch --ref=refs/some/three . dir_single_ref &&\n> +        git -C dir_single_ref for-each-ref refs > actual &&\n> +\tgit for-each-ref refs/some >expect &&\n\nI would expect refs/some/three and nothing else comes from refs/some\nhierarchy, as the clone was done with \"--ref=refs/some/three\".  Is\nthere a particular reason why this for-each-ref is looser and take\nanything under \"refs/some/\"?\n\n> +\tgit for-each-ref refs/tags >>expect &&\n\nThis is because the above is not giving --no-tags, I guess.\n\n> +\ttest_cmp expect actual\n> +'\n> +\n> +test_expect_success '--single-branch with --ref and --no-tags' '\n> +\tgit clone --single-branch --ref=refs/some/three --no-tags . dir_single_ref_notags &&\n> +        git -C dir_single_ref_notags for-each-ref refs > actual &&\n> +\tgit for-each-ref refs/some >expect &&\n\nSame comment on the looseness of this one.  Lack of refs/tags is\nexpected and it is a good thing to test that.\n\n> +\ttest_cmp expect actual\n> +'\n\nThere may be others, but three cases that are not tested but should\nbe I spotted offhand are:\n\n * \"clone --ref=refs/some/three --no-ref\" acts as if we gave no\n   \"--refs\" at all.\n\n * interaction when \"--ref=refs/some/three\" and \"--branch=main\" are\n   given at the same time.\n\n * the command gracefully fail when the given ref points at a tree\n   or a blob.\n\nThanks.\n"},{"id":"503621","messageId":"ZvcHpd26te8TPacz@tapette.crustytoothpaste.net","threadId":"62200","inReplyTo":"xmqqikuggaz5.fsf@gitster.g","subject":"Re: [PATCH] builtin/clone: teach git-clone(1) the --ref= argument","fromName":"brian m. carlson","fromEmail":"sandals@crustytoothpaste.net","sentAt":"2024-09-27T19:29:41Z","receivedAt":"2024-09-27T19:29:50Z","isPatch":true,"sender":{"key":"sandals@crustytoothpaste.net","avatar":"https://avatars.githubusercontent.com/u/497054?v=4"},"body":"On 2024-09-27 at 19:20:14, Junio C Hamano wrote:\n> Toon Claes <toon@iotcl.com> writes:\n> \n> > It's pretty similar to --branch and\n> > while --branch takes a branch name or tag name, it doesn't take a fully\n> > qualified reference. This allows the user to clone a reference that\n> > doesn't start with refs/heads or refs/tags. This can be useful when the\n> > server uses custom references.\n> \n> \"when the server uses custom references\" is a rather weak\n> explanation.\n> \n> The answer to \"Doctor, it hurts when I turn my elbow in this\n> unnatural direction\" is usually \"Well, do not do it then\".  The\n> answer to \"Doctor, I cannot use the --branch option because I use\n> non branches to keep track of some histories\" should be the same.\n> Why do you want to turn your elbow in an unnatural angle in the\n> first place?\n\nI can't speak for what Toon intended here, but GitHub uses some\nreferences under `refs/pull` that are used for tracking pull requests.\nWe even have some in the Git repository on GitHub:\n\n  % git ls-remote upstream refs/pull/* | head -n 5\n  f0d0fd3a5985d5e588da1e1d11c85fba0ae132f8        refs/pull/10/head\n  c8198f6c2c9fc529b25988dfaf5865bae5320cb5        refs/pull/10/merge\n  d604669e32e847c2ba5010c89895dd707ba45f55        refs/pull/100/head\n  55ab0c9399879683b4cc6e1baea5dc41484ca52f        refs/pull/100/merge\n  08d39e0bb5b9dbd16e9e4c2250e75848718c453b        refs/pull/1000/head\n\nThese are not kept under `refs/heads` because `refs/heads` belongs to\nthe user, but it is generally useful to check them out in case of very\nlarge changes or changes with complex binary files which don't render\nwell in the web interface (among other reasons) that might need to be\ninspected for code review.  So I think this is a generally useful\nfeature, although I agree that perhaps the commit message might explain\nthe benefits in a more concrete way for those who don't already\nunderstand the utility of the feature (such as our illustrious\nmaintainer).\n-- \nbrian m. carlson (they/them or he/him)\nToronto, Ontario, CA\n"},{"id":"503622","messageId":"xmqq8qvcg8vr.fsf@gitster.g","threadId":"62200","inReplyTo":"ZvcHpd26te8TPacz@tapette.crustytoothpaste.net","subject":"Re: [PATCH] builtin/clone: teach git-clone(1) the --ref= argument","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2024-09-27T20:05:28Z","receivedAt":"2024-09-27T20:05:31Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"brian m. carlson\" <sandals@crustytoothpaste.net> writes:\n\n> I can't speak for what Toon intended here, but GitHub uses some\n> references under `refs/pull` that are used for tracking pull requests.\n\nAh, that is an excellent example that can be used to strengthen the\nmotivation part of this commit.\n\n    A user may want to clone only \"refs/pull/$n/head\" to take a\n    peek, but unlike refs/heads/ and refs/tags/, for which the\n    \"--branch\" option can be used, there is no good simple way to do\n    so with \"git clone\".\n\nwould make a good intro paragraph for a proposed log message.\n\n> So I think this is a generally useful feature, although I agree\n> that perhaps the commit message might explain the benefits in a\n> more concrete way for those who don't already understand the\n> utility of the feature (such as our illustrious maintainer).\n\nYes, not seeing refs/pull/ without being told was slight lack of\nimagination on my part, but it is a good idea to spell out the\nmotivation in concrete terms when we can.\n\nThanks.\n"},{"id":"503623","messageId":"555169c4-beea-40cf-a542-f8a75bcb44e8@app.fastmail.com","threadId":"62200","inReplyTo":"ZvcHpd26te8TPacz@tapette.crustytoothpaste.net","subject":"Re: [PATCH] builtin/clone: teach git-clone(1) the --ref= argument","fromName":"Kristoffer Haugsbakk","fromEmail":"kristofferhaugsbakk@fastmail.com","sentAt":"2024-09-27T20:10:28Z","receivedAt":"2024-09-27T20:10:49Z","isPatch":true,"sender":{"key":"kristofferhaugsbakk@fastmail.com","avatar":null},"body":"On Fri, Sep 27, 2024, at 21:29, brian m. carlson wrote:\n> On 2024-09-27 at 19:20:14, Junio C Hamano wrote:\n>> Toon Claes <toon@iotcl.com> writes:\n>>\n>> > It's pretty similar to --branch and\n>> > while --branch takes a branch name or tag name, it doesn't take a fully\n>> > qualified reference. This allows the user to clone a reference that\n>> > doesn't start with refs/heads or refs/tags. This can be useful when the\n>> > server uses custom references.\n>>\n>> \"when the server uses custom references\" is a rather weak\n>> explanation.\n>>\n>> The answer to \"Doctor, it hurts when I turn my elbow in this\n>> unnatural direction\" is usually \"Well, do not do it then\".  The\n>> answer to \"Doctor, I cannot use the --branch option because I use\n>> non branches to keep track of some histories\" should be the same.\n>> Why do you want to turn your elbow in an unnatural angle in the\n>> first place?\n>\n> I can't speak for what Toon intended here, but GitHub uses some\n> references under `refs/pull` that are used for tracking pull requests.\n> We even have some in the Git repository on GitHub:\n>\n>   % git ls-remote upstream refs/pull/* | head -n 5\n>   f0d0fd3a5985d5e588da1e1d11c85fba0ae132f8        refs/pull/10/head\n>   c8198f6c2c9fc529b25988dfaf5865bae5320cb5        refs/pull/10/merge\n>   d604669e32e847c2ba5010c89895dd707ba45f55        refs/pull/100/head\n>   55ab0c9399879683b4cc6e1baea5dc41484ca52f        refs/pull/100/merge\n>   08d39e0bb5b9dbd16e9e4c2250e75848718c453b        refs/pull/1000/head\n>\n> These are not kept under `refs/heads` because `refs/heads` belongs to\n> the user, but it is generally useful to check them out in case of very\n> large changes or changes with complex binary files which don't render\n> well in the web interface (among other reasons) that might need to be\n> inspected for code review.  So I think this is a generally useful\n> feature, although I agree that perhaps the commit message might explain\n> the benefits in a more concrete way for those who don't already\n> understand the utility of the feature (such as our illustrious\n> maintainer).\n\nI’ve seen a few times that a change is proposed with a commit message\nthat says that it allows you to do X. And plenty of motivation is\nprovided in a narrow (technical) sense of how X makes things nicer.\nBut without explaining why you’d want to do X. Then someone needs to\nask the the submitter why. Then they say that they need it at\n<organization> to do something specific.\n\nIt’s certainly nice to have all that information in the commit message.\n\n-- \nKristoffer Haugsbakk\n\n"}]}