{"thread":{"id":"55104","subject":"[PATCH] builtin/clone.c: add --no-shallow option","startedAt":"2021-02-04T03:32:36Z","lastAt":"2021-04-01T10:47:42Z","messageCount":48,"participants":["Li Linchao via GitGitGadget","Junio C Hamano","lilinchao@oschina.cn","Johannes Schindelin","lilinchao via GitGitGadget","Derrick Stolee","Jonathan Tan"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"416126","messageId":"pull.865.git.1612409491842.gitgitgadget@gmail.com","threadId":"55104","inReplyTo":null,"subject":"[PATCH] builtin/clone.c: add --no-shallow option","fromName":"Li Linchao via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-02-04T03:31:31Z","receivedAt":"2021-02-04T03:32:36Z","isPatch":true,"sender":{"key":"name:Li Linchao","avatar":null},"body":"From: lilinchao <lilinchao@oschina.cn>\n\nThis patch add a new option that reject to clone a shallow repository.\nClients don't know it's a shallow repository until they download it\nlocally, in some scenariors, clients just don't want to clone this kind\nof repository, and want to exit the process immediately without creating\nany unnecessary files.\n\nSigned-off-by: lilinchao <lilinchao@oschina.cn>\n---\n    builtin/clone.c: add --no-shallow option\n    \n    This patch add a new option that reject to clone a shallow repository.\n    Clients don't know it's a shallow repository until they download it\n    locally, in some scenariors, clients just don't want to clone this kind\n    of repository, and want to exit the process immediately without creating\n    any unnecessary files.\n    \n    Signed-off-by: lilinchao lilinchao@oschina.cn\n\nPublished-As: https://github.com/gitgitgadget/git/releases/tag/pr-865%2FCactusinhand%2Fgit-clone-options-v1\nFetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-865/Cactusinhand/git-clone-options-v1\nPull-Request: https://github.com/gitgitgadget/git/pull/865\n\n Documentation/config/clone.txt |  3 +++\n Documentation/git-clone.txt    |  8 +++++++-\n builtin/clone.c                | 13 +++++++++++++\n t/t5606-clone-options.sh       |  7 +++++++\n t/t5611-clone-config.sh        |  7 +++++++\n 5 files changed, 37 insertions(+), 1 deletion(-)\n mode change 100644 => 100755 builtin/clone.c\n\ndiff --git a/Documentation/config/clone.txt b/Documentation/config/clone.txt\nindex 47de36a5fedf..29b779b8a825 100644\n--- a/Documentation/config/clone.txt\n+++ b/Documentation/config/clone.txt\n@@ -2,3 +2,6 @@ clone.defaultRemoteName::\n \tThe name of the remote to create when cloning a repository.  Defaults to\n \t`origin`, and can be overridden by passing the `--origin` command-line\n \toption to linkgit:git-clone[1].\n+\n+clone.rejectshallow::\n+\tReject to clone a repository if it is a shallow one.\ndiff --git a/Documentation/git-clone.txt b/Documentation/git-clone.txt\nindex 876aedcd472a..94bc76702757 100644\n--- a/Documentation/git-clone.txt\n+++ b/Documentation/git-clone.txt\n@@ -15,7 +15,7 @@ SYNOPSIS\n \t  [--dissociate] [--separate-git-dir <git dir>]\n \t  [--depth <depth>] [--[no-]single-branch] [--no-tags]\n \t  [--recurse-submodules[=<pathspec>]] [--[no-]shallow-submodules]\n-\t  [--[no-]remote-submodules] [--jobs <n>] [--sparse]\n+\t  [--[no-]remote-submodules] [--jobs <n>] [--sparse] [--no-shallow]\n \t  [--filter=<filter>] [--] <repository>\n \t  [<directory>]\n \n@@ -145,6 +145,12 @@ objects from the source repository into a pack in the cloned repository.\n --no-checkout::\n \tNo checkout of HEAD is performed after the clone is complete.\n \n+--no-shallow::\n+\tDon't clone a shallow source repository. In some scenariors, clients\n+\twant the cloned repository information to be complete. Otherwise,\n+\tthe cloning process should end immediately without creating any files,\n+\twhich can save some disk space.\n+\n --bare::\n \tMake a 'bare' Git repository.  That is, instead of\n \tcreating `<directory>` and placing the administrative\ndiff --git a/builtin/clone.c b/builtin/clone.c\nold mode 100644\nnew mode 100755\nindex e335734b4cfd..b07d867e6641\n--- a/builtin/clone.c\n+++ b/builtin/clone.c\n@@ -50,6 +50,7 @@ static int option_no_checkout, option_bare, option_mirror, option_single_branch\n static int option_local = -1, option_no_hardlinks, option_shared;\n static int option_no_tags;\n static int option_shallow_submodules;\n+static int option_no_shallow;\n static int deepen;\n static char *option_template, *option_depth, *option_since;\n static char *option_origin = NULL;\n@@ -90,6 +91,7 @@ static struct option builtin_clone_options[] = {\n \tOPT__VERBOSITY(&option_verbosity),\n \tOPT_BOOL(0, \"progress\", &option_progress,\n \t\t N_(\"force progress reporting\")),\n+\tOPT_BOOL(0, \"no-shallow\", &option_no_shallow, N_(\"don't clone shallow repository\")),\n \tOPT_BOOL('n', \"no-checkout\", &option_no_checkout,\n \t\t N_(\"don't create a checkout\")),\n \tOPT_BOOL(0, \"bare\", &option_bare, N_(\"create a bare repository\")),\n@@ -858,6 +860,9 @@ static int git_clone_config(const char *k, const char *v, void *cb)\n \t\tfree(remote_name);\n \t\tremote_name = xstrdup(v);\n \t}\n+\tif (!strcmp(k, \"clone.rejectshallow\")) {\n+\t\toption_no_shallow = 1;\n+\t}\n \treturn git_default_config(k, v, cb);\n }\n \n@@ -963,6 +968,7 @@ static int path_exists(const char *path)\n int cmd_clone(int argc, const char **argv, const char *prefix)\n {\n \tint is_bundle = 0, is_local;\n+\tint is_shallow = 0;\n \tconst char *repo_name, *repo, *work_tree, *git_dir;\n \tchar *path, *dir, *display_repo = NULL;\n \tint dest_exists, real_dest_exists = 0;\n@@ -1215,6 +1221,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n \t\tif (filter_options.choice)\n \t\t\twarning(_(\"--filter is ignored in local clones; use file:// instead.\"));\n \t\tif (!access(mkpath(\"%s/shallow\", path), F_OK)) {\n+\t\t\tis_shallow = 1;\n \t\t\tif (option_local > 0)\n \t\t\t\twarning(_(\"source repository is shallow, ignoring --local\"));\n \t\t\tis_local = 0;\n@@ -1222,6 +1229,12 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n \t}\n \tif (option_local > 0 && !is_local)\n \t\twarning(_(\"--local is ignored\"));\n+\n+\tif (is_shallow) {\n+\t\tif (option_no_shallow)\n+\t\t\tdie(_(\"source repository is shallow, reject to clone.\"));\n+\t}\n+\n \ttransport->cloning = 1;\n \n \ttransport_set_option(transport, TRANS_OPT_KEEP, \"yes\");\ndiff --git a/t/t5606-clone-options.sh b/t/t5606-clone-options.sh\nindex 7f082fb23b6a..9d310dbb158a 100755\n--- a/t/t5606-clone-options.sh\n+++ b/t/t5606-clone-options.sh\n@@ -42,6 +42,13 @@ test_expect_success 'disallows --bare with --separate-git-dir' '\n \n '\n \n+test_expect_success 'reject clone shallow repository' '\n+\tgit clone --depth=1 --no-local parent shallow-repo &&\n+\ttest_must_fail git clone --no-shallow shallow-repo out 2>err &&\n+\ttest_i18ngrep -e \"source repository is shallow, reject to clone.\" err\n+\n+'\n+\n test_expect_success 'uses \"origin\" for default remote name' '\n \n \tgit clone parent clone-default-origin &&\ndiff --git a/t/t5611-clone-config.sh b/t/t5611-clone-config.sh\nindex 8e0fd398236b..3aab86ad4def 100755\n--- a/t/t5611-clone-config.sh\n+++ b/t/t5611-clone-config.sh\n@@ -92,6 +92,13 @@ test_expect_success 'clone -c remote.<remote>.fetch=<refspec> --origin=<name>' '\n \ttest_cmp expect actual\n '\n \n+test_expect_success 'clone -c clone.rejectshallow' '\n+\trm -rf child &&\n+\tgit clone --depth=1 --no-local . child &&\n+\ttest_must_fail git clone -c clone.rejectshallow child out 2>err &&\n+\ttest_i18ngrep -e \"source repository is shallow, reject to clone.\" err\n+'\n+\n test_expect_success MINGW 'clone -c core.hideDotFiles' '\n \ttest_commit attributes .gitattributes \"\" &&\n \trm -rf child &&\n\nbase-commit: 72c4083ddf91b489b7b7b812df67ee8842177d98\n-- \ngitgitgadget\n"},{"id":"416136","messageId":"xmqq35yc9yan.fsf@gitster.c.googlers.com","threadId":"55104","inReplyTo":"pull.865.git.1612409491842.gitgitgadget@gmail.com","subject":"Re: [PATCH] builtin/clone.c: add --no-shallow option","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-02-04T05:50:24Z","receivedAt":"2021-02-04T05:51:11Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"Li Linchao via GitGitGadget\" <gitgitgadget@gmail.com> writes:\n\n> From: lilinchao <lilinchao@oschina.cn>\n>\n> This patch add a new option that reject to clone a shallow repository.\n\nA canonical form of our log message starts by explaining the need,\nand then presents the solution at the end.\n\n> Clients don't know it's a shallow repository until they download it\n> locally, in some scenariors, clients just don't want to clone this kind\n\n\"scenarios\".  \"in some scenarios\" would have to be clarified a bit\nmore to justify why it is a good idea to have such a feature.\n\n> of repository, and want to exit the process immediately without creating\n> any unnecessary files.\n\n\"clients don't know it's a shallow repository until they download\"\nleading to \"so let's reject immediately upon finding out that they\nare shallow\" does make sense as a flow of thought, though.\n\n> +--no-shallow::\n> +\tDon't clone a shallow source repository. In some scenariors, clients\n\n\"scenarios\" (no 'r').\n\n> diff --git a/builtin/clone.c b/builtin/clone.c\n> old mode 100644\n> new mode 100755\n\nUnwarranted \"chmod +x\"; accidents do happen, but please be careful\nbefore making what you did public ;-)\n\n> @@ -90,6 +91,7 @@ static struct option builtin_clone_options[] = {\n>  \tOPT__VERBOSITY(&option_verbosity),\n>  \tOPT_BOOL(0, \"progress\", &option_progress,\n>  \t\t N_(\"force progress reporting\")),\n> +\tOPT_BOOL(0, \"no-shallow\", &option_no_shallow, N_(\"don't clone shallow repository\")),\n>  \tOPT_BOOL('n', \"no-checkout\", &option_no_checkout,\n>  \t\t N_(\"don't create a checkout\")),\n>  \tOPT_BOOL(0, \"bare\", &option_bare, N_(\"create a bare repository\")),\n\nIt is a bad idea to give a name that begins with \"no-\" to an option\nwhose default can be tweaked by a configuration variable [*].  If\nthe configuration is named \"rejectshallow\", perhaps it is better to\ncall it \"--reject-shallow\" instead.\n\nThis is because configured default must be overridable from the\ncommand line.  I.e. even if you have in your ~/.gitconfig this:\n\n    [clone]\n        rejectshallow = true\n\nyou should be able to say \"allow it only this time\", with\n\n    $ git clone --no-reject-shallow http://github.com/git/git/ git\n\nand you do not want to have to say \"--no-no-shallow\", which sounds\njust silly.\n\n\tSide note. it is a bad idea in general, even if the option\n\tdoes not have corresponding configuration variable.  The\n\texisting \"no-checkout\" is a historical accident that\n\thappened long time ago and cannot be removed due to\n\tcompatibility.  Let's not introduce a new option that\n\tfollows such a bad pattern.\n\n> @@ -963,6 +968,7 @@ static int path_exists(const char *path)\n>  int cmd_clone(int argc, const char **argv, const char *prefix)\n>  {\n>  \tint is_bundle = 0, is_local;\n> +\tint is_shallow = 0;\n>  \tconst char *repo_name, *repo, *work_tree, *git_dir;\n>  \tchar *path, *dir, *display_repo = NULL;\n>  \tint dest_exists, real_dest_exists = 0;\n> @@ -1215,6 +1221,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n>  \t\tif (filter_options.choice)\n>  \t\t\twarning(_(\"--filter is ignored in local clones; use file:// instead.\"));\n>  \t\tif (!access(mkpath(\"%s/shallow\", path), F_OK)) {\n> +\t\t\tis_shallow = 1;\n>  \t\t\tif (option_local > 0)\n>  \t\t\t\twarning(_(\"source repository is shallow, ignoring --local\"));\n>  \t\t\tis_local = 0;\n\nThis change is to the local clone codepath.  Cloning over the wire\nwould not go through this part.  And throughout the patch, this is\nthe only place that sets is_shallow to 1.\n\nAlso let's note that this is after we called parse_options(), so the\nvalue of option_no_shallow is known at this point.\n\nSo, this patch does not even *need* to introduce a new \"is_shallow\"\nvariable at all.  It only needs to add\n\n                        if (option_no_shallow)\n                                die(...);\n\ninstead of adding \"is_shallow = 1\" to the above hunk.\n\nI somehow think that this is only half a feature---wouldn't it be\nmore useful if we also rejected a non-local clone from a shallow\nrepository?\n\nAnd for that ...\n\n\n> diff --git a/t/t5606-clone-options.sh b/t/t5606-clone-options.sh\n> index 7f082fb23b6a..9d310dbb158a 100755\n> --- a/t/t5606-clone-options.sh\n> +++ b/t/t5606-clone-options.sh\n> @@ -42,6 +42,13 @@ test_expect_success 'disallows --bare with --separate-git-dir' '\n>  \n>  '\n>  \n> +test_expect_success 'reject clone shallow repository' '\n> +\tgit clone --depth=1 --no-local parent shallow-repo &&\n> +\ttest_must_fail git clone --no-shallow shallow-repo out 2>err &&\n> +\ttest_i18ngrep -e \"source repository is shallow, reject to clone.\" err\n> +\n> +'\n> +\n\n... in addition to the test for a local clone above, you'd also want\nto test a non-local clone, perhaps like so:\n\ntest_expect_success 'reject clone shallow repository' '\n\trm -fr shallow-repo &&\n\tgit clone --depth=1 --no-local parent shallow-repo &&\n\ttest_must_fail git clone --no-shallow --no-local shallow-repo out 2>err &&\n\ttest_i18ngrep -e \"source repository is shallow, reject to clone.\" err\n\n'\n\nDitto for the other test script.\n\nAlso, you would want to make sure that the command line overrides\nthe configured default.  I.e.\n\n\tgit -c clone.rejectshallow=false clone --reject-shallow\n\nshould refuse to clone from a shallow one, while there should be a\nway to countermand a configured \"I always refuse to clone from a\nshallow repository\" with \"but let's allow it only this time\", i.e.\n\n\tgit -c clone.rejectshallow=true clone --no-reject-shallow\n\nor something along the line.\n\n\n> diff --git a/t/t5611-clone-config.sh b/t/t5611-clone-config.sh\n> index 8e0fd398236b..3aab86ad4def 100755\n> --- a/t/t5611-clone-config.sh\n> +++ b/t/t5611-clone-config.sh\n> @@ -92,6 +92,13 @@ test_expect_success 'clone -c remote.<remote>.fetch=<refspec> --origin=<name>' '\n>  \ttest_cmp expect actual\n>  '\n>  \n> +test_expect_success 'clone -c clone.rejectshallow' '\n> +\trm -rf child &&\n> +\tgit clone --depth=1 --no-local . child &&\n> +\ttest_must_fail git clone -c clone.rejectshallow child out 2>err &&\n\nThis is not quite right, even though it may happen to work.  The\n\"clone.rejectshallow\" variable is a configuration about what should\nhappen when creating a new repository by cloning, so letting \"git\nclone -c var[=val]\" to set the variable _in_ the resulting repository\nwould not make much sense.  Even if the clone succeeded, nobody would\nlook at that particular configuration variable that is set in the\nresulting repository.\n\nI think it would communicate to the readers better what we are\ntrying to do, if we write\n\n\ttest_must_fail git -c clone.rejectshallow=true clone child out\n\ninstead.\n\nThanks.\n"},{"id":"416148","messageId":"57d5526c66d411eb81800024e87935e7@oschina.cn","threadId":"55104","inReplyTo":"xmqq35yc9yan.fsf@gitster.c.googlers.com","subject":"Re: Re: [PATCH] builtin/clone.c: add --no-shallow option","fromName":"lilinchao@oschina.cn","fromEmail":"lilinchao@oschina.cn","sentAt":"2021-02-04T10:32:52Z","receivedAt":"2021-02-04T11:07:27Z","isPatch":true,"sender":{"key":"lilinchao@oschina.cn","avatar":null},"body":"\n--------------\nlilinchao@oschina.cn\n>\"Li Linchao via GitGitGadget\" <gitgitgadget@gmail.com> writes:\n>\n>> From: lilinchao <lilinchao@oschina.cn>\n>>\n>> This patch add a new option that reject to clone a shallow repository.\n>\n>A canonical form of our log message starts by explaining the need,\n>and then presents the solution at the end. \n\nOk, will do.\n\n>\n>> Clients don't know it's a shallow repository until they download it\n>> locally, in some scenariors, clients just don't want to clone this kind\n>\n>\"scenarios\".  \"in some scenarios\" would have to be clarified a bit\n>more to justify why it is a good idea to have such a feature. \n\nI found an issue described like this:\n\n    The blame information can be completely wrong when fetching it from\n    a shallow clone, without errors or warnings. When the outcome is invalid\n    data, it's extremely difficult to diagnose that it comes from a shallow clone.\n    If a line in a file was not changed in the commits that were downloaded as\n    part of the shallow fetch, git will report the first known commit as the author.\n    This has a big impact on the auto-assignment of new issues.\n\nIt looks like this is another scenario that can prove this feature is necessary.\n\n>\n>> of repository, and want to exit the process immediately without creating\n>> any unnecessary files.\n>\n>\"clients don't know it's a shallow repository until they download\"\n>leading to \"so let's reject immediately upon finding out that they\n>are shallow\" does make sense as a flow of thought, though.\n>\n>> +--no-shallow::\n>> +\tDon't clone a shallow source repository. In some scenariors, clients\n>\n>\"scenarios\" (no 'r').\n>\n>> diff --git a/builtin/clone.c b/builtin/clone.c\n>> old mode 100644\n>> new mode 100755\n>\n>Unwarranted \"chmod +x\"; accidents do happen, but please be careful\n>before making what you did public ;-) \n\nOops, this happened when I edited it in VS Code, it noticed me 'permission denied' when\nI want to save the file. \n\n>\n>> @@ -90,6 +91,7 @@ static struct option builtin_clone_options[] = {\n>>  OPT__VERBOSITY(&option_verbosity),\n>>  OPT_BOOL(0, \"progress\", &option_progress,\n>>  N_(\"force progress reporting\")),\n>> +\tOPT_BOOL(0, \"no-shallow\", &option_no_shallow, N_(\"don't clone shallow repository\")),\n>>  OPT_BOOL('n', \"no-checkout\", &option_no_checkout,\n>>  N_(\"don't create a checkout\")),\n>>  OPT_BOOL(0, \"bare\", &option_bare, N_(\"create a bare repository\")),\n>\n>It is a bad idea to give a name that begins with \"no-\" to an option\n>whose default can be tweaked by a configuration variable [*].  If\n>the configuration is named \"rejectshallow\", perhaps it is better to\n>call it \"--reject-shallow\" instead. \n>\n>This is because configured default must be overridable from the\n>command line.  I.e. even if you have in your ~/.gitconfig this:\n>\n>    [clone]\n>        rejectshallow = true\n>\n>you should be able to say \"allow it only this time\", with\n>\n> $ git clone --no-reject-shallow http://github.com/git/git/ git\n>\n>and you do not want to have to say \"--no-no-shallow\", which sounds\n>just silly.\n>\n>\tSide note. it is a bad idea in general, even if the option\n>\tdoes not have corresponding configuration variable.  The\n>\texisting \"no-checkout\" is a historical accident that\n>\thappened long time ago and cannot be removed due to\n>\tcompatibility.  Let's not introduce a new option that\n>\tfollows such a bad pattern. \n> \n\nYou're right, \"--reject-shallow\" is much better. \nI didn't realize that bool options have default [no-] option.\n\n>> @@ -963,6 +968,7 @@ static int path_exists(const char *path)\n>>  int cmd_clone(int argc, const char **argv, const char *prefix)\n>>  {\n>>  int is_bundle = 0, is_local;\n>> +\tint is_shallow = 0;\n>>  const char *repo_name, *repo, *work_tree, *git_dir;\n>>  char *path, *dir, *display_repo = NULL;\n>>  int dest_exists, real_dest_exists = 0;\n>> @@ -1215,6 +1221,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n>>  if (filter_options.choice)\n>>  warning(_(\"--filter is ignored in local clones; use file:// instead.\"));\n>>  if (!access(mkpath(\"%s/shallow\", path), F_OK)) {\n>> +\tis_shallow = 1;\n>>  if (option_local > 0)\n>>  warning(_(\"source repository is shallow, ignoring --local\"));\n>>  is_local = 0;\n>\n>This change is to the local clone codepath.  Cloning over the wire\n>would not go through this part.  And throughout the patch, this is\n>the only place that sets is_shallow to 1.\n>\n>Also let's note that this is after we called parse_options(), so the\n>value of option_no_shallow is known at this point.\n>\n>So, this patch does not even *need* to introduce a new \"is_shallow\"\n>variable at all.  It only needs to add\n>\n>                        if (option_no_shallow)\n>                                die(...);\n>\n>instead of adding \"is_shallow = 1\" to the above hunk.\n>\n>I somehow think that this is only half a feature---wouldn't it be\n>more useful if we also rejected a non-local clone from a shallow\n>repository? \n>\n>And for that ...\n>\n  \nAfter I applied your review suggestions above, then we can reject a \nnon-local clone from shallow repo. For now, it will clone a empty \nrepo with --no-local option.\n\n>\n>> diff --git a/t/t5606-clone-options.sh b/t/t5606-clone-options.sh\n>> index 7f082fb23b6a..9d310dbb158a 100755\n>> --- a/t/t5606-clone-options.sh\n>> +++ b/t/t5606-clone-options.sh\n>> @@ -42,6 +42,13 @@ test_expect_success 'disallows --bare with --separate-git-dir' '\n>> \n>>  '\n>> \n>> +test_expect_success 'reject clone shallow repository' '\n>> +\tgit clone --depth=1 --no-local parent shallow-repo &&\n>> +\ttest_must_fail git clone --no-shallow shallow-repo out 2>err &&\n>> +\ttest_i18ngrep -e \"source repository is shallow, reject to clone.\" err\n>> +\n>> +'\n>> +\n>\n>... in addition to the test for a local clone above, you'd also want\n>to test a non-local clone, perhaps like so:\n>\n>test_expect_success 'reject clone shallow repository' '\n>\trm -fr shallow-repo &&\n>\tgit clone --depth=1 --no-local parent shallow-repo &&\n>\ttest_must_fail git clone --no-shallow --no-local shallow-repo out 2>err &&\n>\ttest_i18ngrep -e \"source repository is shallow, reject to clone.\" err\n>\n>' \n>\n>Ditto for the other test script.\n>\n>Also, you would want to make sure that the command line overrides\n>the configured default.  I.e.\n>\n>\tgit -c clone.rejectshallow=false clone --reject-shallow\n>\n>should refuse to clone from a shallow one, while there should be a\n>way to countermand a configured \"I always refuse to clone from a\n>shallow repository\" with \"but let's allow it only this time\", i.e.\n>\n>\tgit -c clone.rejectshallow=true clone --no-reject-shallow\n>\n>or something along the line.\n>\n>\n>> diff --git a/t/t5611-clone-config.sh b/t/t5611-clone-config.sh\n>> index 8e0fd398236b..3aab86ad4def 100755\n>> --- a/t/t5611-clone-config.sh\n>> +++ b/t/t5611-clone-config.sh\n>> @@ -92,6 +92,13 @@ test_expect_success 'clone -c remote.<remote>.fetch=<refspec> --origin=<name>' '\n>>  test_cmp expect actual\n>>  '\n>> \n>> +test_expect_success 'clone -c clone.rejectshallow' '\n>> +\trm -rf child &&\n>> +\tgit clone --depth=1 --no-local . child &&\n>> +\ttest_must_fail git clone -c clone.rejectshallow child out 2>err &&\n>\n>This is not quite right, even though it may happen to work.  The\n>\"clone.rejectshallow\" variable is a configuration about what should\n>happen when creating a new repository by cloning, so letting \"git\n>clone -c var[=val]\" to set the variable _in_ the resulting repository\n>would not make much sense.  Even if the clone succeeded, nobody would\n>look at that particular configuration variable that is set in the\n>resulting repository.\n>\n>I think it would communicate to the readers better what we are\n>trying to do, if we write\n>\n>\ttest_must_fail git -c clone.rejectshallow=true clone child out\n>\n>instead.\n> \n>Thanks. \n\nThank you for so many effective suggestions, I will write test case more carefully :)"},{"id":"416149","messageId":"nycvar.QRO.7.76.6.2102041458410.54@tvgsbejvaqbjf.bet","threadId":"55104","inReplyTo":"pull.865.git.1612409491842.gitgitgadget@gmail.com","subject":"Re: [PATCH] builtin/clone.c: add --no-shallow option","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2021-02-04T14:00:13Z","receivedAt":"2021-02-04T14:03:40Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nin addition to what Junio said:\n\nOn Thu, 4 Feb 2021, Li Linchao via GitGitGadget wrote:\n\n> diff --git a/builtin/clone.c b/builtin/clone.c\n> index e335734b4cfd..b07d867e6641\n> --- a/builtin/clone.c\n> +++ b/builtin/clone.c\n> @@ -858,6 +860,9 @@ static int git_clone_config(const char *k, const char *v, void *cb)\n>  \t\tfree(remote_name);\n>  \t\tremote_name = xstrdup(v);\n>  \t}\n> +\tif (!strcmp(k, \"clone.rejectshallow\")) {\n> +\t\toption_no_shallow = 1;\n\nThis needs to use `git_config_bool(k, v)` to allow for\n`clone.rejectShallow = false`.\n\nCiao,\nDscho\n"},{"id":"416159","messageId":"xmqqlfc37kt8.fsf@gitster.c.googlers.com","threadId":"55104","inReplyTo":"nycvar.QRO.7.76.6.2102041458410.54@tvgsbejvaqbjf.bet","subject":"Re: [PATCH] builtin/clone.c: add --no-shallow option","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-02-04T18:24:35Z","receivedAt":"2021-02-04T18:25:50Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n\n> Hi,\n>\n> in addition to what Junio said:\n>\n> On Thu, 4 Feb 2021, Li Linchao via GitGitGadget wrote:\n>\n>> diff --git a/builtin/clone.c b/builtin/clone.c\n>> index e335734b4cfd..b07d867e6641\n>> --- a/builtin/clone.c\n>> +++ b/builtin/clone.c\n>> @@ -858,6 +860,9 @@ static int git_clone_config(const char *k, const char *v, void *cb)\n>>  \t\tfree(remote_name);\n>>  \t\tremote_name = xstrdup(v);\n>>  \t}\n>> +\tif (!strcmp(k, \"clone.rejectshallow\")) {\n>> +\t\toption_no_shallow = 1;\n>\n> This needs to use `git_config_bool(k, v)` to allow for\n> `clone.rejectShallow = false`.\n\nThanks.  I completely missed that.\n\n"},{"id":"416161","messageId":"xmqqft2b7k99.fsf@gitster.c.googlers.com","threadId":"55104","inReplyTo":"57d5526c66d411eb81800024e87935e7@oschina.cn","subject":"Re: [PATCH] builtin/clone.c: add --no-shallow option","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-02-04T18:36:34Z","receivedAt":"2021-02-04T18:39:50Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"lilinchao@oschina.cn\" <lilinchao@oschina.cn> writes:\n\n> I found an issue described like this:\n>\n>     The blame information can be completely wrong when fetching it from\n>     a shallow clone, without errors or warnings. When the outcome is invalid\n>     data, it's extremely difficult to diagnose that it comes from a shallow clone.\n>     If a line in a file was not changed in the commits that were downloaded as\n>     part of the shallow fetch, git will report the first known commit as the author.\n>     This has a big impact on the auto-assignment of new issues.\n>\n> It looks like this is another scenario that can prove this feature is necessary.\n\nIn other words:\n\n    Users may want more history than the repository offered for\n    cloning, which happens to be shallow, can give them.\n\nAnd the way chosen by \"--reject-shallow\" is to require that the\nsource repository has the entire history.\n\nI wonder if the design of this UI is flexible enough so that we can\nextend it to allow \"I need the history that goes back at least to X\"\nin the future.  \"I need the history goes back to all the roots\" then\nbecomes a narrow special case of that.\n\n> After I applied your review suggestions above, then we can reject a \n> non-local clone from shallow repo. For now, it will clone a empty \n> repo with --no-local option.\n\nIt is my understanding that your patch with or without my suggestion\nonly deals with local clone and does not change anything for\nnon-local case.  A \"local-only\" solution is a good place to start\nand is a good test bed to experiment with the user experience, but I\nview without support for non-local clone, it would not be ready for\ngeneral use.\n\nThanks.\n"},{"id":"416400","messageId":"2f9602495eb5455351b926a2dcbf5a5650002382.1612773119.git.gitgitgadget@gmail.com","threadId":"55104","inReplyTo":"pull.865.v2.git.1612773119.gitgitgadget@gmail.com","subject":"[PATCH v2 1/2] builtin/clone.c: add --no-shallow option","fromName":"lilinchao via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-02-08T08:31:58Z","receivedAt":"2021-02-08T08:33:42Z","isPatch":true,"sender":{"key":"name:lilinchao","avatar":null},"body":"From: lilinchao <lilinchao@oschina.cn>\n\nThis patch add a new option that reject to clone a shallow repository.\nClients don't know it's a shallow repository until they download it\nlocally, in some scenariors, clients just don't want to clone this kind\nof repository, and want to exit the process immediately without creating\nany unnecessary files.\n\nSigned-off-by: lilinchao <lilinchao@oschina.cn>\n---\n Documentation/config/clone.txt |  3 +++\n Documentation/git-clone.txt    |  8 +++++++-\n builtin/clone.c                | 13 +++++++++++++\n t/t5606-clone-options.sh       |  7 +++++++\n t/t5611-clone-config.sh        |  7 +++++++\n 5 files changed, 37 insertions(+), 1 deletion(-)\n mode change 100644 => 100755 builtin/clone.c\n\ndiff --git a/Documentation/config/clone.txt b/Documentation/config/clone.txt\nindex 47de36a5fedf..29b779b8a825 100644\n--- a/Documentation/config/clone.txt\n+++ b/Documentation/config/clone.txt\n@@ -2,3 +2,6 @@ clone.defaultRemoteName::\n \tThe name of the remote to create when cloning a repository.  Defaults to\n \t`origin`, and can be overridden by passing the `--origin` command-line\n \toption to linkgit:git-clone[1].\n+\n+clone.rejectshallow::\n+\tReject to clone a repository if it is a shallow one.\ndiff --git a/Documentation/git-clone.txt b/Documentation/git-clone.txt\nindex 02d9c19cec75..9ce6e545577f 100644\n--- a/Documentation/git-clone.txt\n+++ b/Documentation/git-clone.txt\n@@ -15,7 +15,7 @@ SYNOPSIS\n \t  [--dissociate] [--separate-git-dir <git dir>]\n \t  [--depth <depth>] [--[no-]single-branch] [--no-tags]\n \t  [--recurse-submodules[=<pathspec>]] [--[no-]shallow-submodules]\n-\t  [--[no-]remote-submodules] [--jobs <n>] [--sparse]\n+\t  [--[no-]remote-submodules] [--jobs <n>] [--sparse] [--no-shallow]\n \t  [--filter=<filter>] [--] <repository>\n \t  [<directory>]\n \n@@ -149,6 +149,12 @@ objects from the source repository into a pack in the cloned repository.\n --no-checkout::\n \tNo checkout of HEAD is performed after the clone is complete.\n \n+--no-shallow::\n+\tDon't clone a shallow source repository. In some scenariors, clients\n+\twant the cloned repository information to be complete. Otherwise,\n+\tthe cloning process should end immediately without creating any files,\n+\twhich can save some disk space.\n+\n --bare::\n \tMake a 'bare' Git repository.  That is, instead of\n \tcreating `<directory>` and placing the administrative\ndiff --git a/builtin/clone.c b/builtin/clone.c\nold mode 100644\nnew mode 100755\nindex e335734b4cfd..b07d867e6641\n--- a/builtin/clone.c\n+++ b/builtin/clone.c\n@@ -50,6 +50,7 @@ static int option_no_checkout, option_bare, option_mirror, option_single_branch\n static int option_local = -1, option_no_hardlinks, option_shared;\n static int option_no_tags;\n static int option_shallow_submodules;\n+static int option_no_shallow;\n static int deepen;\n static char *option_template, *option_depth, *option_since;\n static char *option_origin = NULL;\n@@ -90,6 +91,7 @@ static struct option builtin_clone_options[] = {\n \tOPT__VERBOSITY(&option_verbosity),\n \tOPT_BOOL(0, \"progress\", &option_progress,\n \t\t N_(\"force progress reporting\")),\n+\tOPT_BOOL(0, \"no-shallow\", &option_no_shallow, N_(\"don't clone shallow repository\")),\n \tOPT_BOOL('n', \"no-checkout\", &option_no_checkout,\n \t\t N_(\"don't create a checkout\")),\n \tOPT_BOOL(0, \"bare\", &option_bare, N_(\"create a bare repository\")),\n@@ -858,6 +860,9 @@ static int git_clone_config(const char *k, const char *v, void *cb)\n \t\tfree(remote_name);\n \t\tremote_name = xstrdup(v);\n \t}\n+\tif (!strcmp(k, \"clone.rejectshallow\")) {\n+\t\toption_no_shallow = 1;\n+\t}\n \treturn git_default_config(k, v, cb);\n }\n \n@@ -963,6 +968,7 @@ static int path_exists(const char *path)\n int cmd_clone(int argc, const char **argv, const char *prefix)\n {\n \tint is_bundle = 0, is_local;\n+\tint is_shallow = 0;\n \tconst char *repo_name, *repo, *work_tree, *git_dir;\n \tchar *path, *dir, *display_repo = NULL;\n \tint dest_exists, real_dest_exists = 0;\n@@ -1215,6 +1221,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n \t\tif (filter_options.choice)\n \t\t\twarning(_(\"--filter is ignored in local clones; use file:// instead.\"));\n \t\tif (!access(mkpath(\"%s/shallow\", path), F_OK)) {\n+\t\t\tis_shallow = 1;\n \t\t\tif (option_local > 0)\n \t\t\t\twarning(_(\"source repository is shallow, ignoring --local\"));\n \t\t\tis_local = 0;\n@@ -1222,6 +1229,12 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n \t}\n \tif (option_local > 0 && !is_local)\n \t\twarning(_(\"--local is ignored\"));\n+\n+\tif (is_shallow) {\n+\t\tif (option_no_shallow)\n+\t\t\tdie(_(\"source repository is shallow, reject to clone.\"));\n+\t}\n+\n \ttransport->cloning = 1;\n \n \ttransport_set_option(transport, TRANS_OPT_KEEP, \"yes\");\ndiff --git a/t/t5606-clone-options.sh b/t/t5606-clone-options.sh\nindex 5d6e63a841f7..e9b2bde8be8e 100755\n--- a/t/t5606-clone-options.sh\n+++ b/t/t5606-clone-options.sh\n@@ -45,6 +45,13 @@ test_expect_success 'disallows --bare with --separate-git-dir' '\n \n '\n \n+test_expect_success 'reject clone shallow repository' '\n+\tgit clone --depth=1 --no-local parent shallow-repo &&\n+\ttest_must_fail git clone --no-shallow shallow-repo out 2>err &&\n+\ttest_i18ngrep -e \"source repository is shallow, reject to clone.\" err\n+\n+'\n+\n test_expect_success 'uses \"origin\" for default remote name' '\n \n \tgit clone parent clone-default-origin &&\ndiff --git a/t/t5611-clone-config.sh b/t/t5611-clone-config.sh\nindex 9f555b87ecdf..68fdf5b7ce1b 100755\n--- a/t/t5611-clone-config.sh\n+++ b/t/t5611-clone-config.sh\n@@ -95,6 +95,13 @@ test_expect_success 'clone -c remote.<remote>.fetch=<refspec> --origin=<name>' '\n \ttest_cmp expect actual\n '\n \n+test_expect_success 'clone -c clone.rejectshallow' '\n+\trm -rf child &&\n+\tgit clone --depth=1 --no-local . child &&\n+\ttest_must_fail git clone -c clone.rejectshallow child out 2>err &&\n+\ttest_i18ngrep -e \"source repository is shallow, reject to clone.\" err\n+'\n+\n test_expect_success MINGW 'clone -c core.hideDotFiles' '\n \ttest_commit attributes .gitattributes \"\" &&\n \trm -rf child &&\n-- \ngitgitgadget\n\n"},{"id":"416401","messageId":"cfcfc3ec6b3742b4726301346b0c825d09aace8f.1612773119.git.gitgitgadget@gmail.com","threadId":"55104","inReplyTo":"pull.865.v2.git.1612773119.gitgitgadget@gmail.com","subject":"[PATCH v2 2/2] builtin/clone.c: add --reject-shallow option","fromName":"lilinchao via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-02-08T08:31:59Z","receivedAt":"2021-02-08T08:34:13Z","isPatch":true,"sender":{"key":"name:lilinchao","avatar":null},"body":"From: lilinchao <lilinchao@oschina.cn>\n\nIn some scenarios, users may want more history than the repository\noffered for cloning, which mostly to be a shallow repository, can\ngive them. But users don't know it is a shallow repository until\nthey download it to local, users should have the option to refuse\nto clone this kind of repository, and may want to exit the process\nimmediately without creating any unnecessary files.\n\nAlthought there is an option '--depth=x' for users to decide how\ndeep history they can fetch, but as the unshallow cloning's depth\nis INFINITY, we can't know exactly the minimun 'x' value that can\nsatisfy the minimum integrity, so we can't pass 'x' value to --depth,\nand expect this can obtain a complete history of a repository.\n\nIn other scenarios, given that we have an API that allow us to import\nexternal repository, and then perform various operations on the repo.\nBut if the imported is a shallow one(which is actually possible), it\nwill affect the subsequent operations. So we can choose to refuse to\nclone, and let's just import an unshallow repository.\n\nThis patch offers a new option '--reject-shallow' that can reject to\nclone a shallow repository.\n\nSigned-off-by: lilinchao <lilinchao@oschina.cn>\n---\n Documentation/config/clone.txt |  3 ++-\n Documentation/git-clone.txt    | 20 ++++++++++++++++----\n builtin/clone.c                | 32 ++++++++++++++++++++++++++------\n t/t5606-clone-options.sh       | 33 +++++++++++++++++++++++++++++++--\n t/t5611-clone-config.sh        | 29 +++++++++++++++++++++++++++--\n 5 files changed, 102 insertions(+), 15 deletions(-)\n mode change 100755 => 100644 builtin/clone.c\n\ndiff --git a/Documentation/config/clone.txt b/Documentation/config/clone.txt\nindex 29b779b8a825..50ebc170bb81 100644\n--- a/Documentation/config/clone.txt\n+++ b/Documentation/config/clone.txt\n@@ -4,4 +4,5 @@ clone.defaultRemoteName::\n \toption to linkgit:git-clone[1].\n \n clone.rejectshallow::\n-\tReject to clone a repository if it is a shallow one.\n+\tReject to clone a repository if it is a shallow one, can be overridden by\n+\tpassing option `--reject-shallow` in command line. See linkgit:git-clone[1]\ndiff --git a/Documentation/git-clone.txt b/Documentation/git-clone.txt\nindex 9ce6e545577f..af5a97903a05 100644\n--- a/Documentation/git-clone.txt\n+++ b/Documentation/git-clone.txt\n@@ -15,7 +15,7 @@ SYNOPSIS\n \t  [--dissociate] [--separate-git-dir <git dir>]\n \t  [--depth <depth>] [--[no-]single-branch] [--no-tags]\n \t  [--recurse-submodules[=<pathspec>]] [--[no-]shallow-submodules]\n-\t  [--[no-]remote-submodules] [--jobs <n>] [--sparse] [--no-shallow]\n+\t  [--[no-]remote-submodules] [--jobs <n>] [--sparse] [--reject-shallow]\n \t  [--filter=<filter>] [--] <repository>\n \t  [<directory>]\n \n@@ -149,11 +149,23 @@ objects from the source repository into a pack in the cloned repository.\n --no-checkout::\n \tNo checkout of HEAD is performed after the clone is complete.\n \n---no-shallow::\n-\tDon't clone a shallow source repository. In some scenariors, clients\n+--reject-shallow::\n+\tDon't clone a shallow source repository. In some scenarios, clients\n \twant the cloned repository information to be complete. Otherwise,\n \tthe cloning process should end immediately without creating any files,\n-\twhich can save some disk space.\n+\twhich can save some disk space. This can override `clone.rejectshallow`\n+\tfrom the configuration:\n+\n+\t--------------------------------------------------------------------\n+\t$ git -c clone.rejectshallow=false clone --reject-shallow source out\n+\t--------------------------------------------------------------------\n+\n+\tWhile there is a way to countermand a configured \"I always refuse to\n+\tclone from a shallow repository\" with \"but let's allow it only this time\":\n+\n+\t----------------------------------------------------------------------\n+\t$ git -c clone.rejectshallow=true clone --no-reject-shallow source out\n+\t----------------------------------------------------------------------\n \n --bare::\n \tMake a 'bare' Git repository.  That is, instead of\ndiff --git a/builtin/clone.c b/builtin/clone.c\nold mode 100755\nnew mode 100644\nindex b07d867e6641..a9d49c8e13c2\n--- a/builtin/clone.c\n+++ b/builtin/clone.c\n@@ -50,7 +50,8 @@ static int option_no_checkout, option_bare, option_mirror, option_single_branch\n static int option_local = -1, option_no_hardlinks, option_shared;\n static int option_no_tags;\n static int option_shallow_submodules;\n-static int option_no_shallow;\n+static int option_no_shallow = -1;  /* unspecified */\n+static int config_shallow = -1;    /* unspecified */\n static int deepen;\n static char *option_template, *option_depth, *option_since;\n static char *option_origin = NULL;\n@@ -91,7 +92,8 @@ static struct option builtin_clone_options[] = {\n \tOPT__VERBOSITY(&option_verbosity),\n \tOPT_BOOL(0, \"progress\", &option_progress,\n \t\t N_(\"force progress reporting\")),\n-\tOPT_BOOL(0, \"no-shallow\", &option_no_shallow, N_(\"don't clone shallow repository\")),\n+\tOPT_BOOL(0, \"reject-shallow\", &option_no_shallow,\n+\t\t N_(\"don't clone shallow repository\")),\n \tOPT_BOOL('n', \"no-checkout\", &option_no_checkout,\n \t\t N_(\"don't create a checkout\")),\n \tOPT_BOOL(0, \"bare\", &option_bare, N_(\"create a bare repository\")),\n@@ -861,7 +863,7 @@ static int git_clone_config(const char *k, const char *v, void *cb)\n \t\tremote_name = xstrdup(v);\n \t}\n \tif (!strcmp(k, \"clone.rejectshallow\")) {\n-\t\toption_no_shallow = 1;\n+\t\tconfig_shallow = git_config_bool(k, v);\n \t}\n \treturn git_default_config(k, v, cb);\n }\n@@ -1211,6 +1213,12 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n \n \tpath = get_repo_path(remote->url[0], &is_bundle);\n \tis_local = option_local != 0 && path && !is_bundle;\n+\n+\t/* Detect if the remote repository is shallow */\n+\tif (!access(mkpath(\"%s/shallow\", path), F_OK)) {\n+\t\tis_shallow = 1;\n+\t}\n+\n \tif (is_local) {\n \t\tif (option_depth)\n \t\t\twarning(_(\"--depth is ignored in local clones; use file:// instead.\"));\n@@ -1220,8 +1228,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n \t\t\twarning(_(\"--shallow-exclude is ignored in local clones; use file:// instead.\"));\n \t\tif (filter_options.choice)\n \t\t\twarning(_(\"--filter is ignored in local clones; use file:// instead.\"));\n-\t\tif (!access(mkpath(\"%s/shallow\", path), F_OK)) {\n-\t\t\tis_shallow = 1;\n+\t\tif (is_shallow) {\n \t\t\tif (option_local > 0)\n \t\t\t\twarning(_(\"source repository is shallow, ignoring --local\"));\n \t\t\tis_local = 0;\n@@ -1231,8 +1238,21 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n \t\twarning(_(\"--local is ignored\"));\n \n \tif (is_shallow) {\n-\t\tif (option_no_shallow)\n+\t\tint reject = 0;\n+\n+\t\t/* If option_no_shallow is specified from CLI option,\n+\t\t * ignore config_shallow from git_clone_config.\n+\t\t */\n+\n+\t\tif (config_shallow != -1) {\n+\t\t\treject = config_shallow;\n+\t\t}\n+\t\tif (option_no_shallow != -1) {\n+\t\t\treject = option_no_shallow;\n+\t\t}\n+\t\tif (reject) {\n \t\t\tdie(_(\"source repository is shallow, reject to clone.\"));\n+\t\t}\n \t}\n \n \ttransport->cloning = 1;\ndiff --git a/t/t5606-clone-options.sh b/t/t5606-clone-options.sh\nindex e9b2bde8be8e..bf007448ad20 100755\n--- a/t/t5606-clone-options.sh\n+++ b/t/t5606-clone-options.sh\n@@ -45,13 +45,42 @@ test_expect_success 'disallows --bare with --separate-git-dir' '\n \n '\n \n-test_expect_success 'reject clone shallow repository' '\n+test_expect_success 'fail to clone shallow repository' '\n \tgit clone --depth=1 --no-local parent shallow-repo &&\n-\ttest_must_fail git clone --no-shallow shallow-repo out 2>err &&\n+\ttest_must_fail git clone --reject-shallow shallow-repo out 2>err &&\n \ttest_i18ngrep -e \"source repository is shallow, reject to clone.\" err\n \n '\n \n+test_expect_success 'fail to clone non-local shallow repository' '\n+\trm -rf shallow-repo &&\n+\tgit clone --depth=1 --no-local parent shallow-repo &&\n+\ttest_must_fail git clone --reject-shallow --no-local shallow-repo out 2>err &&\n+\ttest_i18ngrep -e \"source repository is shallow, reject to clone.\" err\n+\n+'\n+\n+test_expect_success 'clone shallow repository with --no-reject-shallow' '\n+\trm -rf shallow-repo &&\n+\tgit clone --depth=1 --no-local parent shallow-repo &&\n+\tgit clone --no-reject-shallow --no-local shallow-repo clone-repo\n+\n+'\n+\n+test_expect_success 'clone normal repository with --reject-shallow' '\n+\trm -rf clone-repo &&\n+\tgit clone --no-local parent normal-repo &&\n+\tgit clone --reject-shallow --no-local normal-repo clone-repo\n+\n+'\n+\n+test_expect_success 'unspecified any configs or options' '\n+\trm -rf shallow-repo clone-repo &&\n+\tgit clone --depth=1 --no-local parent shallow-repo &&\n+\tgit clone shallow-repo clone-repo\n+\n+'\n+\n test_expect_success 'uses \"origin\" for default remote name' '\n \n \tgit clone parent clone-default-origin &&\ndiff --git a/t/t5611-clone-config.sh b/t/t5611-clone-config.sh\nindex 68fdf5b7ce1b..00789d7487d5 100755\n--- a/t/t5611-clone-config.sh\n+++ b/t/t5611-clone-config.sh\n@@ -95,13 +95,38 @@ test_expect_success 'clone -c remote.<remote>.fetch=<refspec> --origin=<name>' '\n \ttest_cmp expect actual\n '\n \n-test_expect_success 'clone -c clone.rejectshallow' '\n+test_expect_success 'clone.rejectshallow=true should fail to clone' '\n \trm -rf child &&\n \tgit clone --depth=1 --no-local . child &&\n-\ttest_must_fail git clone -c clone.rejectshallow child out 2>err &&\n+\ttest_must_fail git -c clone.rejectshallow=true clone --no-local child out 2>err &&\n \ttest_i18ngrep -e \"source repository is shallow, reject to clone.\" err\n '\n \n+test_expect_success 'clone.rejectshallow=false should succeed cloning' '\n+\trm -rf child &&\n+\tgit clone --depth=1 --no-local . child &&\n+\tgit -c clone.rejectshallow=false clone --no-local child out\n+'\n+\n+test_expect_success 'clone.rejectshallow=true should succeed cloning normal repo' '\n+\trm -rf child out &&\n+\tgit clone --no-local . child &&\n+\tgit -c clone.rejectshallow=true clone --no-local child out\n+'\n+\n+test_expect_success 'option --reject-shallow override clone.rejectshallow' '\n+\trm -rf child out &&\n+\tgit clone --depth=1 --no-local . child &&\n+\ttest_must_fail git clone -c clone.rejectshallow=false  --reject-shallow --no-local child out 2>err &&\n+\ttest_i18ngrep -e \"source repository is shallow, reject to clone.\" err\n+'\n+\n+test_expect_success ' option --no-reject-shallow override clone.rejectshallow' '\n+\trm -rf child &&\n+\tgit clone --depth=1 --no-local . child &&\n+\tgit -c clone.rejectshallow=true clone --no-reject-shallow --no-local child out\n+'\n+\n test_expect_success MINGW 'clone -c core.hideDotFiles' '\n \ttest_commit attributes .gitattributes \"\" &&\n \trm -rf child &&\n-- \ngitgitgadget\n"},{"id":"416402","messageId":"pull.865.v2.git.1612773119.gitgitgadget@gmail.com","threadId":"55104","inReplyTo":"pull.865.git.1612409491842.gitgitgadget@gmail.com","subject":"[PATCH v2 0/2] builtin/clone.c: add --no-shallow option","fromName":"Li Linchao via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-02-08T08:31:57Z","receivedAt":"2021-02-08T08:34:20Z","isPatch":true,"sender":{"key":"name:Li Linchao","avatar":null},"body":"Change since v1:\n\n * Rename --no-shallow to --reject-shallow\n * Enable to reject a non-local clone\n * Enable --[no-]reject-shallow from CLI override configuration.\n * Add more testcases.\n * Reword commit messages and relative documentation.\n\nSigned-off-by: lilinchao lilinchao@oschina.cn\n\nlilinchao (2):\n  builtin/clone.c: add --no-shallow option\n  builtin/clone.c: add --reject-shallow option\n\n Documentation/config/clone.txt |  4 ++++\n Documentation/git-clone.txt    | 20 ++++++++++++++++++-\n builtin/clone.c                | 35 ++++++++++++++++++++++++++++++++-\n t/t5606-clone-options.sh       | 36 ++++++++++++++++++++++++++++++++++\n t/t5611-clone-config.sh        | 32 ++++++++++++++++++++++++++++++\n 5 files changed, 125 insertions(+), 2 deletions(-)\n\n\nbase-commit: fb7fa4a1fd273f22efcafdd13c7f897814fd1eb9\nPublished-As: https://github.com/gitgitgadget/git/releases/tag/pr-865%2FCactusinhand%2Fgit-clone-options-v2\nFetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-865/Cactusinhand/git-clone-options-v2\nPull-Request: https://github.com/gitgitgadget/git/pull/865\n\nRange-diff vs v1:\n\n 1:  594323684af0 = 1:  2f9602495eb5 builtin/clone.c: add --no-shallow option\n -:  ------------ > 2:  cfcfc3ec6b37 builtin/clone.c: add --reject-shallow option\n\n-- \ngitgitgadget\n"},{"id":"416408","messageId":"a6228e5f-0552-f4e1-b064-df3bea217f06@gmail.com","threadId":"55104","inReplyTo":"pull.865.v2.git.1612773119.gitgitgadget@gmail.com","subject":"Re: [PATCH v2 0/2] builtin/clone.c: add --no-shallow option","fromName":"Derrick Stolee","fromEmail":"stolee@gmail.com","sentAt":"2021-02-08T13:33:11Z","receivedAt":"2021-02-08T13:34:23Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"On 2/8/2021 3:31 AM, Li Linchao via GitGitGadget wrote:\n> Range-diff vs v1:\n> \n>  1:  594323684af0 = 1:  2f9602495eb5 builtin/clone.c: add --no-shallow option\n>  -:  ------------ > 2:  cfcfc3ec6b37 builtin/clone.c: add --reject-shallow option\n\nYou should rewrite your history to make this one commit again. You can\ndo this by running 'git rebase -i HEAD~2' then editing the todo file\nto 'squash' the second commit in to the first. You will be prompted to\nedit the combined commit message, and you should preserve only the message\nfrom the second commit.\n\nThanks,\n-Stolee\n\n"},{"id":"416411","messageId":"5e201c406a1411ebad520024e87935e7@oschina.cn","threadId":"55104","inReplyTo":"32bb0d006a1211ebb94254a05087d89a835@gmail.com","subject":"Re: Re: [PATCH v2 0/2] builtin/clone.c: add --no-shallow option","fromName":"lilinchao@oschina.cn","fromEmail":"lilinchao@oschina.cn","sentAt":"2021-02-08T13:48:42Z","receivedAt":"2021-02-08T13:49:47Z","isPatch":true,"sender":{"key":"lilinchao@oschina.cn","avatar":null},"body":"Thanks, have done. And I am waiting for all CI on Github to turn green.\n--------------\nlilinchao@oschina.cn\n>On 2/8/2021 3:31 AM, Li Linchao via GitGitGadget wrote:\n>> Range-diff vs v1:\n>>\n>>  1:  594323684af0 = 1:  2f9602495eb5 builtin/clone.c: add --no-shallow option\n>>  -:  ------------ > 2:  cfcfc3ec6b37 builtin/clone.c: add --reject-shallow option\n>\n>You should rewrite your history to make this one commit again. You can\n>do this by running 'git rebase -i HEAD~2' then editing the todo file\n>to 'squash' the second commit in to the first. You will be prompted to\n>edit the combined commit message, and you should preserve only the message\n>from the second commit.\n>\n>Thanks,\n>-Stolee\n>"},{"id":"416412","messageId":"pull.865.v3.git.1612793576910.gitgitgadget@gmail.com","threadId":"55104","inReplyTo":"pull.865.v2.git.1612773119.gitgitgadget@gmail.com","subject":"[PATCH v3] builtin/clone.c: add --reject-shallow option","fromName":"Li Linchao via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-02-08T14:12:56Z","receivedAt":"2021-02-08T14:16:19Z","isPatch":true,"sender":{"key":"name:Li Linchao","avatar":null},"body":"From: lilinchao <lilinchao@oschina.cn>\n\nIn some scenarios, users may want more history than the repository\noffered for cloning, which mostly to be a shallow repository, can\ngive them. But users don't know it is a shallow repository until\nthey download it to local, users should have the option to refuse\nto clone this kind of repository, and may want to exit the process\nimmediately without creating any unnecessary files.\n\nAlthought there is an option '--depth=x' for users to decide how\ndeep history they can fetch, but as the unshallow cloning's depth\nis INFINITY, we can't know exactly the minimun 'x' value that can\nsatisfy the minimum integrity, so we can't pass 'x' value to --depth,\nand expect this can obtain a complete history of a repository.\n\nIn other scenarios, given that we have an API that allow us to import\nexternal repository, and then perform various operations on the repo.\nBut if the imported is a shallow one(which is actually possible), it\nwill affect the subsequent operations. So we can choose to refuse to\nclone, and let's just import an unshallow repository.\n\nThis patch offers a new option '--reject-shallow' that can reject to\nclone a shallow repository.\n\nSigned-off-by: lilinchao <lilinchao@oschina.cn>\n---\n    builtin/clone.c: add --no-shallow option\n    \n    Change since v1:\n    \n     * Rename --no-shallow to --reject-shallow\n     * Enable to reject a non-local clone\n     * Enable --[no-]reject-shallow from CLI override configuration.\n     * Add more testcases.\n     * Reword commit messages and relative documentation.\n    \n    Signed-off-by: lilinchao lilinchao@oschina.cn\n\nPublished-As: https://github.com/gitgitgadget/git/releases/tag/pr-865%2FCactusinhand%2Fgit-clone-options-v3\nFetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-865/Cactusinhand/git-clone-options-v3\nPull-Request: https://github.com/gitgitgadget/git/pull/865\n\nRange-diff vs v2:\n\n 1:  2f9602495eb5 < -:  ------------ builtin/clone.c: add --no-shallow option\n 2:  cfcfc3ec6b37 ! 1:  f5ed6b2e4481 builtin/clone.c: add --reject-shallow option\n     @@ Commit message\n      \n       ## Documentation/config/clone.txt ##\n      @@ Documentation/config/clone.txt: clone.defaultRemoteName::\n     + \tThe name of the remote to create when cloning a repository.  Defaults to\n     + \t`origin`, and can be overridden by passing the `--origin` command-line\n       \toption to linkgit:git-clone[1].\n     - \n     - clone.rejectshallow::\n     --\tReject to clone a repository if it is a shallow one.\n     ++\n     ++clone.rejectshallow::\n      +\tReject to clone a repository if it is a shallow one, can be overridden by\n      +\tpassing option `--reject-shallow` in command line. See linkgit:git-clone[1]\n      \n     @@ Documentation/git-clone.txt: SYNOPSIS\n       \t  [--dissociate] [--separate-git-dir <git dir>]\n       \t  [--depth <depth>] [--[no-]single-branch] [--no-tags]\n       \t  [--recurse-submodules[=<pathspec>]] [--[no-]shallow-submodules]\n     --\t  [--[no-]remote-submodules] [--jobs <n>] [--sparse] [--no-shallow]\n     +-\t  [--[no-]remote-submodules] [--jobs <n>] [--sparse]\n      +\t  [--[no-]remote-submodules] [--jobs <n>] [--sparse] [--reject-shallow]\n       \t  [--filter=<filter>] [--] <repository>\n       \t  [<directory>]\n     @@ Documentation/git-clone.txt: objects from the source repository into a pack in t\n       --no-checkout::\n       \tNo checkout of HEAD is performed after the clone is complete.\n       \n     ----no-shallow::\n     --\tDon't clone a shallow source repository. In some scenariors, clients\n      +--reject-shallow::\n      +\tDon't clone a shallow source repository. In some scenarios, clients\n     - \twant the cloned repository information to be complete. Otherwise,\n     - \tthe cloning process should end immediately without creating any files,\n     --\twhich can save some disk space.\n     ++\twant the cloned repository information to be complete. Otherwise,\n     ++\tthe cloning process should end immediately without creating any files,\n      +\twhich can save some disk space. This can override `clone.rejectshallow`\n      +\tfrom the configuration:\n      +\n     @@ Documentation/git-clone.txt: objects from the source repository into a pack in t\n      +\t----------------------------------------------------------------------\n      +\t$ git -c clone.rejectshallow=true clone --no-reject-shallow source out\n      +\t----------------------------------------------------------------------\n     - \n     ++\n       --bare::\n       \tMake a 'bare' Git repository.  That is, instead of\n     + \tcreating `<directory>` and placing the administrative\n      \n     - ## builtin/clone.c (mode change 100755 => 100644) ##\n     + ## builtin/clone.c ##\n      @@ builtin/clone.c: static int option_no_checkout, option_bare, option_mirror, option_single_branch\n       static int option_local = -1, option_no_hardlinks, option_shared;\n       static int option_no_tags;\n       static int option_shallow_submodules;\n     --static int option_no_shallow;\n      +static int option_no_shallow = -1;  /* unspecified */\n      +static int config_shallow = -1;    /* unspecified */\n       static int deepen;\n     @@ builtin/clone.c: static struct option builtin_clone_options[] = {\n       \tOPT__VERBOSITY(&option_verbosity),\n       \tOPT_BOOL(0, \"progress\", &option_progress,\n       \t\t N_(\"force progress reporting\")),\n     --\tOPT_BOOL(0, \"no-shallow\", &option_no_shallow, N_(\"don't clone shallow repository\")),\n      +\tOPT_BOOL(0, \"reject-shallow\", &option_no_shallow,\n      +\t\t N_(\"don't clone shallow repository\")),\n       \tOPT_BOOL('n', \"no-checkout\", &option_no_checkout,\n       \t\t N_(\"don't create a checkout\")),\n       \tOPT_BOOL(0, \"bare\", &option_bare, N_(\"create a bare repository\")),\n      @@ builtin/clone.c: static int git_clone_config(const char *k, const char *v, void *cb)\n     + \t\tfree(remote_name);\n       \t\tremote_name = xstrdup(v);\n       \t}\n     - \tif (!strcmp(k, \"clone.rejectshallow\")) {\n     --\t\toption_no_shallow = 1;\n     ++\tif (!strcmp(k, \"clone.rejectshallow\")) {\n      +\t\tconfig_shallow = git_config_bool(k, v);\n     - \t}\n     ++\t}\n       \treturn git_default_config(k, v, cb);\n       }\n     + \n     +@@ builtin/clone.c: static int path_exists(const char *path)\n     + int cmd_clone(int argc, const char **argv, const char *prefix)\n     + {\n     + \tint is_bundle = 0, is_local;\n     ++\tint is_shallow = 0;\n     + \tconst char *repo_name, *repo, *work_tree, *git_dir;\n     + \tchar *path, *dir, *display_repo = NULL;\n     + \tint dest_exists, real_dest_exists = 0;\n      @@ builtin/clone.c: int cmd_clone(int argc, const char **argv, const char *prefix)\n       \n       \tpath = get_repo_path(remote->url[0], &is_bundle);\n     @@ builtin/clone.c: int cmd_clone(int argc, const char **argv, const char *prefix)\n       \t\tif (filter_options.choice)\n       \t\t\twarning(_(\"--filter is ignored in local clones; use file:// instead.\"));\n      -\t\tif (!access(mkpath(\"%s/shallow\", path), F_OK)) {\n     --\t\t\tis_shallow = 1;\n      +\t\tif (is_shallow) {\n       \t\t\tif (option_local > 0)\n       \t\t\t\twarning(_(\"source repository is shallow, ignoring --local\"));\n       \t\t\tis_local = 0;\n      @@ builtin/clone.c: int cmd_clone(int argc, const char **argv, const char *prefix)\n     + \t}\n     + \tif (option_local > 0 && !is_local)\n       \t\twarning(_(\"--local is ignored\"));\n     - \n     - \tif (is_shallow) {\n     --\t\tif (option_no_shallow)\n     ++\n     ++\tif (is_shallow) {\n      +\t\tint reject = 0;\n      +\n      +\t\t/* If option_no_shallow is specified from CLI option,\n     @@ builtin/clone.c: int cmd_clone(int argc, const char **argv, const char *prefix)\n      +\t\t\treject = option_no_shallow;\n      +\t\t}\n      +\t\tif (reject) {\n     - \t\t\tdie(_(\"source repository is shallow, reject to clone.\"));\n     ++\t\t\tdie(_(\"source repository is shallow, reject to clone.\"));\n      +\t\t}\n     - \t}\n     - \n     ++\t}\n     ++\n       \ttransport->cloning = 1;\n     + \n     + \ttransport_set_option(transport, TRANS_OPT_KEEP, \"yes\");\n      \n       ## t/t5606-clone-options.sh ##\n      @@ t/t5606-clone-options.sh: test_expect_success 'disallows --bare with --separate-git-dir' '\n       \n       '\n       \n     --test_expect_success 'reject clone shallow repository' '\n      +test_expect_success 'fail to clone shallow repository' '\n     - \tgit clone --depth=1 --no-local parent shallow-repo &&\n     --\ttest_must_fail git clone --no-shallow shallow-repo out 2>err &&\n     ++\tgit clone --depth=1 --no-local parent shallow-repo &&\n      +\ttest_must_fail git clone --reject-shallow shallow-repo out 2>err &&\n     - \ttest_i18ngrep -e \"source repository is shallow, reject to clone.\" err\n     - \n     - '\n     - \n     ++\ttest_i18ngrep -e \"source repository is shallow, reject to clone.\" err\n     ++\n     ++'\n     ++\n      +test_expect_success 'fail to clone non-local shallow repository' '\n      +\trm -rf shallow-repo &&\n      +\tgit clone --depth=1 --no-local parent shallow-repo &&\n     @@ t/t5611-clone-config.sh: test_expect_success 'clone -c remote.<remote>.fetch=<re\n       \ttest_cmp expect actual\n       '\n       \n     --test_expect_success 'clone -c clone.rejectshallow' '\n      +test_expect_success 'clone.rejectshallow=true should fail to clone' '\n     - \trm -rf child &&\n     - \tgit clone --depth=1 --no-local . child &&\n     --\ttest_must_fail git clone -c clone.rejectshallow child out 2>err &&\n     ++\trm -rf child &&\n     ++\tgit clone --depth=1 --no-local . child &&\n      +\ttest_must_fail git -c clone.rejectshallow=true clone --no-local child out 2>err &&\n     - \ttest_i18ngrep -e \"source repository is shallow, reject to clone.\" err\n     - '\n     - \n     ++\ttest_i18ngrep -e \"source repository is shallow, reject to clone.\" err\n     ++'\n     ++\n      +test_expect_success 'clone.rejectshallow=false should succeed cloning' '\n      +\trm -rf child &&\n      +\tgit clone --depth=1 --no-local . child &&\n\n\n Documentation/config/clone.txt |  4 ++++\n Documentation/git-clone.txt    | 20 ++++++++++++++++++-\n builtin/clone.c                | 35 ++++++++++++++++++++++++++++++++-\n t/t5606-clone-options.sh       | 36 ++++++++++++++++++++++++++++++++++\n t/t5611-clone-config.sh        | 32 ++++++++++++++++++++++++++++++\n 5 files changed, 125 insertions(+), 2 deletions(-)\n\ndiff --git a/Documentation/config/clone.txt b/Documentation/config/clone.txt\nindex 47de36a5fedf..50ebc170bb81 100644\n--- a/Documentation/config/clone.txt\n+++ b/Documentation/config/clone.txt\n@@ -2,3 +2,7 @@ clone.defaultRemoteName::\n \tThe name of the remote to create when cloning a repository.  Defaults to\n \t`origin`, and can be overridden by passing the `--origin` command-line\n \toption to linkgit:git-clone[1].\n+\n+clone.rejectshallow::\n+\tReject to clone a repository if it is a shallow one, can be overridden by\n+\tpassing option `--reject-shallow` in command line. See linkgit:git-clone[1]\ndiff --git a/Documentation/git-clone.txt b/Documentation/git-clone.txt\nindex 02d9c19cec75..af5a97903a05 100644\n--- a/Documentation/git-clone.txt\n+++ b/Documentation/git-clone.txt\n@@ -15,7 +15,7 @@ SYNOPSIS\n \t  [--dissociate] [--separate-git-dir <git dir>]\n \t  [--depth <depth>] [--[no-]single-branch] [--no-tags]\n \t  [--recurse-submodules[=<pathspec>]] [--[no-]shallow-submodules]\n-\t  [--[no-]remote-submodules] [--jobs <n>] [--sparse]\n+\t  [--[no-]remote-submodules] [--jobs <n>] [--sparse] [--reject-shallow]\n \t  [--filter=<filter>] [--] <repository>\n \t  [<directory>]\n \n@@ -149,6 +149,24 @@ objects from the source repository into a pack in the cloned repository.\n --no-checkout::\n \tNo checkout of HEAD is performed after the clone is complete.\n \n+--reject-shallow::\n+\tDon't clone a shallow source repository. In some scenarios, clients\n+\twant the cloned repository information to be complete. Otherwise,\n+\tthe cloning process should end immediately without creating any files,\n+\twhich can save some disk space. This can override `clone.rejectshallow`\n+\tfrom the configuration:\n+\n+\t--------------------------------------------------------------------\n+\t$ git -c clone.rejectshallow=false clone --reject-shallow source out\n+\t--------------------------------------------------------------------\n+\n+\tWhile there is a way to countermand a configured \"I always refuse to\n+\tclone from a shallow repository\" with \"but let's allow it only this time\":\n+\n+\t----------------------------------------------------------------------\n+\t$ git -c clone.rejectshallow=true clone --no-reject-shallow source out\n+\t----------------------------------------------------------------------\n+\n --bare::\n \tMake a 'bare' Git repository.  That is, instead of\n \tcreating `<directory>` and placing the administrative\ndiff --git a/builtin/clone.c b/builtin/clone.c\nindex e335734b4cfd..a9d49c8e13c2 100644\n--- a/builtin/clone.c\n+++ b/builtin/clone.c\n@@ -50,6 +50,8 @@ static int option_no_checkout, option_bare, option_mirror, option_single_branch\n static int option_local = -1, option_no_hardlinks, option_shared;\n static int option_no_tags;\n static int option_shallow_submodules;\n+static int option_no_shallow = -1;  /* unspecified */\n+static int config_shallow = -1;    /* unspecified */\n static int deepen;\n static char *option_template, *option_depth, *option_since;\n static char *option_origin = NULL;\n@@ -90,6 +92,8 @@ static struct option builtin_clone_options[] = {\n \tOPT__VERBOSITY(&option_verbosity),\n \tOPT_BOOL(0, \"progress\", &option_progress,\n \t\t N_(\"force progress reporting\")),\n+\tOPT_BOOL(0, \"reject-shallow\", &option_no_shallow,\n+\t\t N_(\"don't clone shallow repository\")),\n \tOPT_BOOL('n', \"no-checkout\", &option_no_checkout,\n \t\t N_(\"don't create a checkout\")),\n \tOPT_BOOL(0, \"bare\", &option_bare, N_(\"create a bare repository\")),\n@@ -858,6 +862,9 @@ static int git_clone_config(const char *k, const char *v, void *cb)\n \t\tfree(remote_name);\n \t\tremote_name = xstrdup(v);\n \t}\n+\tif (!strcmp(k, \"clone.rejectshallow\")) {\n+\t\tconfig_shallow = git_config_bool(k, v);\n+\t}\n \treturn git_default_config(k, v, cb);\n }\n \n@@ -963,6 +970,7 @@ static int path_exists(const char *path)\n int cmd_clone(int argc, const char **argv, const char *prefix)\n {\n \tint is_bundle = 0, is_local;\n+\tint is_shallow = 0;\n \tconst char *repo_name, *repo, *work_tree, *git_dir;\n \tchar *path, *dir, *display_repo = NULL;\n \tint dest_exists, real_dest_exists = 0;\n@@ -1205,6 +1213,12 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n \n \tpath = get_repo_path(remote->url[0], &is_bundle);\n \tis_local = option_local != 0 && path && !is_bundle;\n+\n+\t/* Detect if the remote repository is shallow */\n+\tif (!access(mkpath(\"%s/shallow\", path), F_OK)) {\n+\t\tis_shallow = 1;\n+\t}\n+\n \tif (is_local) {\n \t\tif (option_depth)\n \t\t\twarning(_(\"--depth is ignored in local clones; use file:// instead.\"));\n@@ -1214,7 +1228,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n \t\t\twarning(_(\"--shallow-exclude is ignored in local clones; use file:// instead.\"));\n \t\tif (filter_options.choice)\n \t\t\twarning(_(\"--filter is ignored in local clones; use file:// instead.\"));\n-\t\tif (!access(mkpath(\"%s/shallow\", path), F_OK)) {\n+\t\tif (is_shallow) {\n \t\t\tif (option_local > 0)\n \t\t\t\twarning(_(\"source repository is shallow, ignoring --local\"));\n \t\t\tis_local = 0;\n@@ -1222,6 +1236,25 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n \t}\n \tif (option_local > 0 && !is_local)\n \t\twarning(_(\"--local is ignored\"));\n+\n+\tif (is_shallow) {\n+\t\tint reject = 0;\n+\n+\t\t/* If option_no_shallow is specified from CLI option,\n+\t\t * ignore config_shallow from git_clone_config.\n+\t\t */\n+\n+\t\tif (config_shallow != -1) {\n+\t\t\treject = config_shallow;\n+\t\t}\n+\t\tif (option_no_shallow != -1) {\n+\t\t\treject = option_no_shallow;\n+\t\t}\n+\t\tif (reject) {\n+\t\t\tdie(_(\"source repository is shallow, reject to clone.\"));\n+\t\t}\n+\t}\n+\n \ttransport->cloning = 1;\n \n \ttransport_set_option(transport, TRANS_OPT_KEEP, \"yes\");\ndiff --git a/t/t5606-clone-options.sh b/t/t5606-clone-options.sh\nindex 5d6e63a841f7..bf007448ad20 100755\n--- a/t/t5606-clone-options.sh\n+++ b/t/t5606-clone-options.sh\n@@ -45,6 +45,42 @@ test_expect_success 'disallows --bare with --separate-git-dir' '\n \n '\n \n+test_expect_success 'fail to clone shallow repository' '\n+\tgit clone --depth=1 --no-local parent shallow-repo &&\n+\ttest_must_fail git clone --reject-shallow shallow-repo out 2>err &&\n+\ttest_i18ngrep -e \"source repository is shallow, reject to clone.\" err\n+\n+'\n+\n+test_expect_success 'fail to clone non-local shallow repository' '\n+\trm -rf shallow-repo &&\n+\tgit clone --depth=1 --no-local parent shallow-repo &&\n+\ttest_must_fail git clone --reject-shallow --no-local shallow-repo out 2>err &&\n+\ttest_i18ngrep -e \"source repository is shallow, reject to clone.\" err\n+\n+'\n+\n+test_expect_success 'clone shallow repository with --no-reject-shallow' '\n+\trm -rf shallow-repo &&\n+\tgit clone --depth=1 --no-local parent shallow-repo &&\n+\tgit clone --no-reject-shallow --no-local shallow-repo clone-repo\n+\n+'\n+\n+test_expect_success 'clone normal repository with --reject-shallow' '\n+\trm -rf clone-repo &&\n+\tgit clone --no-local parent normal-repo &&\n+\tgit clone --reject-shallow --no-local normal-repo clone-repo\n+\n+'\n+\n+test_expect_success 'unspecified any configs or options' '\n+\trm -rf shallow-repo clone-repo &&\n+\tgit clone --depth=1 --no-local parent shallow-repo &&\n+\tgit clone shallow-repo clone-repo\n+\n+'\n+\n test_expect_success 'uses \"origin\" for default remote name' '\n \n \tgit clone parent clone-default-origin &&\ndiff --git a/t/t5611-clone-config.sh b/t/t5611-clone-config.sh\nindex 9f555b87ecdf..00789d7487d5 100755\n--- a/t/t5611-clone-config.sh\n+++ b/t/t5611-clone-config.sh\n@@ -95,6 +95,38 @@ test_expect_success 'clone -c remote.<remote>.fetch=<refspec> --origin=<name>' '\n \ttest_cmp expect actual\n '\n \n+test_expect_success 'clone.rejectshallow=true should fail to clone' '\n+\trm -rf child &&\n+\tgit clone --depth=1 --no-local . child &&\n+\ttest_must_fail git -c clone.rejectshallow=true clone --no-local child out 2>err &&\n+\ttest_i18ngrep -e \"source repository is shallow, reject to clone.\" err\n+'\n+\n+test_expect_success 'clone.rejectshallow=false should succeed cloning' '\n+\trm -rf child &&\n+\tgit clone --depth=1 --no-local . child &&\n+\tgit -c clone.rejectshallow=false clone --no-local child out\n+'\n+\n+test_expect_success 'clone.rejectshallow=true should succeed cloning normal repo' '\n+\trm -rf child out &&\n+\tgit clone --no-local . child &&\n+\tgit -c clone.rejectshallow=true clone --no-local child out\n+'\n+\n+test_expect_success 'option --reject-shallow override clone.rejectshallow' '\n+\trm -rf child out &&\n+\tgit clone --depth=1 --no-local . child &&\n+\ttest_must_fail git clone -c clone.rejectshallow=false  --reject-shallow --no-local child out 2>err &&\n+\ttest_i18ngrep -e \"source repository is shallow, reject to clone.\" err\n+'\n+\n+test_expect_success ' option --no-reject-shallow override clone.rejectshallow' '\n+\trm -rf child &&\n+\tgit clone --depth=1 --no-local . child &&\n+\tgit -c clone.rejectshallow=true clone --no-reject-shallow --no-local child out\n+'\n+\n test_expect_success MINGW 'clone -c core.hideDotFiles' '\n \ttest_commit attributes .gitattributes \"\" &&\n \trm -rf child &&\n\nbase-commit: fb7fa4a1fd273f22efcafdd13c7f897814fd1eb9\n-- \ngitgitgadget\n"},{"id":"416551","messageId":"xmqqy2fx0yoj.fsf@gitster.c.googlers.com","threadId":"55104","inReplyTo":"pull.865.v3.git.1612793576910.gitgitgadget@gmail.com","subject":"Re: [PATCH v3] builtin/clone.c: add --reject-shallow option","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-02-09T20:32:44Z","receivedAt":"2021-02-09T20:38:38Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"Li Linchao via GitGitGadget\" <gitgitgadget@gmail.com> writes:\n\n> From: lilinchao <lilinchao@oschina.cn>\n>\n> In some scenarios, users may want more history than the repository\n> offered for cloning, which mostly to be a shallow repository, can\n> give them.\n\nSorry, but I find this hard to understand.  Are you saying that most\nof the repositories that users try to clone from are shallow?\n\n> But users don't know it is a shallow repository until\n> they download it to local, users should have the option to refuse\n> to clone this kind of repository, and may want to exit the process\n> immediately without creating any unnecessary files.\n\nThis one on the other hand is easy to understand, but we would\nprobably need something like s/But/But because/.\n\n> Althought there is an option '--depth=x' for users to decide how\n> deep history they can fetch, but as the unshallow cloning's depth\n> is INFINITY, we can't know exactly the minimun 'x' value that can\n> satisfy the minimum integrity, so we can't pass 'x' value to --depth,\n> and expect this can obtain a complete history of a repository.\n\nHmph, that is an interesting point.  This makes me wonder if we can\nachieve the same without adding a new option at the UI level (e.g.\nby allowing \"--depth\" to take \"infinity\" and reject cloning if we\nfind out that the origin repository is a shallow one).  But we can\nworry about it later once after we get the machinery driven by the\nUI right.\n\n> In other scenarios, given that we have an API that allow us to import\n> external repository, and then perform various operations on the repo.\n\nSorry, but I do not understand what you want to say with these two\nlines (\"Given that X and Y\" needs to be followed by a concluding\nstatement, e.g. \"Given that we have API to import and operate, we\ncan do Z\"---you are missing that \"we can do Z\" part).\n\n> diff --git a/Documentation/git-clone.txt b/Documentation/git-clone.txt\n> index 02d9c19cec75..af5a97903a05 100644\n> --- a/Documentation/git-clone.txt\n> +++ b/Documentation/git-clone.txt\n> @@ -15,7 +15,7 @@ SYNOPSIS\n>  \t  [--dissociate] [--separate-git-dir <git dir>]\n>  \t  [--depth <depth>] [--[no-]single-branch] [--no-tags]\n>  \t  [--recurse-submodules[=<pathspec>]] [--[no-]shallow-submodules]\n> -\t  [--[no-]remote-submodules] [--jobs <n>] [--sparse]\n> +\t  [--[no-]remote-submodules] [--jobs <n>] [--sparse] [--reject-shallow]\n\nIsn't it \"--[no-]reject-shallow\"?  Offering the negation from the\ncommand line is essential if \"[clone] rejectshallow\" configuration\nis allowed to set the default to true.\n\n> +--reject-shallow::\n> +\tDon't clone a shallow source repository. In some scenarios, clients\n> +\twant the cloned repository information to be complete. Otherwise,\n> +\tthe cloning process should end immediately without creating any files,\n> +\twhich can save some disk space. This can override `clone.rejectshallow`\n> +\tfrom the configuration:\n> +\n> +\t--------------------------------------------------------------------\n> +\t$ git -c clone.rejectshallow=false clone --reject-shallow source out\n> +\t--------------------------------------------------------------------\n> +\n> +\tWhile there is a way to countermand a configured \"I always refuse to\n> +\tclone from a shallow repository\" with \"but let's allow it only this time\":\n> +\n> +\t----------------------------------------------------------------------\n> +\t$ git -c clone.rejectshallow=true clone --no-reject-shallow source out\n> +\t----------------------------------------------------------------------\n\n\nThis is way too verbose and gives unnecessary details that readers\nalready know or do not need to know (e.g. setting configuration from\nthe command line and immediately override it from the command line\nis not something end-users would EVER need to do---only test writers\nwho develop Git would need it).  Something like\n\n    Fail if the source repository is a shallow repository.  The\n    `clone.rejectShallow` configuration variable can be used to\n    give the default.   \n\nwould be sufficient.  All readers ought to know when a configuration\nand command line option exist, the latter can be used to override\nthe default former gives, and it is *not* a job for the description\nof an individual option to teach them to such a detail like the\nabove does.\n\n> +static int option_no_shallow = -1;  /* unspecified */\n> +static int config_shallow = -1;    /* unspecified */\n\nHmph.  I would have expected the usual \"prepare a single variable\nand initialize it to the default, read the config to set it, and\nthen parse the command line to overwrite it\" sequence would suffice\nso it is puzzling why we want two separate variables here.\n\nLet's read on to find out.\n\n> @@ -90,6 +92,8 @@ static struct option builtin_clone_options[] = {\n>  \tOPT__VERBOSITY(&option_verbosity),\n>  \tOPT_BOOL(0, \"progress\", &option_progress,\n>  \t\t N_(\"force progress reporting\")),\n> +\tOPT_BOOL(0, \"reject-shallow\", &option_no_shallow,\n> +\t\t N_(\"don't clone shallow repository\")),\n>  \tOPT_BOOL('n', \"no-checkout\", &option_no_checkout,\n>  \t\t N_(\"don't create a checkout\")),\n>  \tOPT_BOOL(0, \"bare\", &option_bare, N_(\"create a bare repository\")),\n> @@ -858,6 +862,9 @@ static int git_clone_config(const char *k, const char *v, void *cb)\n>  \t\tfree(remote_name);\n>  \t\tremote_name = xstrdup(v);\n>  \t}\n> +\tif (!strcmp(k, \"clone.rejectshallow\")) {\n> +\t\tconfig_shallow = git_config_bool(k, v);\n> +\t}\n>  \treturn git_default_config(k, v, cb);\n>  }\n\nYou are adding to git_clone_config(), so instead of setting the\nvalue to config_shallow, setting the value to the same variable that\nwill be used in builtin_clone_options[] array should be sufficient.\n\ncmd_clone() begins like so:\n\n\n\tgit_config(git_clone_config, NULL);\n\targc = parse_options(...);\n\nwhich means that single variable (let's call it reject_shallow)\ncan (1) stay to be its initial value if no config or option is\ngiven, (2) if there is config, the git_config() call will cause\nthat variable assigned, (3) if there is option, parse_options()\ncall will cause that variable assigned, possibly overwriting the\nvalue taken from the config.\n\nWhich is exactly what we want.  So in short, declare just a single\n\n    static int reject_shallow; /* default to false */\n    \ninstead of \"option_no_shallow\" and \"config_shallow\", and use it in\nboth builtin_clone_options[] given to parse_options, and\ngit_clone_config() that is given to git_config(), and you'd be fine,\nI think.\n\n> @@ -963,6 +970,7 @@ static int path_exists(const char *path)\n>  int cmd_clone(int argc, const char **argv, const char *prefix)\n>  {\n>  \tint is_bundle = 0, is_local;\n> +\tint is_shallow = 0;\n>  \tconst char *repo_name, *repo, *work_tree, *git_dir;\n>  \tchar *path, *dir, *display_repo = NULL;\n>  \tint dest_exists, real_dest_exists = 0;\n> @@ -1205,6 +1213,12 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n>  \n>  \tpath = get_repo_path(remote->url[0], &is_bundle);\n>  \tis_local = option_local != 0 && path && !is_bundle;\n> +\n> +\t/* Detect if the remote repository is shallow */\n> +\tif (!access(mkpath(\"%s/shallow\", path), F_OK)) {\n> +\t\tis_shallow = 1;\n> +\t}\n\nThis is only for cloning from a local repository, no?  IOW, path at\nthis point may even be \"git://github.com/git/git/\" and checking with\naccess() does not make sense.\n\nAh, it is even worse.  get_repo_path() can return NULL, so mkpath()\nwill crash in such a case.  This must be at least\n\n\tif (path && !access(mkpath(\"%s/shallow\", path), F_OK))\n\t\tis_shallow = 1;\n\nbut I think the logic fits better in the body of \"if (is_Local)\"\nthing that immediately follows.  It is specific to the case where\ncloning from a local repository and access(mkpath()) that is about\nthe local filesystem (as opposed to going through the transport\nlayer) belongs there.\n\n>  \tif (is_local) {\n>  \t\tif (option_depth)\n>  \t\t\twarning(_(\"--depth is ignored in local clones; use file:// instead.\"));\n> @@ -1214,7 +1228,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n>  \t\t\twarning(_(\"--shallow-exclude is ignored in local clones; use file:// instead.\"));\n>  \t\tif (filter_options.choice)\n>  \t\t\twarning(_(\"--filter is ignored in local clones; use file:// instead.\"));\n> -\t\tif (!access(mkpath(\"%s/shallow\", path), F_OK)) {\n> +\t\tif (is_shallow) {\n>  \t\t\tif (option_local > 0)\n>  \t\t\t\twarning(_(\"source repository is shallow, ignoring --local\"));\n>  \t\t\tis_local = 0;\n\nSo, I think the above two hunks are making the code worse.  If we\nare to detect and reject cloning from the shallow repository when\ngoing through the transport layer (i.e. \"--no-local\" or cloning from\n\"git://github.com/git/git\", or \"https://github.com/git/git\", if it\nwere a shallow repository), that must be handled separately.\n\n> @@ -1222,6 +1236,25 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n>  \t}\n>  \tif (option_local > 0 && !is_local)\n>  \t\twarning(_(\"--local is ignored\"));\n> +\n> +\tif (is_shallow) {\n> +\t\tint reject = 0;\n> +\n> +\t\t/* If option_no_shallow is specified from CLI option,\n> +\t\t * ignore config_shallow from git_clone_config.\n> +\t\t */\n> +\n> +\t\tif (config_shallow != -1) {\n> +\t\t\treject = config_shallow;\n> +\t\t}\n> +\t\tif (option_no_shallow != -1) {\n> +\t\t\treject = option_no_shallow;\n> +\t\t}\n\nI do not think any of the above is necessary with just a single\nreject_shallow variable that is initialized to 0, can be set by\ngit_config() callback, and can further be set by parse_options().\n\n> +\t\tif (reject) {\n> +\t\t\tdie(_(\"source repository is shallow, reject to clone.\"));\n> +\t\t}\n\n> +\t}\n> +\n>  \ttransport->cloning = 1;\n>  \n>  \ttransport_set_option(transport, TRANS_OPT_KEEP, \"yes\");\n\nI do not see how this change would allow users to reject cloning\nhttp://github.com/git/git, if that repository were shallow, though.\nI think that would need changes to the code that interacts with\nthese transport_* functions we see later part of this functrion.\n\nThanks.\n"},{"id":"416609","messageId":"5c4295e46b7f11eb8acc0024e87935e7@oschina.cn","threadId":"55104","inReplyTo":"026bd8966b1611eb975aa4badb2c2b1190694@pobox.com","subject":"Re: Re: [PATCH v3] builtin/clone.c: add --reject-shallow option","fromName":"lilinchao@oschina.cn","fromEmail":"lilinchao@oschina.cn","sentAt":"2021-02-10T09:07:08Z","receivedAt":"2021-02-10T09:15:06Z","isPatch":true,"sender":{"key":"lilinchao@oschina.cn","avatar":null},"body":"--------------\nlilinchao@oschina.cn\n>\"Li Linchao via GitGitGadget\" <gitgitgadget@gmail.com> writes:\n>\n>> From: lilinchao <lilinchao@oschina.cn>\n>>\n>> In some scenarios, users may want more history than the repository\n>> offered for cloning, which mostly to be a shallow repository, can\n>> give them.\n>\n>Sorry, but I find this hard to understand.  Are you saying that most\n>of the repositories that users try to clone from are shallow? \n> \nOh, sorry, it shoud be \"which happens to be a shallow repository\".\n\n>> But users don't know it is a shallow repository until\n>> they download it to local, users should have the option to refuse\n>> to clone this kind of repository, and may want to exit the process\n>> immediately without creating any unnecessary files.\n>\n>This one on the other hand is easy to understand, but we would\n>probably need something like s/But/But because/.\n> \nOk, I will substitute it.\n\n>> Althought there is an option '--depth=x' for users to decide how\n>> deep history they can fetch, but as the unshallow cloning's depth\n>> is INFINITY, we can't know exactly the minimun 'x' value that can\n>> satisfy the minimum integrity, so we can't pass 'x' value to --depth,\n>> and expect this can obtain a complete history of a repository.\n>\n>Hmph, that is an interesting point.  This makes me wonder if we can\n>achieve the same without adding a new option at the UI level (e.g.\n>by allowing \"--depth\" to take \"infinity\" and reject cloning if we\n>find out that the origin repository is a shallow one).  But we can\n>worry about it later once after we get the machinery driven by the\n>UI right.\n> \nPlease let me explain the purpose of this patch in another way:\nIn practice, I may need a filter in clone command to filter out remote\nshallow repository that may appear. I think a new option like '--reject-shallow',\nor '--filter-shallow', or something like that, has a clearer purpose for users, they\ndon't have to come up with a specific depth number to achieve the same purpose.\nA literal filter, or option is just ok, I think.\n\n>> In other scenarios, given that we have an API that allow us to import\n>> external repository, and then perform various operations on the repo.\n>\n>Sorry, but I do not understand what you want to say with these two\n>lines (\"Given that X and Y\" needs to be followed by a concluding\n>statement, e.g. \"Given that we have API to import and operate, we\n>can do Z\"---you are missing that \"we can do Z\" part).\n> \nForgive my crappy English :-(\nWhat I want to express is \"if we have an API to do sth, then do sth\".\n\n>> diff --git a/Documentation/git-clone.txt b/Documentation/git-clone.txt\n>> index 02d9c19cec75..af5a97903a05 100644\n>> --- a/Documentation/git-clone.txt\n>> +++ b/Documentation/git-clone.txt\n>> @@ -15,7 +15,7 @@ SYNOPSIS\n>>    [--dissociate] [--separate-git-dir <git dir>]\n>>    [--depth <depth>] [--[no-]single-branch] [--no-tags]\n>>    [--recurse-submodules[=<pathspec>]] [--[no-]shallow-submodules]\n>> -\t  [--[no-]remote-submodules] [--jobs <n>] [--sparse]\n>> +\t  [--[no-]remote-submodules] [--jobs <n>] [--sparse] [--reject-shallow]\n>\n>Isn't it \"--[no-]reject-shallow\"?  Offering the negation from the\n>command line is essential if \"[clone] rejectshallow\" configuration\n>is allowed to set the default to true.\n> \nOk, that makes sense.\n\n>> +--reject-shallow::\n>> +\tDon't clone a shallow source repository. In some scenarios, clients\n>> +\twant the cloned repository information to be complete. Otherwise,\n>> +\tthe cloning process should end immediately without creating any files,\n>> +\twhich can save some disk space. This can override `clone.rejectshallow`\n>> +\tfrom the configuration:\n>> +\n>> +\t--------------------------------------------------------------------\n>> +\t$ git -c clone.rejectshallow=false clone --reject-shallow source out\n>> +\t--------------------------------------------------------------------\n>> +\n>> +\tWhile there is a way to countermand a configured \"I always refuse to\n>> +\tclone from a shallow repository\" with \"but let's allow it only this time\":\n>> +\n>> +\t----------------------------------------------------------------------\n>> +\t$ git -c clone.rejectshallow=true clone --no-reject-shallow source out\n>> +\t----------------------------------------------------------------------\n>\n>\n>This is way too verbose and gives unnecessary details that readers\n>already know or do not need to know (e.g. setting configuration from\n>the command line and immediately override it from the command line\n>is not something end-users would EVER need to do---only test writers\n>who develop Git would need it).  Something like\n>\n>    Fail if the source repository is a shallow repository.  The\n>    `clone.rejectShallow` configuration variable can be used to\n>    give the default.  \n>\n>would be sufficient.  All readers ought to know when a configuration\n>and command line option exist, the latter can be used to override\n>the default former gives, and it is *not* a job for the description\n>of an individual option to teach them to such a detail like the\n>above does.\n> \n>> +static int option_no_shallow = -1;  /* unspecified */\n>> +static int config_shallow = -1;    /* unspecified */\n>\n>Hmph.  I would have expected the usual \"prepare a single variable\n>and initialize it to the default, read the config to set it, and\n>then parse the command line to overwrite it\" sequence would suffice\n>so it is puzzling why we want two separate variables here.\n>\n>Let's read on to find out.\n>\n>> @@ -90,6 +92,8 @@ static struct option builtin_clone_options[] = {\n>>  OPT__VERBOSITY(&option_verbosity),\n>>  OPT_BOOL(0, \"progress\", &option_progress,\n>>  N_(\"force progress reporting\")),\n>> +\tOPT_BOOL(0, \"reject-shallow\", &option_no_shallow,\n>> +\tN_(\"don't clone shallow repository\")),\n>>  OPT_BOOL('n', \"no-checkout\", &option_no_checkout,\n>>  N_(\"don't create a checkout\")),\n>>  OPT_BOOL(0, \"bare\", &option_bare, N_(\"create a bare repository\")),\n>> @@ -858,6 +862,9 @@ static int git_clone_config(const char *k, const char *v, void *cb)\n>>  free(remote_name);\n>>  remote_name = xstrdup(v);\n>>  }\n>> +\tif (!strcmp(k, \"clone.rejectshallow\")) {\n>> +\tconfig_shallow = git_config_bool(k, v);\n>> +\t}\n>>  return git_default_config(k, v, cb);\n>>  }\n>\n>You are adding to git_clone_config(), so instead of setting the\n>value to config_shallow, setting the value to the same variable that\n>will be used in builtin_clone_options[] array should be sufficient.\n>\n>cmd_clone() begins like so:\n>\n>\n>\tgit_config(git_clone_config, NULL);\n>\targc = parse_options(...);\n>\n>which means that single variable (let's call it reject_shallow)\n>can (1) stay to be its initial value if no config or option is\n>given, (2) if there is config, the git_config() call will cause\n>that variable assigned, (3) if there is option, parse_options()\n>call will cause that variable assigned, possibly overwriting the\n>value taken from the config.\n> \nSorry, you may forget there is a re-read git-config under these lines\n(around at line 1160):\n    \t/*\n\t * additional config can be injected with -c, make sure it's included\n\t * after init_db, which clears the entire config environment.\n\t */\n\twrite_config(&option_config);\n\n\t/*\n\t * re-read config after init_db and write_config to pick up any config\n\t * injected by --template and --config, respectively.\n\t */\n\tgit_config(git_clone_config, NULL);\n\nso, what I can think of is introducing a new variable for git_clone_config,\nand I find that in the other place, \"define a new config_xxx variable for\ngit-config\" is usual.\n\n>Which is exactly what we want.  So in short, declare just a single\n>\n>    static int reject_shallow; /* default to false */\n>   \n>instead of \"option_no_shallow\" and \"config_shallow\", and use it in\n>both builtin_clone_options[] given to parse_options, and\n>git_clone_config() that is given to git_config(), and you'd be fine,\n>I think.\n>\n>> @@ -963,6 +970,7 @@ static int path_exists(const char *path)\n>>  int cmd_clone(int argc, const char **argv, const char *prefix)\n>>  {\n>>  int is_bundle = 0, is_local;\n>> +\tint is_shallow = 0;\n>>  const char *repo_name, *repo, *work_tree, *git_dir;\n>>  char *path, *dir, *display_repo = NULL;\n>>  int dest_exists, real_dest_exists = 0;\n>> @@ -1205,6 +1213,12 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n>> \n>>  path = get_repo_path(remote->url[0], &is_bundle);\n>>  is_local = option_local != 0 && path && !is_bundle;\n>> +\n>> +\t/* Detect if the remote repository is shallow */\n>> +\tif (!access(mkpath(\"%s/shallow\", path), F_OK)) {\n>> +\tis_shallow = 1;\n>> +\t}\n>\n>This is only for cloning from a local repository, no?  IOW, path at\n>this point may even be \"git://github.com/git/git/\" and checking with\n>access() does not make sense.\n>\n>Ah, it is even worse.  get_repo_path() can return NULL, so mkpath()\n>will crash in such a case.  This must be at least\n>\n>\tif (path && !access(mkpath(\"%s/shallow\", path), F_OK))\n>\tis_shallow = 1;\n>\n>but I think the logic fits better in the body of \"if (is_Local)\"\n>thing that immediately follows.  It is specific to the case where\n>cloning from a local repository and access(mkpath()) that is about\n>the local filesystem (as opposed to going through the transport\n>layer) belongs there.\n>\n>>  if (is_local) {\n>>  if (option_depth)\n>>  warning(_(\"--depth is ignored in local clones; use file:// instead.\"));\n>> @@ -1214,7 +1228,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n>>  warning(_(\"--shallow-exclude is ignored in local clones; use file:// instead.\"));\n>>  if (filter_options.choice)\n>>  warning(_(\"--filter is ignored in local clones; use file:// instead.\"));\n>> -\tif (!access(mkpath(\"%s/shallow\", path), F_OK)) {\n>> +\tif (is_shallow) {\n>>  if (option_local > 0)\n>>  warning(_(\"source repository is shallow, ignoring --local\"));\n>>  is_local = 0;\n>\n>So, I think the above two hunks are making the code worse.  If we\n>are to detect and reject cloning from the shallow repository when\n>going through the transport layer (i.e. \"--no-local\" or cloning from\n>\"git://github.com/git/git\", or \"https://github.com/git/git\", if it\n>were a shallow repository), that must be handled separately.\n> \nSorry, I made the question simple. Reject cloning a shallow repository\nshould apply to all four type transport protocols. There still a bunch of\nwork to be done.\n\n>> @@ -1222,6 +1236,25 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n>>  }\n>>  if (option_local > 0 && !is_local)\n>>  warning(_(\"--local is ignored\"));\n>> +\n>> +\tif (is_shallow) {\n>> +\tint reject = 0;\n>> +\n>> +\t/* If option_no_shallow is specified from CLI option,\n>> +\t* ignore config_shallow from git_clone_config.\n>> +\t*/\n>> +\n>> +\tif (config_shallow != -1) {\n>> +\treject = config_shallow;\n>> +\t}\n>> +\tif (option_no_shallow != -1) {\n>> +\treject = option_no_shallow;\n>> +\t}\n>\n>I do not think any of the above is necessary with just a single\n>reject_shallow variable that is initialized to 0, can be set by\n>git_config() callback, and can further be set by parse_options().\n>\n>> +\tif (reject) {\n>> +\tdie(_(\"source repository is shallow, reject to clone.\"));\n>> +\t}\n>\n>> +\t}\n>> +\n>>  transport->cloning = 1;\n>> \n>>  transport_set_option(transport, TRANS_OPT_KEEP, \"yes\");\n>\n>I do not see how this change would allow users to reject cloning\n>http://github.com/git/git, if that repository were shallow, though. \n>I think that would need changes to the code that interacts with\n>these transport_* functions we see later part of this functrion.\n>\n>Thanks. \n\nOn the whole, thank you for all your patience and suggestions!\nI will dig into the code, and base on your suggestions to figure it out.\n\nFinally, I wish you a Happy Lunar New Year! ^-^\n\n-Lilinchao"},{"id":"416641","messageId":"xmqq5z2z51nb.fsf@gitster.c.googlers.com","threadId":"55104","inReplyTo":"5c4295e46b7f11eb8acc0024e87935e7@oschina.cn","subject":"Re: [PATCH v3] builtin/clone.c: add --reject-shallow option","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-02-10T16:27:20Z","receivedAt":"2021-02-10T16:28:42Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"lilinchao@oschina.cn\" <lilinchao@oschina.cn> writes:\n\n> Sorry, you may forget there is a re-read git-config under these lines\n\nAhhhh, thanks.  You're right that \"clone\" is \"special\" in that way\nand requires unusual order---I forgot about that.\n\n>>This is only for cloning from a local repository, no?  IOW, path at\n>>this point may even be \"git://github.com/git/git/\" and checking with\n>>access() does not make sense.\n>>\n>>Ah, it is even worse.  get_repo_path() can return NULL, so mkpath()\n>>will crash in such a case.  This must be at least\n>>\n>>\tif (path && !access(mkpath(\"%s/shallow\", path), F_OK))\n>>\tis_shallow = 1;\n>> ...\n>>\n>>So, I think the above two hunks are making the code worse.  If we\n>>are to detect and reject cloning from the shallow repository when\n>>going through the transport layer (i.e. \"--no-local\" or cloning from\n>>\"git://github.com/git/git\", or \"https://github.com/git/git\", if it\n>>were a shallow repository), that must be handled separately.\n>> \n> Sorry, I made the question simple. Reject cloning a shallow repository\n> should apply to all four type transport protocols. There still a bunch of\n> work to be done.\n\nSorry, but I didn't realize that this is just a work-in-progress\nthat shows an early \"only local transport is covered\" preview.  I\nthink, modulo that the access(mkpath()) thing should be in the \"if\n(is_local)\" block, the patch covers the case of local transport well\nenough.  I think by \"four types of transport\" you mean ssh://, git://\nhttps:// etc., but they all go through the same transport API, so\nhopefully once one of them, say, git://, is covered, that would take\nus a long way.\n\nThanks.\n"},{"id":"417439","messageId":"1689e980736811eb80250026b95c99cc@oschina.cn","threadId":"55104","inReplyTo":"eaa219a86bbc11ebb6c7a4badb2c2b1165032@pobox.com","subject":"Re: Re: [PATCH v3] builtin/clone.c: add --reject-shallow option","fromName":"lilinchao@oschina.cn","fromEmail":"lilinchao@oschina.cn","sentAt":"2021-02-20T10:40:41Z","receivedAt":"2021-02-20T11:09:59Z","isPatch":true,"sender":{"key":"lilinchao@oschina.cn","avatar":null},"body":"\n\n\n--------------\nlilinchao@oschina.cn\n>\"lilinchao@oschina.cn\" <lilinchao@oschina.cn> writes:\n>\n>> Sorry, you may forget there is a re-read git-config under these lines\n>\n>Ahhhh, thanks.  You're right that \"clone\" is \"special\" in that way\n>and requires unusual order---I forgot about that.\n>\n>>>This is only for cloning from a local repository, no?  IOW, path at\n>>>this point may even be \"git://github.com/git/git/\" and checking with\n>>>access() does not make sense.\n>>>\n>>>Ah, it is even worse.  get_repo_path() can return NULL, so mkpath()\n>>>will crash in such a case.  This must be at least\n>>>\n>>>\tif (path && !access(mkpath(\"%s/shallow\", path), F_OK))\n>>>\tis_shallow = 1;\n>>> ...\n>>>\n>>>So, I think the above two hunks are making the code worse.  If we\n>>>are to detect and reject cloning from the shallow repository when\n>>>going through the transport layer (i.e. \"--no-local\" or cloning from\n>>>\"git://github.com/git/git\", or \"https://github.com/git/git\", if it\n>>>were a shallow repository), that must be handled separately.\n>>>\n>> Sorry, I made the question simple. Reject cloning a shallow repository\n>> should apply to all four type transport protocols. There still a bunch of\n>> work to be done.\n>\n>Sorry, but I didn't realize that this is just a work-in-progress\n>that shows an early \"only local transport is covered\" preview.  I\n>think, modulo that the access(mkpath()) thing should be in the \"if\n>(is_local)\" block, the patch covers the case of local transport well\n>enough.  I think by \"four types of transport\" you mean ssh://, git://\n>https:// etc., but they all go through the same transport API, so\n>hopefully once one of them, say, git://, is covered, that would take\n>us a long way.\n> \nHi, after some exploration, I found that the following changes\ncan achieve the goal:\n(around at line 1386 in builtin/clone.c)\n    /* below part is my changes */\n            if (reject_shallow) {\n                    if (local_shallow || is_repository_shallow(the_repository)) {\n                            die(_(\"source repository is shallow, reject to clone.\"));\n                    }\n            }\n    /* end */\n            update_remote_refs(refs, mapped_refs, remote_head_points_at,\n                               branch_top.buf, reflog_msg.buf, transport,\n                               !is_local);\n\nas it shows, using existing method \"is_repository_shallow\" to detect\nwhether the repo is shallow or not, before \"update_remote_refs\"\nI made some tests based on this change, and it seems ok.\nwhat do you think about this?\n\n\n>Thanks."},{"id":"417451","messageId":"pull.865.v4.git.1613891147977.gitgitgadget@gmail.com","threadId":"55104","inReplyTo":"pull.865.v3.git.1612793576910.gitgitgadget@gmail.com","subject":"[PATCH v4] builtin/clone.c: add --reject-shallow option","fromName":"Li Linchao via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-02-21T07:05:47Z","receivedAt":"2021-02-21T07:06:50Z","isPatch":true,"sender":{"key":"name:Li Linchao","avatar":null},"body":"From: lilinchao <lilinchao@oschina.cn>\n\nIn some scenarios, users may want more history than the repository\noffered for cloning, which happens to be a shallow repository, can\ngive them. But because users don't know it is a shallow repository\nuntil they download it to local, users should have the option to\nrefuse to clone this kind of repository, and may want to exit the\nprocess immediately without creating any unnecessary files.\n\nAlthought there is an option '--depth=x' for users to decide how\ndeep history they can fetch, but as the unshallow cloning's depth\nis INFINITY, we can't know exactly the minimun 'x' value that can\nsatisfy the minimum integrity, so we can't pass 'x' value to --depth,\nand expect this can obtain a complete history of a repository.\n\nIn other scenarios, if we have an API that allow us to import external\nrepository, and then perform various operations on the repo.\nBut if the imported is a shallow one(which is actually possible), it\nwill affect the subsequent operations. So we can choose to refuse to\nclone, and let's just import a normal repository.\n\nThis patch offers a new option '--reject-shallow' that can reject to\nclone a shallow repository.\n\nSigned-off-by: lilinchao <lilinchao@oschina.cn>\n---\n    builtin/clone.c: add --reject-shallow option\n    \n    Changes since v1:\n    \n     * Rename --no-shallow to --reject-shallow\n     * Enable to reject a non-local clone\n     * Enable --[no-]reject-shallow from CLI override configuration.\n     * Add more testcases.\n     * Reword commit messages and relative documentation.\n    \n    Changes since v3:\n    \n     * Add support to reject clone shallow repo over https protocol\n     * Add testcase to reject clone shallow repo over https:// transport\n     * Reword commit messages and relative documentation according\n       suggestions from Junio.\n    \n    Signed-off-by: lilinchao lilinchao@oschina.cn\n\nPublished-As: https://github.com/gitgitgadget/git/releases/tag/pr-865%2FCactusinhand%2Fgit-clone-options-v4\nFetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-865/Cactusinhand/git-clone-options-v4\nPull-Request: https://github.com/gitgitgadget/git/pull/865\n\nRange-diff vs v3:\n\n 1:  f5ed6b2e4481 ! 1:  ee4fb840a32f builtin/clone.c: add --reject-shallow option\n     @@ Commit message\n          builtin/clone.c: add --reject-shallow option\n      \n          In some scenarios, users may want more history than the repository\n     -    offered for cloning, which mostly to be a shallow repository, can\n     -    give them. But users don't know it is a shallow repository until\n     -    they download it to local, users should have the option to refuse\n     -    to clone this kind of repository, and may want to exit the process\n     -    immediately without creating any unnecessary files.\n     +    offered for cloning, which happens to be a shallow repository, can\n     +    give them. But because users don't know it is a shallow repository\n     +    until they download it to local, users should have the option to\n     +    refuse to clone this kind of repository, and may want to exit the\n     +    process immediately without creating any unnecessary files.\n      \n          Althought there is an option '--depth=x' for users to decide how\n          deep history they can fetch, but as the unshallow cloning's depth\n     @@ Commit message\n          satisfy the minimum integrity, so we can't pass 'x' value to --depth,\n          and expect this can obtain a complete history of a repository.\n      \n     -    In other scenarios, given that we have an API that allow us to import\n     -    external repository, and then perform various operations on the repo.\n     +    In other scenarios, if we have an API that allow us to import external\n     +    repository, and then perform various operations on the repo.\n          But if the imported is a shallow one(which is actually possible), it\n          will affect the subsequent operations. So we can choose to refuse to\n     -    clone, and let's just import an unshallow repository.\n     +    clone, and let's just import a normal repository.\n      \n          This patch offers a new option '--reject-shallow' that can reject to\n          clone a shallow repository.\n     @@ Documentation/git-clone.txt: SYNOPSIS\n       \t  [--depth <depth>] [--[no-]single-branch] [--no-tags]\n       \t  [--recurse-submodules[=<pathspec>]] [--[no-]shallow-submodules]\n      -\t  [--[no-]remote-submodules] [--jobs <n>] [--sparse]\n     -+\t  [--[no-]remote-submodules] [--jobs <n>] [--sparse] [--reject-shallow]\n     ++\t  [--[no-]remote-submodules] [--jobs <n>] [--sparse] [--[no-]reject-shallow]\n       \t  [--filter=<filter>] [--] <repository>\n       \t  [<directory>]\n       \n     @@ Documentation/git-clone.txt: objects from the source repository into a pack in t\n       --no-checkout::\n       \tNo checkout of HEAD is performed after the clone is complete.\n       \n     -+--reject-shallow::\n     -+\tDon't clone a shallow source repository. In some scenarios, clients\n     -+\twant the cloned repository information to be complete. Otherwise,\n     -+\tthe cloning process should end immediately without creating any files,\n     -+\twhich can save some disk space. This can override `clone.rejectshallow`\n     -+\tfrom the configuration:\n     -+\n     -+\t--------------------------------------------------------------------\n     -+\t$ git -c clone.rejectshallow=false clone --reject-shallow source out\n     -+\t--------------------------------------------------------------------\n     -+\n     -+\tWhile there is a way to countermand a configured \"I always refuse to\n     -+\tclone from a shallow repository\" with \"but let's allow it only this time\":\n     -+\n     -+\t----------------------------------------------------------------------\n     -+\t$ git -c clone.rejectshallow=true clone --no-reject-shallow source out\n     -+\t----------------------------------------------------------------------\n     ++--[no-]reject-shallow::\n     ++\tFail if the source repository is a shallow repository. The\n     ++\t'clone.rejectshallow' configuration variable can be used to\n     ++\tgive the default.\n      +\n       --bare::\n       \tMake a 'bare' Git repository.  That is, instead of\n       \tcreating `<directory>` and placing the administrative\n      \n       ## builtin/clone.c ##\n     +@@\n     + #include \"connected.h\"\n     + #include \"packfile.h\"\n     + #include \"list-objects-filter-options.h\"\n     ++#include \"shallow.h\"\n     + \n     + /*\n     +  * Overall FIXMEs:\n      @@ builtin/clone.c: static int option_no_checkout, option_bare, option_mirror, option_single_branch\n       static int option_local = -1, option_no_hardlinks, option_shared;\n       static int option_no_tags;\n       static int option_shallow_submodules;\n     -+static int option_no_shallow = -1;  /* unspecified */\n     ++static int option_shallow = -1;    /* unspecified */\n      +static int config_shallow = -1;    /* unspecified */\n       static int deepen;\n       static char *option_template, *option_depth, *option_since;\n     @@ builtin/clone.c: static struct option builtin_clone_options[] = {\n       \tOPT__VERBOSITY(&option_verbosity),\n       \tOPT_BOOL(0, \"progress\", &option_progress,\n       \t\t N_(\"force progress reporting\")),\n     -+\tOPT_BOOL(0, \"reject-shallow\", &option_no_shallow,\n     ++\tOPT_BOOL(0, \"reject-shallow\", &option_shallow,\n      +\t\t N_(\"don't clone shallow repository\")),\n       \tOPT_BOOL('n', \"no-checkout\", &option_no_checkout,\n       \t\t N_(\"don't create a checkout\")),\n     @@ builtin/clone.c: static int path_exists(const char *path)\n       int cmd_clone(int argc, const char **argv, const char *prefix)\n       {\n       \tint is_bundle = 0, is_local;\n     -+\tint is_shallow = 0;\n     ++\tint local_shallow = 0;\n     ++\tint reject_shallow = 0;\n       \tconst char *repo_name, *repo, *work_tree, *git_dir;\n       \tchar *path, *dir, *display_repo = NULL;\n       \tint dest_exists, real_dest_exists = 0;\n      @@ builtin/clone.c: int cmd_clone(int argc, const char **argv, const char *prefix)\n     + \t */\n     + \tgit_config(git_clone_config, NULL);\n       \n     - \tpath = get_repo_path(remote->url[0], &is_bundle);\n     - \tis_local = option_local != 0 && path && !is_bundle;\n     -+\n     -+\t/* Detect if the remote repository is shallow */\n     -+\tif (!access(mkpath(\"%s/shallow\", path), F_OK)) {\n     -+\t\tis_shallow = 1;\n     ++\t/*\n     ++\t * If option_shallow is specified from CLI option,\n     ++\t * ignore config_shallow from git_clone_config.\n     ++\t */\n     ++\tif (config_shallow != -1) {\n     ++\t\treject_shallow = config_shallow;\n      +\t}\n     -+\n     - \tif (is_local) {\n     - \t\tif (option_depth)\n     - \t\t\twarning(_(\"--depth is ignored in local clones; use file:// instead.\"));\n     ++\tif (option_shallow != -1) {\n     ++\t\treject_shallow = option_shallow;\n     ++\t}\n     + \t/*\n     + \t * apply the remote name provided by --origin only after this second\n     + \t * call to git_config, to ensure it overrides all config-based values.\n      @@ builtin/clone.c: int cmd_clone(int argc, const char **argv, const char *prefix)\n     - \t\t\twarning(_(\"--shallow-exclude is ignored in local clones; use file:// instead.\"));\n       \t\tif (filter_options.choice)\n       \t\t\twarning(_(\"--filter is ignored in local clones; use file:// instead.\"));\n     --\t\tif (!access(mkpath(\"%s/shallow\", path), F_OK)) {\n     -+\t\tif (is_shallow) {\n     + \t\tif (!access(mkpath(\"%s/shallow\", path), F_OK)) {\n     ++\t\t\tlocal_shallow = 1;\n       \t\t\tif (option_local > 0)\n       \t\t\t\twarning(_(\"source repository is shallow, ignoring --local\"));\n       \t\t\tis_local = 0;\n      @@ builtin/clone.c: int cmd_clone(int argc, const char **argv, const char *prefix)\n     + \t\t\tgoto cleanup;\n       \t}\n     - \tif (option_local > 0 && !is_local)\n     - \t\twarning(_(\"--local is ignored\"));\n     -+\n     -+\tif (is_shallow) {\n     -+\t\tint reject = 0;\n     -+\n     -+\t\t/* If option_no_shallow is specified from CLI option,\n     -+\t\t * ignore config_shallow from git_clone_config.\n     -+\t\t */\n     -+\n     -+\t\tif (config_shallow != -1) {\n     -+\t\t\treject = config_shallow;\n     -+\t\t}\n     -+\t\tif (option_no_shallow != -1) {\n     -+\t\t\treject = option_no_shallow;\n     -+\t\t}\n     -+\t\tif (reject) {\n     + \n     ++\tif (reject_shallow) {\n     ++\t\tif (local_shallow || is_repository_shallow(the_repository)) {\n      +\t\t\tdie(_(\"source repository is shallow, reject to clone.\"));\n      +\t\t}\n      +\t}\n      +\n     - \ttransport->cloning = 1;\n     - \n     - \ttransport_set_option(transport, TRANS_OPT_KEEP, \"yes\");\n     + \tupdate_remote_refs(refs, mapped_refs, remote_head_points_at,\n     + \t\t\t   branch_top.buf, reflog_msg.buf, transport,\n     + \t\t\t   !is_local);\n      \n       ## t/t5606-clone-options.sh ##\n     +@@ t/t5606-clone-options.sh: GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n     + export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n     + \n     + . ./test-lib.sh\n     ++. \"$TEST_DIRECTORY\"/lib-httpd.sh\n     ++start_httpd\n     + \n     + test_expect_success 'setup' '\n     + \n      @@ t/t5606-clone-options.sh: test_expect_success 'disallows --bare with --separate-git-dir' '\n       \n       '\n       \n     ++test_expect_success 'fail to clone http shallow repository' '\n     ++\tgit clone --depth=1 --no-local parent shallow-repo &&\n     ++\tgit clone --bare --no-local shallow-repo \"$HTTPD_DOCUMENT_ROOT_PATH/repo.git\" &&\n     ++\ttest_must_fail git clone --reject-shallow $HTTPD_URL/smart/repo.git out 2>err &&\n     ++\ttest_i18ngrep -e \"source repository is shallow, reject to clone.\" err\n     ++\n     ++'\n     ++\n      +test_expect_success 'fail to clone shallow repository' '\n     ++\trm -rf shallow-repo &&\n      +\tgit clone --depth=1 --no-local parent shallow-repo &&\n      +\ttest_must_fail git clone --reject-shallow shallow-repo out 2>err &&\n      +\ttest_i18ngrep -e \"source repository is shallow, reject to clone.\" err\n     @@ t/t5611-clone-config.sh: test_expect_success 'clone -c remote.<remote>.fetch=<re\n      +\ttest_i18ngrep -e \"source repository is shallow, reject to clone.\" err\n      +'\n      +\n     -+test_expect_success 'clone.rejectshallow=false should succeed cloning' '\n     ++test_expect_success 'clone.rejectshallow=false should succeed' '\n      +\trm -rf child &&\n      +\tgit clone --depth=1 --no-local . child &&\n      +\tgit -c clone.rejectshallow=false clone --no-local child out\n      +'\n      +\n     -+test_expect_success 'clone.rejectshallow=true should succeed cloning normal repo' '\n     ++test_expect_success 'clone.rejectshallow=true should succeed with normal repo' '\n      +\trm -rf child out &&\n      +\tgit clone --no-local . child &&\n      +\tgit -c clone.rejectshallow=true clone --no-local child out\n     @@ t/t5611-clone-config.sh: test_expect_success 'clone -c remote.<remote>.fetch=<re\n      +test_expect_success 'option --reject-shallow override clone.rejectshallow' '\n      +\trm -rf child out &&\n      +\tgit clone --depth=1 --no-local . child &&\n     -+\ttest_must_fail git clone -c clone.rejectshallow=false  --reject-shallow --no-local child out 2>err &&\n     ++\ttest_must_fail git -c clone.rejectshallow=false clone --reject-shallow --no-local child out 2>err &&\n      +\ttest_i18ngrep -e \"source repository is shallow, reject to clone.\" err\n      +'\n      +\n     -+test_expect_success ' option --no-reject-shallow override clone.rejectshallow' '\n     ++test_expect_success 'option --no-reject-shallow override clone.rejectshallow' '\n      +\trm -rf child &&\n      +\tgit clone --depth=1 --no-local . child &&\n      +\tgit -c clone.rejectshallow=true clone --no-reject-shallow --no-local child out\n\n\n Documentation/config/clone.txt |  4 +++\n Documentation/git-clone.txt    |  7 ++++-\n builtin/clone.c                | 27 +++++++++++++++++++\n t/t5606-clone-options.sh       | 47 ++++++++++++++++++++++++++++++++++\n t/t5611-clone-config.sh        | 32 +++++++++++++++++++++++\n 5 files changed, 116 insertions(+), 1 deletion(-)\n\ndiff --git a/Documentation/config/clone.txt b/Documentation/config/clone.txt\nindex 47de36a5fedf..50ebc170bb81 100644\n--- a/Documentation/config/clone.txt\n+++ b/Documentation/config/clone.txt\n@@ -2,3 +2,7 @@ clone.defaultRemoteName::\n \tThe name of the remote to create when cloning a repository.  Defaults to\n \t`origin`, and can be overridden by passing the `--origin` command-line\n \toption to linkgit:git-clone[1].\n+\n+clone.rejectshallow::\n+\tReject to clone a repository if it is a shallow one, can be overridden by\n+\tpassing option `--reject-shallow` in command line. See linkgit:git-clone[1]\ndiff --git a/Documentation/git-clone.txt b/Documentation/git-clone.txt\nindex 02d9c19cec75..cb458123eef6 100644\n--- a/Documentation/git-clone.txt\n+++ b/Documentation/git-clone.txt\n@@ -15,7 +15,7 @@ SYNOPSIS\n \t  [--dissociate] [--separate-git-dir <git dir>]\n \t  [--depth <depth>] [--[no-]single-branch] [--no-tags]\n \t  [--recurse-submodules[=<pathspec>]] [--[no-]shallow-submodules]\n-\t  [--[no-]remote-submodules] [--jobs <n>] [--sparse]\n+\t  [--[no-]remote-submodules] [--jobs <n>] [--sparse] [--[no-]reject-shallow]\n \t  [--filter=<filter>] [--] <repository>\n \t  [<directory>]\n \n@@ -149,6 +149,11 @@ objects from the source repository into a pack in the cloned repository.\n --no-checkout::\n \tNo checkout of HEAD is performed after the clone is complete.\n \n+--[no-]reject-shallow::\n+\tFail if the source repository is a shallow repository. The\n+\t'clone.rejectshallow' configuration variable can be used to\n+\tgive the default.\n+\n --bare::\n \tMake a 'bare' Git repository.  That is, instead of\n \tcreating `<directory>` and placing the administrative\ndiff --git a/builtin/clone.c b/builtin/clone.c\nindex 51e844a2de0a..6807eb954366 100644\n--- a/builtin/clone.c\n+++ b/builtin/clone.c\n@@ -32,6 +32,7 @@\n #include \"connected.h\"\n #include \"packfile.h\"\n #include \"list-objects-filter-options.h\"\n+#include \"shallow.h\"\n \n /*\n  * Overall FIXMEs:\n@@ -50,6 +51,8 @@ static int option_no_checkout, option_bare, option_mirror, option_single_branch\n static int option_local = -1, option_no_hardlinks, option_shared;\n static int option_no_tags;\n static int option_shallow_submodules;\n+static int option_shallow = -1;    /* unspecified */\n+static int config_shallow = -1;    /* unspecified */\n static int deepen;\n static char *option_template, *option_depth, *option_since;\n static char *option_origin = NULL;\n@@ -90,6 +93,8 @@ static struct option builtin_clone_options[] = {\n \tOPT__VERBOSITY(&option_verbosity),\n \tOPT_BOOL(0, \"progress\", &option_progress,\n \t\t N_(\"force progress reporting\")),\n+\tOPT_BOOL(0, \"reject-shallow\", &option_shallow,\n+\t\t N_(\"don't clone shallow repository\")),\n \tOPT_BOOL('n', \"no-checkout\", &option_no_checkout,\n \t\t N_(\"don't create a checkout\")),\n \tOPT_BOOL(0, \"bare\", &option_bare, N_(\"create a bare repository\")),\n@@ -858,6 +863,9 @@ static int git_clone_config(const char *k, const char *v, void *cb)\n \t\tfree(remote_name);\n \t\tremote_name = xstrdup(v);\n \t}\n+\tif (!strcmp(k, \"clone.rejectshallow\")) {\n+\t\tconfig_shallow = git_config_bool(k, v);\n+\t}\n \treturn git_default_config(k, v, cb);\n }\n \n@@ -963,6 +971,8 @@ static int path_exists(const char *path)\n int cmd_clone(int argc, const char **argv, const char *prefix)\n {\n \tint is_bundle = 0, is_local;\n+\tint local_shallow = 0;\n+\tint reject_shallow = 0;\n \tconst char *repo_name, *repo, *work_tree, *git_dir;\n \tchar *path, *dir, *display_repo = NULL;\n \tint dest_exists, real_dest_exists = 0;\n@@ -1156,6 +1166,16 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n \t */\n \tgit_config(git_clone_config, NULL);\n \n+\t/*\n+\t * If option_shallow is specified from CLI option,\n+\t * ignore config_shallow from git_clone_config.\n+\t */\n+\tif (config_shallow != -1) {\n+\t\treject_shallow = config_shallow;\n+\t}\n+\tif (option_shallow != -1) {\n+\t\treject_shallow = option_shallow;\n+\t}\n \t/*\n \t * apply the remote name provided by --origin only after this second\n \t * call to git_config, to ensure it overrides all config-based values.\n@@ -1216,6 +1236,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n \t\tif (filter_options.choice)\n \t\t\twarning(_(\"--filter is ignored in local clones; use file:// instead.\"));\n \t\tif (!access(mkpath(\"%s/shallow\", path), F_OK)) {\n+\t\t\tlocal_shallow = 1;\n \t\t\tif (option_local > 0)\n \t\t\t\twarning(_(\"source repository is shallow, ignoring --local\"));\n \t\t\tis_local = 0;\n@@ -1363,6 +1384,12 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n \t\t\tgoto cleanup;\n \t}\n \n+\tif (reject_shallow) {\n+\t\tif (local_shallow || is_repository_shallow(the_repository)) {\n+\t\t\tdie(_(\"source repository is shallow, reject to clone.\"));\n+\t\t}\n+\t}\n+\n \tupdate_remote_refs(refs, mapped_refs, remote_head_points_at,\n \t\t\t   branch_top.buf, reflog_msg.buf, transport,\n \t\t\t   !is_local);\ndiff --git a/t/t5606-clone-options.sh b/t/t5606-clone-options.sh\nindex 52e5789fb050..6170d0513227 100755\n--- a/t/t5606-clone-options.sh\n+++ b/t/t5606-clone-options.sh\n@@ -5,6 +5,8 @@ GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n \n . ./test-lib.sh\n+. \"$TEST_DIRECTORY\"/lib-httpd.sh\n+start_httpd\n \n test_expect_success 'setup' '\n \n@@ -45,6 +47,51 @@ test_expect_success 'disallows --bare with --separate-git-dir' '\n \n '\n \n+test_expect_success 'fail to clone http shallow repository' '\n+\tgit clone --depth=1 --no-local parent shallow-repo &&\n+\tgit clone --bare --no-local shallow-repo \"$HTTPD_DOCUMENT_ROOT_PATH/repo.git\" &&\n+\ttest_must_fail git clone --reject-shallow $HTTPD_URL/smart/repo.git out 2>err &&\n+\ttest_i18ngrep -e \"source repository is shallow, reject to clone.\" err\n+\n+'\n+\n+test_expect_success 'fail to clone shallow repository' '\n+\trm -rf shallow-repo &&\n+\tgit clone --depth=1 --no-local parent shallow-repo &&\n+\ttest_must_fail git clone --reject-shallow shallow-repo out 2>err &&\n+\ttest_i18ngrep -e \"source repository is shallow, reject to clone.\" err\n+\n+'\n+\n+test_expect_success 'fail to clone non-local shallow repository' '\n+\trm -rf shallow-repo &&\n+\tgit clone --depth=1 --no-local parent shallow-repo &&\n+\ttest_must_fail git clone --reject-shallow --no-local shallow-repo out 2>err &&\n+\ttest_i18ngrep -e \"source repository is shallow, reject to clone.\" err\n+\n+'\n+\n+test_expect_success 'clone shallow repository with --no-reject-shallow' '\n+\trm -rf shallow-repo &&\n+\tgit clone --depth=1 --no-local parent shallow-repo &&\n+\tgit clone --no-reject-shallow --no-local shallow-repo clone-repo\n+\n+'\n+\n+test_expect_success 'clone normal repository with --reject-shallow' '\n+\trm -rf clone-repo &&\n+\tgit clone --no-local parent normal-repo &&\n+\tgit clone --reject-shallow --no-local normal-repo clone-repo\n+\n+'\n+\n+test_expect_success 'unspecified any configs or options' '\n+\trm -rf shallow-repo clone-repo &&\n+\tgit clone --depth=1 --no-local parent shallow-repo &&\n+\tgit clone shallow-repo clone-repo\n+\n+'\n+\n test_expect_success 'uses \"origin\" for default remote name' '\n \n \tgit clone parent clone-default-origin &&\ndiff --git a/t/t5611-clone-config.sh b/t/t5611-clone-config.sh\nindex 9f555b87ecdf..18268256bfe0 100755\n--- a/t/t5611-clone-config.sh\n+++ b/t/t5611-clone-config.sh\n@@ -95,6 +95,38 @@ test_expect_success 'clone -c remote.<remote>.fetch=<refspec> --origin=<name>' '\n \ttest_cmp expect actual\n '\n \n+test_expect_success 'clone.rejectshallow=true should fail to clone' '\n+\trm -rf child &&\n+\tgit clone --depth=1 --no-local . child &&\n+\ttest_must_fail git -c clone.rejectshallow=true clone --no-local child out 2>err &&\n+\ttest_i18ngrep -e \"source repository is shallow, reject to clone.\" err\n+'\n+\n+test_expect_success 'clone.rejectshallow=false should succeed' '\n+\trm -rf child &&\n+\tgit clone --depth=1 --no-local . child &&\n+\tgit -c clone.rejectshallow=false clone --no-local child out\n+'\n+\n+test_expect_success 'clone.rejectshallow=true should succeed with normal repo' '\n+\trm -rf child out &&\n+\tgit clone --no-local . child &&\n+\tgit -c clone.rejectshallow=true clone --no-local child out\n+'\n+\n+test_expect_success 'option --reject-shallow override clone.rejectshallow' '\n+\trm -rf child out &&\n+\tgit clone --depth=1 --no-local . child &&\n+\ttest_must_fail git -c clone.rejectshallow=false clone --reject-shallow --no-local child out 2>err &&\n+\ttest_i18ngrep -e \"source repository is shallow, reject to clone.\" err\n+'\n+\n+test_expect_success 'option --no-reject-shallow override clone.rejectshallow' '\n+\trm -rf child &&\n+\tgit clone --depth=1 --no-local . child &&\n+\tgit -c clone.rejectshallow=true clone --no-reject-shallow --no-local child out\n+'\n+\n test_expect_success MINGW 'clone -c core.hideDotFiles' '\n \ttest_commit attributes .gitattributes \"\" &&\n \trm -rf child &&\n\nbase-commit: 2283e0e9af55689215afa39c03beb2315ce18e83\n-- \ngitgitgadget\n"},{"id":"417477","messageId":"xmqq35xo7yzy.fsf@gitster.g","threadId":"55104","inReplyTo":"pull.865.v4.git.1613891147977.gitgitgadget@gmail.com","subject":"Re: [PATCH v4] builtin/clone.c: add --reject-shallow option","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-02-22T18:12:33Z","receivedAt":"2021-02-22T18:13:27Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"[jc: I've CC'ed Jonathan Tan, who is much more knowledgeable than I\nam on the transport layer issues, to sanity check my assumption]\n\n\"Li Linchao via GitGitGadget\" <gitgitgadget@gmail.com> writes:\n\n> @@ -1363,6 +1384,12 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n>  \t\t\tgoto cleanup;\n>  \t}\n>  \n> +\tif (reject_shallow) {\n> +\t\tif (local_shallow || is_repository_shallow(the_repository)) {\n\nThis may reject to clone from a shallow repository, but at this\npoint the bulk of the tranfer from the origin repository has already\nhappened, no?  Rejecting after transferring many megabytes feels a\nbit too late.  That is one of the reasons why I kept hinting that\nthe transport layer needs to be taught an option to reject talking\nto a shallow counterpart if we want to add this feature [*1*].\n\nAlso, wouldn't \"clone --depth=1 --reject-shallow\" from a repository\nthat is not shallow make the_repository a shallow one at this point\nand makes it fail?  If the goal of the --reject-shallow option were\nto make sure the resulting repository is not shallow, then that is a\ntechnically correct implementation (even though it is wasteful to\ntransfer a full tree worth of megabytes and then abort), but is the\nfeature is explained to reject cloning from a shallow one, then\nusers would be suprised to see ...\n\n> +\t\t\tdie(_(\"source repository is shallow, reject to clone.\"));\n\n... this message, when cloning from well known publich repositories\nthat are not shallow.\n\nI think cloning with --depth=<n> when the source repository is deep\nenough, should be allowed, so the cleanest solution for the latter\nmay be to notice the combination of options that make the resulting\nrepository shallow (I mentioned --depth=<n>, but there may be others)\nand the --reject-shallow option and error out before even talking\nto the other side at the time we parse the command line.\n\nThanks.\n\n\n[Footnote]\n\n*1* Looking at Documentation/technical/pack-protocol.txt, \"git\n    fetch\" seem to learn if the repository is shallow immediately\n    upon contacting \"upload-pack\" during the Reference Discovery\n    phase (we'd see 'shallow' packets if they are shallow). I\n    suspect that the right solution would be to teach the codepath\n    on the \"git fetch\" side that accepts, parses, and acts on this\n    packet to optionally stop communication and error out when the\n    caller asks not to talk with a shallow repository.\n"},{"id":"417980","messageId":"905d20d879ee11eb9c1e0024e87935e7@oschina.cn","threadId":"55104","inReplyTo":"8f3c00de753911eb93d3d4ae5278bc1270191@pobox.com","subject":"Re: Re: [PATCH v4] builtin/clone.c: add --reject-shallow option","fromName":"lilinchao@oschina.cn","fromEmail":"lilinchao@oschina.cn","sentAt":"2021-02-28T17:58:26Z","receivedAt":"2021-02-28T17:59:18Z","isPatch":true,"sender":{"key":"lilinchao@oschina.cn","avatar":null},"body":"\n--------------\nlilinchao@oschina.cn\n>[jc: I've CC'ed Jonathan Tan, who is much more knowledgeable than I\n>am on the transport layer issues, to sanity check my assumption]\n>\n>\"Li Linchao via GitGitGadget\" <gitgitgadget@gmail.com> writes:\n>\n>> @@ -1363,6 +1384,12 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n>>  goto cleanup;\n>>  }\n>> \n>> +\tif (reject_shallow) {\n>> +\tif (local_shallow || is_repository_shallow(the_repository)) {\n>\n>This may reject to clone from a shallow repository, but at this\n>point the bulk of the tranfer from the origin repository has already\n>happened, no?  Rejecting after transferring many megabytes feels a\n>bit too late.  That is one of the reasons why I kept hinting that\n>the transport layer needs to be taught an option to reject talking\n>to a shallow counterpart if we want to add this feature [*1*].\n>\n>Also, wouldn't \"clone --depth=1 --reject-shallow\" from a repository\n>that is not shallow make the_repository a shallow one at this point\n>and makes it fail?  If the goal of the --reject-shallow option were\n>to make sure the resulting repository is not shallow, then that is a\n>technically correct implementation (even though it is wasteful to\n>transfer a full tree worth of megabytes and then abort), but is the\n>feature is explained to reject cloning from a shallow one, then\n>users would be suprised to see ...\n>\n>> +\tdie(_(\"source repository is shallow, reject to clone.\"));\n>\n>... this message, when cloning from well known publich repositories\n>that are not shallow.\n> \nUh, IMO the goal of this new option is not to make sure the cloned repo\nis not shallow, but to prevent(just as optional) the remote repo is shallow, \nwe still allow the resulting repo is shallow by using \"--depth\" option.\nso, if we apply \"clone --depth=1 --reject-shallow=true\" to a clone process,\nthe expected result is a shallow repo.\nOh, wait, what if we apply \"--depth=1\" to a remote shallow repo, in other word,\nshallow a remote shallow repo? then the result will not be what we expected.\nThis can be confusing.\n\n>I think cloning with --depth=<n> when the source repository is deep\n>enough, should be allowed, so the cleanest solution for the latter\n>may be to notice the combination of options that make the resulting\n>repository shallow (I mentioned --depth=<n>, but there may be others)\n>and the --reject-shallow option and error out before even talking\n>to the other side at the time we parse the command line.\n>\n>Thanks.\n>\n>\n>[Footnote]\n>\n>*1* Looking at Documentation/technical/pack-protocol.txt, \"git\n>    fetch\" seem to learn if the repository is shallow immediately\n>    upon contacting \"upload-pack\" during the Reference Discovery\n>    phase (we'd see 'shallow' packets if they are shallow). I\n>    suspect that the right solution would be to teach the codepath\n>    on the \"git fetch\" side that accepts, parses, and acts on this\n>    packet to optionally stop communication and error out when the\n>    caller asks not to talk with a shallow repository. \n\nI took the time to update the patch as you suggested there, it may look\nimperfect, I only tested the local protocol, and the smart protocols,\nnot dumb protocol, and not protocol version1 yet. \nHope to get some suggestions from you."},{"id":"417981","messageId":"pull.865.v5.git.1614535588332.gitgitgadget@gmail.com","threadId":"55104","inReplyTo":"pull.865.v4.git.1613891147977.gitgitgadget@gmail.com","subject":"[PATCH v5] builtin/clone.c: add --reject-shallow option","fromName":"Li Linchao via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-02-28T18:06:27Z","receivedAt":"2021-02-28T18:07:31Z","isPatch":true,"sender":{"key":"name:Li Linchao","avatar":null},"body":"From: lilinchao <lilinchao@oschina.cn>\n\nIn some scenarios, users may want more history than the repository\noffered for cloning, which happens to be a shallow repository, can\ngive them. But because users don't know it is a shallow repository\nuntil they download it to local, users should have the option to\nrefuse to clone this kind of repository, and may want to exit the\nprocess immediately without creating any unnecessary files.\n\nAlthought there is an option '--depth=x' for users to decide how\ndeep history they can fetch, but as the unshallow cloning's depth\nis INFINITY, we can't know exactly the minimun 'x' value that can\nsatisfy the minimum integrity, so we can't pass 'x' value to --depth,\nand expect this can obtain a complete history of a repository.\n\nIn other scenarios, if we have an API that allow us to import external\nrepository, and then perform various operations on the repo.\nBut if the imported is a shallow one(which is actually possible), it\nwill affect the subsequent operations. So we can choose to refuse to\nclone, and let's just import a normal repository.\n\nThis patch offers a new option '--reject-shallow' that can reject to\nclone a shallow repository.\n\nSigned-off-by: lilinchao <lilinchao@oschina.cn>\n---\n    builtin/clone.c: add --reject-shallow option\n    \n    Changes since v1:\n    \n     * Rename --no-shallow to --reject-shallow\n     * Enable to reject a non-local clone\n     * Enable --[no-]reject-shallow from CLI override configuration.\n     * Add more testcases.\n     * Reword commit messages and relative documentation.\n    \n    Changes since v3:\n    \n     * Add support to reject clone shallow repo over https protocol\n     * Add testcase to reject clone shallow repo over https:// transport\n     * Reword commit messages and relative documentation according\n       suggestions from Junio.\n    \n    Signed-off-by: lilinchao lilinchao@oschina.cn\n\nPublished-As: https://github.com/gitgitgadget/git/releases/tag/pr-865%2FCactusinhand%2Fgit-clone-options-v5\nFetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-865/Cactusinhand/git-clone-options-v5\nPull-Request: https://github.com/gitgitgadget/git/pull/865\n\nRange-diff vs v4:\n\n 1:  ee4fb840a32f ! 1:  3f765e49e4a7 builtin/clone.c: add --reject-shallow option\n     @@ Documentation/git-clone.txt: objects from the source repository into a pack in t\n       \tcreating `<directory>` and placing the administrative\n      \n       ## builtin/clone.c ##\n     -@@\n     - #include \"connected.h\"\n     - #include \"packfile.h\"\n     - #include \"list-objects-filter-options.h\"\n     -+#include \"shallow.h\"\n     - \n     - /*\n     -  * Overall FIXMEs:\n      @@ builtin/clone.c: static int option_no_checkout, option_bare, option_mirror, option_single_branch\n       static int option_local = -1, option_no_hardlinks, option_shared;\n       static int option_no_tags;\n     @@ builtin/clone.c: static int path_exists(const char *path)\n       int cmd_clone(int argc, const char **argv, const char *prefix)\n       {\n       \tint is_bundle = 0, is_local;\n     -+\tint local_shallow = 0;\n      +\tint reject_shallow = 0;\n       \tconst char *repo_name, *repo, *work_tree, *git_dir;\n       \tchar *path, *dir, *display_repo = NULL;\n     @@ builtin/clone.c: int cmd_clone(int argc, const char **argv, const char *prefix)\n       \t\tif (filter_options.choice)\n       \t\t\twarning(_(\"--filter is ignored in local clones; use file:// instead.\"));\n       \t\tif (!access(mkpath(\"%s/shallow\", path), F_OK)) {\n     -+\t\t\tlocal_shallow = 1;\n     ++\t\t\tif (reject_shallow)\n     ++\t\t\t\tdie(\"source repository is shallow, reject to clone.\");\n       \t\t\tif (option_local > 0)\n       \t\t\t\twarning(_(\"source repository is shallow, ignoring --local\"));\n       \t\t\tis_local = 0;\n      @@ builtin/clone.c: int cmd_clone(int argc, const char **argv, const char *prefix)\n     - \t\t\tgoto cleanup;\n     - \t}\n       \n     -+\tif (reject_shallow) {\n     -+\t\tif (local_shallow || is_repository_shallow(the_repository)) {\n     -+\t\t\tdie(_(\"source repository is shallow, reject to clone.\"));\n     -+\t\t}\n     -+\t}\n     + \ttransport_set_option(transport, TRANS_OPT_KEEP, \"yes\");\n     + \n     ++\tif (reject_shallow)\n     ++\t\ttransport_set_option(transport, TRANS_OPT_REJECT_SHALLOW, \"1\");\n     + \tif (option_depth)\n     + \t\ttransport_set_option(transport, TRANS_OPT_DEPTH,\n     + \t\t\t\t     option_depth);\n     +\n     + ## fetch-pack.c ##\n     +@@ fetch-pack.c: static struct ref *do_fetch_pack(struct fetch_pack_args *args,\n     + \n     + \tif (args->stateless_rpc)\n     + \t\tpacket_flush(fd[1]);\n     ++\n     ++\tif (!args->deepen && args->remote_shallow)\n     ++\t\tdie(\"source repository is shallow, reject to clone.\");\n      +\n     - \tupdate_remote_refs(refs, mapped_refs, remote_head_points_at,\n     - \t\t\t   branch_top.buf, reflog_msg.buf, transport,\n     - \t\t\t   !is_local);\n     + \tif (args->deepen)\n     + \t\tsetup_alternate_shallow(&shallow_lock, &alternate_shallow_file,\n     + \t\t\t\t\tNULL);\n     +@@ fetch-pack.c: static void receive_shallow_info(struct fetch_pack_args *args,\n     + \t\t * shallow. In v0, remote refs that reach these objects are\n     + \t\t * rejected (unless --update-shallow is set); do the same.\n     + \t\t */\n     ++\t\tif (args->remote_shallow)\n     ++\t\t\tdie(\"source repository is shallow, reject to clone.\");\n     + \t\tprepare_shallow_info(si, shallows);\n     + \t\tif (si->nr_ours || si->nr_theirs)\n     + \t\t\talternate_shallow_file =\n     +\n     + ## fetch-pack.h ##\n     +@@ fetch-pack.h: struct fetch_pack_args {\n     + \tunsigned self_contained_and_connected:1;\n     + \tunsigned cloning:1;\n     + \tunsigned update_shallow:1;\n     ++\tunsigned remote_shallow:1;\n     + \tunsigned deepen:1;\n     + \n     + \t/*\n      \n       ## t/t5606-clone-options.sh ##\n      @@ t/t5606-clone-options.sh: GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n     @@ t/t5611-clone-config.sh: test_expect_success 'clone -c remote.<remote>.fetch=<re\n      +'\n      +\n      +test_expect_success 'clone.rejectshallow=false should succeed' '\n     -+\trm -rf child &&\n     ++\trm -rf child out &&\n      +\tgit clone --depth=1 --no-local . child &&\n      +\tgit -c clone.rejectshallow=false clone --no-local child out\n      +'\n     @@ t/t5611-clone-config.sh: test_expect_success 'clone -c remote.<remote>.fetch=<re\n      +'\n      +\n      +test_expect_success 'option --no-reject-shallow override clone.rejectshallow' '\n     -+\trm -rf child &&\n     ++\trm -rf child out &&\n      +\tgit clone --depth=1 --no-local . child &&\n      +\tgit -c clone.rejectshallow=true clone --no-reject-shallow --no-local child out\n      +'\n     @@ t/t5611-clone-config.sh: test_expect_success 'clone -c remote.<remote>.fetch=<re\n       test_expect_success MINGW 'clone -c core.hideDotFiles' '\n       \ttest_commit attributes .gitattributes \"\" &&\n       \trm -rf child &&\n     +\n     + ## transport-helper.c ##\n     +@@ transport-helper.c: static const char *boolean_options[] = {\n     + \tTRANS_OPT_THIN,\n     + \tTRANS_OPT_KEEP,\n     + \tTRANS_OPT_FOLLOWTAGS,\n     +-\tTRANS_OPT_DEEPEN_RELATIVE\n     ++\tTRANS_OPT_DEEPEN_RELATIVE,\n     ++\tTRANS_OPT_REJECT_SHALLOW\n     + \t};\n     + \n     + static int strbuf_set_helper_option(struct helper_data *data,\n     +\n     + ## transport.c ##\n     +@@ transport.c: static int set_git_option(struct git_transport_options *opts,\n     + \t\tlist_objects_filter_die_if_populated(&opts->filter_options);\n     + \t\tparse_list_objects_filter(&opts->filter_options, value);\n     + \t\treturn 0;\n     ++\t} else if (!strcmp(name, TRANS_OPT_REJECT_SHALLOW)) {\n     ++\t\topts->reject_shallow = !!value;\n     ++\t\treturn 0;\n     + \t}\n     + \treturn 1;\n     + }\n     +@@ transport.c: static int fetch_refs_via_pack(struct transport *transport,\n     + \targs.stateless_rpc = transport->stateless_rpc;\n     + \targs.server_options = transport->server_options;\n     + \targs.negotiation_tips = data->options.negotiation_tips;\n     ++\targs.remote_shallow = transport->smart_options->reject_shallow;\n     + \n     + \tif (!data->got_remote_heads) {\n     + \t\tint i;\n     +\n     + ## transport.h ##\n     +@@ transport.h: struct git_transport_options {\n     + \tunsigned check_self_contained_and_connected : 1;\n     + \tunsigned self_contained_and_connected : 1;\n     + \tunsigned update_shallow : 1;\n     ++\tunsigned reject_shallow : 1;\n     + \tunsigned deepen_relative : 1;\n     + \n     + \t/* see documentation of corresponding flag in fetch-pack.h */\n     +@@ transport.h: void transport_check_allowed(const char *type);\n     + /* Aggressively fetch annotated tags if possible */\n     + #define TRANS_OPT_FOLLOWTAGS \"followtags\"\n     + \n     ++/* reject shallow repo transport  */\n     ++#define TRANS_OPT_REJECT_SHALLOW \"rejectshallow\"\n     ++\n     + /* Accept refs that may update .git/shallow without --depth */\n     + #define TRANS_OPT_UPDATE_SHALLOW \"updateshallow\"\n     + \n\n\n Documentation/config/clone.txt |  4 +++\n Documentation/git-clone.txt    |  7 ++++-\n builtin/clone.c                | 22 ++++++++++++++++\n fetch-pack.c                   |  6 +++++\n fetch-pack.h                   |  1 +\n t/t5606-clone-options.sh       | 47 ++++++++++++++++++++++++++++++++++\n t/t5611-clone-config.sh        | 32 +++++++++++++++++++++++\n transport-helper.c             |  3 ++-\n transport.c                    |  4 +++\n transport.h                    |  4 +++\n 10 files changed, 128 insertions(+), 2 deletions(-)\n\ndiff --git a/Documentation/config/clone.txt b/Documentation/config/clone.txt\nindex 47de36a5fedf..50ebc170bb81 100644\n--- a/Documentation/config/clone.txt\n+++ b/Documentation/config/clone.txt\n@@ -2,3 +2,7 @@ clone.defaultRemoteName::\n \tThe name of the remote to create when cloning a repository.  Defaults to\n \t`origin`, and can be overridden by passing the `--origin` command-line\n \toption to linkgit:git-clone[1].\n+\n+clone.rejectshallow::\n+\tReject to clone a repository if it is a shallow one, can be overridden by\n+\tpassing option `--reject-shallow` in command line. See linkgit:git-clone[1]\ndiff --git a/Documentation/git-clone.txt b/Documentation/git-clone.txt\nindex 02d9c19cec75..cb458123eef6 100644\n--- a/Documentation/git-clone.txt\n+++ b/Documentation/git-clone.txt\n@@ -15,7 +15,7 @@ SYNOPSIS\n \t  [--dissociate] [--separate-git-dir <git dir>]\n \t  [--depth <depth>] [--[no-]single-branch] [--no-tags]\n \t  [--recurse-submodules[=<pathspec>]] [--[no-]shallow-submodules]\n-\t  [--[no-]remote-submodules] [--jobs <n>] [--sparse]\n+\t  [--[no-]remote-submodules] [--jobs <n>] [--sparse] [--[no-]reject-shallow]\n \t  [--filter=<filter>] [--] <repository>\n \t  [<directory>]\n \n@@ -149,6 +149,11 @@ objects from the source repository into a pack in the cloned repository.\n --no-checkout::\n \tNo checkout of HEAD is performed after the clone is complete.\n \n+--[no-]reject-shallow::\n+\tFail if the source repository is a shallow repository. The\n+\t'clone.rejectshallow' configuration variable can be used to\n+\tgive the default.\n+\n --bare::\n \tMake a 'bare' Git repository.  That is, instead of\n \tcreating `<directory>` and placing the administrative\ndiff --git a/builtin/clone.c b/builtin/clone.c\nindex 51e844a2de0a..70d8ca77988f 100644\n--- a/builtin/clone.c\n+++ b/builtin/clone.c\n@@ -50,6 +50,8 @@ static int option_no_checkout, option_bare, option_mirror, option_single_branch\n static int option_local = -1, option_no_hardlinks, option_shared;\n static int option_no_tags;\n static int option_shallow_submodules;\n+static int option_shallow = -1;    /* unspecified */\n+static int config_shallow = -1;    /* unspecified */\n static int deepen;\n static char *option_template, *option_depth, *option_since;\n static char *option_origin = NULL;\n@@ -90,6 +92,8 @@ static struct option builtin_clone_options[] = {\n \tOPT__VERBOSITY(&option_verbosity),\n \tOPT_BOOL(0, \"progress\", &option_progress,\n \t\t N_(\"force progress reporting\")),\n+\tOPT_BOOL(0, \"reject-shallow\", &option_shallow,\n+\t\t N_(\"don't clone shallow repository\")),\n \tOPT_BOOL('n', \"no-checkout\", &option_no_checkout,\n \t\t N_(\"don't create a checkout\")),\n \tOPT_BOOL(0, \"bare\", &option_bare, N_(\"create a bare repository\")),\n@@ -858,6 +862,9 @@ static int git_clone_config(const char *k, const char *v, void *cb)\n \t\tfree(remote_name);\n \t\tremote_name = xstrdup(v);\n \t}\n+\tif (!strcmp(k, \"clone.rejectshallow\")) {\n+\t\tconfig_shallow = git_config_bool(k, v);\n+\t}\n \treturn git_default_config(k, v, cb);\n }\n \n@@ -963,6 +970,7 @@ static int path_exists(const char *path)\n int cmd_clone(int argc, const char **argv, const char *prefix)\n {\n \tint is_bundle = 0, is_local;\n+\tint reject_shallow = 0;\n \tconst char *repo_name, *repo, *work_tree, *git_dir;\n \tchar *path, *dir, *display_repo = NULL;\n \tint dest_exists, real_dest_exists = 0;\n@@ -1156,6 +1164,16 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n \t */\n \tgit_config(git_clone_config, NULL);\n \n+\t/*\n+\t * If option_shallow is specified from CLI option,\n+\t * ignore config_shallow from git_clone_config.\n+\t */\n+\tif (config_shallow != -1) {\n+\t\treject_shallow = config_shallow;\n+\t}\n+\tif (option_shallow != -1) {\n+\t\treject_shallow = option_shallow;\n+\t}\n \t/*\n \t * apply the remote name provided by --origin only after this second\n \t * call to git_config, to ensure it overrides all config-based values.\n@@ -1216,6 +1234,8 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n \t\tif (filter_options.choice)\n \t\t\twarning(_(\"--filter is ignored in local clones; use file:// instead.\"));\n \t\tif (!access(mkpath(\"%s/shallow\", path), F_OK)) {\n+\t\t\tif (reject_shallow)\n+\t\t\t\tdie(\"source repository is shallow, reject to clone.\");\n \t\t\tif (option_local > 0)\n \t\t\t\twarning(_(\"source repository is shallow, ignoring --local\"));\n \t\t\tis_local = 0;\n@@ -1227,6 +1247,8 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n \n \ttransport_set_option(transport, TRANS_OPT_KEEP, \"yes\");\n \n+\tif (reject_shallow)\n+\t\ttransport_set_option(transport, TRANS_OPT_REJECT_SHALLOW, \"1\");\n \tif (option_depth)\n \t\ttransport_set_option(transport, TRANS_OPT_DEPTH,\n \t\t\t\t     option_depth);\ndiff --git a/fetch-pack.c b/fetch-pack.c\nindex 1eaedcb5dc2e..41ef700bde38 100644\n--- a/fetch-pack.c\n+++ b/fetch-pack.c\n@@ -1071,6 +1071,10 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args,\n \n \tif (args->stateless_rpc)\n \t\tpacket_flush(fd[1]);\n+\n+\tif (!args->deepen && args->remote_shallow)\n+\t\tdie(\"source repository is shallow, reject to clone.\");\n+\n \tif (args->deepen)\n \t\tsetup_alternate_shallow(&shallow_lock, &alternate_shallow_file,\n \t\t\t\t\tNULL);\n@@ -1440,6 +1444,8 @@ static void receive_shallow_info(struct fetch_pack_args *args,\n \t\t * shallow. In v0, remote refs that reach these objects are\n \t\t * rejected (unless --update-shallow is set); do the same.\n \t\t */\n+\t\tif (args->remote_shallow)\n+\t\t\tdie(\"source repository is shallow, reject to clone.\");\n \t\tprepare_shallow_info(si, shallows);\n \t\tif (si->nr_ours || si->nr_theirs)\n \t\t\talternate_shallow_file =\ndiff --git a/fetch-pack.h b/fetch-pack.h\nindex 736a3dae467a..6e4f8f0d738c 100644\n--- a/fetch-pack.h\n+++ b/fetch-pack.h\n@@ -39,6 +39,7 @@ struct fetch_pack_args {\n \tunsigned self_contained_and_connected:1;\n \tunsigned cloning:1;\n \tunsigned update_shallow:1;\n+\tunsigned remote_shallow:1;\n \tunsigned deepen:1;\n \n \t/*\ndiff --git a/t/t5606-clone-options.sh b/t/t5606-clone-options.sh\nindex 52e5789fb050..6170d0513227 100755\n--- a/t/t5606-clone-options.sh\n+++ b/t/t5606-clone-options.sh\n@@ -5,6 +5,8 @@ GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n \n . ./test-lib.sh\n+. \"$TEST_DIRECTORY\"/lib-httpd.sh\n+start_httpd\n \n test_expect_success 'setup' '\n \n@@ -45,6 +47,51 @@ test_expect_success 'disallows --bare with --separate-git-dir' '\n \n '\n \n+test_expect_success 'fail to clone http shallow repository' '\n+\tgit clone --depth=1 --no-local parent shallow-repo &&\n+\tgit clone --bare --no-local shallow-repo \"$HTTPD_DOCUMENT_ROOT_PATH/repo.git\" &&\n+\ttest_must_fail git clone --reject-shallow $HTTPD_URL/smart/repo.git out 2>err &&\n+\ttest_i18ngrep -e \"source repository is shallow, reject to clone.\" err\n+\n+'\n+\n+test_expect_success 'fail to clone shallow repository' '\n+\trm -rf shallow-repo &&\n+\tgit clone --depth=1 --no-local parent shallow-repo &&\n+\ttest_must_fail git clone --reject-shallow shallow-repo out 2>err &&\n+\ttest_i18ngrep -e \"source repository is shallow, reject to clone.\" err\n+\n+'\n+\n+test_expect_success 'fail to clone non-local shallow repository' '\n+\trm -rf shallow-repo &&\n+\tgit clone --depth=1 --no-local parent shallow-repo &&\n+\ttest_must_fail git clone --reject-shallow --no-local shallow-repo out 2>err &&\n+\ttest_i18ngrep -e \"source repository is shallow, reject to clone.\" err\n+\n+'\n+\n+test_expect_success 'clone shallow repository with --no-reject-shallow' '\n+\trm -rf shallow-repo &&\n+\tgit clone --depth=1 --no-local parent shallow-repo &&\n+\tgit clone --no-reject-shallow --no-local shallow-repo clone-repo\n+\n+'\n+\n+test_expect_success 'clone normal repository with --reject-shallow' '\n+\trm -rf clone-repo &&\n+\tgit clone --no-local parent normal-repo &&\n+\tgit clone --reject-shallow --no-local normal-repo clone-repo\n+\n+'\n+\n+test_expect_success 'unspecified any configs or options' '\n+\trm -rf shallow-repo clone-repo &&\n+\tgit clone --depth=1 --no-local parent shallow-repo &&\n+\tgit clone shallow-repo clone-repo\n+\n+'\n+\n test_expect_success 'uses \"origin\" for default remote name' '\n \n \tgit clone parent clone-default-origin &&\ndiff --git a/t/t5611-clone-config.sh b/t/t5611-clone-config.sh\nindex 9f555b87ecdf..da10d3f10352 100755\n--- a/t/t5611-clone-config.sh\n+++ b/t/t5611-clone-config.sh\n@@ -95,6 +95,38 @@ test_expect_success 'clone -c remote.<remote>.fetch=<refspec> --origin=<name>' '\n \ttest_cmp expect actual\n '\n \n+test_expect_success 'clone.rejectshallow=true should fail to clone' '\n+\trm -rf child &&\n+\tgit clone --depth=1 --no-local . child &&\n+\ttest_must_fail git -c clone.rejectshallow=true clone --no-local child out 2>err &&\n+\ttest_i18ngrep -e \"source repository is shallow, reject to clone.\" err\n+'\n+\n+test_expect_success 'clone.rejectshallow=false should succeed' '\n+\trm -rf child out &&\n+\tgit clone --depth=1 --no-local . child &&\n+\tgit -c clone.rejectshallow=false clone --no-local child out\n+'\n+\n+test_expect_success 'clone.rejectshallow=true should succeed with normal repo' '\n+\trm -rf child out &&\n+\tgit clone --no-local . child &&\n+\tgit -c clone.rejectshallow=true clone --no-local child out\n+'\n+\n+test_expect_success 'option --reject-shallow override clone.rejectshallow' '\n+\trm -rf child out &&\n+\tgit clone --depth=1 --no-local . child &&\n+\ttest_must_fail git -c clone.rejectshallow=false clone --reject-shallow --no-local child out 2>err &&\n+\ttest_i18ngrep -e \"source repository is shallow, reject to clone.\" err\n+'\n+\n+test_expect_success 'option --no-reject-shallow override clone.rejectshallow' '\n+\trm -rf child out &&\n+\tgit clone --depth=1 --no-local . child &&\n+\tgit -c clone.rejectshallow=true clone --no-reject-shallow --no-local child out\n+'\n+\n test_expect_success MINGW 'clone -c core.hideDotFiles' '\n \ttest_commit attributes .gitattributes \"\" &&\n \trm -rf child &&\ndiff --git a/transport-helper.c b/transport-helper.c\nindex 49b7fb4dcb9a..b57b55c8180c 100644\n--- a/transport-helper.c\n+++ b/transport-helper.c\n@@ -265,7 +265,8 @@ static const char *boolean_options[] = {\n \tTRANS_OPT_THIN,\n \tTRANS_OPT_KEEP,\n \tTRANS_OPT_FOLLOWTAGS,\n-\tTRANS_OPT_DEEPEN_RELATIVE\n+\tTRANS_OPT_DEEPEN_RELATIVE,\n+\tTRANS_OPT_REJECT_SHALLOW\n \t};\n \n static int strbuf_set_helper_option(struct helper_data *data,\ndiff --git a/transport.c b/transport.c\nindex b13fab5dc3b1..34fe01221ee0 100644\n--- a/transport.c\n+++ b/transport.c\n@@ -236,6 +236,9 @@ static int set_git_option(struct git_transport_options *opts,\n \t\tlist_objects_filter_die_if_populated(&opts->filter_options);\n \t\tparse_list_objects_filter(&opts->filter_options, value);\n \t\treturn 0;\n+\t} else if (!strcmp(name, TRANS_OPT_REJECT_SHALLOW)) {\n+\t\topts->reject_shallow = !!value;\n+\t\treturn 0;\n \t}\n \treturn 1;\n }\n@@ -370,6 +373,7 @@ static int fetch_refs_via_pack(struct transport *transport,\n \targs.stateless_rpc = transport->stateless_rpc;\n \targs.server_options = transport->server_options;\n \targs.negotiation_tips = data->options.negotiation_tips;\n+\targs.remote_shallow = transport->smart_options->reject_shallow;\n \n \tif (!data->got_remote_heads) {\n \t\tint i;\ndiff --git a/transport.h b/transport.h\nindex 24e15799e714..f6273d268b09 100644\n--- a/transport.h\n+++ b/transport.h\n@@ -14,6 +14,7 @@ struct git_transport_options {\n \tunsigned check_self_contained_and_connected : 1;\n \tunsigned self_contained_and_connected : 1;\n \tunsigned update_shallow : 1;\n+\tunsigned reject_shallow : 1;\n \tunsigned deepen_relative : 1;\n \n \t/* see documentation of corresponding flag in fetch-pack.h */\n@@ -194,6 +195,9 @@ void transport_check_allowed(const char *type);\n /* Aggressively fetch annotated tags if possible */\n #define TRANS_OPT_FOLLOWTAGS \"followtags\"\n \n+/* reject shallow repo transport  */\n+#define TRANS_OPT_REJECT_SHALLOW \"rejectshallow\"\n+\n /* Accept refs that may update .git/shallow without --depth */\n #define TRANS_OPT_UPDATE_SHALLOW \"updateshallow\"\n \n\nbase-commit: 225365fb5195e804274ab569ac3cc4919451dc7f\n-- \ngitgitgadget\n"},{"id":"418004","messageId":"697bdaee7a5d11eb919e0026b95c99cc@oschina.cn","threadId":"55104","inReplyTo":"pull.865.v5.git.1614535588332.gitgitgadget@gmail.com","subject":"Re: [PATCH v5] builtin/clone.c: add --reject-shallow option","fromName":"lilinchao@oschina.cn","fromEmail":"lilinchao@oschina.cn","sentAt":"2021-03-01T07:11:54Z","receivedAt":"2021-03-01T07:13:31Z","isPatch":true,"sender":{"key":"lilinchao@oschina.cn","avatar":null},"body":"\n\n\n--------------\nlilinchao@oschina.cn\n>From: lilinchao <lilinchao@oschina.cn>\n>\n>In some scenarios, users may want more history than the repository\n>offered for cloning, which happens to be a shallow repository, can\n>give them. But because users don't know it is a shallow repository\n>until they download it to local, users should have the option to\n>refuse to clone this kind of repository, and may want to exit the\n>process immediately without creating any unnecessary files.\n>\n>Althought there is an option '--depth=x' for users to decide how\n>deep history they can fetch, but as the unshallow cloning's depth\n>is INFINITY, we can't know exactly the minimun 'x' value that can\n>satisfy the minimum integrity, so we can't pass 'x' value to --depth,\n>and expect this can obtain a complete history of a repository.\n>\n>In other scenarios, if we have an API that allow us to import external\n>repository, and then perform various operations on the repo.\n>But if the imported is a shallow one(which is actually possible), it\n>will affect the subsequent operations. So we can choose to refuse to\n>clone, and let's just import a normal repository.\n>\n>This patch offers a new option '--reject-shallow' that can reject to\n>clone a shallow repository.\n>\n>Signed-off-by: lilinchao <lilinchao@oschina.cn>\n>---\n>    builtin/clone.c: add --reject-shallow option\n>   \n>    Changes since v1:\n>   \n>     * Rename --no-shallow to --reject-shallow\n>     * Enable to reject a non-local clone\n>     * Enable --[no-]reject-shallow from CLI override configuration.\n>     * Add more testcases.\n>     * Reword commit messages and relative documentation.\n>   \n>    Changes since v3:\n>   \n>     * Add support to reject clone shallow repo over https protocol\n> * Add testcase to reject clone shallow repo over https:// transport\n>     * Reword commit messages and relative documentation according\n>       suggestions from Junio.\n>   \n>    Signed-off-by: lilinchao lilinchao@oschina.cn\n>\n>Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-865%2FCactusinhand%2Fgit-clone-options-v5\n>Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-865/Cactusinhand/git-clone-options-v5\n>Pull-Request: https://github.com/gitgitgadget/git/pull/865\n>\n>Range-diff vs v4:\n>\n> 1:  ee4fb840a32f ! 1:  3f765e49e4a7 builtin/clone.c: add --reject-shallow option\n>     @@ Documentation/git-clone.txt: objects from the source repository into a pack in t\n>       creating `<directory>` and placing the administrative\n>     \n>       ## builtin/clone.c ##\n>     -@@\n>     - #include \"connected.h\"\n>     - #include \"packfile.h\"\n>     - #include \"list-objects-filter-options.h\"\n>     -+#include \"shallow.h\"\n>     -\n>     - /*\n>     -  * Overall FIXMEs:\n>      @@ builtin/clone.c: static int option_no_checkout, option_bare, option_mirror, option_single_branch\n>       static int option_local = -1, option_no_hardlinks, option_shared;\n>       static int option_no_tags;\n>     @@ builtin/clone.c: static int path_exists(const char *path)\n>       int cmd_clone(int argc, const char **argv, const char *prefix)\n>       {\n>       int is_bundle = 0, is_local;\n>     -+\tint local_shallow = 0;\n>      +\tint reject_shallow = 0;\n>       const char *repo_name, *repo, *work_tree, *git_dir;\n>       char *path, *dir, *display_repo = NULL;\n>     @@ builtin/clone.c: int cmd_clone(int argc, const char **argv, const char *prefix)\n>       if (filter_options.choice)\n>       warning(_(\"--filter is ignored in local clones; use file:// instead.\"));\n>       if (!access(mkpath(\"%s/shallow\", path), F_OK)) {\n>     -+\tlocal_shallow = 1;\n>     ++\tif (reject_shallow)\n>     ++\tdie(\"source repository is shallow, reject to clone.\");\n>       if (option_local > 0)\n>       warning(_(\"source repository is shallow, ignoring --local\"));\n>       is_local = 0;\n>      @@ builtin/clone.c: int cmd_clone(int argc, const char **argv, const char *prefix)\n>     - goto cleanup;\n>     - }\n>      \n>     -+\tif (reject_shallow) {\n>     -+\tif (local_shallow || is_repository_shallow(the_repository)) {\n>     -+\tdie(_(\"source repository is shallow, reject to clone.\"));\n>     -+\t}\n>     -+\t}\n>     + transport_set_option(transport, TRANS_OPT_KEEP, \"yes\");\n>     +\n>     ++\tif (reject_shallow)\n>     ++\ttransport_set_option(transport, TRANS_OPT_REJECT_SHALLOW, \"1\");\n>     + if (option_depth)\n>     + transport_set_option(transport, TRANS_OPT_DEPTH,\n>     +      option_depth);\n>     +\n>     + ## fetch-pack.c ##\n>     +@@ fetch-pack.c: static struct ref *do_fetch_pack(struct fetch_pack_args *args,\n>     +\n>     + if (args->stateless_rpc)\n>     + packet_flush(fd[1]);\n>     ++\n>     ++\tif (!args->deepen && args->remote_shallow)\n>     ++\tdie(\"source repository is shallow, reject to clone.\");\n>      +\n>     - update_remote_refs(refs, mapped_refs, remote_head_points_at,\n>     -    branch_top.buf, reflog_msg.buf, transport,\n>     -    !is_local);\n>     + if (args->deepen)\n>     + setup_alternate_shallow(&shallow_lock, &alternate_shallow_file,\n>     + NULL);\n>     +@@ fetch-pack.c: static void receive_shallow_info(struct fetch_pack_args *args,\n>     + * shallow. In v0, remote refs that reach these objects are\n>     + * rejected (unless --update-shallow is set); do the same.\n>     + */\n>     ++\tif (args->remote_shallow)\n>     ++\tdie(\"source repository is shallow, reject to clone.\");\n>     + prepare_shallow_info(si, shallows);\n>     + if (si->nr_ours || si->nr_theirs)\n>     + alternate_shallow_file =\n>     +\n>     + ## fetch-pack.h ##\n>     +@@ fetch-pack.h: struct fetch_pack_args {\n>     + unsigned self_contained_and_connected:1;\n>     + unsigned cloning:1;\n>     + unsigned update_shallow:1;\n>     ++\tunsigned remote_shallow:1;\n>     + unsigned deepen:1;\n>     +\n>     + /*\n>     \n>       ## t/t5606-clone-options.sh ##\n>      @@ t/t5606-clone-options.sh: GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n>     @@ t/t5611-clone-config.sh: test_expect_success 'clone -c remote.<remote>.fetch=<re\n>      +'\n>      +\n>      +test_expect_success 'clone.rejectshallow=false should succeed' '\n>     -+\trm -rf child &&\n>     ++\trm -rf child out &&\n>      +\tgit clone --depth=1 --no-local . child &&\n>      +\tgit -c clone.rejectshallow=false clone --no-local child out\n>      +'\n>     @@ t/t5611-clone-config.sh: test_expect_success 'clone -c remote.<remote>.fetch=<re\n>      +'\n>      +\n>      +test_expect_success 'option --no-reject-shallow override clone.rejectshallow' '\n>     -+\trm -rf child &&\n>     ++\trm -rf child out &&\n>      +\tgit clone --depth=1 --no-local . child &&\n>      +\tgit -c clone.rejectshallow=true clone --no-reject-shallow --no-local child out\n>      +'\n>     @@ t/t5611-clone-config.sh: test_expect_success 'clone -c remote.<remote>.fetch=<re\n>       test_expect_success MINGW 'clone -c core.hideDotFiles' '\n>       test_commit attributes .gitattributes \"\" &&\n>       rm -rf child &&\n>     +\n>     + ## transport-helper.c ##\n>     +@@ transport-helper.c: static const char *boolean_options[] = {\n>     + TRANS_OPT_THIN,\n>     + TRANS_OPT_KEEP,\n>     + TRANS_OPT_FOLLOWTAGS,\n>     +-\tTRANS_OPT_DEEPEN_RELATIVE\n>     ++\tTRANS_OPT_DEEPEN_RELATIVE,\n>     ++\tTRANS_OPT_REJECT_SHALLOW\n>     + };\n>     +\n>     + static int strbuf_set_helper_option(struct helper_data *data,\n>     +\n>     + ## transport.c ##\n>     +@@ transport.c: static int set_git_option(struct git_transport_options *opts,\n>     + list_objects_filter_die_if_populated(&opts->filter_options);\n>     + parse_list_objects_filter(&opts->filter_options, value);\n>     + return 0;\n>     ++\t} else if (!strcmp(name, TRANS_OPT_REJECT_SHALLOW)) {\n>     ++\topts->reject_shallow = !!value;\n>     ++\treturn 0;\n>     + }\n>     + return 1;\n>     + }\n>     +@@ transport.c: static int fetch_refs_via_pack(struct transport *transport,\n>     + args.stateless_rpc = transport->stateless_rpc;\n>     + args.server_options = transport->server_options;\n>     + args.negotiation_tips = data->options.negotiation_tips;\n>     ++\targs.remote_shallow = transport->smart_options->reject_shallow;\n>     +\n>     + if (!data->got_remote_heads) {\n>     + int i;\n>     +\n>     + ## transport.h ##\n>     +@@ transport.h: struct git_transport_options {\n>     + unsigned check_self_contained_and_connected : 1;\n>     + unsigned self_contained_and_connected : 1;\n>     + unsigned update_shallow : 1;\n>     ++\tunsigned reject_shallow : 1;\n>     + unsigned deepen_relative : 1;\n>     +\n>     + /* see documentation of corresponding flag in fetch-pack.h */\n>     +@@ transport.h: void transport_check_allowed(const char *type);\n>     + /* Aggressively fetch annotated tags if possible */\n>     + #define TRANS_OPT_FOLLOWTAGS \"followtags\"\n>     +\n>     ++/* reject shallow repo transport  */\n>     ++#define TRANS_OPT_REJECT_SHALLOW \"rejectshallow\"\n>     ++\n>     + /* Accept refs that may update .git/shallow without --depth */\n>     + #define TRANS_OPT_UPDATE_SHALLOW \"updateshallow\"\n>     +\n>\n>\n> Documentation/config/clone.txt |  4 +++\n> Documentation/git-clone.txt    |  7 ++++-\n> builtin/clone.c                | 22 ++++++++++++++++\n> fetch-pack.c                   |  6 +++++\n> fetch-pack.h                   |  1 +\n> t/t5606-clone-options.sh       | 47 ++++++++++++++++++++++++++++++++++\n> t/t5611-clone-config.sh        | 32 +++++++++++++++++++++++\n> transport-helper.c             |  3 ++-\n> transport.c                    |  4 +++\n> transport.h                    |  4 +++\n> 10 files changed, 128 insertions(+), 2 deletions(-)\n>\n>diff --git a/Documentation/config/clone.txt b/Documentation/config/clone.txt\n>index 47de36a5fedf..50ebc170bb81 100644\n>--- a/Documentation/config/clone.txt\n>+++ b/Documentation/config/clone.txt\n>@@ -2,3 +2,7 @@ clone.defaultRemoteName::\n> The name of the remote to create when cloning a repository.  Defaults to\n> `origin`, and can be overridden by passing the `--origin` command-line\n> option to linkgit:git-clone[1].\n>+\n>+clone.rejectshallow::\n>+\tReject to clone a repository if it is a shallow one, can be overridden by\n>+\tpassing option `--reject-shallow` in command line. See linkgit:git-clone[1]\n>diff --git a/Documentation/git-clone.txt b/Documentation/git-clone.txt\n>index 02d9c19cec75..cb458123eef6 100644\n>--- a/Documentation/git-clone.txt\n>+++ b/Documentation/git-clone.txt\n>@@ -15,7 +15,7 @@ SYNOPSIS\n>   [--dissociate] [--separate-git-dir <git dir>]\n>   [--depth <depth>] [--[no-]single-branch] [--no-tags]\n>   [--recurse-submodules[=<pathspec>]] [--[no-]shallow-submodules]\n>-\t  [--[no-]remote-submodules] [--jobs <n>] [--sparse]\n>+\t  [--[no-]remote-submodules] [--jobs <n>] [--sparse] [--[no-]reject-shallow]\n>   [--filter=<filter>] [--] <repository>\n>   [<directory>]\n>\n>@@ -149,6 +149,11 @@ objects from the source repository into a pack in the cloned repository.\n> --no-checkout::\n> No checkout of HEAD is performed after the clone is complete.\n>\n>+--[no-]reject-shallow::\n>+\tFail if the source repository is a shallow repository. The\n>+\t'clone.rejectshallow' configuration variable can be used to\n>+\tgive the default.\n>+\n> --bare::\n> Make a 'bare' Git repository.  That is, instead of\n> creating `<directory>` and placing the administrative\n>diff --git a/builtin/clone.c b/builtin/clone.c\n>index 51e844a2de0a..70d8ca77988f 100644\n>--- a/builtin/clone.c\n>+++ b/builtin/clone.c\n>@@ -50,6 +50,8 @@ static int option_no_checkout, option_bare, option_mirror, option_single_branch\n> static int option_local = -1, option_no_hardlinks, option_shared;\n> static int option_no_tags;\n> static int option_shallow_submodules;\n>+static int option_shallow = -1;    /* unspecified */\n>+static int config_shallow = -1;    /* unspecified */\n> static int deepen;\n> static char *option_template, *option_depth, *option_since;\n> static char *option_origin = NULL;\n>@@ -90,6 +92,8 @@ static struct option builtin_clone_options[] = {\n> OPT__VERBOSITY(&option_verbosity),\n> OPT_BOOL(0, \"progress\", &option_progress,\n> N_(\"force progress reporting\")),\n>+\tOPT_BOOL(0, \"reject-shallow\", &option_shallow,\n>+\tN_(\"don't clone shallow repository\")),\n> OPT_BOOL('n', \"no-checkout\", &option_no_checkout,\n> N_(\"don't create a checkout\")),\n> OPT_BOOL(0, \"bare\", &option_bare, N_(\"create a bare repository\")),\n>@@ -858,6 +862,9 @@ static int git_clone_config(const char *k, const char *v, void *cb)\n> free(remote_name);\n> remote_name = xstrdup(v);\n> }\n>+\tif (!strcmp(k, \"clone.rejectshallow\")) {\n>+\tconfig_shallow = git_config_bool(k, v);\n>+\t}\n> return git_default_config(k, v, cb);\n> }\n>\n>@@ -963,6 +970,7 @@ static int path_exists(const char *path)\n> int cmd_clone(int argc, const char **argv, const char *prefix)\n> {\n> int is_bundle = 0, is_local;\n>+\tint reject_shallow = 0;\n> const char *repo_name, *repo, *work_tree, *git_dir;\n> char *path, *dir, *display_repo = NULL;\n> int dest_exists, real_dest_exists = 0;\n>@@ -1156,6 +1164,16 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n> */\n> git_config(git_clone_config, NULL);\n>\n>+\t/*\n>+\t* If option_shallow is specified from CLI option,\n>+\t* ignore config_shallow from git_clone_config.\n>+\t*/\n>+\tif (config_shallow != -1) {\n>+\treject_shallow = config_shallow;\n>+\t}\n>+\tif (option_shallow != -1) {\n>+\treject_shallow = option_shallow;\n>+\t}\n> /*\n> * apply the remote name provided by --origin only after this second\n> * call to git_config, to ensure it overrides all config-based values.\n>@@ -1216,6 +1234,8 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n> if (filter_options.choice)\n> warning(_(\"--filter is ignored in local clones; use file:// instead.\"));\n> if (!access(mkpath(\"%s/shallow\", path), F_OK)) {\n>+\tif (reject_shallow)\n>+\tdie(\"source repository is shallow, reject to clone.\");\n> if (option_local > 0)\n> warning(_(\"source repository is shallow, ignoring --local\"));\n> is_local = 0;\n>@@ -1227,6 +1247,8 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n>\n> transport_set_option(transport, TRANS_OPT_KEEP, \"yes\");\n>\n>+\tif (reject_shallow)\n>+\ttransport_set_option(transport, TRANS_OPT_REJECT_SHALLOW, \"1\");\n> if (option_depth)\n> transport_set_option(transport, TRANS_OPT_DEPTH,\n>      option_depth);\n>diff --git a/fetch-pack.c b/fetch-pack.c\n>index 1eaedcb5dc2e..41ef700bde38 100644\n>--- a/fetch-pack.c\n>+++ b/fetch-pack.c\n>@@ -1071,6 +1071,10 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args,\n>\n> if (args->stateless_rpc)\n> packet_flush(fd[1]);\n>+\n>+\tif (!args->deepen && args->remote_shallow)\n>+\tdie(\"source repository is shallow, reject to clone.\");\n>+ \nI am not sure if we should apply this to non-protocol-v2.\n\n> if (args->deepen)\n> setup_alternate_shallow(&shallow_lock, &alternate_shallow_file,\n> NULL);\n>@@ -1440,6 +1444,8 @@ static void receive_shallow_info(struct fetch_pack_args *args,\n> * shallow. In v0, remote refs that reach these objects are\n> * rejected (unless --update-shallow is set); do the same.\n> */\n>+\tif (args->remote_shallow)\n>+\tdie(\"source repository is shallow, reject to clone.\"); \n\nI just found that Johannes Schindelin wrote a document 14 year ago\nin Documentation/technical/shallow.txt:\n\n\"There are some unfinished ends of the whole shallow business:\n\nA special handling of a shallow upstream is needed. At some stage,\nupload-pack has to check if it sends a shallow commit, and it should\nsend that information early (or fail, if the client does not support \nshallow repositories). There is no support at all for this in this patch\nseries.\"\n\nIt seems that my patch can sovle his worry in some degree,\nand maybe we could warn client in fetch-pack stage, if we don't\nchoose to reject shallow cloning.\n\n\t\tif (args->remote_shallow)\n\t\t\tdie(\"source repository is shallow, reject to clone.\");\n\t\telse\n\t\t\twarning(\"remote source repository is shallow.\");\n\n> prepare_shallow_info(si, shallows);\n> if (si->nr_ours || si->nr_theirs)\n> alternate_shallow_file =\n>diff --git a/fetch-pack.h b/fetch-pack.h\n>index 736a3dae467a..6e4f8f0d738c 100644\n>--- a/fetch-pack.h\n>+++ b/fetch-pack.h\n>@@ -39,6 +39,7 @@ struct fetch_pack_args {\n> unsigned self_contained_and_connected:1;\n> unsigned cloning:1;\n> unsigned update_shallow:1;\n>+\tunsigned remote_shallow:1;\n> unsigned deepen:1;\n>\n> /*\n>diff --git a/t/t5606-clone-options.sh b/t/t5606-clone-options.sh\n>index 52e5789fb050..6170d0513227 100755\n>--- a/t/t5606-clone-options.sh\n>+++ b/t/t5606-clone-options.sh\n>@@ -5,6 +5,8 @@ GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n> export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n>\n> . ./test-lib.sh\n>+. \"$TEST_DIRECTORY\"/lib-httpd.sh\n>+start_httpd\n>\n> test_expect_success 'setup' '\n>\n>@@ -45,6 +47,51 @@ test_expect_success 'disallows --bare with --separate-git-dir' '\n>\n> '\n>\n>+test_expect_success 'fail to clone http shallow repository' '\n>+\tgit clone --depth=1 --no-local parent shallow-repo &&\n>+\tgit clone --bare --no-local shallow-repo \"$HTTPD_DOCUMENT_ROOT_PATH/repo.git\" &&\n>+\ttest_must_fail git clone --reject-shallow $HTTPD_URL/smart/repo.git out 2>err &&\n>+\ttest_i18ngrep -e \"source repository is shallow, reject to clone.\" err\n>+\n>+'\n>+\n>+test_expect_success 'fail to clone shallow repository' '\n>+\trm -rf shallow-repo &&\n>+\tgit clone --depth=1 --no-local parent shallow-repo &&\n>+\ttest_must_fail git clone --reject-shallow shallow-repo out 2>err &&\n>+\ttest_i18ngrep -e \"source repository is shallow, reject to clone.\" err\n>+\n>+'\n>+\n>+test_expect_success 'fail to clone non-local shallow repository' '\n>+\trm -rf shallow-repo &&\n>+\tgit clone --depth=1 --no-local parent shallow-repo &&\n>+\ttest_must_fail git clone --reject-shallow --no-local shallow-repo out 2>err &&\n>+\ttest_i18ngrep -e \"source repository is shallow, reject to clone.\" err\n>+\n>+'\n>+\n>+test_expect_success 'clone shallow repository with --no-reject-shallow' '\n>+\trm -rf shallow-repo &&\n>+\tgit clone --depth=1 --no-local parent shallow-repo &&\n>+\tgit clone --no-reject-shallow --no-local shallow-repo clone-repo\n>+\n>+'\n>+\n>+test_expect_success 'clone normal repository with --reject-shallow' '\n>+\trm -rf clone-repo &&\n>+\tgit clone --no-local parent normal-repo &&\n>+\tgit clone --reject-shallow --no-local normal-repo clone-repo\n>+\n>+'\n>+\n>+test_expect_success 'unspecified any configs or options' '\n>+\trm -rf shallow-repo clone-repo &&\n>+\tgit clone --depth=1 --no-local parent shallow-repo &&\n>+\tgit clone shallow-repo clone-repo\n>+\n>+'\n>+\n> test_expect_success 'uses \"origin\" for default remote name' '\n>\n> git clone parent clone-default-origin &&\n>diff --git a/t/t5611-clone-config.sh b/t/t5611-clone-config.sh\n>index 9f555b87ecdf..da10d3f10352 100755\n>--- a/t/t5611-clone-config.sh\n>+++ b/t/t5611-clone-config.sh\n>@@ -95,6 +95,38 @@ test_expect_success 'clone -c remote.<remote>.fetch=<refspec> --origin=<name>' '\n> test_cmp expect actual\n> '\n>\n>+test_expect_success 'clone.rejectshallow=true should fail to clone' '\n>+\trm -rf child &&\n>+\tgit clone --depth=1 --no-local . child &&\n>+\ttest_must_fail git -c clone.rejectshallow=true clone --no-local child out 2>err &&\n>+\ttest_i18ngrep -e \"source repository is shallow, reject to clone.\" err\n>+'\n>+\n>+test_expect_success 'clone.rejectshallow=false should succeed' '\n>+\trm -rf child out &&\n>+\tgit clone --depth=1 --no-local . child &&\n>+\tgit -c clone.rejectshallow=false clone --no-local child out\n>+'\n>+\n>+test_expect_success 'clone.rejectshallow=true should succeed with normal repo' '\n>+\trm -rf child out &&\n>+\tgit clone --no-local . child &&\n>+\tgit -c clone.rejectshallow=true clone --no-local child out\n>+'\n>+\n>+test_expect_success 'option --reject-shallow override clone.rejectshallow' '\n>+\trm -rf child out &&\n>+\tgit clone --depth=1 --no-local . child &&\n>+\ttest_must_fail git -c clone.rejectshallow=false clone --reject-shallow --no-local child out 2>err &&\n>+\ttest_i18ngrep -e \"source repository is shallow, reject to clone.\" err\n>+'\n>+\n>+test_expect_success 'option --no-reject-shallow override clone.rejectshallow' '\n>+\trm -rf child out &&\n>+\tgit clone --depth=1 --no-local . child &&\n>+\tgit -c clone.rejectshallow=true clone --no-reject-shallow --no-local child out\n>+'\n>+\n> test_expect_success MINGW 'clone -c core.hideDotFiles' '\n> test_commit attributes .gitattributes \"\" &&\n> rm -rf child &&\n>diff --git a/transport-helper.c b/transport-helper.c\n>index 49b7fb4dcb9a..b57b55c8180c 100644\n>--- a/transport-helper.c\n>+++ b/transport-helper.c\n>@@ -265,7 +265,8 @@ static const char *boolean_options[] = {\n> TRANS_OPT_THIN,\n> TRANS_OPT_KEEP,\n> TRANS_OPT_FOLLOWTAGS,\n>-\tTRANS_OPT_DEEPEN_RELATIVE\n>+\tTRANS_OPT_DEEPEN_RELATIVE,\n>+\tTRANS_OPT_REJECT_SHALLOW\n> };\n>\n> static int strbuf_set_helper_option(struct helper_data *data,\n>diff --git a/transport.c b/transport.c\n>index b13fab5dc3b1..34fe01221ee0 100644\n>--- a/transport.c\n>+++ b/transport.c\n>@@ -236,6 +236,9 @@ static int set_git_option(struct git_transport_options *opts,\n> list_objects_filter_die_if_populated(&opts->filter_options);\n> parse_list_objects_filter(&opts->filter_options, value);\n> return 0;\n>+\t} else if (!strcmp(name, TRANS_OPT_REJECT_SHALLOW)) {\n>+\topts->reject_shallow = !!value;\n>+\treturn 0;\n> }\n> return 1;\n> }\n>@@ -370,6 +373,7 @@ static int fetch_refs_via_pack(struct transport *transport,\n> args.stateless_rpc = transport->stateless_rpc;\n> args.server_options = transport->server_options;\n> args.negotiation_tips = data->options.negotiation_tips;\n>+\targs.remote_shallow = transport->smart_options->reject_shallow;\n>\n> if (!data->got_remote_heads) {\n> int i;\n>diff --git a/transport.h b/transport.h\n>index 24e15799e714..f6273d268b09 100644\n>--- a/transport.h\n>+++ b/transport.h\n>@@ -14,6 +14,7 @@ struct git_transport_options {\n> unsigned check_self_contained_and_connected : 1;\n> unsigned self_contained_and_connected : 1;\n> unsigned update_shallow : 1;\n>+\tunsigned reject_shallow : 1;\n> unsigned deepen_relative : 1;\n>\n> /* see documentation of corresponding flag in fetch-pack.h */\n>@@ -194,6 +195,9 @@ void transport_check_allowed(const char *type);\n> /* Aggressively fetch annotated tags if possible */\n> #define TRANS_OPT_FOLLOWTAGS \"followtags\"\n>\n>+/* reject shallow repo transport  */\n>+#define TRANS_OPT_REJECT_SHALLOW \"rejectshallow\"\n>+\n> /* Accept refs that may update .git/shallow without --depth */\n> #define TRANS_OPT_UPDATE_SHALLOW \"updateshallow\"\n>\n>\n>base-commit: 225365fb5195e804274ab569ac3cc4919451dc7f\n>--\n>gitgitgadget\n>"},{"id":"418078","messageId":"20210301220319.3426185-1-jonathantanmy@google.com","threadId":"55104","inReplyTo":"xmqq35xo7yzy.fsf@gitster.g","subject":"Re: [PATCH v4] builtin/clone.c: add --reject-shallow option","fromName":"Jonathan Tan","fromEmail":"jonathantanmy@google.com","sentAt":"2021-03-01T22:03:19Z","receivedAt":"2021-03-01T22:05:36Z","isPatch":true,"sender":{"key":"jonathantanmy@fastmail.com","avatar":null},"body":"> This may reject to clone from a shallow repository, but at this\n> point the bulk of the tranfer from the origin repository has already\n> happened, no?  Rejecting after transferring many megabytes feels a\n> bit too late.  That is one of the reasons why I kept hinting that\n> the transport layer needs to be taught an option to reject talking\n> to a shallow counterpart if we want to add this feature [*1*].\n\nExtending the transport layer in this way might not be too difficult in\nthe case of native (SSH, git://) protocols and using protocol v0, since\nhandshake() in transport.c (called indirectly from\ntransport_get_remote_refs()) writes shallow information to a data\nstructure that we could potentially expose for the caller to use (before\nit calls transport_fetch_refs(). I couldn't see how remote-using\nprotocols (e.g. HTTP) communicate shallow information, though\n(remote-curl.c seems to just keep it for itself), so that will be a more\ndifficult task. And of course there's the matter of protocol v2, which I\ndiscuss below.\n\n> [Footnote]\n> \n> *1* Looking at Documentation/technical/pack-protocol.txt, \"git\n>     fetch\" seem to learn if the repository is shallow immediately\n>     upon contacting \"upload-pack\" during the Reference Discovery\n>     phase (we'd see 'shallow' packets if they are shallow). I\n>     suspect that the right solution would be to teach the codepath\n>     on the \"git fetch\" side that accepts, parses, and acts on this\n>     packet to optionally stop communication and error out when the\n>     caller asks not to talk with a shallow repository.\n\nThis is true with protocol v0, but protocol v2 bundles all shallow\ninformation (whether coming from the fact that the remote is shallow or\nthe fact that the fetcher specified --depth etc.) and sends them\ntogether with the packfile. It may be possible to stop packfile download\n(saving bandwidth on the client side, at least) once such information is\nreturned, though.\n"},{"id":"418087","messageId":"xmqqh7lumrkg.fsf@gitster.c.googlers.com","threadId":"55104","inReplyTo":"20210301220319.3426185-1-jonathantanmy@google.com","subject":"Re: [PATCH v4] builtin/clone.c: add --reject-shallow option","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-03-01T22:34:23Z","receivedAt":"2021-03-01T23:34:59Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jonathan Tan <jonathantanmy@google.com> writes:\n\n>> This may reject to clone from a shallow repository, but at this\n>> point the bulk of the tranfer from the origin repository has already\n>> happened, no?  Rejecting after transferring many megabytes feels a\n>> bit too late.  That is one of the reasons why I kept hinting that\n>> the transport layer needs to be taught an option to reject talking\n>> to a shallow counterpart if we want to add this feature [*1*].\n>\n> Extending the transport layer in this way might not be too difficult in\n> the case of native (SSH, git://) protocols and using protocol v0, since\n> handshake() in transport.c (called indirectly from\n> transport_get_remote_refs()) writes shallow information to a data\n> structure that we could potentially expose for the caller to use (before\n> it calls transport_fetch_refs(). I couldn't see how remote-using\n> protocols (e.g. HTTP) communicate shallow information, though\n> (remote-curl.c seems to just keep it for itself), so that will be a more\n> difficult task. And of course there's the matter of protocol v2, which I\n> discuss below.\n>\n>> [Footnote]\n>> \n>> *1* Looking at Documentation/technical/pack-protocol.txt, \"git\n>>     fetch\" seem to learn if the repository is shallow immediately\n>>     upon contacting \"upload-pack\" during the Reference Discovery\n>>     phase (we'd see 'shallow' packets if they are shallow). I\n>>     suspect that the right solution would be to teach the codepath\n>>     on the \"git fetch\" side that accepts, parses, and acts on this\n>>     packet to optionally stop communication and error out when the\n>>     caller asks not to talk with a shallow repository.\n>\n> This is true with protocol v0, but protocol v2 bundles all shallow\n> information (whether coming from the fact that the remote is shallow or\n> the fact that the fetcher specified --depth etc.) and sends them\n> together with the packfile. It may be possible to stop packfile download\n> (saving bandwidth on the client side, at least) once such information is\n> returned, though.\n\nSo in short, the \"we do not want to clone from a shallow upstream\"\nwould not be possible to implement sensibly without significantly\ncleaning up the protocol layers first, which makes the whole thing\npretty much moot.\n\nThanks for a review and insight.\n\n\n"},{"id":"418089","messageId":"nycvar.QRO.7.76.6.2103012338100.57@tvgsbejvaqbjf.bet","threadId":"55104","inReplyTo":"697bdaee7a5d11eb919e0026b95c99cc@oschina.cn","subject":"Re: [PATCH v5] builtin/clone.c: add --reject-shallow option","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2021-03-01T22:40:06Z","receivedAt":"2021-03-01T23:35:05Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Mon, 1 Mar 2021, lilinchao@oschina.cn wrote:\n\n> >@@ -1440,6 +1444,8 @@ static void receive_shallow_info(struct fetch_pack_args *args,\n> > * shallow. In v0, remote refs that reach these objects are\n> > * rejected (unless --update-shallow is set); do the same.\n> > */\n> >+\tif (args->remote_shallow)\n> >+\tdie(\"source repository is shallow, reject to clone.\");\n>\n> I just found that Johannes Schindelin wrote a document 14 year ago\n> in Documentation/technical/shallow.txt:\n>\n> \"There are some unfinished ends of the whole shallow business:\n>\n> A special handling of a shallow upstream is needed. At some stage,\n> upload-pack has to check if it sends a shallow commit, and it should\n> send that information early (or fail, if the client does not support \n> shallow repositories). There is no support at all for this in this patch\n> series.\"\n\nOh wow, what a blast from the past.\n\nI do agree that your patch is an improvement over the current situation.\n\nThanks,\nJohannes\n\n> It seems that my patch can sovle his worry in some degree,\n> and maybe we could warn client in fetch-pack stage, if we don't\n> choose to reject shallow cloning.\n>\n> \t\tif (args->remote_shallow)\n> \t\t\tdie(\"source repository is shallow, reject to clone.\");\n> \t\telse\n> \t\t\twarning(\"remote source repository is shallow.\");\n>\n> > prepare_shallow_info(si, shallows);\n> > if (si->nr_ours || si->nr_theirs)\n> > alternate_shallow_file =\n> >diff --git a/fetch-pack.h b/fetch-pack.h\n> >index 736a3dae467a..6e4f8f0d738c 100644\n> >--- a/fetch-pack.h\n> >+++ b/fetch-pack.h\n> >@@ -39,6 +39,7 @@ struct fetch_pack_args {\n> > unsigned self_contained_and_connected:1;\n> > unsigned cloning:1;\n> > unsigned update_shallow:1;\n> >+\tunsigned remote_shallow:1;\n> > unsigned deepen:1;\n> >\n> > /*\n> >diff --git a/t/t5606-clone-options.sh b/t/t5606-clone-options.sh\n> >index 52e5789fb050..6170d0513227 100755\n> >--- a/t/t5606-clone-options.sh\n> >+++ b/t/t5606-clone-options.sh\n> >@@ -5,6 +5,8 @@ GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n> > export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n> >\n> > . ./test-lib.sh\n> >+. \"$TEST_DIRECTORY\"/lib-httpd.sh\n> >+start_httpd\n> >\n> > test_expect_success 'setup' '\n> >\n> >@@ -45,6 +47,51 @@ test_expect_success 'disallows --bare with --separate-git-dir' '\n> >\n> > '\n> >\n> >+test_expect_success 'fail to clone http shallow repository' '\n> >+\tgit clone --depth=1 --no-local parent shallow-repo &&\n> >+\tgit clone --bare --no-local shallow-repo \"$HTTPD_DOCUMENT_ROOT_PATH/repo.git\" &&\n> >+\ttest_must_fail git clone --reject-shallow $HTTPD_URL/smart/repo.git out 2>err &&\n> >+\ttest_i18ngrep -e \"source repository is shallow, reject to clone.\" err\n> >+\n> >+'\n> >+\n> >+test_expect_success 'fail to clone shallow repository' '\n> >+\trm -rf shallow-repo &&\n> >+\tgit clone --depth=1 --no-local parent shallow-repo &&\n> >+\ttest_must_fail git clone --reject-shallow shallow-repo out 2>err &&\n> >+\ttest_i18ngrep -e \"source repository is shallow, reject to clone.\" err\n> >+\n> >+'\n> >+\n> >+test_expect_success 'fail to clone non-local shallow repository' '\n> >+\trm -rf shallow-repo &&\n> >+\tgit clone --depth=1 --no-local parent shallow-repo &&\n> >+\ttest_must_fail git clone --reject-shallow --no-local shallow-repo out 2>err &&\n> >+\ttest_i18ngrep -e \"source repository is shallow, reject to clone.\" err\n> >+\n> >+'\n> >+\n> >+test_expect_success 'clone shallow repository with --no-reject-shallow' '\n> >+\trm -rf shallow-repo &&\n> >+\tgit clone --depth=1 --no-local parent shallow-repo &&\n> >+\tgit clone --no-reject-shallow --no-local shallow-repo clone-repo\n> >+\n> >+'\n> >+\n> >+test_expect_success 'clone normal repository with --reject-shallow' '\n> >+\trm -rf clone-repo &&\n> >+\tgit clone --no-local parent normal-repo &&\n> >+\tgit clone --reject-shallow --no-local normal-repo clone-repo\n> >+\n> >+'\n> >+\n> >+test_expect_success 'unspecified any configs or options' '\n> >+\trm -rf shallow-repo clone-repo &&\n> >+\tgit clone --depth=1 --no-local parent shallow-repo &&\n> >+\tgit clone shallow-repo clone-repo\n> >+\n> >+'\n> >+\n> > test_expect_success 'uses \"origin\" for default remote name' '\n> >\n> > git clone parent clone-default-origin &&\n> >diff --git a/t/t5611-clone-config.sh b/t/t5611-clone-config.sh\n> >index 9f555b87ecdf..da10d3f10352 100755\n> >--- a/t/t5611-clone-config.sh\n> >+++ b/t/t5611-clone-config.sh\n> >@@ -95,6 +95,38 @@ test_expect_success 'clone -c remote.<remote>.fetch=<refspec> --origin=<name>' '\n> > test_cmp expect actual\n> > '\n> >\n> >+test_expect_success 'clone.rejectshallow=true should fail to clone' '\n> >+\trm -rf child &&\n> >+\tgit clone --depth=1 --no-local . child &&\n> >+\ttest_must_fail git -c clone.rejectshallow=true clone --no-local child out 2>err &&\n> >+\ttest_i18ngrep -e \"source repository is shallow, reject to clone.\" err\n> >+'\n> >+\n> >+test_expect_success 'clone.rejectshallow=false should succeed' '\n> >+\trm -rf child out &&\n> >+\tgit clone --depth=1 --no-local . child &&\n> >+\tgit -c clone.rejectshallow=false clone --no-local child out\n> >+'\n> >+\n> >+test_expect_success 'clone.rejectshallow=true should succeed with normal repo' '\n> >+\trm -rf child out &&\n> >+\tgit clone --no-local . child &&\n> >+\tgit -c clone.rejectshallow=true clone --no-local child out\n> >+'\n> >+\n> >+test_expect_success 'option --reject-shallow override clone.rejectshallow' '\n> >+\trm -rf child out &&\n> >+\tgit clone --depth=1 --no-local . child &&\n> >+\ttest_must_fail git -c clone.rejectshallow=false clone --reject-shallow --no-local child out 2>err &&\n> >+\ttest_i18ngrep -e \"source repository is shallow, reject to clone.\" err\n> >+'\n> >+\n> >+test_expect_success 'option --no-reject-shallow override clone.rejectshallow' '\n> >+\trm -rf child out &&\n> >+\tgit clone --depth=1 --no-local . child &&\n> >+\tgit -c clone.rejectshallow=true clone --no-reject-shallow --no-local child out\n> >+'\n> >+\n> > test_expect_success MINGW 'clone -c core.hideDotFiles' '\n> > test_commit attributes .gitattributes \"\" &&\n> > rm -rf child &&\n> >diff --git a/transport-helper.c b/transport-helper.c\n> >index 49b7fb4dcb9a..b57b55c8180c 100644\n> >--- a/transport-helper.c\n> >+++ b/transport-helper.c\n> >@@ -265,7 +265,8 @@ static const char *boolean_options[] = {\n> > TRANS_OPT_THIN,\n> > TRANS_OPT_KEEP,\n> > TRANS_OPT_FOLLOWTAGS,\n> >-\tTRANS_OPT_DEEPEN_RELATIVE\n> >+\tTRANS_OPT_DEEPEN_RELATIVE,\n> >+\tTRANS_OPT_REJECT_SHALLOW\n> > };\n> >\n> > static int strbuf_set_helper_option(struct helper_data *data,\n> >diff --git a/transport.c b/transport.c\n> >index b13fab5dc3b1..34fe01221ee0 100644\n> >--- a/transport.c\n> >+++ b/transport.c\n> >@@ -236,6 +236,9 @@ static int set_git_option(struct git_transport_options *opts,\n> > list_objects_filter_die_if_populated(&opts->filter_options);\n> > parse_list_objects_filter(&opts->filter_options, value);\n> > return 0;\n> >+\t} else if (!strcmp(name, TRANS_OPT_REJECT_SHALLOW)) {\n> >+\topts->reject_shallow = !!value;\n> >+\treturn 0;\n> > }\n> > return 1;\n> > }\n> >@@ -370,6 +373,7 @@ static int fetch_refs_via_pack(struct transport *transport,\n> > args.stateless_rpc = transport->stateless_rpc;\n> > args.server_options = transport->server_options;\n> > args.negotiation_tips = data->options.negotiation_tips;\n> >+\targs.remote_shallow = transport->smart_options->reject_shallow;\n> >\n> > if (!data->got_remote_heads) {\n> > int i;\n> >diff --git a/transport.h b/transport.h\n> >index 24e15799e714..f6273d268b09 100644\n> >--- a/transport.h\n> >+++ b/transport.h\n> >@@ -14,6 +14,7 @@ struct git_transport_options {\n> > unsigned check_self_contained_and_connected : 1;\n> > unsigned self_contained_and_connected : 1;\n> > unsigned update_shallow : 1;\n> >+\tunsigned reject_shallow : 1;\n> > unsigned deepen_relative : 1;\n> >\n> > /* see documentation of corresponding flag in fetch-pack.h */\n> >@@ -194,6 +195,9 @@ void transport_check_allowed(const char *type);\n> > /* Aggressively fetch annotated tags if possible */\n> > #define TRANS_OPT_FOLLOWTAGS \"followtags\"\n> >\n> >+/* reject shallow repo transport  */\n> >+#define TRANS_OPT_REJECT_SHALLOW \"rejectshallow\"\n> >+\n> > /* Accept refs that may update .git/shallow without --depth */\n> > #define TRANS_OPT_UPDATE_SHALLOW \"updateshallow\"\n> >\n> >\n> >base-commit: 225365fb5195e804274ab569ac3cc4919451dc7f\n> >--\n> >gitgitgadget\n> >\n"},{"id":"418100","messageId":"7a2fa0ec7b3311eb83b80024e87935e7@oschina.cn","threadId":"55104","inReplyTo":"20210301220319.3426185-1-jonathantanmy@google.com","subject":"Re: Re: [PATCH v4] builtin/clone.c: add --reject-shallow option","fromName":"lilinchao@oschina.cn","fromEmail":"lilinchao@oschina.cn","sentAt":"2021-03-02T08:44:13Z","receivedAt":"2021-03-02T15:28:36Z","isPatch":true,"sender":{"key":"lilinchao@oschina.cn","avatar":null},"body":"\n--------------\nlilinchao\n>> This may reject to clone from a shallow repository, but at this\n>> point the bulk of the tranfer from the origin repository has already\n>> happened, no?  Rejecting after transferring many megabytes feels a\n>> bit too late.  That is one of the reasons why I kept hinting that\n>> the transport layer needs to be taught an option to reject talking\n>> to a shallow counterpart if we want to add this feature [*1*].\n>\n>Extending the transport layer in this way might not be too difficult in\n>the case of native (SSH, git://) protocols and using protocol v0, since\n>handshake() in transport.c (called indirectly from\n>transport_get_remote_refs()) writes shallow information to a data\n>structure that we could potentially expose for the caller to use (before\n>it calls transport_fetch_refs(). I couldn't see how remote-using\n>protocols (e.g. HTTP) communicate shallow information, though\n>(remote-curl.c seems to just keep it for itself), so that will be a more\n>difficult task. And of course there's the matter of protocol v2, which I\n>discuss below.\n> \nThese discussions were based on PATCH_v4, which is quiet immature then,\nIn the latest PATCH_v5, for the case of native protocols(ssh, git, file://), they\nwill eventually call do_fetch_pack_v2() if it's protocol v2,  and then will call\nreceive_shallow_info() for the case FETCH_GET_PACK, so this is the place\nI made the change.\nAs for HTTPs protocl, as long as it's still smart protocol, which means do not\nfallback to dumb protocol, it will also call do_fetch_pack_v2(), and go to \n\"check shallow info\" trigger in receive_shallow_info().\n\nSo, base on PATCH_V5, I have tested protocol v2, which goes like this:\n\nFirst, I created a shallow repo on gitlab and gitee respectively.\n\nThen tried to clone them in my terminal, (in order not to look too verbose,\nI ommited the result when GIT_TRACE_PACKET is on).\n\n$ ./git-clone --reject-shallow https://gitlab.com/Cactusinhand/rugged.git\nCloning into 'rugged'...\nfatal: source repository is shallow, reject to clone.\n\n$ ./git-clone --reject-shallow ssh://gitlab.com/Cactusinhand/rugged.git\nCloning into 'rugged'...\nfatal: source repository is shallow, reject to clone.\n\n$ ./git-clone --reject-shallow git@gitlab.com:Cactusinhand/rugged.git\nCloning into 'rugged'...\nfatal: source repository is shallow, reject to clone.\n\n$ ./git-clone --reject-shallow https://gitee.com/cactusinhand/rugged.git\nCloning into 'rugged'...\nfatal: source repository is shallow, reject to clone.\n\n$ ./git-clone --reject-shallow ssh://gitee.com/cactusinhand/rugged.git\nCloning into 'rugged'...\nfatal: source repository is shallow, reject to clone.\n\n$ ./git-clone --reject-shallow git@gitee.com:cactusinhand/rugged.git\nCloning into 'rugged'...\nfatal: source repository is shallow, reject to clone.\n\nI haven't tested protocol v1, but I've made the change in do_fetch_pack(),\nwhich is for protocol version 0 and version 1.\n\nI hope you can review the latest patch, and give me some suggestions.\n\nThanks!\n\n>> [Footnote]\n>>\n>> *1* Looking at Documentation/technical/pack-protocol.txt, \"git\n>>     fetch\" seem to learn if the repository is shallow immediately\n>>     upon contacting \"upload-pack\" during the Reference Discovery\n>>     phase (we'd see 'shallow' packets if they are shallow). I\n>>     suspect that the right solution would be to teach the codepath\n>>     on the \"git fetch\" side that accepts, parses, and acts on this\n>>     packet to optionally stop communication and error out when the\n>>     caller asks not to talk with a shallow repository.\n>\n>This is true with protocol v0, but protocol v2 bundles all shallow\n>information (whether coming from the fact that the remote is shallow or\n>the fact that the fetcher specified --depth etc.) and sends them\n>together with the packfile. It may be possible to stop packfile download\n>(saving bandwidth on the client side, at least) once such information is\n>returned, though.\n> \n\n"},{"id":"418180","messageId":"xmqq35xbj01p.fsf@gitster.c.googlers.com","threadId":"55104","inReplyTo":"pull.865.v5.git.1614535588332.gitgitgadget@gmail.com","subject":"Re: [PATCH v5] builtin/clone.c: add --reject-shallow option","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-03-03T23:21:38Z","receivedAt":"2021-03-04T00:26:53Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"Li Linchao via GitGitGadget\" <gitgitgadget@gmail.com> writes:\n\n> diff --git a/Documentation/config/clone.txt b/Documentation/config/clone.txt\n> index 47de36a5fedf..50ebc170bb81 100644\n> --- a/Documentation/config/clone.txt\n> +++ b/Documentation/config/clone.txt\n> @@ -2,3 +2,7 @@ clone.defaultRemoteName::\n>  \tThe name of the remote to create when cloning a repository.  Defaults to\n>  \t`origin`, and can be overridden by passing the `--origin` command-line\n>  \toption to linkgit:git-clone[1].\n> +\n> +clone.rejectshallow::\n> +\tReject to clone a repository if it is a shallow one, can be overridden by\n> +\tpassing option `--reject-shallow` in command line. See linkgit:git-clone[1]\n\nLet's camelCase this, i.e. \"clone.rejectShallow\", as this file would\nbe a good candidate to be the authoritative record of canonical\nspelling of these variables.\n\ncf. https://lore.kernel.org/git/xmqq7dmy389l.fsf@gitster.g/\n\n> +--[no-]reject-shallow::\n> +\tFail if the source repository is a shallow repository. The\n> +\t'clone.rejectshallow' configuration variable can be used to\n> +\tgive the default.\n\nLet's camelCase the reference to the variable, too.  Also, typeset\nin monospace, i.e.\n\n\tThe `clone.rejectShallow` configuration variable ...\n\n> @@ -858,6 +862,9 @@ static int git_clone_config(const char *k, const char *v, void *cb)\n>  \t\tfree(remote_name);\n>  \t\tremote_name = xstrdup(v);\n>  \t}\n> +\tif (!strcmp(k, \"clone.rejectshallow\")) {\n> +\t\tconfig_shallow = git_config_bool(k, v);\n> +\t}\n\nNo need to use {} around a single-statement block, especially when\nthe \"if\" statement does not have an \"else\" block.\n\nThe use of strcmp() against the variable name in all lowercase is\ncorrect here.\n\n> @@ -1156,6 +1164,16 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n>  \t */\n>  \tgit_config(git_clone_config, NULL);\n>  \n> +\t/*\n> +\t * If option_shallow is specified from CLI option,\n> +\t * ignore config_shallow from git_clone_config.\n> +\t */\n> +\tif (config_shallow != -1) {\n> +\t\treject_shallow = config_shallow;\n> +\t}\n> +\tif (option_shallow != -1) {\n> +\t\treject_shallow = option_shallow;\n> +\t}\n\nNeedless use of {} around single-statement blocks.\n\nAs reject_shallow is initialized to 0, this lets the option to be of\nthe most priority, then the config (presumably coming from the per-user\nor per-system configuration), by without them, defaults to false.  Good.\n\nWe'll have an extra git_config() call later, but that one will only\nread into config_shallow, never to be looked at because we will use\nreject_shallow variable anyway.  OK.\n\n> @@ -1216,6 +1234,8 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n>  \t\tif (filter_options.choice)\n>  \t\t\twarning(_(\"--filter is ignored in local clones; use file:// instead.\"));\n>  \t\tif (!access(mkpath(\"%s/shallow\", path), F_OK)) {\n> +\t\t\tif (reject_shallow)\n> +\t\t\t\tdie(\"source repository is shallow, reject to clone.\");\n\nWith the local transport, it (hopefully) is trivial to see if the\nsource is shallow.  OK.\n\n>  \t\t\tif (option_local > 0)\n>  \t\t\t\twarning(_(\"source repository is shallow, ignoring --local\"));\n>  \t\t\tis_local = 0;\n> @@ -1227,6 +1247,8 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n>  \n>  \ttransport_set_option(transport, TRANS_OPT_KEEP, \"yes\");\n>  \n> +\tif (reject_shallow)\n> +\t\ttransport_set_option(transport, TRANS_OPT_REJECT_SHALLOW, \"1\");\n>  \tif (option_depth)\n>  \t\ttransport_set_option(transport, TRANS_OPT_DEPTH,\n>  \t\t\t\t     option_depth);\n\nOK.  What is really interesting will all happen inside the transport\nlayer; the caller only has to ask for it.\n\nThe asymmetry with other options like \"--depth\" stands out and\npuzzles readers, though.\n\nDo we really want to add the clone.rejectShallow configuration?\nAfter all, we do not give \"clone.depth = 1\" etc., and that is the\nreason why we only need \"if (option_depth)\" here in the near-by\ncode.\n\nI'd stop here for today, hoping that somebody much more familiar\nwith the transport layer than I am will review and comment on the\nchanges there.\n\nThanks.\n"},{"id":"418181","messageId":"xmqqy2f3hjqf.fsf@gitster.c.googlers.com","threadId":"55104","inReplyTo":"20210301220319.3426185-1-jonathantanmy@google.com","subject":"Re: [PATCH v4] builtin/clone.c: add --reject-shallow option","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-03-03T23:59:20Z","receivedAt":"2021-03-04T00:26:55Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jonathan Tan <jonathantanmy@google.com> writes:\n\n> This is true with protocol v0, but protocol v2 bundles all shallow\n> information (whether coming from the fact that the remote is shallow or\n> the fact that the fetcher specified --depth etc.) and sends them\n> together with the packfile.\n\nBy the above do you mean what happens in FETCH_GET_PACK arm where\nreceive_shallow_info() is called when \"shallow-info\" header is seen,\nbefore the code continues to process wanted-refs, packfile-uris and\nthen finally the packfile?\n\nI do not think it makes much sense to ask any option to make us\nshallow (like --depth=<n>) while --reject-shallow is in use (after\nall, if the other side is deep enough to make us <n> commits deep,\nthere is no reason to reject the other side as the source), so your\n\"whether coming from the fact ...\" part, while is a valid\nobservation, can be ignored in practice (meaning: it is OK to make\n\"--reject-shallow\" be in effect only when we are trying to make a\nfull clone, and reject combinations of it with --depth=<n> and such\nat the command parsing time).\n\n> It may be possible to stop packfile download (saving bandwidth on\n> the client side, at least) once such information is returned,\n> though.\n\nJust like \"upload-pack\" does not get upset by a client that comes\nonly for the initial refs advertisement and disconnects without\nasking for any \"want\" (aka \"ls-remote\"), the server side should be\nprepared to see if the other side cuts off after seeing the\n\"shallow-info\" section header or after seeing the the whole\n\"shallow-info\" section, so we should be able to leave early without\nhaving to download the bulk data.  If the \"upload-pack\" spends\nunnecessary cycles when it happens, then we need to fix that.  Even\nif the \"fetch\" client does not willingly disconnect in the middle,\nthe network disconnect may happen at any point in the exchange, and\nwe'd need to be prepared for it.\n\nDo we need to read and parse the \"shallow-info\" section, or would\nthe mere presense of the section mean the other side knows this side\nneeds to futz with the .git/shallow information (either because we\nasked to be made shallow with --depth and the like, or because we\ntried to clone from them and they are shallow)?\n\nThanks.\n\n"},{"id":"418205","messageId":"20210304015305.1800570-1-jonathantanmy@google.com","threadId":"55104","inReplyTo":"xmqqy2f3hjqf.fsf@gitster.c.googlers.com","subject":"Re: [PATCH v4] builtin/clone.c: add --reject-shallow option","fromName":"Jonathan Tan","fromEmail":"jonathantanmy@google.com","sentAt":"2021-03-04T01:53:04Z","receivedAt":"2021-03-04T01:54:13Z","isPatch":true,"sender":{"key":"jonathantanmy@fastmail.com","avatar":null},"body":"> Jonathan Tan <jonathantanmy@google.com> writes:\n> \n> > This is true with protocol v0, but protocol v2 bundles all shallow\n> > information (whether coming from the fact that the remote is shallow or\n> > the fact that the fetcher specified --depth etc.) and sends them\n> > together with the packfile.\n> \n> By the above do you mean what happens in FETCH_GET_PACK arm where\n> receive_shallow_info() is called when \"shallow-info\" header is seen,\n> before the code continues to process wanted-refs, packfile-uris and\n> then finally the packfile?\n\nYes.\n\n> I do not think it makes much sense to ask any option to make us\n> shallow (like --depth=<n>) while --reject-shallow is in use (after\n> all, if the other side is deep enough to make us <n> commits deep,\n> there is no reason to reject the other side as the source), so your\n> \"whether coming from the fact ...\" part, while is a valid\n> observation, can be ignored in practice (meaning: it is OK to make\n> \"--reject-shallow\" be in effect only when we are trying to make a\n> full clone, and reject combinations of it with --depth=<n> and such\n> at the command parsing time).\n> \n> > It may be possible to stop packfile download (saving bandwidth on\n> > the client side, at least) once such information is returned,\n> > though.\n> \n> Just like \"upload-pack\" does not get upset by a client that comes\n> only for the initial refs advertisement and disconnects without\n> asking for any \"want\" (aka \"ls-remote\"), the server side should be\n> prepared to see if the other side cuts off after seeing the\n> \"shallow-info\" section header or after seeing the the whole\n> \"shallow-info\" section, so we should be able to leave early without\n> having to download the bulk data.  If the \"upload-pack\" spends\n> unnecessary cycles when it happens, then we need to fix that.  Even\n> if the \"fetch\" client does not willingly disconnect in the middle,\n> the network disconnect may happen at any point in the exchange, and\n> we'd need to be prepared for it.\n> \n> Do we need to read and parse the \"shallow-info\" section, or would\n> the mere presense of the section mean the other side knows this side\n> needs to futz with the .git/shallow information (either because we\n> asked to be made shallow with --depth and the like, or because we\n> tried to clone from them and they are shallow)?\n\nReading the documentation, the mere presence should be enough. Yes, I\nthink upload-pack will spend unnecessary cycles if the channel is\nterminated halfway (and I don't know if we can prevent spending these\ncycles, since I/O can be buffered). I think it should be possible for\nthe client to cut off when it sees shallow-info (besides the possible\nwastage of cycles and I/O on the server's end).\n\nHaving said that, I think this different from the ls-remote case. There,\nthe server is awaiting another request from the user before sending more\ninformation, but here, the server intends to send everything at once.\n"},{"id":"418217","messageId":"880ae0707cad11eb8ccc0024e87935e7@oschina.cn","threadId":"55104","inReplyTo":"xmqq35xbj01p.fsf@gitster.c.googlers.com","subject":"Re: Re: [PATCH v5] builtin/clone.c: add --reject-shallow option","fromName":"lilinchao@oschina.cn","fromEmail":"lilinchao@oschina.cn","sentAt":"2021-03-04T05:50:27Z","receivedAt":"2021-03-04T06:14:49Z","isPatch":true,"sender":{"key":"lilinchao@oschina.cn","avatar":null},"body":">\"Li Linchao via GitGitGadget\" <gitgitgadget@gmail.com> writes:\n>\n>> diff --git a/Documentation/config/clone.txt b/Documentation/config/clone.txt\n>> index 47de36a5fedf..50ebc170bb81 100644\n>> --- a/Documentation/config/clone.txt\n>> +++ b/Documentation/config/clone.txt\n>> @@ -2,3 +2,7 @@ clone.defaultRemoteName::\n>>  The name of the remote to create when cloning a repository.  Defaults to\n>>  `origin`, and can be overridden by passing the `--origin` command-line\n>>  option to linkgit:git-clone[1].\n>> +\n>> +clone.rejectshallow::\n>> +\tReject to clone a repository if it is a shallow one, can be overridden by\n>> +\tpassing option `--reject-shallow` in command line. See linkgit:git-clone[1]\n>\n>Let's camelCase this, i.e. \"clone.rejectShallow\", as this file would\n>be a good candidate to be the authoritative record of canonical\n>spelling of these variables.\n>\n>cf. https://lore.kernel.org/git/xmqq7dmy389l.fsf@gitster.g/\n\nOK，thanks for reminding this.\n\n>\n>> +--[no-]reject-shallow::\n>> +\tFail if the source repository is a shallow repository. The\n>> +\t'clone.rejectshallow' configuration variable can be used to\n>> +\tgive the default.\n>\n>Let's camelCase the reference to the variable, too.  Also, typeset\n>in monospace, i.e.\n>\n>\tThe `clone.rejectShallow` configuration variable ...\n>\n>> @@ -858,6 +862,9 @@ static int git_clone_config(const char *k, const char *v, void *cb)\n>>  free(remote_name);\n>>  remote_name = xstrdup(v);\n>>  }\n>> +\tif (!strcmp(k, \"clone.rejectshallow\")) {\n>> +\tconfig_shallow = git_config_bool(k, v);\n>> +\t}\n>\n>No need to use {} around a single-statement block, especially when\n>the \"if\" statement does not have an \"else\" block.\n>\n>The use of strcmp() against the variable name in all lowercase is\n>correct here.\n>\n>> @@ -1156,6 +1164,16 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n>>  */\n>>  git_config(git_clone_config, NULL);\n>> \n>> +\t/*\n>> +\t* If option_shallow is specified from CLI option,\n>> +\t* ignore config_shallow from git_clone_config.\n>> +\t*/\n>> +\tif (config_shallow != -1) {\n>> +\treject_shallow = config_shallow;\n>> +\t}\n>> +\tif (option_shallow != -1) {\n>> +\treject_shallow = option_shallow;\n>> +\t}\n>\n>Needless use of {} around single-statement blocks.\n>\n>As reject_shallow is initialized to 0, this lets the option to be of\n>the most priority, then the config (presumably coming from the per-user\n>or per-system configuration), by without them, defaults to false.  Good.\n>\n>We'll have an extra git_config() call later, but that one will only\n>read into config_shallow, never to be looked at because we will use\n>reject_shallow variable anyway.  OK.\n>\n>> @@ -1216,6 +1234,8 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n>>  if (filter_options.choice)\n>>  warning(_(\"--filter is ignored in local clones; use file:// instead.\"));\n>>  if (!access(mkpath(\"%s/shallow\", path), F_OK)) {\n>> +\tif (reject_shallow)\n>> +\tdie(\"source repository is shallow, reject to clone.\");\n>\n>With the local transport, it (hopefully) is trivial to see if the\n>source is shallow.  OK.\n>\n>>  if (option_local > 0)\n>>  warning(_(\"source repository is shallow, ignoring --local\"));\n>>  is_local = 0;\n>> @@ -1227,6 +1247,8 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n>> \n>>  transport_set_option(transport, TRANS_OPT_KEEP, \"yes\");\n>> \n>> +\tif (reject_shallow)\n>> +\ttransport_set_option(transport, TRANS_OPT_REJECT_SHALLOW, \"1\");\n>>  if (option_depth)\n>>  transport_set_option(transport, TRANS_OPT_DEPTH,\n>>       option_depth);\n>\n>OK.  What is really interesting will all happen inside the transport\n>layer; the caller only has to ask for it.\n>\n>The asymmetry with other options like \"--depth\" stands out and\n>puzzles readers, though.\n> \n\nTrue, but since `reject_shallow` is not just an option from CLI, so I think\nwe can't just call it \"option_shallow\" here to order to keep symmetry\nwith other options below.\n\n>Do we really want to add the clone.rejectShallow configuration? \n\nOnly when I want all my git clone in my machine can avoid cloning\nshallow repo, and no need to provide the option \"--reject-shallow\"\nevery time, then this configuration would make sense.\n\n>After all, we do not give \"clone.depth = 1\" etc., and that is the\n>reason why we only need \"if (option_depth)\" here in the near-by\n>code.\n>\n>I'd stop here for today, hoping that somebody much more familiar\n>with the transport layer than I am will review and comment on the\n>changes there.\n>\n>Thanks.\n>"},{"id":"418219","messageId":"84f2a4d67cb211eb83f10024e87935e7@oschina.cn","threadId":"55104","inReplyTo":"nycvar.QRO.7.76.6.2103012338100.57@tvgsbejvaqbjf.bet","subject":"Re: Re: [PATCH v5] builtin/clone.c: add --reject-shallow option","fromName":"lilinchao@oschina.cn","fromEmail":"lilinchao@oschina.cn","sentAt":"2021-03-04T06:26:09Z","receivedAt":"2021-03-04T06:27:59Z","isPatch":true,"sender":{"key":"lilinchao@oschina.cn","avatar":null},"body":">Hi,\n>\n>On Mon, 1 Mar 2021, lilinchao@oschina.cn wrote:\n>\n>> >@@ -1440,6 +1444,8 @@ static void receive_shallow_info(struct fetch_pack_args *args,\n>> > * shallow. In v0, remote refs that reach these objects are\n>> > * rejected (unless --update-shallow is set); do the same.\n>> > */\n>> >+\tif (args->remote_shallow)\n>> >+\tdie(\"source repository is shallow, reject to clone.\");\n>>\n>> I just found that Johannes Schindelin wrote a document 14 year ago\n>> in Documentation/technical/shallow.txt:\n>>\n>> \"There are some unfinished ends of the whole shallow business:\n>>\n>> A special handling of a shallow upstream is needed. At some stage,\n>> upload-pack has to check if it sends a shallow commit, and it should\n>> send that information early (or fail, if the client does not support \n>> shallow repositories). There is no support at all for this in this patch\n>> series.\"\n>\n>Oh wow, what a blast from the past.\n>\n>I do agree that your patch is an improvement over the current situation. \n> \n\nThanks. Glad to hear the voice from you, hope I can get some suggestions\nfrom you too, especially on the transport part.\n\n>Thanks,\n>Johannes\n>\n>> It seems that my patch can sovle his worry in some degree,\n>> and maybe we could warn client in fetch-pack stage, if we don't\n>> choose to reject shallow cloning.\n>>\n>> if (args->remote_shallow)\n>> die(\"source repository is shallow, reject to clone.\");\n>> else\n>> warning(\"remote source repository is shallow.\");\n>> \n\n\n\n"},{"id":"418241","messageId":"pull.865.v6.git.1614878345754.gitgitgadget@gmail.com","threadId":"55104","inReplyTo":"pull.865.v5.git.1614535588332.gitgitgadget@gmail.com","subject":"[PATCH v6] builtin/clone.c: add --reject-shallow option","fromName":"Li Linchao via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-03-04T17:19:05Z","receivedAt":"2021-03-04T17:21:07Z","isPatch":true,"sender":{"key":"name:Li Linchao","avatar":null},"body":"From: lilinchao <lilinchao@oschina.cn>\n\nIn some scenarios, users may want more history than the repository\noffered for cloning, which happens to be a shallow repository, can\ngive them. But because users don't know it is a shallow repository\nuntil they download it to local, users should have the option to\nrefuse to clone this kind of repository, and may want to exit the\nprocess immediately without creating any unnecessary files.\n\nAlthought there is an option '--depth=x' for users to decide how\ndeep history they can fetch, but as the unshallow cloning's depth\nis INFINITY, we can't know exactly the minimun 'x' value that can\nsatisfy the minimum integrity, so we can't pass 'x' value to --depth,\nand expect this can obtain a complete history of a repository.\n\nIn other scenarios, if we have an API that allow us to import external\nrepository, and then perform various operations on the repo.\nBut if the imported is a shallow one(which is actually possible), it\nwill affect the subsequent operations. So we can choose to refuse to\nclone, and let's just import a normal repository.\n\nThis patch offers a new option '--reject-shallow' that can reject to\nclone a shallow repository.\n\nSigned-off-by: lilinchao <lilinchao@oschina.cn>\n---\n    builtin/clone.c: add --reject-shallow option\n    \n    Changes since v1:\n    \n     * Rename --no-shallow to --reject-shallow\n     * Enable to reject a non-local clone\n     * Enable --[no-]reject-shallow from CLI override configuration.\n     * Add more testcases.\n     * Reword commit messages and relative documentation.\n    \n    Changes since v3:\n    \n     * Add support to reject clone shallow repo over https protocol\n     * Add testcase to reject clone shallow repo over https:// transport\n     * Reword commit messages and relative documentation according\n       suggestions from Junio.\n    \n    Changes since v5:\n    \n     * camelcase config variable\n     * warning client that source repo is shallow\n     * better support ssh:// and git:// protocol v1, v2\n    \n    Signed-off-by: lilinchao lilinchao@oschina.cn\n\nPublished-As: https://github.com/gitgitgadget/git/releases/tag/pr-865%2FCactusinhand%2Fgit-clone-options-v6\nFetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-865/Cactusinhand/git-clone-options-v6\nPull-Request: https://github.com/gitgitgadget/git/pull/865\n\nRange-diff vs v5:\n\n 1:  3f765e49e4a7 ! 1:  953122588ca8 builtin/clone.c: add --reject-shallow option\n     @@ Documentation/config/clone.txt: clone.defaultRemoteName::\n       \t`origin`, and can be overridden by passing the `--origin` command-line\n       \toption to linkgit:git-clone[1].\n      +\n     -+clone.rejectshallow::\n     ++clone.rejectShallow::\n      +\tReject to clone a repository if it is a shallow one, can be overridden by\n      +\tpassing option `--reject-shallow` in command line. See linkgit:git-clone[1]\n      \n     @@ Documentation/git-clone.txt: objects from the source repository into a pack in t\n       \tNo checkout of HEAD is performed after the clone is complete.\n       \n      +--[no-]reject-shallow::\n     -+\tFail if the source repository is a shallow repository. The\n     -+\t'clone.rejectshallow' configuration variable can be used to\n     ++\tFail if the source repository is a shallow repository.\n     ++\tThe 'clone.rejectShallow' configuration variable can be used to\n      +\tgive the default.\n      +\n       --bare::\n     @@ builtin/clone.c: static int git_clone_config(const char *k, const char *v, void\n       \t\tfree(remote_name);\n       \t\tremote_name = xstrdup(v);\n       \t}\n     -+\tif (!strcmp(k, \"clone.rejectshallow\")) {\n     ++\tif (!strcmp(k, \"clone.rejectshallow\"))\n      +\t\tconfig_shallow = git_config_bool(k, v);\n     -+\t}\n     ++\n       \treturn git_default_config(k, v, cb);\n       }\n       \n     @@ builtin/clone.c: int cmd_clone(int argc, const char **argv, const char *prefix)\n      +\t * If option_shallow is specified from CLI option,\n      +\t * ignore config_shallow from git_clone_config.\n      +\t */\n     -+\tif (config_shallow != -1) {\n     ++\tif (config_shallow != -1)\n      +\t\treject_shallow = config_shallow;\n     -+\t}\n     -+\tif (option_shallow != -1) {\n     ++\n     ++\tif (option_shallow != -1)\n      +\t\treject_shallow = option_shallow;\n     -+\t}\n     ++\n       \t/*\n       \t * apply the remote name provided by --origin only after this second\n       \t * call to git_config, to ensure it overrides all config-based values.\n     @@ builtin/clone.c: int cmd_clone(int argc, const char **argv, const char *prefix)\n       \t\tif (!access(mkpath(\"%s/shallow\", path), F_OK)) {\n      +\t\t\tif (reject_shallow)\n      +\t\t\t\tdie(\"source repository is shallow, reject to clone.\");\n     ++\t\t\telse\n     ++\t\t\t\twarning(\"source repository is shallow.\");\n       \t\t\tif (option_local > 0)\n       \t\t\t\twarning(_(\"source repository is shallow, ignoring --local\"));\n       \t\t\tis_local = 0;\n     @@ fetch-pack.c: static struct ref *do_fetch_pack(struct fetch_pack_args *args,\n       \n       \tif (args->stateless_rpc)\n       \t\tpacket_flush(fd[1]);\n     -+\n     -+\tif (!args->deepen && args->remote_shallow)\n     -+\t\tdie(\"source repository is shallow, reject to clone.\");\n      +\n       \tif (args->deepen)\n       \t\tsetup_alternate_shallow(&shallow_lock, &alternate_shallow_file,\n       \t\t\t\t\tNULL);\n     +-\telse if (si->nr_ours || si->nr_theirs)\n     ++\telse if (si->nr_ours || si->nr_theirs) {\n     ++\t\tif (args->remote_shallow)\n     ++\t\t\tdie(\"source repository is shallow, reject to clone.\");\n     ++\t\telse\n     ++\t\t\twarning(\"source repository is shallow.\");\n     + \t\talternate_shallow_file = setup_temporary_shallow(si->shallow);\n     +-\telse\n     ++\t} else\n     + \t\talternate_shallow_file = NULL;\n     + \tif (get_pack(args, fd, pack_lockfiles, NULL, sought, nr_sought,\n     + \t\t     &gitmodules_oids))\n      @@ fetch-pack.c: static void receive_shallow_info(struct fetch_pack_args *args,\n     - \t\t * shallow. In v0, remote refs that reach these objects are\n       \t\t * rejected (unless --update-shallow is set); do the same.\n       \t\t */\n     -+\t\tif (args->remote_shallow)\n     -+\t\t\tdie(\"source repository is shallow, reject to clone.\");\n       \t\tprepare_shallow_info(si, shallows);\n     - \t\tif (si->nr_ours || si->nr_theirs)\n     +-\t\tif (si->nr_ours || si->nr_theirs)\n     ++\t\tif (si->nr_ours || si->nr_theirs) {\n     ++\t\t\tif (args->remote_shallow)\n     ++\t\t\t\tdie(\"source repository is shallow, reject to clone.\");\n     ++\t\t\telse\n     ++\t\t\t\twarning(\"source repository is shallow.\");\n       \t\t\talternate_shallow_file =\n     + \t\t\t\tsetup_temporary_shallow(si->shallow);\n     +-\t\telse\n     ++\t\t} else\n     + \t\t\talternate_shallow_file = NULL;\n     + \t} else {\n     + \t\talternate_shallow_file = NULL;\n      \n       ## fetch-pack.h ##\n      @@ fetch-pack.h: struct fetch_pack_args {\n     @@ t/t5611-clone-config.sh: test_expect_success 'clone -c remote.<remote>.fetch=<re\n       \ttest_commit attributes .gitattributes \"\" &&\n       \trm -rf child &&\n      \n     - ## transport-helper.c ##\n     -@@ transport-helper.c: static const char *boolean_options[] = {\n     - \tTRANS_OPT_THIN,\n     - \tTRANS_OPT_KEEP,\n     - \tTRANS_OPT_FOLLOWTAGS,\n     --\tTRANS_OPT_DEEPEN_RELATIVE\n     -+\tTRANS_OPT_DEEPEN_RELATIVE,\n     -+\tTRANS_OPT_REJECT_SHALLOW\n     - \t};\n     - \n     - static int strbuf_set_helper_option(struct helper_data *data,\n     -\n       ## transport.c ##\n      @@ transport.c: static int set_git_option(struct git_transport_options *opts,\n       \t\tlist_objects_filter_die_if_populated(&opts->filter_options);\n     @@ transport.h: void transport_check_allowed(const char *type);\n       /* Aggressively fetch annotated tags if possible */\n       #define TRANS_OPT_FOLLOWTAGS \"followtags\"\n       \n     -+/* reject shallow repo transport  */\n     ++/* Reject shallow repo transport */\n      +#define TRANS_OPT_REJECT_SHALLOW \"rejectshallow\"\n      +\n       /* Accept refs that may update .git/shallow without --depth */\n\n\n Documentation/config/clone.txt |  4 +++\n Documentation/git-clone.txt    |  7 ++++-\n builtin/clone.c                | 24 +++++++++++++++++\n fetch-pack.c                   | 17 +++++++++---\n fetch-pack.h                   |  1 +\n t/t5606-clone-options.sh       | 47 ++++++++++++++++++++++++++++++++++\n t/t5611-clone-config.sh        | 32 +++++++++++++++++++++++\n transport.c                    |  4 +++\n transport.h                    |  4 +++\n 9 files changed, 135 insertions(+), 5 deletions(-)\n\ndiff --git a/Documentation/config/clone.txt b/Documentation/config/clone.txt\nindex 47de36a5fedf..7bcfbd18a52a 100644\n--- a/Documentation/config/clone.txt\n+++ b/Documentation/config/clone.txt\n@@ -2,3 +2,7 @@ clone.defaultRemoteName::\n \tThe name of the remote to create when cloning a repository.  Defaults to\n \t`origin`, and can be overridden by passing the `--origin` command-line\n \toption to linkgit:git-clone[1].\n+\n+clone.rejectShallow::\n+\tReject to clone a repository if it is a shallow one, can be overridden by\n+\tpassing option `--reject-shallow` in command line. See linkgit:git-clone[1]\ndiff --git a/Documentation/git-clone.txt b/Documentation/git-clone.txt\nindex 02d9c19cec75..0adc98fa7eee 100644\n--- a/Documentation/git-clone.txt\n+++ b/Documentation/git-clone.txt\n@@ -15,7 +15,7 @@ SYNOPSIS\n \t  [--dissociate] [--separate-git-dir <git dir>]\n \t  [--depth <depth>] [--[no-]single-branch] [--no-tags]\n \t  [--recurse-submodules[=<pathspec>]] [--[no-]shallow-submodules]\n-\t  [--[no-]remote-submodules] [--jobs <n>] [--sparse]\n+\t  [--[no-]remote-submodules] [--jobs <n>] [--sparse] [--[no-]reject-shallow]\n \t  [--filter=<filter>] [--] <repository>\n \t  [<directory>]\n \n@@ -149,6 +149,11 @@ objects from the source repository into a pack in the cloned repository.\n --no-checkout::\n \tNo checkout of HEAD is performed after the clone is complete.\n \n+--[no-]reject-shallow::\n+\tFail if the source repository is a shallow repository.\n+\tThe 'clone.rejectShallow' configuration variable can be used to\n+\tgive the default.\n+\n --bare::\n \tMake a 'bare' Git repository.  That is, instead of\n \tcreating `<directory>` and placing the administrative\ndiff --git a/builtin/clone.c b/builtin/clone.c\nindex 51e844a2de0a..5c64837e8f7b 100644\n--- a/builtin/clone.c\n+++ b/builtin/clone.c\n@@ -50,6 +50,8 @@ static int option_no_checkout, option_bare, option_mirror, option_single_branch\n static int option_local = -1, option_no_hardlinks, option_shared;\n static int option_no_tags;\n static int option_shallow_submodules;\n+static int option_shallow = -1;    /* unspecified */\n+static int config_shallow = -1;    /* unspecified */\n static int deepen;\n static char *option_template, *option_depth, *option_since;\n static char *option_origin = NULL;\n@@ -90,6 +92,8 @@ static struct option builtin_clone_options[] = {\n \tOPT__VERBOSITY(&option_verbosity),\n \tOPT_BOOL(0, \"progress\", &option_progress,\n \t\t N_(\"force progress reporting\")),\n+\tOPT_BOOL(0, \"reject-shallow\", &option_shallow,\n+\t\t N_(\"don't clone shallow repository\")),\n \tOPT_BOOL('n', \"no-checkout\", &option_no_checkout,\n \t\t N_(\"don't create a checkout\")),\n \tOPT_BOOL(0, \"bare\", &option_bare, N_(\"create a bare repository\")),\n@@ -858,6 +862,9 @@ static int git_clone_config(const char *k, const char *v, void *cb)\n \t\tfree(remote_name);\n \t\tremote_name = xstrdup(v);\n \t}\n+\tif (!strcmp(k, \"clone.rejectshallow\"))\n+\t\tconfig_shallow = git_config_bool(k, v);\n+\n \treturn git_default_config(k, v, cb);\n }\n \n@@ -963,6 +970,7 @@ static int path_exists(const char *path)\n int cmd_clone(int argc, const char **argv, const char *prefix)\n {\n \tint is_bundle = 0, is_local;\n+\tint reject_shallow = 0;\n \tconst char *repo_name, *repo, *work_tree, *git_dir;\n \tchar *path, *dir, *display_repo = NULL;\n \tint dest_exists, real_dest_exists = 0;\n@@ -1156,6 +1164,16 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n \t */\n \tgit_config(git_clone_config, NULL);\n \n+\t/*\n+\t * If option_shallow is specified from CLI option,\n+\t * ignore config_shallow from git_clone_config.\n+\t */\n+\tif (config_shallow != -1)\n+\t\treject_shallow = config_shallow;\n+\n+\tif (option_shallow != -1)\n+\t\treject_shallow = option_shallow;\n+\n \t/*\n \t * apply the remote name provided by --origin only after this second\n \t * call to git_config, to ensure it overrides all config-based values.\n@@ -1216,6 +1234,10 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n \t\tif (filter_options.choice)\n \t\t\twarning(_(\"--filter is ignored in local clones; use file:// instead.\"));\n \t\tif (!access(mkpath(\"%s/shallow\", path), F_OK)) {\n+\t\t\tif (reject_shallow)\n+\t\t\t\tdie(\"source repository is shallow, reject to clone.\");\n+\t\t\telse\n+\t\t\t\twarning(\"source repository is shallow.\");\n \t\t\tif (option_local > 0)\n \t\t\t\twarning(_(\"source repository is shallow, ignoring --local\"));\n \t\t\tis_local = 0;\n@@ -1227,6 +1249,8 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n \n \ttransport_set_option(transport, TRANS_OPT_KEEP, \"yes\");\n \n+\tif (reject_shallow)\n+\t\ttransport_set_option(transport, TRANS_OPT_REJECT_SHALLOW, \"1\");\n \tif (option_depth)\n \t\ttransport_set_option(transport, TRANS_OPT_DEPTH,\n \t\t\t\t     option_depth);\ndiff --git a/fetch-pack.c b/fetch-pack.c\nindex 0cb59acc4866..860ff45d46e7 100644\n--- a/fetch-pack.c\n+++ b/fetch-pack.c\n@@ -1126,12 +1126,17 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args,\n \n \tif (args->stateless_rpc)\n \t\tpacket_flush(fd[1]);\n+\n \tif (args->deepen)\n \t\tsetup_alternate_shallow(&shallow_lock, &alternate_shallow_file,\n \t\t\t\t\tNULL);\n-\telse if (si->nr_ours || si->nr_theirs)\n+\telse if (si->nr_ours || si->nr_theirs) {\n+\t\tif (args->remote_shallow)\n+\t\t\tdie(\"source repository is shallow, reject to clone.\");\n+\t\telse\n+\t\t\twarning(\"source repository is shallow.\");\n \t\talternate_shallow_file = setup_temporary_shallow(si->shallow);\n-\telse\n+\t} else\n \t\talternate_shallow_file = NULL;\n \tif (get_pack(args, fd, pack_lockfiles, NULL, sought, nr_sought,\n \t\t     &gitmodules_oids))\n@@ -1498,10 +1503,14 @@ static void receive_shallow_info(struct fetch_pack_args *args,\n \t\t * rejected (unless --update-shallow is set); do the same.\n \t\t */\n \t\tprepare_shallow_info(si, shallows);\n-\t\tif (si->nr_ours || si->nr_theirs)\n+\t\tif (si->nr_ours || si->nr_theirs) {\n+\t\t\tif (args->remote_shallow)\n+\t\t\t\tdie(\"source repository is shallow, reject to clone.\");\n+\t\t\telse\n+\t\t\t\twarning(\"source repository is shallow.\");\n \t\t\talternate_shallow_file =\n \t\t\t\tsetup_temporary_shallow(si->shallow);\n-\t\telse\n+\t\t} else\n \t\t\talternate_shallow_file = NULL;\n \t} else {\n \t\talternate_shallow_file = NULL;\ndiff --git a/fetch-pack.h b/fetch-pack.h\nindex 736a3dae467a..6e4f8f0d738c 100644\n--- a/fetch-pack.h\n+++ b/fetch-pack.h\n@@ -39,6 +39,7 @@ struct fetch_pack_args {\n \tunsigned self_contained_and_connected:1;\n \tunsigned cloning:1;\n \tunsigned update_shallow:1;\n+\tunsigned remote_shallow:1;\n \tunsigned deepen:1;\n \n \t/*\ndiff --git a/t/t5606-clone-options.sh b/t/t5606-clone-options.sh\nindex 52e5789fb050..6170d0513227 100755\n--- a/t/t5606-clone-options.sh\n+++ b/t/t5606-clone-options.sh\n@@ -5,6 +5,8 @@ GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n \n . ./test-lib.sh\n+. \"$TEST_DIRECTORY\"/lib-httpd.sh\n+start_httpd\n \n test_expect_success 'setup' '\n \n@@ -45,6 +47,51 @@ test_expect_success 'disallows --bare with --separate-git-dir' '\n \n '\n \n+test_expect_success 'fail to clone http shallow repository' '\n+\tgit clone --depth=1 --no-local parent shallow-repo &&\n+\tgit clone --bare --no-local shallow-repo \"$HTTPD_DOCUMENT_ROOT_PATH/repo.git\" &&\n+\ttest_must_fail git clone --reject-shallow $HTTPD_URL/smart/repo.git out 2>err &&\n+\ttest_i18ngrep -e \"source repository is shallow, reject to clone.\" err\n+\n+'\n+\n+test_expect_success 'fail to clone shallow repository' '\n+\trm -rf shallow-repo &&\n+\tgit clone --depth=1 --no-local parent shallow-repo &&\n+\ttest_must_fail git clone --reject-shallow shallow-repo out 2>err &&\n+\ttest_i18ngrep -e \"source repository is shallow, reject to clone.\" err\n+\n+'\n+\n+test_expect_success 'fail to clone non-local shallow repository' '\n+\trm -rf shallow-repo &&\n+\tgit clone --depth=1 --no-local parent shallow-repo &&\n+\ttest_must_fail git clone --reject-shallow --no-local shallow-repo out 2>err &&\n+\ttest_i18ngrep -e \"source repository is shallow, reject to clone.\" err\n+\n+'\n+\n+test_expect_success 'clone shallow repository with --no-reject-shallow' '\n+\trm -rf shallow-repo &&\n+\tgit clone --depth=1 --no-local parent shallow-repo &&\n+\tgit clone --no-reject-shallow --no-local shallow-repo clone-repo\n+\n+'\n+\n+test_expect_success 'clone normal repository with --reject-shallow' '\n+\trm -rf clone-repo &&\n+\tgit clone --no-local parent normal-repo &&\n+\tgit clone --reject-shallow --no-local normal-repo clone-repo\n+\n+'\n+\n+test_expect_success 'unspecified any configs or options' '\n+\trm -rf shallow-repo clone-repo &&\n+\tgit clone --depth=1 --no-local parent shallow-repo &&\n+\tgit clone shallow-repo clone-repo\n+\n+'\n+\n test_expect_success 'uses \"origin\" for default remote name' '\n \n \tgit clone parent clone-default-origin &&\ndiff --git a/t/t5611-clone-config.sh b/t/t5611-clone-config.sh\nindex 9f555b87ecdf..da10d3f10352 100755\n--- a/t/t5611-clone-config.sh\n+++ b/t/t5611-clone-config.sh\n@@ -95,6 +95,38 @@ test_expect_success 'clone -c remote.<remote>.fetch=<refspec> --origin=<name>' '\n \ttest_cmp expect actual\n '\n \n+test_expect_success 'clone.rejectshallow=true should fail to clone' '\n+\trm -rf child &&\n+\tgit clone --depth=1 --no-local . child &&\n+\ttest_must_fail git -c clone.rejectshallow=true clone --no-local child out 2>err &&\n+\ttest_i18ngrep -e \"source repository is shallow, reject to clone.\" err\n+'\n+\n+test_expect_success 'clone.rejectshallow=false should succeed' '\n+\trm -rf child out &&\n+\tgit clone --depth=1 --no-local . child &&\n+\tgit -c clone.rejectshallow=false clone --no-local child out\n+'\n+\n+test_expect_success 'clone.rejectshallow=true should succeed with normal repo' '\n+\trm -rf child out &&\n+\tgit clone --no-local . child &&\n+\tgit -c clone.rejectshallow=true clone --no-local child out\n+'\n+\n+test_expect_success 'option --reject-shallow override clone.rejectshallow' '\n+\trm -rf child out &&\n+\tgit clone --depth=1 --no-local . child &&\n+\ttest_must_fail git -c clone.rejectshallow=false clone --reject-shallow --no-local child out 2>err &&\n+\ttest_i18ngrep -e \"source repository is shallow, reject to clone.\" err\n+'\n+\n+test_expect_success 'option --no-reject-shallow override clone.rejectshallow' '\n+\trm -rf child out &&\n+\tgit clone --depth=1 --no-local . child &&\n+\tgit -c clone.rejectshallow=true clone --no-reject-shallow --no-local child out\n+'\n+\n test_expect_success MINGW 'clone -c core.hideDotFiles' '\n \ttest_commit attributes .gitattributes \"\" &&\n \trm -rf child &&\ndiff --git a/transport.c b/transport.c\nindex b13fab5dc3b1..34fe01221ee0 100644\n--- a/transport.c\n+++ b/transport.c\n@@ -236,6 +236,9 @@ static int set_git_option(struct git_transport_options *opts,\n \t\tlist_objects_filter_die_if_populated(&opts->filter_options);\n \t\tparse_list_objects_filter(&opts->filter_options, value);\n \t\treturn 0;\n+\t} else if (!strcmp(name, TRANS_OPT_REJECT_SHALLOW)) {\n+\t\topts->reject_shallow = !!value;\n+\t\treturn 0;\n \t}\n \treturn 1;\n }\n@@ -370,6 +373,7 @@ static int fetch_refs_via_pack(struct transport *transport,\n \targs.stateless_rpc = transport->stateless_rpc;\n \targs.server_options = transport->server_options;\n \targs.negotiation_tips = data->options.negotiation_tips;\n+\targs.remote_shallow = transport->smart_options->reject_shallow;\n \n \tif (!data->got_remote_heads) {\n \t\tint i;\ndiff --git a/transport.h b/transport.h\nindex 24e15799e714..4d5db0a7f22b 100644\n--- a/transport.h\n+++ b/transport.h\n@@ -14,6 +14,7 @@ struct git_transport_options {\n \tunsigned check_self_contained_and_connected : 1;\n \tunsigned self_contained_and_connected : 1;\n \tunsigned update_shallow : 1;\n+\tunsigned reject_shallow : 1;\n \tunsigned deepen_relative : 1;\n \n \t/* see documentation of corresponding flag in fetch-pack.h */\n@@ -194,6 +195,9 @@ void transport_check_allowed(const char *type);\n /* Aggressively fetch annotated tags if possible */\n #define TRANS_OPT_FOLLOWTAGS \"followtags\"\n \n+/* Reject shallow repo transport */\n+#define TRANS_OPT_REJECT_SHALLOW \"rejectshallow\"\n+\n /* Accept refs that may update .git/shallow without --depth */\n #define TRANS_OPT_UPDATE_SHALLOW \"updateshallow\"\n \n\nbase-commit: f01623b2c9d14207e497b21ebc6b3ec4afaf4b46\n-- \ngitgitgadget\n"},{"id":"418977","messageId":"5e9d62c6836011eb9c540026b95c99cc@oschina.cn","threadId":"55104","inReplyTo":"pull.865.v6.git.1614878345754.gitgitgadget@gmail.com","subject":"Re: [PATCH v6] builtin/clone.c: add --reject-shallow option","fromName":"lilinchao@oschina.cn","fromEmail":"lilinchao@oschina.cn","sentAt":"2021-03-12T18:25:44Z","receivedAt":"2021-03-12T18:26:29Z","isPatch":true,"sender":{"key":"lilinchao@oschina.cn","avatar":null},"body":"PING.\n\nSeems that this patch have been forgotten by everyone.\n\n--------------\n\n>From: lilinchao <lilinchao@oschina.cn>\n>\n>In some scenarios, users may want more history than the repository\n>offered for cloning, which happens to be a shallow repository, can\n>give them. But because users don't know it is a shallow repository\n>until they download it to local, users should have the option to\n>refuse to clone this kind of repository, and may want to exit the\n>process immediately without creating any unnecessary files.\n>\n>Althought there is an option '--depth=x' for users to decide how\n>deep history they can fetch, but as the unshallow cloning's depth\n>is INFINITY, we can't know exactly the minimun 'x' value that can\n>satisfy the minimum integrity, so we can't pass 'x' value to --depth,\n>and expect this can obtain a complete history of a repository.\n>\n>In other scenarios, if we have an API that allow us to import external\n>repository, and then perform various operations on the repo.\n>But if the imported is a shallow one(which is actually possible), it\n>will affect the subsequent operations. So we can choose to refuse to\n>clone, and let's just import a normal repository.\n>\n>This patch offers a new option '--reject-shallow' that can reject to\n>clone a shallow repository.\n>\n>Signed-off-by: lilinchao <lilinchao@oschina.cn>\n>---\n>    builtin/clone.c: add --reject-shallow option\n>   \n>    Changes since v1:\n>   \n>     * Rename --no-shallow to --reject-shallow\n>     * Enable to reject a non-local clone\n>     * Enable --[no-]reject-shallow from CLI override configuration.\n>     * Add more testcases.\n>     * Reword commit messages and relative documentation.\n>   \n>    Changes since v3:\n>   \n>     * Add support to reject clone shallow repo over https protocol\n> * Add testcase to reject clone shallow repo over https:// transport\n>     * Reword commit messages and relative documentation according\n>       suggestions from Junio.\n>   \n>    Changes since v5:\n>   \n>     * camelcase config variable\n>     * warning client that source repo is shallow\n>     * better support ssh:// and git:// protocol v1, v2\n>   \n>    Signed-off-by: lilinchao lilinchao@oschina.cn\n>\n>Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-865%2FCactusinhand%2Fgit-clone-options-v6\n>Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-865/Cactusinhand/git-clone-options-v6\n>Pull-Request: https://github.com/gitgitgadget/git/pull/865\n>\n>Range-diff vs v5:\n>\n> 1:  3f765e49e4a7 ! 1:  953122588ca8 builtin/clone.c: add --reject-shallow option\n>     @@ Documentation/config/clone.txt: clone.defaultRemoteName::\n>       `origin`, and can be overridden by passing the `--origin` command-line\n>       option to linkgit:git-clone[1].\n>      +\n>     -+clone.rejectshallow::\n>     ++clone.rejectShallow::\n>      +\tReject to clone a repository if it is a shallow one, can be overridden by\n>      +\tpassing option `--reject-shallow` in command line. See linkgit:git-clone[1]\n>     \n>     @@ Documentation/git-clone.txt: objects from the source repository into a pack in t\n>       No checkout of HEAD is performed after the clone is complete.\n>      \n>      +--[no-]reject-shallow::\n>     -+\tFail if the source repository is a shallow repository. The\n>     -+\t'clone.rejectshallow' configuration variable can be used to\n>     ++\tFail if the source repository is a shallow repository.\n>     ++\tThe 'clone.rejectShallow' configuration variable can be used to\n>      +\tgive the default.\n>      +\n>       --bare::\n>     @@ builtin/clone.c: static int git_clone_config(const char *k, const char *v, void\n>       free(remote_name);\n>       remote_name = xstrdup(v);\n>       }\n>     -+\tif (!strcmp(k, \"clone.rejectshallow\")) {\n>     ++\tif (!strcmp(k, \"clone.rejectshallow\"))\n>      +\tconfig_shallow = git_config_bool(k, v);\n>     -+\t}\n>     ++\n>       return git_default_config(k, v, cb);\n>       }\n>      \n>     @@ builtin/clone.c: int cmd_clone(int argc, const char **argv, const char *prefix)\n>      +\t* If option_shallow is specified from CLI option,\n>      +\t* ignore config_shallow from git_clone_config.\n>      +\t*/\n>     -+\tif (config_shallow != -1) {\n>     ++\tif (config_shallow != -1)\n>      +\treject_shallow = config_shallow;\n>     -+\t}\n>     -+\tif (option_shallow != -1) {\n>     ++\n>     ++\tif (option_shallow != -1)\n>      +\treject_shallow = option_shallow;\n>     -+\t}\n>     ++\n>       /*\n>       * apply the remote name provided by --origin only after this second\n>       * call to git_config, to ensure it overrides all config-based values.\n>     @@ builtin/clone.c: int cmd_clone(int argc, const char **argv, const char *prefix)\n>       if (!access(mkpath(\"%s/shallow\", path), F_OK)) {\n>      +\tif (reject_shallow)\n>      +\tdie(\"source repository is shallow, reject to clone.\");\n>     ++\telse\n>     ++\twarning(\"source repository is shallow.\");\n>       if (option_local > 0)\n>       warning(_(\"source repository is shallow, ignoring --local\"));\n>       is_local = 0;\n>     @@ fetch-pack.c: static struct ref *do_fetch_pack(struct fetch_pack_args *args,\n>      \n>       if (args->stateless_rpc)\n>       packet_flush(fd[1]);\n>     -+\n>     -+\tif (!args->deepen && args->remote_shallow)\n>     -+\tdie(\"source repository is shallow, reject to clone.\");\n>      +\n>       if (args->deepen)\n>       setup_alternate_shallow(&shallow_lock, &alternate_shallow_file,\n>       NULL);\n>     +-\telse if (si->nr_ours || si->nr_theirs)\n>     ++\telse if (si->nr_ours || si->nr_theirs) {\n>     ++\tif (args->remote_shallow)\n>     ++\tdie(\"source repository is shallow, reject to clone.\");\n>     ++\telse\n>     ++\twarning(\"source repository is shallow.\");\n>     + alternate_shallow_file = setup_temporary_shallow(si->shallow);\n>     +-\telse\n>     ++\t} else\n>     + alternate_shallow_file = NULL;\n>     + if (get_pack(args, fd, pack_lockfiles, NULL, sought, nr_sought,\n>     +      &gitmodules_oids))\n>      @@ fetch-pack.c: static void receive_shallow_info(struct fetch_pack_args *args,\n>     - * shallow. In v0, remote refs that reach these objects are\n>       * rejected (unless --update-shallow is set); do the same.\n>       */\n>     -+\tif (args->remote_shallow)\n>     -+\tdie(\"source repository is shallow, reject to clone.\");\n>       prepare_shallow_info(si, shallows);\n>     - if (si->nr_ours || si->nr_theirs)\n>     +-\tif (si->nr_ours || si->nr_theirs)\n>     ++\tif (si->nr_ours || si->nr_theirs) {\n>     ++\tif (args->remote_shallow)\n>     ++\tdie(\"source repository is shallow, reject to clone.\");\n>     ++\telse\n>     ++\twarning(\"source repository is shallow.\");\n>       alternate_shallow_file =\n>     + setup_temporary_shallow(si->shallow);\n>     +-\telse\n>     ++\t} else\n>     + alternate_shallow_file = NULL;\n>     + } else {\n>     + alternate_shallow_file = NULL;\n>     \n>       ## fetch-pack.h ##\n>      @@ fetch-pack.h: struct fetch_pack_args {\n>     @@ t/t5611-clone-config.sh: test_expect_success 'clone -c remote.<remote>.fetch=<re\n>       test_commit attributes .gitattributes \"\" &&\n>       rm -rf child &&\n>     \n>     - ## transport-helper.c ##\n>     -@@ transport-helper.c: static const char *boolean_options[] = {\n>     - TRANS_OPT_THIN,\n>     - TRANS_OPT_KEEP,\n>     - TRANS_OPT_FOLLOWTAGS,\n>     --\tTRANS_OPT_DEEPEN_RELATIVE\n>     -+\tTRANS_OPT_DEEPEN_RELATIVE,\n>     -+\tTRANS_OPT_REJECT_SHALLOW\n>     - };\n>     -\n>     - static int strbuf_set_helper_option(struct helper_data *data,\n>     -\n>       ## transport.c ##\n>      @@ transport.c: static int set_git_option(struct git_transport_options *opts,\n>       list_objects_filter_die_if_populated(&opts->filter_options);\n>     @@ transport.h: void transport_check_allowed(const char *type);\n>       /* Aggressively fetch annotated tags if possible */\n>       #define TRANS_OPT_FOLLOWTAGS \"followtags\"\n>      \n>     -+/* reject shallow repo transport  */\n>     ++/* Reject shallow repo transport */\n>      +#define TRANS_OPT_REJECT_SHALLOW \"rejectshallow\"\n>      +\n>       /* Accept refs that may update .git/shallow without --depth */\n>\n>\n> Documentation/config/clone.txt |  4 +++\n> Documentation/git-clone.txt    |  7 ++++-\n> builtin/clone.c                | 24 +++++++++++++++++\n> fetch-pack.c                   | 17 +++++++++---\n> fetch-pack.h                   |  1 +\n> t/t5606-clone-options.sh       | 47 ++++++++++++++++++++++++++++++++++\n> t/t5611-clone-config.sh        | 32 +++++++++++++++++++++++\n> transport.c                    |  4 +++\n> transport.h                    |  4 +++\n> 9 files changed, 135 insertions(+), 5 deletions(-)\n>\n>diff --git a/Documentation/config/clone.txt b/Documentation/config/clone.txt\n>index 47de36a5fedf..7bcfbd18a52a 100644\n>--- a/Documentation/config/clone.txt\n>+++ b/Documentation/config/clone.txt\n>@@ -2,3 +2,7 @@ clone.defaultRemoteName::\n> The name of the remote to create when cloning a repository.  Defaults to\n> `origin`, and can be overridden by passing the `--origin` command-line\n> option to linkgit:git-clone[1].\n>+\n>+clone.rejectShallow::\n>+\tReject to clone a repository if it is a shallow one, can be overridden by\n>+\tpassing option `--reject-shallow` in command line. See linkgit:git-clone[1]\n>diff --git a/Documentation/git-clone.txt b/Documentation/git-clone.txt\n>index 02d9c19cec75..0adc98fa7eee 100644\n>--- a/Documentation/git-clone.txt\n>+++ b/Documentation/git-clone.txt\n>@@ -15,7 +15,7 @@ SYNOPSIS\n>   [--dissociate] [--separate-git-dir <git dir>]\n>   [--depth <depth>] [--[no-]single-branch] [--no-tags]\n>   [--recurse-submodules[=<pathspec>]] [--[no-]shallow-submodules]\n>-\t  [--[no-]remote-submodules] [--jobs <n>] [--sparse]\n>+\t  [--[no-]remote-submodules] [--jobs <n>] [--sparse] [--[no-]reject-shallow]\n>   [--filter=<filter>] [--] <repository>\n>   [<directory>]\n>\n>@@ -149,6 +149,11 @@ objects from the source repository into a pack in the cloned repository.\n> --no-checkout::\n> No checkout of HEAD is performed after the clone is complete.\n>\n>+--[no-]reject-shallow::\n>+\tFail if the source repository is a shallow repository.\n>+\tThe 'clone.rejectShallow' configuration variable can be used to\n>+\tgive the default.\n>+\n> --bare::\n> Make a 'bare' Git repository.  That is, instead of\n> creating `<directory>` and placing the administrative\n>diff --git a/builtin/clone.c b/builtin/clone.c\n>index 51e844a2de0a..5c64837e8f7b 100644\n>--- a/builtin/clone.c\n>+++ b/builtin/clone.c\n>@@ -50,6 +50,8 @@ static int option_no_checkout, option_bare, option_mirror, option_single_branch\n> static int option_local = -1, option_no_hardlinks, option_shared;\n> static int option_no_tags;\n> static int option_shallow_submodules;\n>+static int option_shallow = -1;    /* unspecified */\n>+static int config_shallow = -1;    /* unspecified */\n> static int deepen;\n> static char *option_template, *option_depth, *option_since;\n> static char *option_origin = NULL;\n>@@ -90,6 +92,8 @@ static struct option builtin_clone_options[] = {\n> OPT__VERBOSITY(&option_verbosity),\n> OPT_BOOL(0, \"progress\", &option_progress,\n> N_(\"force progress reporting\")),\n>+\tOPT_BOOL(0, \"reject-shallow\", &option_shallow,\n>+\tN_(\"don't clone shallow repository\")),\n> OPT_BOOL('n', \"no-checkout\", &option_no_checkout,\n> N_(\"don't create a checkout\")),\n> OPT_BOOL(0, \"bare\", &option_bare, N_(\"create a bare repository\")),\n>@@ -858,6 +862,9 @@ static int git_clone_config(const char *k, const char *v, void *cb)\n> free(remote_name);\n> remote_name = xstrdup(v);\n> }\n>+\tif (!strcmp(k, \"clone.rejectshallow\"))\n>+\tconfig_shallow = git_config_bool(k, v);\n>+\n> return git_default_config(k, v, cb);\n> }\n>\n>@@ -963,6 +970,7 @@ static int path_exists(const char *path)\n> int cmd_clone(int argc, const char **argv, const char *prefix)\n> {\n> int is_bundle = 0, is_local;\n>+\tint reject_shallow = 0;\n> const char *repo_name, *repo, *work_tree, *git_dir;\n> char *path, *dir, *display_repo = NULL;\n> int dest_exists, real_dest_exists = 0;\n>@@ -1156,6 +1164,16 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n> */\n> git_config(git_clone_config, NULL);\n>\n>+\t/*\n>+\t* If option_shallow is specified from CLI option,\n>+\t* ignore config_shallow from git_clone_config.\n>+\t*/\n>+\tif (config_shallow != -1)\n>+\treject_shallow = config_shallow;\n>+\n>+\tif (option_shallow != -1)\n>+\treject_shallow = option_shallow;\n>+\n> /*\n> * apply the remote name provided by --origin only after this second\n> * call to git_config, to ensure it overrides all config-based values.\n>@@ -1216,6 +1234,10 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n> if (filter_options.choice)\n> warning(_(\"--filter is ignored in local clones; use file:// instead.\"));\n> if (!access(mkpath(\"%s/shallow\", path), F_OK)) {\n>+\tif (reject_shallow)\n>+\tdie(\"source repository is shallow, reject to clone.\");\n>+\telse\n>+\twarning(\"source repository is shallow.\");\n> if (option_local > 0)\n> warning(_(\"source repository is shallow, ignoring --local\"));\n> is_local = 0;\n>@@ -1227,6 +1249,8 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n>\n> transport_set_option(transport, TRANS_OPT_KEEP, \"yes\");\n>\n>+\tif (reject_shallow)\n>+\ttransport_set_option(transport, TRANS_OPT_REJECT_SHALLOW, \"1\");\n> if (option_depth)\n> transport_set_option(transport, TRANS_OPT_DEPTH,\n>      option_depth);\n>diff --git a/fetch-pack.c b/fetch-pack.c\n>index 0cb59acc4866..860ff45d46e7 100644\n>--- a/fetch-pack.c\n>+++ b/fetch-pack.c\n>@@ -1126,12 +1126,17 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args,\n>\n> if (args->stateless_rpc)\n> packet_flush(fd[1]);\n>+\n> if (args->deepen)\n> setup_alternate_shallow(&shallow_lock, &alternate_shallow_file,\n> NULL);\n>-\telse if (si->nr_ours || si->nr_theirs)\n>+\telse if (si->nr_ours || si->nr_theirs) {\n>+\tif (args->remote_shallow)\n>+\tdie(\"source repository is shallow, reject to clone.\");\n>+\telse\n>+\twarning(\"source repository is shallow.\");\n> alternate_shallow_file = setup_temporary_shallow(si->shallow);\n>-\telse\n>+\t} else\n> alternate_shallow_file = NULL;\n> if (get_pack(args, fd, pack_lockfiles, NULL, sought, nr_sought,\n>      &gitmodules_oids))\n>@@ -1498,10 +1503,14 @@ static void receive_shallow_info(struct fetch_pack_args *args,\n> * rejected (unless --update-shallow is set); do the same.\n> */\n> prepare_shallow_info(si, shallows);\n>-\tif (si->nr_ours || si->nr_theirs)\n>+\tif (si->nr_ours || si->nr_theirs) {\n>+\tif (args->remote_shallow)\n>+\tdie(\"source repository is shallow, reject to clone.\");\n>+\telse\n>+\twarning(\"source repository is shallow.\");\n> alternate_shallow_file =\n> setup_temporary_shallow(si->shallow);\n>-\telse\n>+\t} else\n> alternate_shallow_file = NULL;\n> } else {\n> alternate_shallow_file = NULL;\n>diff --git a/fetch-pack.h b/fetch-pack.h\n>index 736a3dae467a..6e4f8f0d738c 100644\n>--- a/fetch-pack.h\n>+++ b/fetch-pack.h\n>@@ -39,6 +39,7 @@ struct fetch_pack_args {\n> unsigned self_contained_and_connected:1;\n> unsigned cloning:1;\n> unsigned update_shallow:1;\n>+\tunsigned remote_shallow:1;\n> unsigned deepen:1;\n>\n> /*\n>diff --git a/t/t5606-clone-options.sh b/t/t5606-clone-options.sh\n>index 52e5789fb050..6170d0513227 100755\n>--- a/t/t5606-clone-options.sh\n>+++ b/t/t5606-clone-options.sh\n>@@ -5,6 +5,8 @@ GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n> export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n>\n> . ./test-lib.sh\n>+. \"$TEST_DIRECTORY\"/lib-httpd.sh\n>+start_httpd\n>\n> test_expect_success 'setup' '\n>\n>@@ -45,6 +47,51 @@ test_expect_success 'disallows --bare with --separate-git-dir' '\n>\n> '\n>\n>+test_expect_success 'fail to clone http shallow repository' '\n>+\tgit clone --depth=1 --no-local parent shallow-repo &&\n>+\tgit clone --bare --no-local shallow-repo \"$HTTPD_DOCUMENT_ROOT_PATH/repo.git\" &&\n>+\ttest_must_fail git clone --reject-shallow $HTTPD_URL/smart/repo.git out 2>err &&\n>+\ttest_i18ngrep -e \"source repository is shallow, reject to clone.\" err\n>+\n>+'\n>+\n>+test_expect_success 'fail to clone shallow repository' '\n>+\trm -rf shallow-repo &&\n>+\tgit clone --depth=1 --no-local parent shallow-repo &&\n>+\ttest_must_fail git clone --reject-shallow shallow-repo out 2>err &&\n>+\ttest_i18ngrep -e \"source repository is shallow, reject to clone.\" err\n>+\n>+'\n>+\n>+test_expect_success 'fail to clone non-local shallow repository' '\n>+\trm -rf shallow-repo &&\n>+\tgit clone --depth=1 --no-local parent shallow-repo &&\n>+\ttest_must_fail git clone --reject-shallow --no-local shallow-repo out 2>err &&\n>+\ttest_i18ngrep -e \"source repository is shallow, reject to clone.\" err\n>+\n>+'\n>+\n>+test_expect_success 'clone shallow repository with --no-reject-shallow' '\n>+\trm -rf shallow-repo &&\n>+\tgit clone --depth=1 --no-local parent shallow-repo &&\n>+\tgit clone --no-reject-shallow --no-local shallow-repo clone-repo\n>+\n>+'\n>+\n>+test_expect_success 'clone normal repository with --reject-shallow' '\n>+\trm -rf clone-repo &&\n>+\tgit clone --no-local parent normal-repo &&\n>+\tgit clone --reject-shallow --no-local normal-repo clone-repo\n>+\n>+'\n>+\n>+test_expect_success 'unspecified any configs or options' '\n>+\trm -rf shallow-repo clone-repo &&\n>+\tgit clone --depth=1 --no-local parent shallow-repo &&\n>+\tgit clone shallow-repo clone-repo\n>+\n>+'\n>+\n> test_expect_success 'uses \"origin\" for default remote name' '\n>\n> git clone parent clone-default-origin &&\n>diff --git a/t/t5611-clone-config.sh b/t/t5611-clone-config.sh\n>index 9f555b87ecdf..da10d3f10352 100755\n>--- a/t/t5611-clone-config.sh\n>+++ b/t/t5611-clone-config.sh\n>@@ -95,6 +95,38 @@ test_expect_success 'clone -c remote.<remote>.fetch=<refspec> --origin=<name>' '\n> test_cmp expect actual\n> '\n>\n>+test_expect_success 'clone.rejectshallow=true should fail to clone' '\n>+\trm -rf child &&\n>+\tgit clone --depth=1 --no-local . child &&\n>+\ttest_must_fail git -c clone.rejectshallow=true clone --no-local child out 2>err &&\n>+\ttest_i18ngrep -e \"source repository is shallow, reject to clone.\" err\n>+'\n>+\n>+test_expect_success 'clone.rejectshallow=false should succeed' '\n>+\trm -rf child out &&\n>+\tgit clone --depth=1 --no-local . child &&\n>+\tgit -c clone.rejectshallow=false clone --no-local child out\n>+'\n>+\n>+test_expect_success 'clone.rejectshallow=true should succeed with normal repo' '\n>+\trm -rf child out &&\n>+\tgit clone --no-local . child &&\n>+\tgit -c clone.rejectshallow=true clone --no-local child out\n>+'\n>+\n>+test_expect_success 'option --reject-shallow override clone.rejectshallow' '\n>+\trm -rf child out &&\n>+\tgit clone --depth=1 --no-local . child &&\n>+\ttest_must_fail git -c clone.rejectshallow=false clone --reject-shallow --no-local child out 2>err &&\n>+\ttest_i18ngrep -e \"source repository is shallow, reject to clone.\" err\n>+'\n>+\n>+test_expect_success 'option --no-reject-shallow override clone.rejectshallow' '\n>+\trm -rf child out &&\n>+\tgit clone --depth=1 --no-local . child &&\n>+\tgit -c clone.rejectshallow=true clone --no-reject-shallow --no-local child out\n>+'\n>+\n> test_expect_success MINGW 'clone -c core.hideDotFiles' '\n> test_commit attributes .gitattributes \"\" &&\n> rm -rf child &&\n>diff --git a/transport.c b/transport.c\n>index b13fab5dc3b1..34fe01221ee0 100644\n>--- a/transport.c\n>+++ b/transport.c\n>@@ -236,6 +236,9 @@ static int set_git_option(struct git_transport_options *opts,\n> list_objects_filter_die_if_populated(&opts->filter_options);\n> parse_list_objects_filter(&opts->filter_options, value);\n> return 0;\n>+\t} else if (!strcmp(name, TRANS_OPT_REJECT_SHALLOW)) {\n>+\topts->reject_shallow = !!value;\n>+\treturn 0;\n> }\n> return 1;\n> }\n>@@ -370,6 +373,7 @@ static int fetch_refs_via_pack(struct transport *transport,\n> args.stateless_rpc = transport->stateless_rpc;\n> args.server_options = transport->server_options;\n> args.negotiation_tips = data->options.negotiation_tips;\n>+\targs.remote_shallow = transport->smart_options->reject_shallow;\n>\n> if (!data->got_remote_heads) {\n> int i;\n>diff --git a/transport.h b/transport.h\n>index 24e15799e714..4d5db0a7f22b 100644\n>--- a/transport.h\n>+++ b/transport.h\n>@@ -14,6 +14,7 @@ struct git_transport_options {\n> unsigned check_self_contained_and_connected : 1;\n> unsigned self_contained_and_connected : 1;\n> unsigned update_shallow : 1;\n>+\tunsigned reject_shallow : 1;\n> unsigned deepen_relative : 1;\n>\n> /* see documentation of corresponding flag in fetch-pack.h */\n>@@ -194,6 +195,9 @@ void transport_check_allowed(const char *type);\n> /* Aggressively fetch annotated tags if possible */\n> #define TRANS_OPT_FOLLOWTAGS \"followtags\"\n>\n>+/* Reject shallow repo transport */\n>+#define TRANS_OPT_REJECT_SHALLOW \"rejectshallow\"\n>+\n> /* Accept refs that may update .git/shallow without --depth */\n> #define TRANS_OPT_UPDATE_SHALLOW \"updateshallow\"\n>\n>\n>base-commit: f01623b2c9d14207e497b21ebc6b3ec4afaf4b46\n>--\n>gitgitgadget\n>"},{"id":"420187","messageId":"pull.865.v7.git.1616670558261.gitgitgadget@gmail.com","threadId":"55104","inReplyTo":"pull.865.v6.git.1614878345754.gitgitgadget@gmail.com","subject":"[PATCH v7] builtin/clone.c: add --reject-shallow option","fromName":"Li Linchao via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-03-25T11:09:17Z","receivedAt":"2021-03-25T11:10:18Z","isPatch":true,"sender":{"key":"name:Li Linchao","avatar":null},"body":"From: lilinchao <lilinchao@oschina.cn>\n\nIn some scenarios, users may want more history than the repository\noffered for cloning, which happens to be a shallow repository, can\ngive them. But because users don't know it is a shallow repository\nuntil they download it to local, users should have the option to\nrefuse to clone this kind of repository, and may want to exit the\nprocess immediately without creating any unnecessary files.\n\nAlthought there is an option '--depth=x' for users to decide how\ndeep history they can fetch, but as the unshallow cloning's depth\nis INFINITY, we can't know exactly the minimun 'x' value that can\nsatisfy the minimum integrity, so we can't pass 'x' value to --depth,\nand expect this can obtain a complete history of a repository.\n\nIn other scenarios, if we have an API that allow us to import external\nrepository, and then perform various operations on the repo.\nBut if the imported is a shallow one(which is actually possible), it\nwill affect the subsequent operations. So we can choose to refuse to\nclone, and let's just import a normal repository.\n\nThis patch offers a new option '--reject-shallow' that can reject to\nclone a shallow repository.\n\nSigned-off-by: lilinchao <lilinchao@oschina.cn>\n\nReviewed-by: Derrick Stolee <dstolee@microsoft.com>\nReviewed-by: Junio C Hamano<gitster@pobox.com>\nReviewed-by: Johannes Schindelin <johannes.schindelin@gmx.de>\nReviewed-by: Jonathan Tan<jonathantanmy@google.com>\n---\n    builtin/clone.c: add --reject-shallow option\n    \n    Changes since v1:\n    \n     * Rename --no-shallow to --reject-shallow\n     * Enable to reject a non-local clone\n     * Enable --[no-]reject-shallow from CLI override configuration.\n     * Add more testcases.\n     * Reword commit messages and relative documentation.\n    \n    Changes since v3:\n    \n     * Add support to reject clone shallow repo over https protocol\n     * Add testcase to reject clone shallow repo over https:// transport\n     * Reword commit messages and relative documentation according\n       suggestions from Junio.\n    \n    Changes since v5:\n    \n     * camelcase config variable\n     * warning client that source repo is shallow\n     * better support ssh:// and git:// protocol v1, v2\n    \n    Changes since v6:\n    \n     * use _() for warning/die statement\n    \n    Signed-off-by: lilinchao lilinchao@oschina.cn\n\nPublished-As: https://github.com/gitgitgadget/git/releases/tag/pr-865%2FCactusinhand%2Fgit-clone-options-v7\nFetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-865/Cactusinhand/git-clone-options-v7\nPull-Request: https://github.com/gitgitgadget/git/pull/865\n\nRange-diff vs v6:\n\n 1:  953122588ca8 ! 1:  b957b77bfd79 builtin/clone.c: add --reject-shallow option\n     @@ Commit message\n      \n          Signed-off-by: lilinchao <lilinchao@oschina.cn>\n      \n     +    Reviewed-by: Derrick Stolee <dstolee@microsoft.com>\n     +    Reviewed-by: Junio C Hamano<gitster@pobox.com>\n     +    Reviewed-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n     +    Reviewed-by: Jonathan Tan<jonathantanmy@google.com>\n     +\n       ## Documentation/config/clone.txt ##\n      @@ Documentation/config/clone.txt: clone.defaultRemoteName::\n       \tThe name of the remote to create when cloning a repository.  Defaults to\n     @@ builtin/clone.c: int cmd_clone(int argc, const char **argv, const char *prefix)\n       \t\t\twarning(_(\"--filter is ignored in local clones; use file:// instead.\"));\n       \t\tif (!access(mkpath(\"%s/shallow\", path), F_OK)) {\n      +\t\t\tif (reject_shallow)\n     -+\t\t\t\tdie(\"source repository is shallow, reject to clone.\");\n     ++\t\t\t\tdie(_(\"source repository is shallow, reject to clone.\"));\n      +\t\t\telse\n     -+\t\t\t\twarning(\"source repository is shallow.\");\n     ++\t\t\t\twarning(_(\"source repository is shallow.\"));\n       \t\t\tif (option_local > 0)\n       \t\t\t\twarning(_(\"source repository is shallow, ignoring --local\"));\n       \t\t\tis_local = 0;\n     @@ builtin/clone.c: int cmd_clone(int argc, const char **argv, const char *prefix)\n      \n       ## fetch-pack.c ##\n      @@ fetch-pack.c: static struct ref *do_fetch_pack(struct fetch_pack_args *args,\n     - \n     - \tif (args->stateless_rpc)\n     - \t\tpacket_flush(fd[1]);\n     -+\n       \tif (args->deepen)\n       \t\tsetup_alternate_shallow(&shallow_lock, &alternate_shallow_file,\n       \t\t\t\t\tNULL);\n      -\telse if (si->nr_ours || si->nr_theirs)\n      +\telse if (si->nr_ours || si->nr_theirs) {\n      +\t\tif (args->remote_shallow)\n     -+\t\t\tdie(\"source repository is shallow, reject to clone.\");\n     ++\t\t\tdie(_(\"source repository is shallow, reject to clone.\"));\n      +\t\telse\n     -+\t\t\twarning(\"source repository is shallow.\");\n     ++\t\t\twarning(_(\"source repository is shallow.\"));\n       \t\talternate_shallow_file = setup_temporary_shallow(si->shallow);\n      -\telse\n      +\t} else\n     @@ fetch-pack.c: static void receive_shallow_info(struct fetch_pack_args *args,\n      -\t\tif (si->nr_ours || si->nr_theirs)\n      +\t\tif (si->nr_ours || si->nr_theirs) {\n      +\t\t\tif (args->remote_shallow)\n     -+\t\t\t\tdie(\"source repository is shallow, reject to clone.\");\n     ++\t\t\t\tdie(_(\"source repository is shallow, reject to clone.\"));\n      +\t\t\telse\n     -+\t\t\t\twarning(\"source repository is shallow.\");\n     ++\t\t\t\twarning(_(\"source repository is shallow.\"));\n       \t\t\talternate_shallow_file =\n       \t\t\t\tsetup_temporary_shallow(si->shallow);\n      -\t\telse\n\n\n Documentation/config/clone.txt |  4 +++\n Documentation/git-clone.txt    |  7 ++++-\n builtin/clone.c                | 24 +++++++++++++++++\n fetch-pack.c                   | 16 +++++++++---\n fetch-pack.h                   |  1 +\n t/t5606-clone-options.sh       | 47 ++++++++++++++++++++++++++++++++++\n t/t5611-clone-config.sh        | 32 +++++++++++++++++++++++\n transport.c                    |  4 +++\n transport.h                    |  4 +++\n 9 files changed, 134 insertions(+), 5 deletions(-)\n\ndiff --git a/Documentation/config/clone.txt b/Documentation/config/clone.txt\nindex 47de36a5fedf..7bcfbd18a52a 100644\n--- a/Documentation/config/clone.txt\n+++ b/Documentation/config/clone.txt\n@@ -2,3 +2,7 @@ clone.defaultRemoteName::\n \tThe name of the remote to create when cloning a repository.  Defaults to\n \t`origin`, and can be overridden by passing the `--origin` command-line\n \toption to linkgit:git-clone[1].\n+\n+clone.rejectShallow::\n+\tReject to clone a repository if it is a shallow one, can be overridden by\n+\tpassing option `--reject-shallow` in command line. See linkgit:git-clone[1]\ndiff --git a/Documentation/git-clone.txt b/Documentation/git-clone.txt\nindex 02d9c19cec75..0adc98fa7eee 100644\n--- a/Documentation/git-clone.txt\n+++ b/Documentation/git-clone.txt\n@@ -15,7 +15,7 @@ SYNOPSIS\n \t  [--dissociate] [--separate-git-dir <git dir>]\n \t  [--depth <depth>] [--[no-]single-branch] [--no-tags]\n \t  [--recurse-submodules[=<pathspec>]] [--[no-]shallow-submodules]\n-\t  [--[no-]remote-submodules] [--jobs <n>] [--sparse]\n+\t  [--[no-]remote-submodules] [--jobs <n>] [--sparse] [--[no-]reject-shallow]\n \t  [--filter=<filter>] [--] <repository>\n \t  [<directory>]\n \n@@ -149,6 +149,11 @@ objects from the source repository into a pack in the cloned repository.\n --no-checkout::\n \tNo checkout of HEAD is performed after the clone is complete.\n \n+--[no-]reject-shallow::\n+\tFail if the source repository is a shallow repository.\n+\tThe 'clone.rejectShallow' configuration variable can be used to\n+\tgive the default.\n+\n --bare::\n \tMake a 'bare' Git repository.  That is, instead of\n \tcreating `<directory>` and placing the administrative\ndiff --git a/builtin/clone.c b/builtin/clone.c\nindex 51e844a2de0a..a1956b0c9c6f 100644\n--- a/builtin/clone.c\n+++ b/builtin/clone.c\n@@ -50,6 +50,8 @@ static int option_no_checkout, option_bare, option_mirror, option_single_branch\n static int option_local = -1, option_no_hardlinks, option_shared;\n static int option_no_tags;\n static int option_shallow_submodules;\n+static int option_shallow = -1;    /* unspecified */\n+static int config_shallow = -1;    /* unspecified */\n static int deepen;\n static char *option_template, *option_depth, *option_since;\n static char *option_origin = NULL;\n@@ -90,6 +92,8 @@ static struct option builtin_clone_options[] = {\n \tOPT__VERBOSITY(&option_verbosity),\n \tOPT_BOOL(0, \"progress\", &option_progress,\n \t\t N_(\"force progress reporting\")),\n+\tOPT_BOOL(0, \"reject-shallow\", &option_shallow,\n+\t\t N_(\"don't clone shallow repository\")),\n \tOPT_BOOL('n', \"no-checkout\", &option_no_checkout,\n \t\t N_(\"don't create a checkout\")),\n \tOPT_BOOL(0, \"bare\", &option_bare, N_(\"create a bare repository\")),\n@@ -858,6 +862,9 @@ static int git_clone_config(const char *k, const char *v, void *cb)\n \t\tfree(remote_name);\n \t\tremote_name = xstrdup(v);\n \t}\n+\tif (!strcmp(k, \"clone.rejectshallow\"))\n+\t\tconfig_shallow = git_config_bool(k, v);\n+\n \treturn git_default_config(k, v, cb);\n }\n \n@@ -963,6 +970,7 @@ static int path_exists(const char *path)\n int cmd_clone(int argc, const char **argv, const char *prefix)\n {\n \tint is_bundle = 0, is_local;\n+\tint reject_shallow = 0;\n \tconst char *repo_name, *repo, *work_tree, *git_dir;\n \tchar *path, *dir, *display_repo = NULL;\n \tint dest_exists, real_dest_exists = 0;\n@@ -1156,6 +1164,16 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n \t */\n \tgit_config(git_clone_config, NULL);\n \n+\t/*\n+\t * If option_shallow is specified from CLI option,\n+\t * ignore config_shallow from git_clone_config.\n+\t */\n+\tif (config_shallow != -1)\n+\t\treject_shallow = config_shallow;\n+\n+\tif (option_shallow != -1)\n+\t\treject_shallow = option_shallow;\n+\n \t/*\n \t * apply the remote name provided by --origin only after this second\n \t * call to git_config, to ensure it overrides all config-based values.\n@@ -1216,6 +1234,10 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n \t\tif (filter_options.choice)\n \t\t\twarning(_(\"--filter is ignored in local clones; use file:// instead.\"));\n \t\tif (!access(mkpath(\"%s/shallow\", path), F_OK)) {\n+\t\t\tif (reject_shallow)\n+\t\t\t\tdie(_(\"source repository is shallow, reject to clone.\"));\n+\t\t\telse\n+\t\t\t\twarning(_(\"source repository is shallow.\"));\n \t\t\tif (option_local > 0)\n \t\t\t\twarning(_(\"source repository is shallow, ignoring --local\"));\n \t\t\tis_local = 0;\n@@ -1227,6 +1249,8 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n \n \ttransport_set_option(transport, TRANS_OPT_KEEP, \"yes\");\n \n+\tif (reject_shallow)\n+\t\ttransport_set_option(transport, TRANS_OPT_REJECT_SHALLOW, \"1\");\n \tif (option_depth)\n \t\ttransport_set_option(transport, TRANS_OPT_DEPTH,\n \t\t\t\t     option_depth);\ndiff --git a/fetch-pack.c b/fetch-pack.c\nindex fb04a76ca263..72b378449a07 100644\n--- a/fetch-pack.c\n+++ b/fetch-pack.c\n@@ -1129,9 +1129,13 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args,\n \tif (args->deepen)\n \t\tsetup_alternate_shallow(&shallow_lock, &alternate_shallow_file,\n \t\t\t\t\tNULL);\n-\telse if (si->nr_ours || si->nr_theirs)\n+\telse if (si->nr_ours || si->nr_theirs) {\n+\t\tif (args->remote_shallow)\n+\t\t\tdie(_(\"source repository is shallow, reject to clone.\"));\n+\t\telse\n+\t\t\twarning(_(\"source repository is shallow.\"));\n \t\talternate_shallow_file = setup_temporary_shallow(si->shallow);\n-\telse\n+\t} else\n \t\talternate_shallow_file = NULL;\n \tif (get_pack(args, fd, pack_lockfiles, NULL, sought, nr_sought,\n \t\t     &gitmodules_oids))\n@@ -1498,10 +1502,14 @@ static void receive_shallow_info(struct fetch_pack_args *args,\n \t\t * rejected (unless --update-shallow is set); do the same.\n \t\t */\n \t\tprepare_shallow_info(si, shallows);\n-\t\tif (si->nr_ours || si->nr_theirs)\n+\t\tif (si->nr_ours || si->nr_theirs) {\n+\t\t\tif (args->remote_shallow)\n+\t\t\t\tdie(_(\"source repository is shallow, reject to clone.\"));\n+\t\t\telse\n+\t\t\t\twarning(_(\"source repository is shallow.\"));\n \t\t\talternate_shallow_file =\n \t\t\t\tsetup_temporary_shallow(si->shallow);\n-\t\telse\n+\t\t} else\n \t\t\talternate_shallow_file = NULL;\n \t} else {\n \t\talternate_shallow_file = NULL;\ndiff --git a/fetch-pack.h b/fetch-pack.h\nindex 736a3dae467a..6e4f8f0d738c 100644\n--- a/fetch-pack.h\n+++ b/fetch-pack.h\n@@ -39,6 +39,7 @@ struct fetch_pack_args {\n \tunsigned self_contained_and_connected:1;\n \tunsigned cloning:1;\n \tunsigned update_shallow:1;\n+\tunsigned remote_shallow:1;\n \tunsigned deepen:1;\n \n \t/*\ndiff --git a/t/t5606-clone-options.sh b/t/t5606-clone-options.sh\nindex 428b0aac93fa..2863b8b28d44 100755\n--- a/t/t5606-clone-options.sh\n+++ b/t/t5606-clone-options.sh\n@@ -5,6 +5,8 @@ GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n \n . ./test-lib.sh\n+. \"$TEST_DIRECTORY\"/lib-httpd.sh\n+start_httpd\n \n test_expect_success 'setup' '\n \n@@ -45,6 +47,51 @@ test_expect_success 'disallows --bare with --separate-git-dir' '\n \n '\n \n+test_expect_success 'fail to clone http shallow repository' '\n+\tgit clone --depth=1 --no-local parent shallow-repo &&\n+\tgit clone --bare --no-local shallow-repo \"$HTTPD_DOCUMENT_ROOT_PATH/repo.git\" &&\n+\ttest_must_fail git clone --reject-shallow $HTTPD_URL/smart/repo.git out 2>err &&\n+\ttest_i18ngrep -e \"source repository is shallow, reject to clone.\" err\n+\n+'\n+\n+test_expect_success 'fail to clone shallow repository' '\n+\trm -rf shallow-repo &&\n+\tgit clone --depth=1 --no-local parent shallow-repo &&\n+\ttest_must_fail git clone --reject-shallow shallow-repo out 2>err &&\n+\ttest_i18ngrep -e \"source repository is shallow, reject to clone.\" err\n+\n+'\n+\n+test_expect_success 'fail to clone non-local shallow repository' '\n+\trm -rf shallow-repo &&\n+\tgit clone --depth=1 --no-local parent shallow-repo &&\n+\ttest_must_fail git clone --reject-shallow --no-local shallow-repo out 2>err &&\n+\ttest_i18ngrep -e \"source repository is shallow, reject to clone.\" err\n+\n+'\n+\n+test_expect_success 'clone shallow repository with --no-reject-shallow' '\n+\trm -rf shallow-repo &&\n+\tgit clone --depth=1 --no-local parent shallow-repo &&\n+\tgit clone --no-reject-shallow --no-local shallow-repo clone-repo\n+\n+'\n+\n+test_expect_success 'clone normal repository with --reject-shallow' '\n+\trm -rf clone-repo &&\n+\tgit clone --no-local parent normal-repo &&\n+\tgit clone --reject-shallow --no-local normal-repo clone-repo\n+\n+'\n+\n+test_expect_success 'unspecified any configs or options' '\n+\trm -rf shallow-repo clone-repo &&\n+\tgit clone --depth=1 --no-local parent shallow-repo &&\n+\tgit clone shallow-repo clone-repo\n+\n+'\n+\n test_expect_success 'uses \"origin\" for default remote name' '\n \n \tgit clone parent clone-default-origin &&\ndiff --git a/t/t5611-clone-config.sh b/t/t5611-clone-config.sh\nindex 9f555b87ecdf..da10d3f10352 100755\n--- a/t/t5611-clone-config.sh\n+++ b/t/t5611-clone-config.sh\n@@ -95,6 +95,38 @@ test_expect_success 'clone -c remote.<remote>.fetch=<refspec> --origin=<name>' '\n \ttest_cmp expect actual\n '\n \n+test_expect_success 'clone.rejectshallow=true should fail to clone' '\n+\trm -rf child &&\n+\tgit clone --depth=1 --no-local . child &&\n+\ttest_must_fail git -c clone.rejectshallow=true clone --no-local child out 2>err &&\n+\ttest_i18ngrep -e \"source repository is shallow, reject to clone.\" err\n+'\n+\n+test_expect_success 'clone.rejectshallow=false should succeed' '\n+\trm -rf child out &&\n+\tgit clone --depth=1 --no-local . child &&\n+\tgit -c clone.rejectshallow=false clone --no-local child out\n+'\n+\n+test_expect_success 'clone.rejectshallow=true should succeed with normal repo' '\n+\trm -rf child out &&\n+\tgit clone --no-local . child &&\n+\tgit -c clone.rejectshallow=true clone --no-local child out\n+'\n+\n+test_expect_success 'option --reject-shallow override clone.rejectshallow' '\n+\trm -rf child out &&\n+\tgit clone --depth=1 --no-local . child &&\n+\ttest_must_fail git -c clone.rejectshallow=false clone --reject-shallow --no-local child out 2>err &&\n+\ttest_i18ngrep -e \"source repository is shallow, reject to clone.\" err\n+'\n+\n+test_expect_success 'option --no-reject-shallow override clone.rejectshallow' '\n+\trm -rf child out &&\n+\tgit clone --depth=1 --no-local . child &&\n+\tgit -c clone.rejectshallow=true clone --no-reject-shallow --no-local child out\n+'\n+\n test_expect_success MINGW 'clone -c core.hideDotFiles' '\n \ttest_commit attributes .gitattributes \"\" &&\n \trm -rf child &&\ndiff --git a/transport.c b/transport.c\nindex 1c4ab676d1b1..a6b9f404d86a 100644\n--- a/transport.c\n+++ b/transport.c\n@@ -236,6 +236,9 @@ static int set_git_option(struct git_transport_options *opts,\n \t\tlist_objects_filter_die_if_populated(&opts->filter_options);\n \t\tparse_list_objects_filter(&opts->filter_options, value);\n \t\treturn 0;\n+\t} else if (!strcmp(name, TRANS_OPT_REJECT_SHALLOW)) {\n+\t\topts->reject_shallow = !!value;\n+\t\treturn 0;\n \t}\n \treturn 1;\n }\n@@ -370,6 +373,7 @@ static int fetch_refs_via_pack(struct transport *transport,\n \targs.stateless_rpc = transport->stateless_rpc;\n \targs.server_options = transport->server_options;\n \targs.negotiation_tips = data->options.negotiation_tips;\n+\targs.remote_shallow = transport->smart_options->reject_shallow;\n \n \tif (!data->got_remote_heads) {\n \t\tint i;\ndiff --git a/transport.h b/transport.h\nindex 24e15799e714..4d5db0a7f22b 100644\n--- a/transport.h\n+++ b/transport.h\n@@ -14,6 +14,7 @@ struct git_transport_options {\n \tunsigned check_self_contained_and_connected : 1;\n \tunsigned self_contained_and_connected : 1;\n \tunsigned update_shallow : 1;\n+\tunsigned reject_shallow : 1;\n \tunsigned deepen_relative : 1;\n \n \t/* see documentation of corresponding flag in fetch-pack.h */\n@@ -194,6 +195,9 @@ void transport_check_allowed(const char *type);\n /* Aggressively fetch annotated tags if possible */\n #define TRANS_OPT_FOLLOWTAGS \"followtags\"\n \n+/* Reject shallow repo transport */\n+#define TRANS_OPT_REJECT_SHALLOW \"rejectshallow\"\n+\n /* Accept refs that may update .git/shallow without --depth */\n #define TRANS_OPT_UPDATE_SHALLOW \"updateshallow\"\n \n\nbase-commit: 9198c13e34f6d51c983b31a9397d4d62bc2147ac\n-- \ngitgitgadget\n"},{"id":"420207","messageId":"xmqqa6qrug9u.fsf@gitster.g","threadId":"55104","inReplyTo":"pull.865.v7.git.1616670558261.gitgitgadget@gmail.com","subject":"Re: [PATCH v7] builtin/clone.c: add --reject-shallow option","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-03-25T20:31:25Z","receivedAt":"2021-03-25T20:32:23Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"Li Linchao via GitGitGadget\" <gitgitgadget@gmail.com> writes:\n\n> ...\n> This patch offers a new option '--reject-shallow' that can reject to\n> clone a shallow repository.\n>\n> Signed-off-by: lilinchao <lilinchao@oschina.cn>\n>\n> Reviewed-by: Derrick Stolee <dstolee@microsoft.com>\n> Reviewed-by: Junio C Hamano<gitster@pobox.com>\n> Reviewed-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n> Reviewed-by: Jonathan Tan<jonathantanmy@google.com>\n\nThe Reviewed-by trailer means something quite different from what\nyou seem to think here.  It is only given by the reviewers to the\npatch when they carefully reviewed and agrees what is in the patch.\nThe patch authors are in no position to add it, unless they are\nexplicitly told by reviewers that \"this patch now can have my\nReviewed-by:\" or some equivalent.\n\nThe (ideal) flow of events is\n\n 0. The author comes up with an idea and writes a patch.\n\n 1. The patch is sent to the list and Cc'ed to people who may be\n    familiar with the area the patch touches.  For second and\n    subsequent iterations, those who gave review comments to the\n    previous iterations are also good people to Cc to.\n\n 2. People give comments as reponses to the patch.\n\n    (a) some may be happy with the iteration of the patch they\n        reviewed, and may say \"Thanks for contributing, this is now\n        Reviewed-by: me\".  For second and subsequent iterations,\n        they may say \"This was improved relative to the previous\n        iteration, and it still looks good and you have my\n        Reviewed-by:\".\n\n    (b) some may give constructive criticism, alternatives,\n        enhancements, or outright \"not a good idea, don't do this\n        because ...\".\n\n    (c) some may just act as cheerleaders.\n\n 3. The author thinks about the review comments and also may find\n    improvement him/herself.\n\n    (a) There may need an update to the patch.  If the patch has\n        changed since the previous version in any way, ignore\n        Reviewed-by: received in 2-(a).  When a significant help was\n        given to update the patch, you may add \"Helped-by:\" trailer\n        to credit the person's contribution.\n\n        Your own \"Signed-off-by:\" appears the last in the trailers\n        (i.e. \"this iteration of the patch was written with help\n        from these people, and then I am signing it off just before\n        sending it out\").\n\n\tGo back to 1. and repeat as many times as it takes.\n    \n    (b) There may not be a need for any update to the patch.  Only\n        add the Reviewed-by: received in 2-(a) and otherwise do not\n        change anything.  Your own \"Signed-off-by:\" appears the last\n        in the trailers.  Send it to the list and to the maintainer\n        (me).\n\n 4. The maintainer applies the patch, unless there is no other\n    comments received on that supposedly-the-final version sent in\n    3-(b), but a late review comment may make us realize that it was\n    premature, in which case we may go back to 3-(a).\n\n"},{"id":"420209","messageId":"xmqqmtuqu9io.fsf@gitster.g","threadId":"55104","inReplyTo":"pull.865.v7.git.1616670558261.gitgitgadget@gmail.com","subject":"Re: [PATCH v7] builtin/clone.c: add --reject-shallow option","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-03-25T22:57:19Z","receivedAt":"2021-03-25T22:58:10Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"Li Linchao via GitGitGadget\" <gitgitgadget@gmail.com> writes:\n\n> @@ -1216,6 +1234,10 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n>  \t\tif (filter_options.choice)\n>  \t\t\twarning(_(\"--filter is ignored in local clones; use file:// instead.\"));\n>  \t\tif (!access(mkpath(\"%s/shallow\", path), F_OK)) {\n> +\t\t\tif (reject_shallow)\n> +\t\t\t\tdie(_(\"source repository is shallow, reject to clone.\"));\n> +\t\t\telse\n> +\t\t\t\twarning(_(\"source repository is shallow.\"));\n\nHmph, is it an improvement to warn() when the user does not mind\ncloning a shallow repository?\n\n\t$ git clone --depth=3 $URL clone-1\n\t$ git clone file://$(pwd)/clone-1 clone-2\n\nwould give us clone-2 that is just as functional as clone-1 is, no?\nclone-1 may be missing objects that is needed far into the past, and\nclone-2 would lack the same set of objects as clone-1 does, but a\nuser who is happily using clone-1 would be happy with clone-2 the\nsame way, no?\n\n> diff --git a/fetch-pack.c b/fetch-pack.c\n> index fb04a76ca263..72b378449a07 100644\n> --- a/fetch-pack.c\n> +++ b/fetch-pack.c\n> @@ -1129,9 +1129,13 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args,\n>  \tif (args->deepen)\n>  \t\tsetup_alternate_shallow(&shallow_lock, &alternate_shallow_file,\n>  \t\t\t\t\tNULL);\n> -\telse if (si->nr_ours || si->nr_theirs)\n> +\telse if (si->nr_ours || si->nr_theirs) {\n> +\t\tif (args->remote_shallow)\n> +\t\t\tdie(_(\"source repository is shallow, reject to clone.\"));\n\nStopping early before calling get_pack() would significantly reduce\nthe overhead, which is good.\n\n> +\t\telse\n> +\t\t\twarning(_(\"source repository is shallow.\"));\n\nThe same question on the wisdom of warning here.\n\n> @@ -1498,10 +1502,14 @@ static void receive_shallow_info(struct fetch_pack_args *args,\n>  \t\t * rejected (unless --update-shallow is set); do the same.\n>  \t\t */\n>  \t\tprepare_shallow_info(si, shallows);\n> -\t\tif (si->nr_ours || si->nr_theirs)\n> +\t\tif (si->nr_ours || si->nr_theirs) {\n> +\t\t\tif (args->remote_shallow)\n> +\t\t\t\tdie(_(\"source repository is shallow, reject to clone.\"));\n> +\t\t\telse\n> +\t\t\t\twarning(_(\"source repository is shallow.\"));\n\nOK, so, this is the equivalent of the above for protocol-v2?  The\nsame comments apply, then.\n\n> diff --git a/t/t5606-clone-options.sh b/t/t5606-clone-options.sh\n> index 428b0aac93fa..2863b8b28d44 100755\n> --- a/t/t5606-clone-options.sh\n> +++ b/t/t5606-clone-options.sh\n> @@ -5,6 +5,8 @@ GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n>  export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n>  \n>  . ./test-lib.sh\n> +. \"$TEST_DIRECTORY\"/lib-httpd.sh\n> +start_httpd\n>  \n>  test_expect_success 'setup' '\n>  \n> @@ -45,6 +47,51 @@ test_expect_success 'disallows --bare with --separate-git-dir' '\n>  \n>  '\n>  \n> +test_expect_success 'fail to clone http shallow repository' '\n\ns/fail to clone/reject cloning/, perhaps.\n\n> +test_expect_success 'clone shallow repository with --no-reject-shallow' '\n> +\trm -rf shallow-repo &&\n> +\tgit clone --depth=1 --no-local parent shallow-repo &&\n> +\tgit clone --no-reject-shallow --no-local shallow-repo clone-repo\n\nOK.  Also without \"--no-reject-shallow\" option, the command would\nsuccessfully clone from the shallow-repo, I presume?\n\nThe changes look more-or-less good to me, except for the \"warning()\"\nbit, which I do not think is a good idea.\n"},{"id":"420226","messageId":"33a250a28de411eb9ccd0024e87935e7@oschina.cn","threadId":"55104","inReplyTo":"19c9dc128da911ebacc7d4ae5278bc1233465@pobox.com","subject":"Re: Re: [PATCH v7] builtin/clone.c: add --reject-shallow option","fromName":"lilinchao@oschina.cn","fromEmail":"lilinchao@oschina.cn","sentAt":"2021-03-26T03:34:36Z","receivedAt":"2021-03-26T03:35:23Z","isPatch":true,"sender":{"key":"lilinchao@oschina.cn","avatar":null},"body":"\n\n\n--------------\nlilinchao@oschina.cn\n>\"Li Linchao via GitGitGadget\" <gitgitgadget@gmail.com> writes:\n>\n>> ...\n>> This patch offers a new option '--reject-shallow' that can reject to\n>> clone a shallow repository.\n>>\n>> Signed-off-by: lilinchao <lilinchao@oschina.cn>\n>>\n>> Reviewed-by: Derrick Stolee <dstolee@microsoft.com>\n>> Reviewed-by: Junio C Hamano<gitster@pobox.com>\n>> Reviewed-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n>> Reviewed-by: Jonathan Tan<jonathantanmy@google.com>\n>\n>The Reviewed-by trailer means something quite different from what\n>you seem to think here.  It is only given by the reviewers to the\n>patch when they carefully reviewed and agrees what is in the patch.\n>The patch authors are in no position to add it, unless they are\n>explicitly told by reviewers that \"this patch now can have my\n>Reviewed-by:\" or some equivalent.\n>\n>The (ideal) flow of events is\n>\n> 0. The author comes up with an idea and writes a patch.\n>\n> 1. The patch is sent to the list and Cc'ed to people who may be\n>    familiar with the area the patch touches.  For second and\n>    subsequent iterations, those who gave review comments to the\n>    previous iterations are also good people to Cc to.\n>\n> 2. People give comments as reponses to the patch.\n>\n>    (a) some may be happy with the iteration of the patch they\n>        reviewed, and may say \"Thanks for contributing, this is now\n>        Reviewed-by: me\".  For second and subsequent iterations,\n>        they may say \"This was improved relative to the previous\n>        iteration, and it still looks good and you have my\n>        Reviewed-by:\".\n>\n>    (b) some may give constructive criticism, alternatives,\n>        enhancements, or outright \"not a good idea, don't do this\n>        because ...\".\n>\n>    (c) some may just act as cheerleaders.\n>\n> 3. The author thinks about the review comments and also may find\n>    improvement him/herself.\n>\n>    (a) There may need an update to the patch.  If the patch has\n>        changed since the previous version in any way, ignore\n>        Reviewed-by: received in 2-(a).  When a significant help was\n>        given to update the patch, you may add \"Helped-by:\" trailer\n>        to credit the person's contribution.\n>\n>        Your own \"Signed-off-by:\" appears the last in the trailers\n>        (i.e. \"this iteration of the patch was written with help\n>        from these people, and then I am signing it off just before\n>        sending it out\").\n>\n>\tGo back to 1. and repeat as many times as it takes.\n>   \n>    (b) There may not be a need for any update to the patch.  Only\n>        add the Reviewed-by: received in 2-(a) and otherwise do not\n>        change anything.  Your own \"Signed-off-by:\" appears the last\n>        in the trailers.  Send it to the list and to the maintainer\n>        (me).\n>\n> 4. The maintainer applies the patch, unless there is no other\n>    comments received on that supposedly-the-final version sent in\n>    3-(b), but a late review comment may make us realize that it was\n>    premature, in which case we may go back to 3-(a).\n> \n\nMany thanks for so detailed instructions about work flow in git community.\nI will follow this flow tightly."},{"id":"420227","messageId":"552b26168de611eb8af90024e87935e7@oschina.cn","threadId":"55104","inReplyTo":"7a71c96c8dbd11eb8bb0d4ae5278bc1296681@pobox.com","subject":"Re: Re: [PATCH v7] builtin/clone.c: add --reject-shallow option","fromName":"lilinchao@oschina.cn","fromEmail":"lilinchao@oschina.cn","sentAt":"2021-03-26T03:49:52Z","receivedAt":"2021-03-26T03:50:59Z","isPatch":true,"sender":{"key":"lilinchao@oschina.cn","avatar":null},"body":"Thanks for your review.\n\n\n--------------\nlilinchao@oschina.cn\n>\"Li Linchao via GitGitGadget\" <gitgitgadget@gmail.com> writes:\n>\n>> @@ -1216,6 +1234,10 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n>>  if (filter_options.choice)\n>>  warning(_(\"--filter is ignored in local clones; use file:// instead.\"));\n>>  if (!access(mkpath(\"%s/shallow\", path), F_OK)) {\n>> +\tif (reject_shallow)\n>> +\tdie(_(\"source repository is shallow, reject to clone.\"));\n>> +\telse\n>> +\twarning(_(\"source repository is shallow.\"));\n>\n>Hmph, is it an improvement to warn() when the user does not mind\n>cloning a shallow repository?\n> \nUh, the idea to warn comes from previous comments I wrote 25 days ago:\n\n  \"and maybe we could warn client in fetch-pack stage, if we don't choose \n   to reject shallow cloning.\"\n\nthen no one response to that point, so I think is ok to apply it.\nNow, it seems like a bad idea. I will remove it.\n\n\n>\t$ git clone --depth=3 $URL clone-1\n>\t$ git clone file://$(pwd)/clone-1 clone-2\n>\n>would give us clone-2 that is just as functional as clone-1 is, no?\n>clone-1 may be missing objects that is needed far into the past, and\n>clone-2 would lack the same set of objects as clone-1 does, but a\n>user who is happily using clone-1 would be happy with clone-2 the\n>same way, no?\n>\n>> diff --git a/fetch-pack.c b/fetch-pack.c\n>> index fb04a76ca263..72b378449a07 100644\n>> --- a/fetch-pack.c\n>> +++ b/fetch-pack.c\n>> @@ -1129,9 +1129,13 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args,\n>>  if (args->deepen)\n>>  setup_alternate_shallow(&shallow_lock, &alternate_shallow_file,\n>>  NULL);\n>> -\telse if (si->nr_ours || si->nr_theirs)\n>> +\telse if (si->nr_ours || si->nr_theirs) {\n>> +\tif (args->remote_shallow)\n>> +\tdie(_(\"source repository is shallow, reject to clone.\"));\n>\n>Stopping early before calling get_pack() would significantly reduce\n>the overhead, which is good.\n>\n>> +\telse\n>> +\twarning(_(\"source repository is shallow.\"));\n>\n>The same question on the wisdom of warning here. \n>\n>> @@ -1498,10 +1502,14 @@ static void receive_shallow_info(struct fetch_pack_args *args,\n>>  * rejected (unless --update-shallow is set); do the same.\n>>  */\n>>  prepare_shallow_info(si, shallows);\n>> -\tif (si->nr_ours || si->nr_theirs)\n>> +\tif (si->nr_ours || si->nr_theirs) {\n>> +\tif (args->remote_shallow)\n>> +\tdie(_(\"source repository is shallow, reject to clone.\"));\n>> +\telse\n>> +\twarning(_(\"source repository is shallow.\"));\n>\n>OK, so, this is the equivalent of the above for protocol-v2?  The\n>same comments apply, then. \n\nYes, this is for protocol v2.\n>\n>> diff --git a/t/t5606-clone-options.sh b/t/t5606-clone-options.sh\n>> index 428b0aac93fa..2863b8b28d44 100755\n>> --- a/t/t5606-clone-options.sh\n>> +++ b/t/t5606-clone-options.sh\n>> @@ -5,6 +5,8 @@ GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n>>  export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n>> \n>>  . ./test-lib.sh\n>> +. \"$TEST_DIRECTORY\"/lib-httpd.sh\n>> +start_httpd\n>> \n>>  test_expect_success 'setup' '\n>> \n>> @@ -45,6 +47,51 @@ test_expect_success 'disallows --bare with --separate-git-dir' '\n>> \n>>  '\n>> \n>> +test_expect_success 'fail to clone http shallow repository' '\n>\n>s/fail to clone/reject cloning/, perhaps. \n\nOk, will do.\n>\n>> +test_expect_success 'clone shallow repository with --no-reject-shallow' '\n>> +\trm -rf shallow-repo &&\n>> +\tgit clone --depth=1 --no-local parent shallow-repo &&\n>> +\tgit clone --no-reject-shallow --no-local shallow-repo clone-repo\n>\n>OK.  Also without \"--no-reject-shallow\" option, the command would\n>successfully clone from the shallow-repo, I presume? \n\nYes, exactly.\n>\n>The changes look more-or-less good to me, except for the \"warning()\"\n>bit, which I do not think is a good idea. \n\nI will remove the unnecessary warning() part.\n\nThanks!\n\n\n"},{"id":"420442","messageId":"pull.865.v8.git.1617013145206.gitgitgadget@gmail.com","threadId":"55104","inReplyTo":"pull.865.v7.git.1616670558261.gitgitgadget@gmail.com","subject":"[PATCH v8] builtin/clone.c: add --reject-shallow option","fromName":"Li Linchao via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-03-29T10:19:04Z","receivedAt":"2021-03-29T10:20:13Z","isPatch":true,"sender":{"key":"name:Li Linchao","avatar":null},"body":"From: lilinchao <lilinchao@oschina.cn>\n\nIn some scenarios, users may want more history than the repository\noffered for cloning, which happens to be a shallow repository, can\ngive them. But because users don't know it is a shallow repository\nuntil they download it to local, users should have the option to\nrefuse to clone this kind of repository, and may want to exit the\nprocess immediately without creating any unnecessary files.\n\nAlthought there is an option '--depth=x' for users to decide how\ndeep history they can fetch, but as the unshallow cloning's depth\nis INFINITY, we can't know exactly the minimun 'x' value that can\nsatisfy the minimum integrity, so we can't pass 'x' value to --depth,\nand expect this can obtain a complete history of a repository.\n\nIn other scenarios, if we have an API that allow us to import external\nrepository, and then perform various operations on the repo.\nBut if the imported is a shallow one(which is actually possible), it\nwill affect the subsequent operations. So we can choose to refuse to\nclone, and let's just import a normal repository.\n\nThis patch offers a new option '--reject-shallow' that can reject to\nclone a shallow repository.\n\nSigned-off-by: lilinchao <lilinchao@oschina.cn>\n---\n    builtin/clone.c: add --reject-shallow option\n    \n    Changes since v1:\n    \n     * Rename --no-shallow to --reject-shallow\n     * Enable to reject a non-local clone\n     * Enable --[no-]reject-shallow from CLI override configuration.\n     * Add more testcases.\n     * Reword commit messages and relative documentation.\n    \n    Changes since v3:\n    \n     * Add support to reject clone shallow repo over https protocol\n     * Add testcase to reject clone shallow repo over https:// transport\n     * Reword commit messages and relative documentation according\n       suggestions from Junio.\n    \n    Changes since v5:\n    \n     * camelcase config variable\n     * warning client that source repo is shallow\n     * better support ssh:// and git:// protocol v1, v2\n    \n    Changes since v6:\n    \n     * use _() for warning/die statement\n    \n    Signed-off-by: lilinchao lilinchao@oschina.cn\n\nPublished-As: https://github.com/gitgitgadget/git/releases/tag/pr-865%2FCactusinhand%2Fgit-clone-options-v8\nFetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-865/Cactusinhand/git-clone-options-v8\nPull-Request: https://github.com/gitgitgadget/git/pull/865\n\nRange-diff vs v7:\n\n 1:  b957b77bfd79 ! 1:  83a724f6d771 builtin/clone.c: add --reject-shallow option\n     @@ Commit message\n      \n          Signed-off-by: lilinchao <lilinchao@oschina.cn>\n      \n     -    Reviewed-by: Derrick Stolee <dstolee@microsoft.com>\n     -    Reviewed-by: Junio C Hamano<gitster@pobox.com>\n     -    Reviewed-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n     -    Reviewed-by: Jonathan Tan<jonathantanmy@google.com>\n     -\n       ## Documentation/config/clone.txt ##\n      @@ Documentation/config/clone.txt: clone.defaultRemoteName::\n       \tThe name of the remote to create when cloning a repository.  Defaults to\n     @@ builtin/clone.c: int cmd_clone(int argc, const char **argv, const char *prefix)\n       \t\tif (!access(mkpath(\"%s/shallow\", path), F_OK)) {\n      +\t\t\tif (reject_shallow)\n      +\t\t\t\tdie(_(\"source repository is shallow, reject to clone.\"));\n     -+\t\t\telse\n     -+\t\t\t\twarning(_(\"source repository is shallow.\"));\n       \t\t\tif (option_local > 0)\n       \t\t\t\twarning(_(\"source repository is shallow, ignoring --local\"));\n       \t\t\tis_local = 0;\n     @@ fetch-pack.c: static struct ref *do_fetch_pack(struct fetch_pack_args *args,\n      +\telse if (si->nr_ours || si->nr_theirs) {\n      +\t\tif (args->remote_shallow)\n      +\t\t\tdie(_(\"source repository is shallow, reject to clone.\"));\n     -+\t\telse\n     -+\t\t\twarning(_(\"source repository is shallow.\"));\n       \t\talternate_shallow_file = setup_temporary_shallow(si->shallow);\n      -\telse\n      +\t} else\n     @@ fetch-pack.c: static void receive_shallow_info(struct fetch_pack_args *args,\n      +\t\tif (si->nr_ours || si->nr_theirs) {\n      +\t\t\tif (args->remote_shallow)\n      +\t\t\t\tdie(_(\"source repository is shallow, reject to clone.\"));\n     -+\t\t\telse\n     -+\t\t\t\twarning(_(\"source repository is shallow.\"));\n       \t\t\talternate_shallow_file =\n       \t\t\t\tsetup_temporary_shallow(si->shallow);\n      -\t\telse\n     @@ t/t5606-clone-options.sh: test_expect_success 'disallows --bare with --separate-\n       \n       '\n       \n     -+test_expect_success 'fail to clone http shallow repository' '\n     ++test_expect_success 'reject cloning http shallow repository' '\n      +\tgit clone --depth=1 --no-local parent shallow-repo &&\n      +\tgit clone --bare --no-local shallow-repo \"$HTTPD_DOCUMENT_ROOT_PATH/repo.git\" &&\n      +\ttest_must_fail git clone --reject-shallow $HTTPD_URL/smart/repo.git out 2>err &&\n     @@ t/t5606-clone-options.sh: test_expect_success 'disallows --bare with --separate-\n      +\n      +'\n      +\n     -+test_expect_success 'fail to clone shallow repository' '\n     ++test_expect_success 'reject cloning shallow repository' '\n      +\trm -rf shallow-repo &&\n      +\tgit clone --depth=1 --no-local parent shallow-repo &&\n      +\ttest_must_fail git clone --reject-shallow shallow-repo out 2>err &&\n     @@ t/t5606-clone-options.sh: test_expect_success 'disallows --bare with --separate-\n      +\n      +'\n      +\n     -+test_expect_success 'fail to clone non-local shallow repository' '\n     ++test_expect_success 'reject cloning non-local shallow repository' '\n      +\trm -rf shallow-repo &&\n      +\tgit clone --depth=1 --no-local parent shallow-repo &&\n      +\ttest_must_fail git clone --reject-shallow --no-local shallow-repo out 2>err &&\n     @@ t/t5611-clone-config.sh: test_expect_success 'clone -c remote.<remote>.fetch=<re\n       \ttest_cmp expect actual\n       '\n       \n     -+test_expect_success 'clone.rejectshallow=true should fail to clone' '\n     ++test_expect_success 'clone.rejectshallow=true should reject cloning' '\n      +\trm -rf child &&\n      +\tgit clone --depth=1 --no-local . child &&\n      +\ttest_must_fail git -c clone.rejectshallow=true clone --no-local child out 2>err &&\n\n\n Documentation/config/clone.txt |  4 +++\n Documentation/git-clone.txt    |  7 ++++-\n builtin/clone.c                | 22 ++++++++++++++++\n fetch-pack.c                   | 12 ++++++---\n fetch-pack.h                   |  1 +\n t/t5606-clone-options.sh       | 47 ++++++++++++++++++++++++++++++++++\n t/t5611-clone-config.sh        | 32 +++++++++++++++++++++++\n transport.c                    |  4 +++\n transport.h                    |  4 +++\n 9 files changed, 128 insertions(+), 5 deletions(-)\n\ndiff --git a/Documentation/config/clone.txt b/Documentation/config/clone.txt\nindex 47de36a5fedf..7bcfbd18a52a 100644\n--- a/Documentation/config/clone.txt\n+++ b/Documentation/config/clone.txt\n@@ -2,3 +2,7 @@ clone.defaultRemoteName::\n \tThe name of the remote to create when cloning a repository.  Defaults to\n \t`origin`, and can be overridden by passing the `--origin` command-line\n \toption to linkgit:git-clone[1].\n+\n+clone.rejectShallow::\n+\tReject to clone a repository if it is a shallow one, can be overridden by\n+\tpassing option `--reject-shallow` in command line. See linkgit:git-clone[1]\ndiff --git a/Documentation/git-clone.txt b/Documentation/git-clone.txt\nindex 02d9c19cec75..0adc98fa7eee 100644\n--- a/Documentation/git-clone.txt\n+++ b/Documentation/git-clone.txt\n@@ -15,7 +15,7 @@ SYNOPSIS\n \t  [--dissociate] [--separate-git-dir <git dir>]\n \t  [--depth <depth>] [--[no-]single-branch] [--no-tags]\n \t  [--recurse-submodules[=<pathspec>]] [--[no-]shallow-submodules]\n-\t  [--[no-]remote-submodules] [--jobs <n>] [--sparse]\n+\t  [--[no-]remote-submodules] [--jobs <n>] [--sparse] [--[no-]reject-shallow]\n \t  [--filter=<filter>] [--] <repository>\n \t  [<directory>]\n \n@@ -149,6 +149,11 @@ objects from the source repository into a pack in the cloned repository.\n --no-checkout::\n \tNo checkout of HEAD is performed after the clone is complete.\n \n+--[no-]reject-shallow::\n+\tFail if the source repository is a shallow repository.\n+\tThe 'clone.rejectShallow' configuration variable can be used to\n+\tgive the default.\n+\n --bare::\n \tMake a 'bare' Git repository.  That is, instead of\n \tcreating `<directory>` and placing the administrative\ndiff --git a/builtin/clone.c b/builtin/clone.c\nindex 51e844a2de0a..eeddd68a51f4 100644\n--- a/builtin/clone.c\n+++ b/builtin/clone.c\n@@ -50,6 +50,8 @@ static int option_no_checkout, option_bare, option_mirror, option_single_branch\n static int option_local = -1, option_no_hardlinks, option_shared;\n static int option_no_tags;\n static int option_shallow_submodules;\n+static int option_shallow = -1;    /* unspecified */\n+static int config_shallow = -1;    /* unspecified */\n static int deepen;\n static char *option_template, *option_depth, *option_since;\n static char *option_origin = NULL;\n@@ -90,6 +92,8 @@ static struct option builtin_clone_options[] = {\n \tOPT__VERBOSITY(&option_verbosity),\n \tOPT_BOOL(0, \"progress\", &option_progress,\n \t\t N_(\"force progress reporting\")),\n+\tOPT_BOOL(0, \"reject-shallow\", &option_shallow,\n+\t\t N_(\"don't clone shallow repository\")),\n \tOPT_BOOL('n', \"no-checkout\", &option_no_checkout,\n \t\t N_(\"don't create a checkout\")),\n \tOPT_BOOL(0, \"bare\", &option_bare, N_(\"create a bare repository\")),\n@@ -858,6 +862,9 @@ static int git_clone_config(const char *k, const char *v, void *cb)\n \t\tfree(remote_name);\n \t\tremote_name = xstrdup(v);\n \t}\n+\tif (!strcmp(k, \"clone.rejectshallow\"))\n+\t\tconfig_shallow = git_config_bool(k, v);\n+\n \treturn git_default_config(k, v, cb);\n }\n \n@@ -963,6 +970,7 @@ static int path_exists(const char *path)\n int cmd_clone(int argc, const char **argv, const char *prefix)\n {\n \tint is_bundle = 0, is_local;\n+\tint reject_shallow = 0;\n \tconst char *repo_name, *repo, *work_tree, *git_dir;\n \tchar *path, *dir, *display_repo = NULL;\n \tint dest_exists, real_dest_exists = 0;\n@@ -1156,6 +1164,16 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n \t */\n \tgit_config(git_clone_config, NULL);\n \n+\t/*\n+\t * If option_shallow is specified from CLI option,\n+\t * ignore config_shallow from git_clone_config.\n+\t */\n+\tif (config_shallow != -1)\n+\t\treject_shallow = config_shallow;\n+\n+\tif (option_shallow != -1)\n+\t\treject_shallow = option_shallow;\n+\n \t/*\n \t * apply the remote name provided by --origin only after this second\n \t * call to git_config, to ensure it overrides all config-based values.\n@@ -1216,6 +1234,8 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n \t\tif (filter_options.choice)\n \t\t\twarning(_(\"--filter is ignored in local clones; use file:// instead.\"));\n \t\tif (!access(mkpath(\"%s/shallow\", path), F_OK)) {\n+\t\t\tif (reject_shallow)\n+\t\t\t\tdie(_(\"source repository is shallow, reject to clone.\"));\n \t\t\tif (option_local > 0)\n \t\t\t\twarning(_(\"source repository is shallow, ignoring --local\"));\n \t\t\tis_local = 0;\n@@ -1227,6 +1247,8 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n \n \ttransport_set_option(transport, TRANS_OPT_KEEP, \"yes\");\n \n+\tif (reject_shallow)\n+\t\ttransport_set_option(transport, TRANS_OPT_REJECT_SHALLOW, \"1\");\n \tif (option_depth)\n \t\ttransport_set_option(transport, TRANS_OPT_DEPTH,\n \t\t\t\t     option_depth);\ndiff --git a/fetch-pack.c b/fetch-pack.c\nindex fb04a76ca263..34d0c2896e2e 100644\n--- a/fetch-pack.c\n+++ b/fetch-pack.c\n@@ -1129,9 +1129,11 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args,\n \tif (args->deepen)\n \t\tsetup_alternate_shallow(&shallow_lock, &alternate_shallow_file,\n \t\t\t\t\tNULL);\n-\telse if (si->nr_ours || si->nr_theirs)\n+\telse if (si->nr_ours || si->nr_theirs) {\n+\t\tif (args->remote_shallow)\n+\t\t\tdie(_(\"source repository is shallow, reject to clone.\"));\n \t\talternate_shallow_file = setup_temporary_shallow(si->shallow);\n-\telse\n+\t} else\n \t\talternate_shallow_file = NULL;\n \tif (get_pack(args, fd, pack_lockfiles, NULL, sought, nr_sought,\n \t\t     &gitmodules_oids))\n@@ -1498,10 +1500,12 @@ static void receive_shallow_info(struct fetch_pack_args *args,\n \t\t * rejected (unless --update-shallow is set); do the same.\n \t\t */\n \t\tprepare_shallow_info(si, shallows);\n-\t\tif (si->nr_ours || si->nr_theirs)\n+\t\tif (si->nr_ours || si->nr_theirs) {\n+\t\t\tif (args->remote_shallow)\n+\t\t\t\tdie(_(\"source repository is shallow, reject to clone.\"));\n \t\t\talternate_shallow_file =\n \t\t\t\tsetup_temporary_shallow(si->shallow);\n-\t\telse\n+\t\t} else\n \t\t\talternate_shallow_file = NULL;\n \t} else {\n \t\talternate_shallow_file = NULL;\ndiff --git a/fetch-pack.h b/fetch-pack.h\nindex 736a3dae467a..6e4f8f0d738c 100644\n--- a/fetch-pack.h\n+++ b/fetch-pack.h\n@@ -39,6 +39,7 @@ struct fetch_pack_args {\n \tunsigned self_contained_and_connected:1;\n \tunsigned cloning:1;\n \tunsigned update_shallow:1;\n+\tunsigned remote_shallow:1;\n \tunsigned deepen:1;\n \n \t/*\ndiff --git a/t/t5606-clone-options.sh b/t/t5606-clone-options.sh\nindex 428b0aac93fa..de1cd85983ed 100755\n--- a/t/t5606-clone-options.sh\n+++ b/t/t5606-clone-options.sh\n@@ -5,6 +5,8 @@ GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n \n . ./test-lib.sh\n+. \"$TEST_DIRECTORY\"/lib-httpd.sh\n+start_httpd\n \n test_expect_success 'setup' '\n \n@@ -45,6 +47,51 @@ test_expect_success 'disallows --bare with --separate-git-dir' '\n \n '\n \n+test_expect_success 'reject cloning http shallow repository' '\n+\tgit clone --depth=1 --no-local parent shallow-repo &&\n+\tgit clone --bare --no-local shallow-repo \"$HTTPD_DOCUMENT_ROOT_PATH/repo.git\" &&\n+\ttest_must_fail git clone --reject-shallow $HTTPD_URL/smart/repo.git out 2>err &&\n+\ttest_i18ngrep -e \"source repository is shallow, reject to clone.\" err\n+\n+'\n+\n+test_expect_success 'reject cloning shallow repository' '\n+\trm -rf shallow-repo &&\n+\tgit clone --depth=1 --no-local parent shallow-repo &&\n+\ttest_must_fail git clone --reject-shallow shallow-repo out 2>err &&\n+\ttest_i18ngrep -e \"source repository is shallow, reject to clone.\" err\n+\n+'\n+\n+test_expect_success 'reject cloning non-local shallow repository' '\n+\trm -rf shallow-repo &&\n+\tgit clone --depth=1 --no-local parent shallow-repo &&\n+\ttest_must_fail git clone --reject-shallow --no-local shallow-repo out 2>err &&\n+\ttest_i18ngrep -e \"source repository is shallow, reject to clone.\" err\n+\n+'\n+\n+test_expect_success 'clone shallow repository with --no-reject-shallow' '\n+\trm -rf shallow-repo &&\n+\tgit clone --depth=1 --no-local parent shallow-repo &&\n+\tgit clone --no-reject-shallow --no-local shallow-repo clone-repo\n+\n+'\n+\n+test_expect_success 'clone normal repository with --reject-shallow' '\n+\trm -rf clone-repo &&\n+\tgit clone --no-local parent normal-repo &&\n+\tgit clone --reject-shallow --no-local normal-repo clone-repo\n+\n+'\n+\n+test_expect_success 'unspecified any configs or options' '\n+\trm -rf shallow-repo clone-repo &&\n+\tgit clone --depth=1 --no-local parent shallow-repo &&\n+\tgit clone shallow-repo clone-repo\n+\n+'\n+\n test_expect_success 'uses \"origin\" for default remote name' '\n \n \tgit clone parent clone-default-origin &&\ndiff --git a/t/t5611-clone-config.sh b/t/t5611-clone-config.sh\nindex 9f555b87ecdf..adf873f60300 100755\n--- a/t/t5611-clone-config.sh\n+++ b/t/t5611-clone-config.sh\n@@ -95,6 +95,38 @@ test_expect_success 'clone -c remote.<remote>.fetch=<refspec> --origin=<name>' '\n \ttest_cmp expect actual\n '\n \n+test_expect_success 'clone.rejectshallow=true should reject cloning' '\n+\trm -rf child &&\n+\tgit clone --depth=1 --no-local . child &&\n+\ttest_must_fail git -c clone.rejectshallow=true clone --no-local child out 2>err &&\n+\ttest_i18ngrep -e \"source repository is shallow, reject to clone.\" err\n+'\n+\n+test_expect_success 'clone.rejectshallow=false should succeed' '\n+\trm -rf child out &&\n+\tgit clone --depth=1 --no-local . child &&\n+\tgit -c clone.rejectshallow=false clone --no-local child out\n+'\n+\n+test_expect_success 'clone.rejectshallow=true should succeed with normal repo' '\n+\trm -rf child out &&\n+\tgit clone --no-local . child &&\n+\tgit -c clone.rejectshallow=true clone --no-local child out\n+'\n+\n+test_expect_success 'option --reject-shallow override clone.rejectshallow' '\n+\trm -rf child out &&\n+\tgit clone --depth=1 --no-local . child &&\n+\ttest_must_fail git -c clone.rejectshallow=false clone --reject-shallow --no-local child out 2>err &&\n+\ttest_i18ngrep -e \"source repository is shallow, reject to clone.\" err\n+'\n+\n+test_expect_success 'option --no-reject-shallow override clone.rejectshallow' '\n+\trm -rf child out &&\n+\tgit clone --depth=1 --no-local . child &&\n+\tgit -c clone.rejectshallow=true clone --no-reject-shallow --no-local child out\n+'\n+\n test_expect_success MINGW 'clone -c core.hideDotFiles' '\n \ttest_commit attributes .gitattributes \"\" &&\n \trm -rf child &&\ndiff --git a/transport.c b/transport.c\nindex 1c4ab676d1b1..a6b9f404d86a 100644\n--- a/transport.c\n+++ b/transport.c\n@@ -236,6 +236,9 @@ static int set_git_option(struct git_transport_options *opts,\n \t\tlist_objects_filter_die_if_populated(&opts->filter_options);\n \t\tparse_list_objects_filter(&opts->filter_options, value);\n \t\treturn 0;\n+\t} else if (!strcmp(name, TRANS_OPT_REJECT_SHALLOW)) {\n+\t\topts->reject_shallow = !!value;\n+\t\treturn 0;\n \t}\n \treturn 1;\n }\n@@ -370,6 +373,7 @@ static int fetch_refs_via_pack(struct transport *transport,\n \targs.stateless_rpc = transport->stateless_rpc;\n \targs.server_options = transport->server_options;\n \targs.negotiation_tips = data->options.negotiation_tips;\n+\targs.remote_shallow = transport->smart_options->reject_shallow;\n \n \tif (!data->got_remote_heads) {\n \t\tint i;\ndiff --git a/transport.h b/transport.h\nindex 24e15799e714..4d5db0a7f22b 100644\n--- a/transport.h\n+++ b/transport.h\n@@ -14,6 +14,7 @@ struct git_transport_options {\n \tunsigned check_self_contained_and_connected : 1;\n \tunsigned self_contained_and_connected : 1;\n \tunsigned update_shallow : 1;\n+\tunsigned reject_shallow : 1;\n \tunsigned deepen_relative : 1;\n \n \t/* see documentation of corresponding flag in fetch-pack.h */\n@@ -194,6 +195,9 @@ void transport_check_allowed(const char *type);\n /* Aggressively fetch annotated tags if possible */\n #define TRANS_OPT_FOLLOWTAGS \"followtags\"\n \n+/* Reject shallow repo transport */\n+#define TRANS_OPT_REJECT_SHALLOW \"rejectshallow\"\n+\n /* Accept refs that may update .git/shallow without --depth */\n #define TRANS_OPT_UPDATE_SHALLOW \"updateshallow\"\n \n\nbase-commit: 84d06cdc06389ae7c462434cb7b1db0980f63860\n-- \ngitgitgadget\n"},{"id":"420522","messageId":"xmqqa6qliqwb.fsf@gitster.g","threadId":"55104","inReplyTo":"pull.865.v8.git.1617013145206.gitgitgadget@gmail.com","subject":"Re: [PATCH v8] builtin/clone.c: add --reject-shallow option","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-03-29T21:36:20Z","receivedAt":"2021-03-29T21:37:20Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"Li Linchao via GitGitGadget\" <gitgitgadget@gmail.com> writes:\n\n>     Changes since v6:\n>     \n>      * use _() for warning/die statement\n\nThis round tweaks a few test titles and drops warning() that was\ngiven in v7 when the feature is not in use.\n\nComments from other reviewers who are more familiar with the\ntransport layer than I am?  As far as I see, this version is\ndone cleanly enough to call it ready for 'next'.\n\nThanks.\n"},{"id":"420567","messageId":"nycvar.QRO.7.76.6.2103301110040.52@tvgsbejvaqbjf.bet","threadId":"55104","inReplyTo":"pull.865.v8.git.1617013145206.gitgitgadget@gmail.com","subject":"Re: [PATCH v8] builtin/clone.c: add --reject-shallow option","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2021-03-30T09:54:30Z","receivedAt":"2021-03-30T09:55:43Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Mon, 29 Mar 2021, Li Linchao via GitGitGadget wrote:\n\n> From: lilinchao <lilinchao@oschina.cn>\n\nI see \"Li Linchao\" in the email, but \"lilinchao\" in the author\ninformation. Maybe you want to align them? Or maybe even use Unicode to\nwrite your non-Latinized name?\n\n> In some scenarios, users may want more history than the repository\n> offered for cloning, which happens to be a shallow repository, can\n> give them. But because users don't know it is a shallow repository\n> until they download it to local, users should have the option to\n> refuse to clone this kind of repository, and may want to exit the\n> process immediately without creating any unnecessary files.\n>\n> Althought there is an option '--depth=x' for users to decide how\n> deep history they can fetch, but as the unshallow cloning's depth\n> is INFINITY, we can't know exactly the minimun 'x' value that can\n> satisfy the minimum integrity, so we can't pass 'x' value to --depth,\n> and expect this can obtain a complete history of a repository.\n>\n> In other scenarios, if we have an API that allow us to import external\n> repository, and then perform various operations on the repo.\n> But if the imported is a shallow one(which is actually possible), it\n> will affect the subsequent operations. So we can choose to refuse to\n> clone, and let's just import a normal repository.\n>\n> This patch offers a new option '--reject-shallow' that can reject to\n> clone a shallow repository.\n\nGood.\n\nI like most of the patch, and will only point out a couple of things that\nI think can be improved even further.\n\n> diff --git a/Documentation/git-clone.txt b/Documentation/git-clone.txt\n> index 02d9c19cec75..0adc98fa7eee 100644\n> --- a/Documentation/git-clone.txt\n> +++ b/Documentation/git-clone.txt\n> @@ -149,6 +149,11 @@ objects from the source repository into a pack in the cloned repository.\n>  --no-checkout::\n>  \tNo checkout of HEAD is performed after the clone is complete.\n>\n> +--[no-]reject-shallow::\n> +\tFail if the source repository is a shallow repository.\n> +\tThe 'clone.rejectShallow' configuration variable can be used to\n> +\tgive the default.\n\nI am not a native speaker, either, but I believe that it would \"roll off\nthe tongue\" a bit better to say \"to specify the default\".\n\n> +\n>  --bare::\n>  \tMake a 'bare' Git repository.  That is, instead of\n>  \tcreating `<directory>` and placing the administrative\n> diff --git a/builtin/clone.c b/builtin/clone.c\n> index 51e844a2de0a..eeddd68a51f4 100644\n> --- a/builtin/clone.c\n> +++ b/builtin/clone.c\n> @@ -50,6 +50,8 @@ static int option_no_checkout, option_bare, option_mirror, option_single_branch\n>  static int option_local = -1, option_no_hardlinks, option_shared;\n>  static int option_no_tags;\n>  static int option_shallow_submodules;\n> +static int option_shallow = -1;    /* unspecified */\n> +static int config_shallow = -1;    /* unspecified */\n\nI would much prefer those variable names to include an indicator that this\nis about _rejecting_ shallow clones. I.e. `option_reject_shallow`.\n\nAlso, I think that we can do with just a single `option_reject_shallow`\n(we do not even need that `reject_shallow` variable in `cmd_clone()`):\n\n- in `git_clone_config()`, only override it if it is still unspecified:\n\n\tif (!strcmp(k, \"clone.rejectshallow\") && option_reject_shallow < 0)\n\t\toption_reject_shallow = git_config_bool(k,v);\n\n- in `cmd_clone()`, test for a _positive_ value:\n\n\tif (option_reject_shallow > 0)\n\t\tdie(_(\"source repository is shallow, reject to clone.\"));\n\n  and\n\n\tif (option_reject_shallow > 0)\n \t\ttransport_set_option(transport, TRANS_OPT_REJECT_SHALLOW, \"1\");\n\nOne thing to note (in the commit message, would be my preference) is that\n`cmd_clone()` is _particular_ in that it runs `git_config()` _twice_. Once\nbefore the command-line options are parsed, and once after the new Git\nrepository has been initialized. Note that my suggestion still works with\nthat: if either the original config, or the new config set\n`clone.rejectShallow`, it is picked up correctly, with the latter\noverriding the former if both configs want to set it.\n\n> diff --git a/fetch-pack.c b/fetch-pack.c\n> index fb04a76ca263..34d0c2896e2e 100644\n> --- a/fetch-pack.c\n> +++ b/fetch-pack.c\n> @@ -1129,9 +1129,11 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args,\n>  \tif (args->deepen)\n>  \t\tsetup_alternate_shallow(&shallow_lock, &alternate_shallow_file,\n>  \t\t\t\t\tNULL);\n> -\telse if (si->nr_ours || si->nr_theirs)\n> +\telse if (si->nr_ours || si->nr_theirs) {\n> +\t\tif (args->remote_shallow)\n\nEven as a non-casual reader, this name `remote_shallow` leads me to assume\nincorrect things. This option is not about wanting a remote shallow\nrepository, it is about rejecting a remote shallow repository.\n\nPlease name this attribute `reject_shallow` instead of `remote_shallow`.\nThat will prevent future puzzlement.\n\n> +\t\t\tdie(_(\"source repository is shallow, reject to clone.\"));\n>  \t\talternate_shallow_file = setup_temporary_shallow(si->shallow);\n> -\telse\n> +\t} else\n>  \t\talternate_shallow_file = NULL;\n>  \tif (get_pack(args, fd, pack_lockfiles, NULL, sought, nr_sought,\n>  \t\t     &gitmodules_oids))\n> [...]\n> diff --git a/t/t5606-clone-options.sh b/t/t5606-clone-options.sh\n> index 428b0aac93fa..de1cd85983ed 100755\n> --- a/t/t5606-clone-options.sh\n> +++ b/t/t5606-clone-options.sh\n> @@ -5,6 +5,8 @@ GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n>  export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n>\n>  . ./test-lib.sh\n> +. \"$TEST_DIRECTORY\"/lib-httpd.sh\n> +start_httpd\n\nThat's not good. What happens if there is no `httpd`? Then the rest of the\ntests are either skipped, or if `GIT_TEST_HTTPD` is set to `true`, we\nfail. The failure is intentional, but the skipping is not. There are many\ntests in t5606 that do not require a running HTTP daemon, and we should\nnot skip them (for example, in our CI runs, there are quite a few jobs\nthat run without any working `httpd`).\n\nA much better alternative, I think, would be to move those new test cases\nthat require `httpd` to be running to t5601 (which _already_ calls\n`start_httpd`, near the end, so as to not skip any tests that do not\nrequire `httpd`).\n\n>\n>  test_expect_success 'setup' '\n>\n> @@ -45,6 +47,51 @@ test_expect_success 'disallows --bare with --separate-git-dir' '\n>\n>  '\n>\n> +test_expect_success 'reject cloning http shallow repository' '\n> +\tgit clone --depth=1 --no-local parent shallow-repo &&\n> +\tgit clone --bare --no-local shallow-repo \"$HTTPD_DOCUMENT_ROOT_PATH/repo.git\" &&\n> +\ttest_must_fail git clone --reject-shallow $HTTPD_URL/smart/repo.git out 2>err &&\n> +\ttest_i18ngrep -e \"source repository is shallow, reject to clone.\" err\n> +\n> +'\n> +\n> +test_expect_success 'reject cloning shallow repository' '\n> +\trm -rf shallow-repo &&\n\nShould this line not come immediately after the bare clone into\n<DOCUMENT_ROOT>/repo.git? Or even better, as a `test_when_finished`\ncommand.\n\nAnd maybe you want to extract this preparatory step into its own test\ncase:\n\ntest_expect_success 'set up shallow http repository' '\n\ttest_when_finished \"rm -rf shallow-repo\" &&\n\tgit clone --depth=1 --no-local parent shallow-repo &&\n\tgit clone --bare --no-local shallow-repo \"$HTTPD_DOCUMENT_ROOT_PATH/repo.git\"\n'\n\n> +\tgit clone --depth=1 --no-local parent shallow-repo &&\n> +\ttest_must_fail git clone --reject-shallow shallow-repo out 2>err &&\n> +\ttest_i18ngrep -e \"source repository is shallow, reject to clone.\" err\n> +\n\nPlease remove the extra empty line. (This goes for at least a couple test\ncases added by this patch.)\n\n> +'\n\nThis test case does not require `start_httpd`, and should therefore come\nbefore the test cases that do require it (actually, it should come before\nthe `start_httpd` call, even).\n\n> +\n> +test_expect_success 'reject cloning non-local shallow repository' '\n> +\trm -rf shallow-repo &&\n> +\tgit clone --depth=1 --no-local parent shallow-repo &&\n> +\ttest_must_fail git clone --reject-shallow --no-local shallow-repo out 2>err &&\n> +\ttest_i18ngrep -e \"source repository is shallow, reject to clone.\" err\n> +\n> +'\n\nHmm. Reading through three test cases that all create `shallow-repo` in\nthe same way, I wonder whether we should not simply set it up once, and\nthen not even bother removing it. I think that would simplify not only the\npatch, it would also simplify debugging later on.\n\n> +\n> +test_expect_success 'clone shallow repository with --no-reject-shallow' '\n> +\trm -rf shallow-repo &&\n> +\tgit clone --depth=1 --no-local parent shallow-repo &&\n> +\tgit clone --no-reject-shallow --no-local shallow-repo clone-repo\n> +\n> +'\n> +\n> +test_expect_success 'clone normal repository with --reject-shallow' '\n> +\trm -rf clone-repo &&\n> +\tgit clone --no-local parent normal-repo &&\n> +\tgit clone --reject-shallow --no-local normal-repo clone-repo\n> +\n> +'\n> +\n> +test_expect_success 'unspecified any configs or options' '\n> +\trm -rf shallow-repo clone-repo &&\n> +\tgit clone --depth=1 --no-local parent shallow-repo &&\n> +\tgit clone shallow-repo clone-repo\n> +\n> +'\n> +\n\nHaving read through these test cases, I think they can be simplified by\n\n- first setting up `shallow-repo`\n\n- then testing in the same test case whether `--reject-shallow` fails and\n  `--no-reject-shallow` succeeds, without `--no-local`\n\n- then testing the same _with_ `--no-local`\n\nThese can go to t5606, no problem.\n\nThen, in t5601, after the `start_httpd` call, add a single test case that\n\n- sets up the shallow clone _directly_, i.e.\n\n\tgit clone --bare --no-local --depth=1 parent \\\n\t\t\"$HTTPD_DOCUMENT_ROOT_PATH/repo.git\"\n\n- verifies that `--reject-shallow` fails as expected, and\n\n- verifies that `--no-reject-shallow` works as expected.\n\n>  test_expect_success 'uses \"origin\" for default remote name' '\n>\n>  \tgit clone parent clone-default-origin &&\n> diff --git a/t/t5611-clone-config.sh b/t/t5611-clone-config.sh\n> index 9f555b87ecdf..adf873f60300 100755\n> --- a/t/t5611-clone-config.sh\n> +++ b/t/t5611-clone-config.sh\n> @@ -95,6 +95,38 @@ test_expect_success 'clone -c remote.<remote>.fetch=<refspec> --origin=<name>' '\n>  \ttest_cmp expect actual\n>  '\n>\n> +test_expect_success 'clone.rejectshallow=true should reject cloning' '\n> +\trm -rf child &&\n> +\tgit clone --depth=1 --no-local . child &&\n\nIn the following, this shallow repository is needed a couple of times.\nBetter set it up once, in a dedicated `set up shallow repository` test\ncase.\n\nAnd `shallow-repo` would probably make for a much better name than\n`child`.\n\n> +\ttest_must_fail git -c clone.rejectshallow=true clone --no-local child out 2>err &&\n> +\ttest_i18ngrep -e \"source repository is shallow, reject to clone.\" err\n> +'\n> +\n> +test_expect_success 'clone.rejectshallow=false should succeed' '\n> +\trm -rf child out &&\n> +\tgit clone --depth=1 --no-local . child &&\n> +\tgit -c clone.rejectshallow=false clone --no-local child out\n> +'\n\nThese two can be combined (and should, if you ask me, to simplify things).\n\n> +\n> +test_expect_success 'clone.rejectshallow=true should succeed with normal repo' '\n> +\trm -rf child out &&\n> +\tgit clone --no-local . child &&\n> +\tgit -c clone.rejectshallow=true clone --no-local child out\n> +'\n> +\n> +test_expect_success 'option --reject-shallow override clone.rejectshallow' '\n> +\trm -rf child out &&\n> +\tgit clone --depth=1 --no-local . child &&\n> +\ttest_must_fail git -c clone.rejectshallow=false clone --reject-shallow --no-local child out 2>err &&\n> +\ttest_i18ngrep -e \"source repository is shallow, reject to clone.\" err\n> +'\n> +\n> +test_expect_success 'option --no-reject-shallow override clone.rejectshallow' '\n> +\trm -rf child out &&\n> +\tgit clone --depth=1 --no-local . child &&\n> +\tgit -c clone.rejectshallow=true clone --no-reject-shallow --no-local child out\n> +'\n> +\n\nPersonally, I think this is overkill. What I would do is to have a single\ntest case that verifies that\n\n- `clone.rejectShallow=true` fails as expected,\n\n- `clone.rejectShallow=false [...] --reject-shallow` fails as expected, and\n\n- `clone.rejectShallow=false` succeeds.\n\nIf we do this, we do not even need a preparatory test case setting up the\nshallow repository.\n\n>  test_expect_success MINGW 'clone -c core.hideDotFiles' '\n>  \ttest_commit attributes .gitattributes \"\" &&\n>  \trm -rf child &&\n> diff --git a/transport.c b/transport.c\n> index 1c4ab676d1b1..a6b9f404d86a 100644\n> --- a/transport.c\n> +++ b/transport.c\n> @@ -236,6 +236,9 @@ static int set_git_option(struct git_transport_options *opts,\n>  \t\tlist_objects_filter_die_if_populated(&opts->filter_options);\n>  \t\tparse_list_objects_filter(&opts->filter_options, value);\n>  \t\treturn 0;\n> +\t} else if (!strcmp(name, TRANS_OPT_REJECT_SHALLOW)) {\n> +\t\topts->reject_shallow = !!value;\n\nI see that this is the established pattern (I am so grateful that I have\nhttps://github.com/gitgitgadget/git/pull/865/files to look at the context,\nsomething with which a pure mail-only patch contribution would not bless\nme!), that those Boolean options are `NULL` vs non-`NULL`. So while you\npass `\"1\"` as the `value` parameter to `set_git_option()`, the parameter\n`\"0\"` would _enable that option just the same_, you would have to pass\n`NULL` to turn it off. I find that highly unintuitive, but that's not the\nfault of your patch. The pattern is established, and you did the right\nthing by following it.\n\n> +\t\treturn 0;\n>  \t}\n>  \treturn 1;\n>  }\n\nAs I said, the rest of the patch looks good to me. With the few\nsuggestions I offered, I would be totally fine with this patch entering\n`next`.\n\nThank you,\nDscho\n"},{"id":"420634","messageId":"xmqqk0poedr4.fsf@gitster.g","threadId":"55104","inReplyTo":"nycvar.QRO.7.76.6.2103301110040.52@tvgsbejvaqbjf.bet","subject":"Re: [PATCH v8] builtin/clone.c: add --reject-shallow option","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-03-30T17:46:07Z","receivedAt":"2021-03-30T17:47:10Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n\n>> +static int option_shallow = -1;    /* unspecified */\n>> +static int config_shallow = -1;    /* unspecified */\n>\n> I would much prefer those variable names to include an indicator that this\n> is about _rejecting_ shallow clones. I.e. `option_reject_shallow`.\n\nGood.\n\n> ... repository has been initialized. Note that my suggestion still works with\n> that: if either the original config, or the new config set\n> `clone.rejectShallow`, it is picked up correctly, with the latter\n> overriding the former if both configs want to set it.\n\nAll four combinations (\"set it to true\" vs \"set it to false\" makes\ntwo, and before and after doubles that to four)?\n\n>> diff --git a/fetch-pack.c b/fetch-pack.c\n>> index fb04a76ca263..34d0c2896e2e 100644\n>> --- a/fetch-pack.c\n>> +++ b/fetch-pack.c\n>> @@ -1129,9 +1129,11 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args,\n>>  \tif (args->deepen)\n>>  \t\tsetup_alternate_shallow(&shallow_lock, &alternate_shallow_file,\n>>  \t\t\t\t\tNULL);\n>> -\telse if (si->nr_ours || si->nr_theirs)\n>> +\telse if (si->nr_ours || si->nr_theirs) {\n>> +\t\tif (args->remote_shallow)\n>\n> Even as a non-casual reader, this name `remote_shallow` leads me to assume\n> incorrect things. This option is not about wanting a remote shallow\n> repository, it is about rejecting a remote shallow repository.\n\nYeah, I've seen this code too long that my eyes were contaminated\n;-)  Thanks for an extra set of eyeballs to spot this.  What this\noption means is to \"reject-shallow-remote\", and I agree 'reject'\nis the most important part (args->reject_shallow_remote is not\noverly long, either).\n\n>> diff --git a/t/t5606-clone-options.sh b/t/t5606-clone-options.sh\n>> index 428b0aac93fa..de1cd85983ed 100755\n>> --- a/t/t5606-clone-options.sh\n>> +++ b/t/t5606-clone-options.sh\n>> @@ -5,6 +5,8 @@ GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n>>  export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n>>\n>>  . ./test-lib.sh\n>> +. \"$TEST_DIRECTORY\"/lib-httpd.sh\n>> +start_httpd\n>\n> That's not good. What happens if there is no `httpd`? Then the rest of the\n> tests are either skipped, or if `GIT_TEST_HTTPD` is set to `true`, we\n> fail. The failure is intentional, but the skipping is not. There are many\n> tests in t5606 that do not require a running HTTP daemon, and we should\n> not skip them (for example, in our CI runs, there are quite a few jobs\n> that run without any working `httpd`).\n>\n> A much better alternative, I think, would be to move those new test cases\n> that require `httpd` to be running to t5601 (which _already_ calls\n> `start_httpd`, near the end, so as to not skip any tests that do not\n> require `httpd`).\n\nOr move the tests that require httpd, and use of httpd library, to\nthe end of this script.\n\n> Having read through these test cases, I think they can be simplified by\n>\n> - first setting up `shallow-repo`\n>\n> - then testing in the same test case whether `--reject-shallow` fails and\n>   `--no-reject-shallow` succeeds, without `--no-local`\n>\n> - then testing the same _with_ `--no-local`\n>\n> These can go to t5606, no problem.\n>\n> Then, in t5601, after the `start_httpd` call, add a single test case that\n>\n> - sets up the shallow clone _directly_, i.e.\n>\n> \tgit clone --bare --no-local --depth=1 parent \\\n> \t\t\"$HTTPD_DOCUMENT_ROOT_PATH/repo.git\"\n>\n> - verifies that `--reject-shallow` fails as expected, and\n>\n> - verifies that `--no-reject-shallow` works as expected.\n>\n>>  test_expect_success 'uses \"origin\" for default remote name' '\n\nThat would work.\n\n> ...\n> As I said, the rest of the patch looks good to me. With the few\n> suggestions I offered, I would be totally fine with this patch entering\n> `next`.\n\nThanks for a review.  Looking good.\n"},{"id":"420699","messageId":"ae318eca921011eb92330026b95c99cc@oschina.cn","threadId":"55104","inReplyTo":"f8b2582c913d11ebaddbd4ae5278bc1214940@gmx.de","subject":"Re: Re: [PATCH v8] builtin/clone.c: add --reject-shallow option","fromName":"lilinchao@oschina.cn","fromEmail":"lilinchao@oschina.cn","sentAt":"2021-03-31T11:03:07Z","receivedAt":"2021-03-31T11:03:50Z","isPatch":true,"sender":{"key":"lilinchao@oschina.cn","avatar":null},"body":"Thanks for your suggestions!\n\nI've combined your suggestions in this comment with Junio's which based on yours.\n\n>Hi,\n>\n>On Mon, 29 Mar 2021, Li Linchao via GitGitGadget wrote:\n>\n>> From: lilinchao <lilinchao@oschina.cn>\n>\n>I see \"Li Linchao\" in the email, but \"lilinchao\" in the author\n>information. Maybe you want to align them? Or maybe even use Unicode to\n>write your non-Latinized name?\n> \nThe \"Li Lilinchao\" comes from my Github profile name. Actually, it contains my \nfirst name and last name respectively.\nI will changed it to \"Li Linchao\" in the author info to keep these info consistent.\n\n>> In some scenarios, users may want more history than the repository\n>> offered for cloning, which happens to be a shallow repository, can\n>> give them. But because users don't know it is a shallow repository\n>> until they download it to local, users should have the option to\n>> refuse to clone this kind of repository, and may want to exit the\n>> process immediately without creating any unnecessary files.\n>>\n>> Althought there is an option '--depth=x' for users to decide how\n>> deep history they can fetch, but as the unshallow cloning's depth\n>> is INFINITY, we can't know exactly the minimun 'x' value that can\n>> satisfy the minimum integrity, so we can't pass 'x' value to --depth,\n>> and expect this can obtain a complete history of a repository.\n>>\n>> In other scenarios, if we have an API that allow us to import external\n>> repository, and then perform various operations on the repo.\n>> But if the imported is a shallow one(which is actually possible), it\n>> will affect the subsequent operations. So we can choose to refuse to\n>> clone, and let's just import a normal repository.\n>>\n>> This patch offers a new option '--reject-shallow' that can reject to\n>> clone a shallow repository.\n>\n>Good.\n>\n>I like most of the patch, and will only point out a couple of things that\n>I think can be improved even further.\n>\n>> diff --git a/Documentation/git-clone.txt b/Documentation/git-clone.txt\n>> index 02d9c19cec75..0adc98fa7eee 100644\n>> --- a/Documentation/git-clone.txt\n>> +++ b/Documentation/git-clone.txt\n>> @@ -149,6 +149,11 @@ objects from the source repository into a pack in the cloned repository.\n>>  --no-checkout::\n>>  No checkout of HEAD is performed after the clone is complete.\n>>\n>> +--[no-]reject-shallow::\n>> +\tFail if the source repository is a shallow repository.\n>> +\tThe 'clone.rejectShallow' configuration variable can be used to\n>> +\tgive the default.\n>\n>I am not a native speaker, either, but I believe that it would \"roll off\n>the tongue\" a bit better to say \"to specify the default\".\n>\n>> +\n>>  --bare::\n>>  Make a 'bare' Git repository.  That is, instead of\n>>  creating `<directory>` and placing the administrative\n>> diff --git a/builtin/clone.c b/builtin/clone.c\n>> index 51e844a2de0a..eeddd68a51f4 100644\n>> --- a/builtin/clone.c\n>> +++ b/builtin/clone.c\n>> @@ -50,6 +50,8 @@ static int option_no_checkout, option_bare, option_mirror, option_single_branch\n>>  static int option_local = -1, option_no_hardlinks, option_shared;\n>>  static int option_no_tags;\n>>  static int option_shallow_submodules;\n>> +static int option_shallow = -1;    /* unspecified */\n>> +static int config_shallow = -1;    /* unspecified */\n>\n>I would much prefer those variable names to include an indicator that this\n>is about _rejecting_ shallow clones. I.e. `option_reject_shallow`.\n>\n>Also, I think that we can do with just a single `option_reject_shallow`\n>(we do not even need that `reject_shallow` variable in `cmd_clone()`):\n>\n>- in `git_clone_config()`, only override it if it is still unspecified:\n>\n>\tif (!strcmp(k, \"clone.rejectshallow\") && option_reject_shallow < 0)\n>\toption_reject_shallow = git_config_bool(k,v);\n>\n>- in `cmd_clone()`, test for a _positive_ value:\n>\n>\tif (option_reject_shallow > 0)\n>\tdie(_(\"source repository is shallow, reject to clone.\"));\n>\n>  and\n>\n>\tif (option_reject_shallow > 0)\n> transport_set_option(transport, TRANS_OPT_REJECT_SHALLOW, \"1\");\n>\n>One thing to note (in the commit message, would be my preference) is that\n>`cmd_clone()` is _particular_ in that it runs `git_config()` _twice_. Once\n>before the command-line options are parsed, and once after the new Git\n>repository has been initialized. Note that my suggestion still works with\n>that: if either the original config, or the new config set\n>`clone.rejectShallow`, it is picked up correctly, with the latter\n>overriding the former if both configs want to set it.\n>\n>> diff --git a/fetch-pack.c b/fetch-pack.c\n>> index fb04a76ca263..34d0c2896e2e 100644\n>> --- a/fetch-pack.c\n>> +++ b/fetch-pack.c\n>> @@ -1129,9 +1129,11 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args,\n>>  if (args->deepen)\n>>  setup_alternate_shallow(&shallow_lock, &alternate_shallow_file,\n>>  NULL);\n>> -\telse if (si->nr_ours || si->nr_theirs)\n>> +\telse if (si->nr_ours || si->nr_theirs) {\n>> +\tif (args->remote_shallow)\n>\n>Even as a non-casual reader, this name `remote_shallow` leads me to assume\n>incorrect things. This option is not about wanting a remote shallow\n>repository, it is about rejecting a remote shallow repository.\n>\n>Please name this attribute `reject_shallow` instead of `remote_shallow`.\n>That will prevent future puzzlement.\n>\n>> +\tdie(_(\"source repository is shallow, reject to clone.\"));\n>>  alternate_shallow_file = setup_temporary_shallow(si->shallow);\n>> -\telse\n>> +\t} else\n>>  alternate_shallow_file = NULL;\n>>  if (get_pack(args, fd, pack_lockfiles, NULL, sought, nr_sought,\n>>       &gitmodules_oids))\n>> [...]\n>> diff --git a/t/t5606-clone-options.sh b/t/t5606-clone-options.sh\n>> index 428b0aac93fa..de1cd85983ed 100755\n>> --- a/t/t5606-clone-options.sh\n>> +++ b/t/t5606-clone-options.sh\n>> @@ -5,6 +5,8 @@ GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n>>  export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n>>\n>>  . ./test-lib.sh\n>> +. \"$TEST_DIRECTORY\"/lib-httpd.sh\n>> +start_httpd\n>\n>That's not good. What happens if there is no `httpd`? Then the rest of the\n>tests are either skipped, or if `GIT_TEST_HTTPD` is set to `true`, we\n>fail. The failure is intentional, but the skipping is not. There are many\n>tests in t5606 that do not require a running HTTP daemon, and we should\n>not skip them (for example, in our CI runs, there are quite a few jobs\n>that run without any working `httpd`).\n>\n>A much better alternative, I think, would be to move those new test cases\n>that require `httpd` to be running to t5601 (which _already_ calls\n>`start_httpd`, near the end, so as to not skip any tests that do not\n>require `httpd`).\n>\n>>\n>>  test_expect_success 'setup' '\n>>\n>> @@ -45,6 +47,51 @@ test_expect_success 'disallows --bare with --separate-git-dir' '\n>>\n>>  '\n>>\n>> +test_expect_success 'reject cloning http shallow repository' '\n>> +\tgit clone --depth=1 --no-local parent shallow-repo &&\n>> +\tgit clone --bare --no-local shallow-repo \"$HTTPD_DOCUMENT_ROOT_PATH/repo.git\" &&\n>> +\ttest_must_fail git clone --reject-shallow $HTTPD_URL/smart/repo.git out 2>err &&\n>> +\ttest_i18ngrep -e \"source repository is shallow, reject to clone.\" err\n>> +\n>> +'\n>> +\n>> +test_expect_success 'reject cloning shallow repository' '\n>> +\trm -rf shallow-repo &&\n>\n>Should this line not come immediately after the bare clone into\n><DOCUMENT_ROOT>/repo.git? Or even better, as a `test_when_finished`\n>command.\n>\n>And maybe you want to extract this preparatory step into its own test\n>case:\n>\n>test_expect_success 'set up shallow http repository' '\n>\ttest_when_finished \"rm -rf shallow-repo\" &&\n>\tgit clone --depth=1 --no-local parent shallow-repo &&\n>\tgit clone --bare --no-local shallow-repo \"$HTTPD_DOCUMENT_ROOT_PATH/repo.git\"\n>'\n>\n>> +\tgit clone --depth=1 --no-local parent shallow-repo &&\n>> +\ttest_must_fail git clone --reject-shallow shallow-repo out 2>err &&\n>> +\ttest_i18ngrep -e \"source repository is shallow, reject to clone.\" err\n>> +\n>\n>Please remove the extra empty line. (This goes for at least a couple test\n>cases added by this patch.)\n>\n>> +'\n>\n>This test case does not require `start_httpd`, and should therefore come\n>before the test cases that do require it (actually, it should come before\n>the `start_httpd` call, even).\n>\n>> +\n>> +test_expect_success 'reject cloning non-local shallow repository' '\n>> +\trm -rf shallow-repo &&\n>> +\tgit clone --depth=1 --no-local parent shallow-repo &&\n>> +\ttest_must_fail git clone --reject-shallow --no-local shallow-repo out 2>err &&\n>> +\ttest_i18ngrep -e \"source repository is shallow, reject to clone.\" err\n>> +\n>> +'\n>\n>Hmm. Reading through three test cases that all create `shallow-repo` in\n>the same way, I wonder whether we should not simply set it up once, and\n>then not even bother removing it. I think that would simplify not only the\n>patch, it would also simplify debugging later on.\n>\n>> +\n>> +test_expect_success 'clone shallow repository with --no-reject-shallow' '\n>> +\trm -rf shallow-repo &&\n>> +\tgit clone --depth=1 --no-local parent shallow-repo &&\n>> +\tgit clone --no-reject-shallow --no-local shallow-repo clone-repo\n>> +\n>> +'\n>> +\n>> +test_expect_success 'clone normal repository with --reject-shallow' '\n>> +\trm -rf clone-repo &&\n>> +\tgit clone --no-local parent normal-repo &&\n>> +\tgit clone --reject-shallow --no-local normal-repo clone-repo\n>> +\n>> +'\n>> +\n>> +test_expect_success 'unspecified any configs or options' '\n>> +\trm -rf shallow-repo clone-repo &&\n>> +\tgit clone --depth=1 --no-local parent shallow-repo &&\n>> +\tgit clone shallow-repo clone-repo\n>> +\n>> +'\n>> +\n>\n>Having read through these test cases, I think they can be simplified by\n>\n>- first setting up `shallow-repo`\n>\n>- then testing in the same test case whether `--reject-shallow` fails and\n>  `--no-reject-shallow` succeeds, without `--no-local`\n>\n>- then testing the same _with_ `--no-local`\n>\n>These can go to t5606, no problem.\n>\n>Then, in t5601, after the `start_httpd` call, add a single test case that\n>\n>- sets up the shallow clone _directly_, i.e.\n>\n>\tgit clone --bare --no-local --depth=1 parent \\\n>\t\"$HTTPD_DOCUMENT_ROOT_PATH/repo.git\"\n>\n>- verifies that `--reject-shallow` fails as expected, and\n>\n>- verifies that `--no-reject-shallow` works as expected.\n>\n>>  test_expect_success 'uses \"origin\" for default remote name' '\n>>\n>>  git clone parent clone-default-origin &&\n>> diff --git a/t/t5611-clone-config.sh b/t/t5611-clone-config.sh\n>> index 9f555b87ecdf..adf873f60300 100755\n>> --- a/t/t5611-clone-config.sh\n>> +++ b/t/t5611-clone-config.sh\n>> @@ -95,6 +95,38 @@ test_expect_success 'clone -c remote.<remote>.fetch=<refspec> --origin=<name>' '\n>>  test_cmp expect actual\n>>  '\n>>\n>> +test_expect_success 'clone.rejectshallow=true should reject cloning' '\n>> +\trm -rf child &&\n>> +\tgit clone --depth=1 --no-local . child &&\n>\n>In the following, this shallow repository is needed a couple of times.\n>Better set it up once, in a dedicated `set up shallow repository` test\n>case.\n>\n>And `shallow-repo` would probably make for a much better name than\n>`child`.\n>\n>> +\ttest_must_fail git -c clone.rejectshallow=true clone --no-local child out 2>err &&\n>> +\ttest_i18ngrep -e \"source repository is shallow, reject to clone.\" err\n>> +'\n>> +\n>> +test_expect_success 'clone.rejectshallow=false should succeed' '\n>> +\trm -rf child out &&\n>> +\tgit clone --depth=1 --no-local . child &&\n>> +\tgit -c clone.rejectshallow=false clone --no-local child out\n>> +'\n>\n>These two can be combined (and should, if you ask me, to simplify things).\n>\n>> +\n>> +test_expect_success 'clone.rejectshallow=true should succeed with normal repo' '\n>> +\trm -rf child out &&\n>> +\tgit clone --no-local . child &&\n>> +\tgit -c clone.rejectshallow=true clone --no-local child out\n>> +'\n>> +\n>> +test_expect_success 'option --reject-shallow override clone.rejectshallow' '\n>> +\trm -rf child out &&\n>> +\tgit clone --depth=1 --no-local . child &&\n>> +\ttest_must_fail git -c clone.rejectshallow=false clone --reject-shallow --no-local child out 2>err &&\n>> +\ttest_i18ngrep -e \"source repository is shallow, reject to clone.\" err\n>> +'\n>> +\n>> +test_expect_success 'option --no-reject-shallow override clone.rejectshallow' '\n>> +\trm -rf child out &&\n>> +\tgit clone --depth=1 --no-local . child &&\n>> +\tgit -c clone.rejectshallow=true clone --no-reject-shallow --no-local child out\n>> +'\n>> +\n>\n>Personally, I think this is overkill. What I would do is to have a single\n>test case that verifies that\n>\n>- `clone.rejectShallow=true` fails as expected,\n>\n>- `clone.rejectShallow=false [...] --reject-shallow` fails as expected, and\n>\n>- `clone.rejectShallow=false` succeeds.\n>\n>If we do this, we do not even need a preparatory test case setting up the\n>shallow repository.\n>\n>>  test_expect_success MINGW 'clone -c core.hideDotFiles' '\n>>  test_commit attributes .gitattributes \"\" &&\n>>  rm -rf child &&\n>> diff --git a/transport.c b/transport.c\n>> index 1c4ab676d1b1..a6b9f404d86a 100644\n>> --- a/transport.c\n>> +++ b/transport.c\n>> @@ -236,6 +236,9 @@ static int set_git_option(struct git_transport_options *opts,\n>>  list_objects_filter_die_if_populated(&opts->filter_options);\n>>  parse_list_objects_filter(&opts->filter_options, value);\n>>  return 0;\n>> +\t} else if (!strcmp(name, TRANS_OPT_REJECT_SHALLOW)) {\n>> +\topts->reject_shallow = !!value;\n>\n>I see that this is the established pattern (I am so grateful that I have\n>https://github.com/gitgitgadget/git/pull/865/files to look at the context,\n>something with which a pure mail-only patch contribution would not bless\n>me!), that those Boolean options are `NULL` vs non-`NULL`. So while you\n>pass `\"1\"` as the `value` parameter to `set_git_option()`, the parameter\n>`\"0\"` would _enable that option just the same_, you would have to pass\n>`NULL` to turn it off. I find that highly unintuitive, but that's not the\n>fault of your patch. The pattern is established, and you did the right\n>thing by following it.\n>\n>> +\treturn 0;\n>>  }\n>>  return 1;\n>>  }\n>\n>As I said, the rest of the patch looks good to me. With the few\n>suggestions I offered, I would be totally fine with this patch entering\n>`next`.\n>\n>Thank you,\n>Dscho"},{"id":"420704","messageId":"nycvar.QRO.7.76.6.2103311146210.52@tvgsbejvaqbjf.bet","threadId":"55104","inReplyTo":"xmqqk0poedr4.fsf@gitster.g","subject":"Re: [PATCH v8] builtin/clone.c: add --reject-shallow option","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2021-03-31T13:30:06Z","receivedAt":"2021-03-31T13:31:15Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio,\n\nOn Tue, 30 Mar 2021, Junio C Hamano wrote:\n\n> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n>\n> > ... repository has been initialized. Note that my suggestion still works with\n> > that: if either the original config, or the new config set\n> > `clone.rejectShallow`, it is picked up correctly, with the latter\n> > overriding the former if both configs want to set it.\n>\n> All four combinations (\"set it to true\" vs \"set it to false\" makes\n> two, and before and after doubles that to four)?\n\nI don't think we need to test all four combinations, that's part of my\npoint. We only need to verify that we can reject a shallow clone, and that\nwe can allow it, and that the command-line option overrides the config\noption. That's three ;-)\n\n> >> diff --git a/t/t5606-clone-options.sh b/t/t5606-clone-options.sh\n> >> index 428b0aac93fa..de1cd85983ed 100755\n> >> --- a/t/t5606-clone-options.sh\n> >> +++ b/t/t5606-clone-options.sh\n> >> @@ -5,6 +5,8 @@ GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n> >>  export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n> >>\n> >>  . ./test-lib.sh\n> >> +. \"$TEST_DIRECTORY\"/lib-httpd.sh\n> >> +start_httpd\n> >\n> > That's not good. What happens if there is no `httpd`? Then the rest of the\n> > tests are either skipped, or if `GIT_TEST_HTTPD` is set to `true`, we\n> > fail. The failure is intentional, but the skipping is not. There are many\n> > tests in t5606 that do not require a running HTTP daemon, and we should\n> > not skip them (for example, in our CI runs, there are quite a few jobs\n> > that run without any working `httpd`).\n> >\n> > A much better alternative, I think, would be to move those new test cases\n> > that require `httpd` to be running to t5601 (which _already_ calls\n> > `start_httpd`, near the end, so as to not skip any tests that do not\n> > require `httpd`).\n>\n> Or move the tests that require httpd, and use of httpd library, to\n> the end of this script.\n\nIn the interest of saving some time during the already-quite-long test\nruns, I would recommend piggy-backing onto a script that _already_ spins\nup its own Apache server.\n\nThanks,\nDscho\n"},{"id":"420710","messageId":"pull.865.v9.git.1617205915876.gitgitgadget@gmail.com","threadId":"55104","inReplyTo":"pull.865.v8.git.1617013145206.gitgitgadget@gmail.com","subject":"[PATCH v9] builtin/clone.c: add --reject-shallow option","fromName":"lilinchao via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-03-31T15:51:55Z","receivedAt":"2021-03-31T15:52:39Z","isPatch":true,"sender":{"key":"name:lilinchao","avatar":null},"body":"From: lilinchao <lilinchao@oschina.cn>\n\nIn some scenarios, users may want more history than the repository\noffered for cloning, which happens to be a shallow repository, can\ngive them. But because users don't know it is a shallow repository\nuntil they download it to local, users should have the option to\nrefuse to clone this kind of repository, and may want to exit the\nprocess immediately without creating any unnecessary files.\n\nAlthought there is an option '--depth=x' for users to decide how\ndeep history they can fetch, but as the unshallow cloning's depth\nis INFINITY, we can't know exactly the minimun 'x' value that can\nsatisfy the minimum integrity, so we can't pass 'x' value to --depth,\nand expect this can obtain a complete history of a repository.\n\nIn other scenarios, if we have an API that allow us to import external\nrepository, and then perform various operations on the repo.\nBut if the imported is a shallow one(which is actually possible), it\nwill affect the subsequent operations. So we can choose to refuse to\nclone, and let's just import a normal repository.\n\nThis patch offers a new option '--reject-shallow' that can reject to\nclone a shallow repository.\n\nSigned-off-by: Li Linchao <lilinchao@oschina.cn>\n---\n    builtin/clone.c: add --reject-shallow option\n    \n    Changes since v1:\n    \n     * Rename --no-shallow to --reject-shallow\n     * Enable to reject a non-local clone\n     * Enable --[no-]reject-shallow from CLI override configuration.\n     * Add more testcases.\n     * Reword commit messages and relative documentation.\n    \n    Changes since v3:\n    \n     * Add support to reject clone shallow repo over https protocol\n     * Add testcase to reject clone shallow repo over https:// transport\n     * Reword commit messages and relative documentation according\n       suggestions from Junio.\n    \n    Changes since v5:\n    \n     * camelcase config variable\n     * warning client that source repo is shallow\n     * better support ssh:// and git:// protocol v1, v2\n    \n    Changes since v6:\n    \n     * use _() for warning/die statement\n    \n    Signed-off-by: lilinchao lilinchao@oschina.cn\n\nPublished-As: https://github.com/gitgitgadget/git/releases/tag/pr-865%2FCactusinhand%2Fgit-clone-options-v9\nFetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-865/Cactusinhand/git-clone-options-v9\nPull-Request: https://github.com/gitgitgadget/git/pull/865\n\nRange-diff vs v8:\n\n 1:  83a724f6d771 ! 1:  11043a2344dd builtin/clone.c: add --reject-shallow option\n     @@ Commit message\n          This patch offers a new option '--reject-shallow' that can reject to\n          clone a shallow repository.\n      \n     -    Signed-off-by: lilinchao <lilinchao@oschina.cn>\n     +    Signed-off-by: Li Linchao <lilinchao@oschina.cn>\n      \n       ## Documentation/config/clone.txt ##\n      @@ Documentation/config/clone.txt: clone.defaultRemoteName::\n     @@ Documentation/git-clone.txt: objects from the source repository into a pack in t\n      +--[no-]reject-shallow::\n      +\tFail if the source repository is a shallow repository.\n      +\tThe 'clone.rejectShallow' configuration variable can be used to\n     -+\tgive the default.\n     ++\tspecify the default.\n      +\n       --bare::\n       \tMake a 'bare' Git repository.  That is, instead of\n     @@ builtin/clone.c: static int option_no_checkout, option_bare, option_mirror, opti\n       static int option_local = -1, option_no_hardlinks, option_shared;\n       static int option_no_tags;\n       static int option_shallow_submodules;\n     -+static int option_shallow = -1;    /* unspecified */\n     -+static int config_shallow = -1;    /* unspecified */\n     ++static int option_reject_shallow = -1;    /* unspecified */\n       static int deepen;\n       static char *option_template, *option_depth, *option_since;\n       static char *option_origin = NULL;\n     @@ builtin/clone.c: static struct option builtin_clone_options[] = {\n       \tOPT__VERBOSITY(&option_verbosity),\n       \tOPT_BOOL(0, \"progress\", &option_progress,\n       \t\t N_(\"force progress reporting\")),\n     -+\tOPT_BOOL(0, \"reject-shallow\", &option_shallow,\n     ++\tOPT_BOOL(0, \"reject-shallow\", &option_reject_shallow,\n      +\t\t N_(\"don't clone shallow repository\")),\n       \tOPT_BOOL('n', \"no-checkout\", &option_no_checkout,\n       \t\t N_(\"don't create a checkout\")),\n     @@ builtin/clone.c: static int git_clone_config(const char *k, const char *v, void\n       \t\tfree(remote_name);\n       \t\tremote_name = xstrdup(v);\n       \t}\n     -+\tif (!strcmp(k, \"clone.rejectshallow\"))\n     -+\t\tconfig_shallow = git_config_bool(k, v);\n     ++\tif (!strcmp(k, \"clone.rejectshallow\") && option_reject_shallow < 0)\n     ++\t\toption_reject_shallow = git_config_bool(k, v);\n      +\n       \treturn git_default_config(k, v, cb);\n       }\n       \n     -@@ builtin/clone.c: static int path_exists(const char *path)\n     - int cmd_clone(int argc, const char **argv, const char *prefix)\n     - {\n     - \tint is_bundle = 0, is_local;\n     -+\tint reject_shallow = 0;\n     - \tconst char *repo_name, *repo, *work_tree, *git_dir;\n     - \tchar *path, *dir, *display_repo = NULL;\n     - \tint dest_exists, real_dest_exists = 0;\n     -@@ builtin/clone.c: int cmd_clone(int argc, const char **argv, const char *prefix)\n     - \t */\n     - \tgit_config(git_clone_config, NULL);\n     - \n     -+\t/*\n     -+\t * If option_shallow is specified from CLI option,\n     -+\t * ignore config_shallow from git_clone_config.\n     -+\t */\n     -+\tif (config_shallow != -1)\n     -+\t\treject_shallow = config_shallow;\n     -+\n     -+\tif (option_shallow != -1)\n     -+\t\treject_shallow = option_shallow;\n     -+\n     - \t/*\n     - \t * apply the remote name provided by --origin only after this second\n     - \t * call to git_config, to ensure it overrides all config-based values.\n      @@ builtin/clone.c: int cmd_clone(int argc, const char **argv, const char *prefix)\n       \t\tif (filter_options.choice)\n       \t\t\twarning(_(\"--filter is ignored in local clones; use file:// instead.\"));\n       \t\tif (!access(mkpath(\"%s/shallow\", path), F_OK)) {\n     -+\t\t\tif (reject_shallow)\n     ++\t\t\tif (option_reject_shallow > 0)\n      +\t\t\t\tdie(_(\"source repository is shallow, reject to clone.\"));\n       \t\t\tif (option_local > 0)\n       \t\t\t\twarning(_(\"source repository is shallow, ignoring --local\"));\n     @@ builtin/clone.c: int cmd_clone(int argc, const char **argv, const char *prefix)\n       \n       \ttransport_set_option(transport, TRANS_OPT_KEEP, \"yes\");\n       \n     -+\tif (reject_shallow)\n     ++\tif (option_reject_shallow > 0)\n      +\t\ttransport_set_option(transport, TRANS_OPT_REJECT_SHALLOW, \"1\");\n       \tif (option_depth)\n       \t\ttransport_set_option(transport, TRANS_OPT_DEPTH,\n     @@ fetch-pack.c: static struct ref *do_fetch_pack(struct fetch_pack_args *args,\n       \t\t\t\t\tNULL);\n      -\telse if (si->nr_ours || si->nr_theirs)\n      +\telse if (si->nr_ours || si->nr_theirs) {\n     -+\t\tif (args->remote_shallow)\n     ++\t\tif (args->reject_shallow_remote)\n      +\t\t\tdie(_(\"source repository is shallow, reject to clone.\"));\n       \t\talternate_shallow_file = setup_temporary_shallow(si->shallow);\n      -\telse\n     @@ fetch-pack.c: static void receive_shallow_info(struct fetch_pack_args *args,\n       \t\tprepare_shallow_info(si, shallows);\n      -\t\tif (si->nr_ours || si->nr_theirs)\n      +\t\tif (si->nr_ours || si->nr_theirs) {\n     -+\t\t\tif (args->remote_shallow)\n     ++\t\t\tif (args->reject_shallow_remote)\n      +\t\t\t\tdie(_(\"source repository is shallow, reject to clone.\"));\n       \t\t\talternate_shallow_file =\n       \t\t\t\tsetup_temporary_shallow(si->shallow);\n     @@ fetch-pack.h: struct fetch_pack_args {\n       \tunsigned self_contained_and_connected:1;\n       \tunsigned cloning:1;\n       \tunsigned update_shallow:1;\n     -+\tunsigned remote_shallow:1;\n     ++\tunsigned reject_shallow_remote:1;\n       \tunsigned deepen:1;\n       \n       \t/*\n      \n     - ## t/t5606-clone-options.sh ##\n     -@@ t/t5606-clone-options.sh: GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n     - export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n     + ## t/t5601-clone.sh ##\n     +@@ t/t5601-clone.sh: test_expect_success 'partial clone using HTTP' '\n     + \tpartial_clone \"$HTTPD_DOCUMENT_ROOT_PATH/server\" \"$HTTPD_URL/smart/server\"\n     + '\n       \n     - . ./test-lib.sh\n     -+. \"$TEST_DIRECTORY\"/lib-httpd.sh\n     -+start_httpd\n     ++test_expect_success 'reject cloning shallow repository using HTTP' '\n     ++\ttest_when_finished \"rm -rf repo\" &&\n     ++\tgit clone --bare --no-local --depth=1 src \"$HTTPD_DOCUMENT_ROOT_PATH/repo.git\" &&\n     ++\ttest_must_fail git clone --reject-shallow $HTTPD_URL/smart/repo.git repo 2>err &&\n     ++\ttest_i18ngrep -e \"source repository is shallow, reject to clone.\" err &&\n     ++\n     ++\tgit clone --no-reject-shallow $HTTPD_URL/smart/repo.git repo\n     ++'\n     ++\n     + # DO NOT add non-httpd-specific tests here, because the last part of this\n     + # test script is only executed when httpd is available and enabled.\n     + \n     +\n     + ## t/t5606-clone-options.sh ##\n     +@@ t/t5606-clone-options.sh: test_expect_success 'setup' '\n     + \tmkdir parent &&\n     + \t(cd parent && git init &&\n     + \t echo one >file && git add file &&\n     +-\t git commit -m one)\n     ++\t git commit -m one) &&\n     ++\tgit clone --depth=1 --no-local parent shallow-repo\n       \n     - test_expect_success 'setup' '\n     + '\n       \n      @@ t/t5606-clone-options.sh: test_expect_success 'disallows --bare with --separate-git-dir' '\n       \n       '\n       \n     -+test_expect_success 'reject cloning http shallow repository' '\n     -+\tgit clone --depth=1 --no-local parent shallow-repo &&\n     -+\tgit clone --bare --no-local shallow-repo \"$HTTPD_DOCUMENT_ROOT_PATH/repo.git\" &&\n     -+\ttest_must_fail git clone --reject-shallow $HTTPD_URL/smart/repo.git out 2>err &&\n     -+\ttest_i18ngrep -e \"source repository is shallow, reject to clone.\" err\n     -+\n     -+'\n     -+\n      +test_expect_success 'reject cloning shallow repository' '\n     -+\trm -rf shallow-repo &&\n     -+\tgit clone --depth=1 --no-local parent shallow-repo &&\n     ++\ttest_when_finished \"rm -rf repo\" &&\n      +\ttest_must_fail git clone --reject-shallow shallow-repo out 2>err &&\n     -+\ttest_i18ngrep -e \"source repository is shallow, reject to clone.\" err\n     ++\ttest_i18ngrep -e \"source repository is shallow, reject to clone.\" err &&\n      +\n     ++\tgit clone --no-reject-shallow shallow-repo repo\n      +'\n      +\n      +test_expect_success 'reject cloning non-local shallow repository' '\n     -+\trm -rf shallow-repo &&\n     -+\tgit clone --depth=1 --no-local parent shallow-repo &&\n     ++\ttest_when_finished \"rm -rf repo\" &&\n      +\ttest_must_fail git clone --reject-shallow --no-local shallow-repo out 2>err &&\n     -+\ttest_i18ngrep -e \"source repository is shallow, reject to clone.\" err\n     ++\ttest_i18ngrep -e \"source repository is shallow, reject to clone.\" err &&\n      +\n     ++\tgit clone --no-reject-shallow --no-local shallow-repo repo\n      +'\n      +\n     -+test_expect_success 'clone shallow repository with --no-reject-shallow' '\n     -+\trm -rf shallow-repo &&\n     -+\tgit clone --depth=1 --no-local parent shallow-repo &&\n     -+\tgit clone --no-reject-shallow --no-local shallow-repo clone-repo\n     -+\n     -+'\n     -+\n     -+test_expect_success 'clone normal repository with --reject-shallow' '\n     -+\trm -rf clone-repo &&\n     -+\tgit clone --no-local parent normal-repo &&\n     -+\tgit clone --reject-shallow --no-local normal-repo clone-repo\n     -+\n     -+'\n     -+\n     -+test_expect_success 'unspecified any configs or options' '\n     -+\trm -rf shallow-repo clone-repo &&\n     -+\tgit clone --depth=1 --no-local parent shallow-repo &&\n     -+\tgit clone shallow-repo clone-repo\n     -+\n     ++test_expect_success 'succeed cloning normal repository' '\n     ++\ttest_when_finished \"rm -rf chilad1 child2 child3 child4 \" &&\n     ++\tgit clone --reject-shallow parent child1 &&\n     ++\tgit clone --reject-shallow --no-local parent child2 &&\n     ++\tgit clone --no-reject-shallow parent child3 &&\n     ++\tgit clone --no-reject-shallow --no-local parent child4\n      +'\n      +\n       test_expect_success 'uses \"origin\" for default remote name' '\n     @@ t/t5611-clone-config.sh: test_expect_success 'clone -c remote.<remote>.fetch=<re\n       \ttest_cmp expect actual\n       '\n       \n     -+test_expect_success 'clone.rejectshallow=true should reject cloning' '\n     -+\trm -rf child &&\n     -+\tgit clone --depth=1 --no-local . child &&\n     -+\ttest_must_fail git -c clone.rejectshallow=true clone --no-local child out 2>err &&\n     -+\ttest_i18ngrep -e \"source repository is shallow, reject to clone.\" err\n     ++test_expect_success 'set up shallow repository' '\n     ++\tgit clone --depth=1 --no-local . shallow-repo\n      +'\n      +\n     -+test_expect_success 'clone.rejectshallow=false should succeed' '\n     -+\trm -rf child out &&\n     -+\tgit clone --depth=1 --no-local . child &&\n     -+\tgit -c clone.rejectshallow=false clone --no-local child out\n     -+'\n     ++test_expect_success 'clone.rejectshallow=true should reject cloning shallow repo' '\n     ++\ttest_when_finished \"rm -rf out\" &&\n     ++\ttest_must_fail git -c clone.rejectshallow=true clone --no-local shallow-repo out 2>err &&\n     ++\ttest_i18ngrep -e \"source repository is shallow, reject to clone.\" err &&\n      +\n     -+test_expect_success 'clone.rejectshallow=true should succeed with normal repo' '\n     -+\trm -rf child out &&\n     -+\tgit clone --no-local . child &&\n     -+\tgit -c clone.rejectshallow=true clone --no-local child out\n     ++\tgit -c clone.rejectshallow=false clone --no-local shallow-repo out\n      +'\n      +\n     -+test_expect_success 'option --reject-shallow override clone.rejectshallow' '\n     -+\trm -rf child out &&\n     -+\tgit clone --depth=1 --no-local . child &&\n     -+\ttest_must_fail git -c clone.rejectshallow=false clone --reject-shallow --no-local child out 2>err &&\n     -+\ttest_i18ngrep -e \"source repository is shallow, reject to clone.\" err\n     ++test_expect_success 'option --[no-]reject-shallow override clone.rejectshallow config' '\n     ++\ttest_when_finished \"rm -rf out\" &&\n     ++\ttest_must_fail git -c clone.rejectshallow=false clone --reject-shallow --no-local shallow-repo out 2>err &&\n     ++\ttest_i18ngrep -e \"source repository is shallow, reject to clone.\" err &&\n     ++\n     ++\tgit -c clone.rejectshallow=true clone --no-reject-shallow --no-local shallow-repo out\n      +'\n      +\n     -+test_expect_success 'option --no-reject-shallow override clone.rejectshallow' '\n     -+\trm -rf child out &&\n     -+\tgit clone --depth=1 --no-local . child &&\n     -+\tgit -c clone.rejectshallow=true clone --no-reject-shallow --no-local child out\n     ++test_expect_success 'clone.rejectshallow=true should succeed cloning normal repo' '\n     ++\ttest_when_finished \"rm -rf out\" &&\n     ++\tgit -c clone.rejectshallow=true clone --no-local . out\n      +'\n      +\n       test_expect_success MINGW 'clone -c core.hideDotFiles' '\n     @@ transport.c: static int fetch_refs_via_pack(struct transport *transport,\n       \targs.stateless_rpc = transport->stateless_rpc;\n       \targs.server_options = transport->server_options;\n       \targs.negotiation_tips = data->options.negotiation_tips;\n     -+\targs.remote_shallow = transport->smart_options->reject_shallow;\n     ++\targs.reject_shallow_remote = transport->smart_options->reject_shallow;\n       \n       \tif (!data->got_remote_heads) {\n       \t\tint i;\n\n\n Documentation/config/clone.txt |  4 ++++\n Documentation/git-clone.txt    |  7 ++++++-\n builtin/clone.c                | 10 ++++++++++\n fetch-pack.c                   | 12 ++++++++----\n fetch-pack.h                   |  1 +\n t/t5601-clone.sh               |  9 +++++++++\n t/t5606-clone-options.sh       | 27 ++++++++++++++++++++++++++-\n t/t5611-clone-config.sh        | 25 +++++++++++++++++++++++++\n transport.c                    |  4 ++++\n transport.h                    |  4 ++++\n 10 files changed, 97 insertions(+), 6 deletions(-)\n\ndiff --git a/Documentation/config/clone.txt b/Documentation/config/clone.txt\nindex 47de36a5fedf..7bcfbd18a52a 100644\n--- a/Documentation/config/clone.txt\n+++ b/Documentation/config/clone.txt\n@@ -2,3 +2,7 @@ clone.defaultRemoteName::\n \tThe name of the remote to create when cloning a repository.  Defaults to\n \t`origin`, and can be overridden by passing the `--origin` command-line\n \toption to linkgit:git-clone[1].\n+\n+clone.rejectShallow::\n+\tReject to clone a repository if it is a shallow one, can be overridden by\n+\tpassing option `--reject-shallow` in command line. See linkgit:git-clone[1]\ndiff --git a/Documentation/git-clone.txt b/Documentation/git-clone.txt\nindex 02d9c19cec75..3fe3810f1ce1 100644\n--- a/Documentation/git-clone.txt\n+++ b/Documentation/git-clone.txt\n@@ -15,7 +15,7 @@ SYNOPSIS\n \t  [--dissociate] [--separate-git-dir <git dir>]\n \t  [--depth <depth>] [--[no-]single-branch] [--no-tags]\n \t  [--recurse-submodules[=<pathspec>]] [--[no-]shallow-submodules]\n-\t  [--[no-]remote-submodules] [--jobs <n>] [--sparse]\n+\t  [--[no-]remote-submodules] [--jobs <n>] [--sparse] [--[no-]reject-shallow]\n \t  [--filter=<filter>] [--] <repository>\n \t  [<directory>]\n \n@@ -149,6 +149,11 @@ objects from the source repository into a pack in the cloned repository.\n --no-checkout::\n \tNo checkout of HEAD is performed after the clone is complete.\n \n+--[no-]reject-shallow::\n+\tFail if the source repository is a shallow repository.\n+\tThe 'clone.rejectShallow' configuration variable can be used to\n+\tspecify the default.\n+\n --bare::\n \tMake a 'bare' Git repository.  That is, instead of\n \tcreating `<directory>` and placing the administrative\ndiff --git a/builtin/clone.c b/builtin/clone.c\nindex 51e844a2de0a..d67a9f254c80 100644\n--- a/builtin/clone.c\n+++ b/builtin/clone.c\n@@ -50,6 +50,7 @@ static int option_no_checkout, option_bare, option_mirror, option_single_branch\n static int option_local = -1, option_no_hardlinks, option_shared;\n static int option_no_tags;\n static int option_shallow_submodules;\n+static int option_reject_shallow = -1;    /* unspecified */\n static int deepen;\n static char *option_template, *option_depth, *option_since;\n static char *option_origin = NULL;\n@@ -90,6 +91,8 @@ static struct option builtin_clone_options[] = {\n \tOPT__VERBOSITY(&option_verbosity),\n \tOPT_BOOL(0, \"progress\", &option_progress,\n \t\t N_(\"force progress reporting\")),\n+\tOPT_BOOL(0, \"reject-shallow\", &option_reject_shallow,\n+\t\t N_(\"don't clone shallow repository\")),\n \tOPT_BOOL('n', \"no-checkout\", &option_no_checkout,\n \t\t N_(\"don't create a checkout\")),\n \tOPT_BOOL(0, \"bare\", &option_bare, N_(\"create a bare repository\")),\n@@ -858,6 +861,9 @@ static int git_clone_config(const char *k, const char *v, void *cb)\n \t\tfree(remote_name);\n \t\tremote_name = xstrdup(v);\n \t}\n+\tif (!strcmp(k, \"clone.rejectshallow\") && option_reject_shallow < 0)\n+\t\toption_reject_shallow = git_config_bool(k, v);\n+\n \treturn git_default_config(k, v, cb);\n }\n \n@@ -1216,6 +1222,8 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n \t\tif (filter_options.choice)\n \t\t\twarning(_(\"--filter is ignored in local clones; use file:// instead.\"));\n \t\tif (!access(mkpath(\"%s/shallow\", path), F_OK)) {\n+\t\t\tif (option_reject_shallow > 0)\n+\t\t\t\tdie(_(\"source repository is shallow, reject to clone.\"));\n \t\t\tif (option_local > 0)\n \t\t\t\twarning(_(\"source repository is shallow, ignoring --local\"));\n \t\t\tis_local = 0;\n@@ -1227,6 +1235,8 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n \n \ttransport_set_option(transport, TRANS_OPT_KEEP, \"yes\");\n \n+\tif (option_reject_shallow > 0)\n+\t\ttransport_set_option(transport, TRANS_OPT_REJECT_SHALLOW, \"1\");\n \tif (option_depth)\n \t\ttransport_set_option(transport, TRANS_OPT_DEPTH,\n \t\t\t\t     option_depth);\ndiff --git a/fetch-pack.c b/fetch-pack.c\nindex fb04a76ca263..40392692ad07 100644\n--- a/fetch-pack.c\n+++ b/fetch-pack.c\n@@ -1129,9 +1129,11 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args,\n \tif (args->deepen)\n \t\tsetup_alternate_shallow(&shallow_lock, &alternate_shallow_file,\n \t\t\t\t\tNULL);\n-\telse if (si->nr_ours || si->nr_theirs)\n+\telse if (si->nr_ours || si->nr_theirs) {\n+\t\tif (args->reject_shallow_remote)\n+\t\t\tdie(_(\"source repository is shallow, reject to clone.\"));\n \t\talternate_shallow_file = setup_temporary_shallow(si->shallow);\n-\telse\n+\t} else\n \t\talternate_shallow_file = NULL;\n \tif (get_pack(args, fd, pack_lockfiles, NULL, sought, nr_sought,\n \t\t     &gitmodules_oids))\n@@ -1498,10 +1500,12 @@ static void receive_shallow_info(struct fetch_pack_args *args,\n \t\t * rejected (unless --update-shallow is set); do the same.\n \t\t */\n \t\tprepare_shallow_info(si, shallows);\n-\t\tif (si->nr_ours || si->nr_theirs)\n+\t\tif (si->nr_ours || si->nr_theirs) {\n+\t\t\tif (args->reject_shallow_remote)\n+\t\t\t\tdie(_(\"source repository is shallow, reject to clone.\"));\n \t\t\talternate_shallow_file =\n \t\t\t\tsetup_temporary_shallow(si->shallow);\n-\t\telse\n+\t\t} else\n \t\t\talternate_shallow_file = NULL;\n \t} else {\n \t\talternate_shallow_file = NULL;\ndiff --git a/fetch-pack.h b/fetch-pack.h\nindex 736a3dae467a..f114d7277567 100644\n--- a/fetch-pack.h\n+++ b/fetch-pack.h\n@@ -39,6 +39,7 @@ struct fetch_pack_args {\n \tunsigned self_contained_and_connected:1;\n \tunsigned cloning:1;\n \tunsigned update_shallow:1;\n+\tunsigned reject_shallow_remote:1;\n \tunsigned deepen:1;\n \n \t/*\ndiff --git a/t/t5601-clone.sh b/t/t5601-clone.sh\nindex e7e6c089554c..329ae599fd3c 100755\n--- a/t/t5601-clone.sh\n+++ b/t/t5601-clone.sh\n@@ -759,6 +759,15 @@ test_expect_success 'partial clone using HTTP' '\n \tpartial_clone \"$HTTPD_DOCUMENT_ROOT_PATH/server\" \"$HTTPD_URL/smart/server\"\n '\n \n+test_expect_success 'reject cloning shallow repository using HTTP' '\n+\ttest_when_finished \"rm -rf repo\" &&\n+\tgit clone --bare --no-local --depth=1 src \"$HTTPD_DOCUMENT_ROOT_PATH/repo.git\" &&\n+\ttest_must_fail git clone --reject-shallow $HTTPD_URL/smart/repo.git repo 2>err &&\n+\ttest_i18ngrep -e \"source repository is shallow, reject to clone.\" err &&\n+\n+\tgit clone --no-reject-shallow $HTTPD_URL/smart/repo.git repo\n+'\n+\n # DO NOT add non-httpd-specific tests here, because the last part of this\n # test script is only executed when httpd is available and enabled.\n \ndiff --git a/t/t5606-clone-options.sh b/t/t5606-clone-options.sh\nindex 428b0aac93fa..3a595c0f82c7 100755\n--- a/t/t5606-clone-options.sh\n+++ b/t/t5606-clone-options.sh\n@@ -11,7 +11,8 @@ test_expect_success 'setup' '\n \tmkdir parent &&\n \t(cd parent && git init &&\n \t echo one >file && git add file &&\n-\t git commit -m one)\n+\t git commit -m one) &&\n+\tgit clone --depth=1 --no-local parent shallow-repo\n \n '\n \n@@ -45,6 +46,30 @@ test_expect_success 'disallows --bare with --separate-git-dir' '\n \n '\n \n+test_expect_success 'reject cloning shallow repository' '\n+\ttest_when_finished \"rm -rf repo\" &&\n+\ttest_must_fail git clone --reject-shallow shallow-repo out 2>err &&\n+\ttest_i18ngrep -e \"source repository is shallow, reject to clone.\" err &&\n+\n+\tgit clone --no-reject-shallow shallow-repo repo\n+'\n+\n+test_expect_success 'reject cloning non-local shallow repository' '\n+\ttest_when_finished \"rm -rf repo\" &&\n+\ttest_must_fail git clone --reject-shallow --no-local shallow-repo out 2>err &&\n+\ttest_i18ngrep -e \"source repository is shallow, reject to clone.\" err &&\n+\n+\tgit clone --no-reject-shallow --no-local shallow-repo repo\n+'\n+\n+test_expect_success 'succeed cloning normal repository' '\n+\ttest_when_finished \"rm -rf chilad1 child2 child3 child4 \" &&\n+\tgit clone --reject-shallow parent child1 &&\n+\tgit clone --reject-shallow --no-local parent child2 &&\n+\tgit clone --no-reject-shallow parent child3 &&\n+\tgit clone --no-reject-shallow --no-local parent child4\n+'\n+\n test_expect_success 'uses \"origin\" for default remote name' '\n \n \tgit clone parent clone-default-origin &&\ndiff --git a/t/t5611-clone-config.sh b/t/t5611-clone-config.sh\nindex 9f555b87ecdf..f8625f915821 100755\n--- a/t/t5611-clone-config.sh\n+++ b/t/t5611-clone-config.sh\n@@ -95,6 +95,31 @@ test_expect_success 'clone -c remote.<remote>.fetch=<refspec> --origin=<name>' '\n \ttest_cmp expect actual\n '\n \n+test_expect_success 'set up shallow repository' '\n+\tgit clone --depth=1 --no-local . shallow-repo\n+'\n+\n+test_expect_success 'clone.rejectshallow=true should reject cloning shallow repo' '\n+\ttest_when_finished \"rm -rf out\" &&\n+\ttest_must_fail git -c clone.rejectshallow=true clone --no-local shallow-repo out 2>err &&\n+\ttest_i18ngrep -e \"source repository is shallow, reject to clone.\" err &&\n+\n+\tgit -c clone.rejectshallow=false clone --no-local shallow-repo out\n+'\n+\n+test_expect_success 'option --[no-]reject-shallow override clone.rejectshallow config' '\n+\ttest_when_finished \"rm -rf out\" &&\n+\ttest_must_fail git -c clone.rejectshallow=false clone --reject-shallow --no-local shallow-repo out 2>err &&\n+\ttest_i18ngrep -e \"source repository is shallow, reject to clone.\" err &&\n+\n+\tgit -c clone.rejectshallow=true clone --no-reject-shallow --no-local shallow-repo out\n+'\n+\n+test_expect_success 'clone.rejectshallow=true should succeed cloning normal repo' '\n+\ttest_when_finished \"rm -rf out\" &&\n+\tgit -c clone.rejectshallow=true clone --no-local . out\n+'\n+\n test_expect_success MINGW 'clone -c core.hideDotFiles' '\n \ttest_commit attributes .gitattributes \"\" &&\n \trm -rf child &&\ndiff --git a/transport.c b/transport.c\nindex 1c4ab676d1b1..b231894f9039 100644\n--- a/transport.c\n+++ b/transport.c\n@@ -236,6 +236,9 @@ static int set_git_option(struct git_transport_options *opts,\n \t\tlist_objects_filter_die_if_populated(&opts->filter_options);\n \t\tparse_list_objects_filter(&opts->filter_options, value);\n \t\treturn 0;\n+\t} else if (!strcmp(name, TRANS_OPT_REJECT_SHALLOW)) {\n+\t\topts->reject_shallow = !!value;\n+\t\treturn 0;\n \t}\n \treturn 1;\n }\n@@ -370,6 +373,7 @@ static int fetch_refs_via_pack(struct transport *transport,\n \targs.stateless_rpc = transport->stateless_rpc;\n \targs.server_options = transport->server_options;\n \targs.negotiation_tips = data->options.negotiation_tips;\n+\targs.reject_shallow_remote = transport->smart_options->reject_shallow;\n \n \tif (!data->got_remote_heads) {\n \t\tint i;\ndiff --git a/transport.h b/transport.h\nindex 24e15799e714..4d5db0a7f22b 100644\n--- a/transport.h\n+++ b/transport.h\n@@ -14,6 +14,7 @@ struct git_transport_options {\n \tunsigned check_self_contained_and_connected : 1;\n \tunsigned self_contained_and_connected : 1;\n \tunsigned update_shallow : 1;\n+\tunsigned reject_shallow : 1;\n \tunsigned deepen_relative : 1;\n \n \t/* see documentation of corresponding flag in fetch-pack.h */\n@@ -194,6 +195,9 @@ void transport_check_allowed(const char *type);\n /* Aggressively fetch annotated tags if possible */\n #define TRANS_OPT_FOLLOWTAGS \"followtags\"\n \n+/* Reject shallow repo transport */\n+#define TRANS_OPT_REJECT_SHALLOW \"rejectshallow\"\n+\n /* Accept refs that may update .git/shallow without --depth */\n #define TRANS_OPT_UPDATE_SHALLOW \"updateshallow\"\n \n\nbase-commit: a65ce7f831aa5fcc596c6d23fcde543d98b39bd7\n-- \ngitgitgadget\n"},{"id":"420745","messageId":"xmqqim576spj.fsf@gitster.g","threadId":"55104","inReplyTo":"pull.865.v9.git.1617205915876.gitgitgadget@gmail.com","subject":"Re: [PATCH v9] builtin/clone.c: add --reject-shallow option","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-03-31T19:14:48Z","receivedAt":"2021-03-31T19:15:45Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"lilinchao via GitGitGadget\" <gitgitgadget@gmail.com> writes:\n\n> From: lilinchao <lilinchao@oschina.cn>\n\nThis is not corrected, and no longer matches your sign-off.\n\nI can edit your 'From:' to match this time, but please make sure\nthey match for the next time.\n\n> In some scenarios, users may want more history than the repository\n> offered for cloning, which happens to be a shallow repository, can\n> give them. But because users don't know it is a shallow repository\n> until they download it to local, users should have the option to\n\nI find the \"should\" too strong, but let's keep reading.\n\n> refuse to clone this kind of repository, and may want to exit the\n> process immediately without creating any unnecessary files.\n\nYes, that is too strong and also redundant.\n\n> Althought there is an option '--depth=x' for users to decide how\n> deep history they can fetch, but as the unshallow cloning's depth\n\n\"Although\"; if you begin with \"although\", you shouldn't write \"but\".\n\n> is INFINITY, we can't know exactly the minimun 'x' value that can\n> satisfy the minimum integrity,\n> so we can't pass 'x' value to --depth,\n> and expect this can obtain a complete history of a repository.\n\nIf the argument were \"we might start with depth x, and the source\nmay be deep enough to give us x right now, but we may want to deepen\nmore than they can offer, and such a user would want to be able to\nsay 'I want depth=x now, but make sure they are not shallow'\", I\nwould understand it, but I do not see where the \"minimum integrity\"\ncomes from---there doesn't appear to be anything related to\nintegrity here.\n\n> In other scenarios, if we have an API that allow us to import external\n\n\"allows\"\n\n> repository, and then perform various operations on the repo.\n> But if the imported is a shallow one(which is actually possible), it\n> will affect the subsequent operations. So we can choose to refuse to\n> clone, and let's just import a normal repository.\n\nI'd suggest dropping this entire paragraph.  That is not any new\nscenario at all.  API or not, you essentially just said \"you can do\nvarious things on your clone once you have it, but some things you\nmay want to do you would want a full history\".  That is what you\nstarted the whole discussion above and does not add any new\ninformation.\n\n> This patch offers a new option '--reject-shallow' that can reject to\n> clone a shallow repository.\n\n\tTeach '--reject-shallow' option to \"git clone\" to abort as\n\tsoon as we find out that we are cloning from a shallow\n\trepository.\n\nperhaps?  cf. Documentation/SubmittingPatches[[describe-changes]]\nespecially [[imperative-mood]].\n\n\n> Signed-off-by: Li Linchao <lilinchao@oschina.cn>\n> ---\n>     builtin/clone.c: add --reject-shallow option\n>     \n>     Changes since v1:\n>...\n>     Changes since v6:\n>     \n>      * use _() for warning/die statement\n\nWhat are the more recent changes?\n\n>     Signed-off-by: lilinchao lilinchao@oschina.cn\n\n> @@ -858,6 +861,9 @@ static int git_clone_config(const char *k, const char *v, void *cb)\n>  \t\tfree(remote_name);\n>  \t\tremote_name = xstrdup(v);\n>  \t}\n> +\tif (!strcmp(k, \"clone.rejectshallow\") && option_reject_shallow < 0)\n> +\t\toption_reject_shallow = git_config_bool(k, v);\n\nDoes this \"single variable is enough\" really work?\n\nImagine that the first time around we'd read from $HOME/.gitconfig\nthat says true (flips the variable from \"unspecified\").  Further\nimagine that we are running \"git clone -c config.rejectShallow=no\"\nto countermand the global config.  We call write_config() to write\nthe extra configuration value out, and then call git_config() to\nread from the repository configuration again.\n\nBecause of the value taken from $HOME/.gitconfig, however, the\nattempt to override is silently ignored, isn't it?\n\nOther than that, the changes to the code from the previous round\nlooked sensible.\n\n\n"},{"id":"420752","messageId":"nycvar.QRO.7.76.6.2104010021510.54@tvgsbejvaqbjf.bet","threadId":"55104","inReplyTo":"xmqqim576spj.fsf@gitster.g","subject":"Re: [PATCH v9] builtin/clone.c: add --reject-shallow option","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2021-03-31T22:24:32Z","receivedAt":"2021-03-31T22:25:36Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio,\n\nOn Wed, 31 Mar 2021, Junio C Hamano wrote:\n\n> > @@ -858,6 +861,9 @@ static int git_clone_config(const char *k, const char *v, void *cb)\n> >  \t\tfree(remote_name);\n> >  \t\tremote_name = xstrdup(v);\n> >  \t}\n> > +\tif (!strcmp(k, \"clone.rejectshallow\") && option_reject_shallow < 0)\n> > +\t\toption_reject_shallow = git_config_bool(k, v);\n>\n> Does this \"single variable is enough\" really work?\n>\n> Imagine that the first time around we'd read from $HOME/.gitconfig\n> that says true (flips the variable from \"unspecified\").  Further\n> imagine that we are running \"git clone -c config.rejectShallow=no\"\n> to countermand the global config.  We call write_config() to write\n> the extra configuration value out, and then call git_config() to\n> read from the repository configuration again.\n>\n> Because of the value taken from $HOME/.gitconfig, however, the\n> attempt to override is silently ignored, isn't it?\n\nThat's my mistake, and you don't even need a second config to see the\nproblem:\n\n\t[clone]\n\t\trejectShallow = false\n\t\trejectShallow = true\n\nClearly, the second one should override the first one, but the code I\nsuggested (and which was integrated into this iteration) simply does not\nwork correctly.\n\nSo yes, I think you're right, and we need to have that second variable,\nand then do something like this:\n\n\tif (option_reject_shallow < 0)\n\t\toption_reject_shallow = config_reject_shallow;\n\nSorry for the trouble!\nDscho\n"},{"id":"420755","messageId":"xmqqim5754rl.fsf@gitster.g","threadId":"55104","inReplyTo":"nycvar.QRO.7.76.6.2104010021510.54@tvgsbejvaqbjf.bet","subject":"Re: [PATCH v9] builtin/clone.c: add --reject-shallow option","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-03-31T22:37:18Z","receivedAt":"2021-03-31T22:38:17Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n\n>> Does this \"single variable is enough\" really work?\n>> ...\n> That's my mistake, and you don't even need a second config to see the\n> problem:\n>\n> \t[clone]\n> \t\trejectShallow = false\n> \t\trejectShallow = true\n\nYup, that is a much simpler example.\n\n> Sorry for the trouble!\n\nDon't feel bad---in an earlier round of review I made a similar\nmistake to wonder if these two variables can be reduced into one\nmyself ;-).  As you said, this one is special in making two calls to\ngit_config() and need to be handled carefully.\n\nThanks.\n\n\n"},{"id":"420805","messageId":"pull.865.v10.git.1617274020517.gitgitgadget@gmail.com","threadId":"55104","inReplyTo":"pull.865.v9.git.1617205915876.gitgitgadget@gmail.com","subject":"[PATCH v10] builtin/clone.c: add --reject-shallow option","fromName":"Li Linchao via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-04-01T10:46:59Z","receivedAt":"2021-04-01T10:47:42Z","isPatch":true,"sender":{"key":"name:Li Linchao","avatar":null},"body":"From: Li Linchao <lilinchao@oschina.cn>\n\nIn some scenarios, users may want more history than the repository\noffered for cloning, which happens to be a shallow repository, can\ngive them. But because users don't know it is a shallow repository\nuntil they download it to local, we may want to refuse to clone\nthis kind of repository, without creating any unnecessary files.\n\nAlthought there is an option '--depth=x' for users to decide how\ndeep history they can fetch, as the unshallow cloning's depth is\nINFINITY, we might start with depth x, and the source may be deep\nenough to give us x right now, but we may want to deepen more than\nthey can offer, and such a user would want to be able to say\n\"I want depth=x now, but make sure they are not shallow\".\nit's hard to find such a x value to make sure they are not shallow.\n\nTeach '--reject-shallow' option to \"git clone\" to abort as\nsoon as we find out that we are cloning from a shallow\nrepository.\n\nSigned-off-by: Li Linchao <lilinchao@oschina.cn>\n---\n    builtin/clone.c: add --reject-shallow option\n    \n    Changes since v1:\n    \n     * Rename --no-shallow to --reject-shallow\n     * Enable to reject a non-local clone\n     * Enable --[no-]reject-shallow from CLI override configuration.\n     * Add more testcases.\n     * Reword commit messages and relative documentation.\n    \n    Changes since v3:\n    \n     * Add support to reject clone shallow repo over https protocol\n     * Add testcase to reject clone shallow repo over https:// transport\n     * Reword commit messages and relative documentation according\n       suggestions from Junio.\n    \n    Changes since v5:\n    \n     * camelcase config variable\n     * warning client that source repo is shallow\n     * better support ssh:// and git:// protocol v1, v2\n    \n    Changes since v6:\n    \n     * use _() for warning/die statement\n    \n    Changes since v7:\n    \n     * Clear reviewed-by trailer\n     * Drop warning() statement\n     * Reword a few test titles\n    \n    Changes since v8:\n    \n     * Use single variable for option and config\n     * Rename remote_shallow to reject_shallow_remote\n     * Simplify test cases\n    \n    Changes since v9:\n    \n     * Reword commit message\n     * Use option_reject_shallow and config_reject_shallow\n    \n    Signed-off-by: Li Linchao lilinchao@oschina.cn\n\nPublished-As: https://github.com/gitgitgadget/git/releases/tag/pr-865%2FCactusinhand%2Fgit-clone-options-v10\nFetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-865/Cactusinhand/git-clone-options-v10\nPull-Request: https://github.com/gitgitgadget/git/pull/865\n\nRange-diff vs v9:\n\n 1:  11043a2344dd ! 1:  cacda5a6af07 builtin/clone.c: add --reject-shallow option\n     @@\n       ## Metadata ##\n     -Author: lilinchao <lilinchao@oschina.cn>\n     +Author: Li Linchao <lilinchao@oschina.cn>\n      \n       ## Commit message ##\n          builtin/clone.c: add --reject-shallow option\n     @@ Commit message\n          In some scenarios, users may want more history than the repository\n          offered for cloning, which happens to be a shallow repository, can\n          give them. But because users don't know it is a shallow repository\n     -    until they download it to local, users should have the option to\n     -    refuse to clone this kind of repository, and may want to exit the\n     -    process immediately without creating any unnecessary files.\n     +    until they download it to local, we may want to refuse to clone\n     +    this kind of repository, without creating any unnecessary files.\n      \n          Althought there is an option '--depth=x' for users to decide how\n     -    deep history they can fetch, but as the unshallow cloning's depth\n     -    is INFINITY, we can't know exactly the minimun 'x' value that can\n     -    satisfy the minimum integrity, so we can't pass 'x' value to --depth,\n     -    and expect this can obtain a complete history of a repository.\n     +    deep history they can fetch, as the unshallow cloning's depth is\n     +    INFINITY, we might start with depth x, and the source may be deep\n     +    enough to give us x right now, but we may want to deepen more than\n     +    they can offer, and such a user would want to be able to say\n     +    \"I want depth=x now, but make sure they are not shallow\".\n     +    it's hard to find such a x value to make sure they are not shallow.\n      \n     -    In other scenarios, if we have an API that allow us to import external\n     -    repository, and then perform various operations on the repo.\n     -    But if the imported is a shallow one(which is actually possible), it\n     -    will affect the subsequent operations. So we can choose to refuse to\n     -    clone, and let's just import a normal repository.\n     -\n     -    This patch offers a new option '--reject-shallow' that can reject to\n     -    clone a shallow repository.\n     +    Teach '--reject-shallow' option to \"git clone\" to abort as\n     +    soon as we find out that we are cloning from a shallow\n     +    repository.\n      \n          Signed-off-by: Li Linchao <lilinchao@oschina.cn>\n      \n     @@ builtin/clone.c: static int option_no_checkout, option_bare, option_mirror, opti\n       static int option_no_tags;\n       static int option_shallow_submodules;\n      +static int option_reject_shallow = -1;    /* unspecified */\n     ++static int config_reject_shallow = -1;    /* unspecified */\n       static int deepen;\n       static char *option_template, *option_depth, *option_since;\n       static char *option_origin = NULL;\n     @@ builtin/clone.c: static int git_clone_config(const char *k, const char *v, void\n       \t\tfree(remote_name);\n       \t\tremote_name = xstrdup(v);\n       \t}\n     -+\tif (!strcmp(k, \"clone.rejectshallow\") && option_reject_shallow < 0)\n     -+\t\toption_reject_shallow = git_config_bool(k, v);\n     ++\tif (!strcmp(k, \"clone.rejectshallow\"))\n     ++\t\tconfig_reject_shallow = git_config_bool(k, v);\n      +\n       \treturn git_default_config(k, v, cb);\n       }\n       \n     +@@ builtin/clone.c: static int path_exists(const char *path)\n     + int cmd_clone(int argc, const char **argv, const char *prefix)\n     + {\n     + \tint is_bundle = 0, is_local;\n     ++\tint reject_shallow = 0;\n     + \tconst char *repo_name, *repo, *work_tree, *git_dir;\n     + \tchar *path, *dir, *display_repo = NULL;\n     + \tint dest_exists, real_dest_exists = 0;\n     +@@ builtin/clone.c: int cmd_clone(int argc, const char **argv, const char *prefix)\n     + \t */\n     + \tgit_config(git_clone_config, NULL);\n     + \n     ++\t/*\n     ++\t * If option_reject_shallow is specified from CLI option,\n     ++\t * ignore config_reject_shallow from git_clone_config.\n     ++\t */\n     ++\tif (config_reject_shallow != -1)\n     ++\t\treject_shallow = config_reject_shallow;\n     ++\tif (option_reject_shallow != -1)\n     ++\t\treject_shallow = option_reject_shallow;\n     ++\n     + \t/*\n     + \t * apply the remote name provided by --origin only after this second\n     + \t * call to git_config, to ensure it overrides all config-based values.\n      @@ builtin/clone.c: int cmd_clone(int argc, const char **argv, const char *prefix)\n       \t\tif (filter_options.choice)\n       \t\t\twarning(_(\"--filter is ignored in local clones; use file:// instead.\"));\n       \t\tif (!access(mkpath(\"%s/shallow\", path), F_OK)) {\n     -+\t\t\tif (option_reject_shallow > 0)\n     ++\t\t\tif (reject_shallow)\n      +\t\t\t\tdie(_(\"source repository is shallow, reject to clone.\"));\n       \t\t\tif (option_local > 0)\n       \t\t\t\twarning(_(\"source repository is shallow, ignoring --local\"));\n     @@ builtin/clone.c: int cmd_clone(int argc, const char **argv, const char *prefix)\n       \n       \ttransport_set_option(transport, TRANS_OPT_KEEP, \"yes\");\n       \n     -+\tif (option_reject_shallow > 0)\n     ++\tif (reject_shallow)\n      +\t\ttransport_set_option(transport, TRANS_OPT_REJECT_SHALLOW, \"1\");\n       \tif (option_depth)\n       \t\ttransport_set_option(transport, TRANS_OPT_DEPTH,\n\n\n Documentation/config/clone.txt |  4 ++++\n Documentation/git-clone.txt    |  7 ++++++-\n builtin/clone.c                | 21 +++++++++++++++++++++\n fetch-pack.c                   | 12 ++++++++----\n fetch-pack.h                   |  1 +\n t/t5601-clone.sh               |  9 +++++++++\n t/t5606-clone-options.sh       | 27 ++++++++++++++++++++++++++-\n t/t5611-clone-config.sh        | 25 +++++++++++++++++++++++++\n transport.c                    |  4 ++++\n transport.h                    |  4 ++++\n 10 files changed, 108 insertions(+), 6 deletions(-)\n\ndiff --git a/Documentation/config/clone.txt b/Documentation/config/clone.txt\nindex 47de36a5fedf..7bcfbd18a52a 100644\n--- a/Documentation/config/clone.txt\n+++ b/Documentation/config/clone.txt\n@@ -2,3 +2,7 @@ clone.defaultRemoteName::\n \tThe name of the remote to create when cloning a repository.  Defaults to\n \t`origin`, and can be overridden by passing the `--origin` command-line\n \toption to linkgit:git-clone[1].\n+\n+clone.rejectShallow::\n+\tReject to clone a repository if it is a shallow one, can be overridden by\n+\tpassing option `--reject-shallow` in command line. See linkgit:git-clone[1]\ndiff --git a/Documentation/git-clone.txt b/Documentation/git-clone.txt\nindex 02d9c19cec75..3fe3810f1ce1 100644\n--- a/Documentation/git-clone.txt\n+++ b/Documentation/git-clone.txt\n@@ -15,7 +15,7 @@ SYNOPSIS\n \t  [--dissociate] [--separate-git-dir <git dir>]\n \t  [--depth <depth>] [--[no-]single-branch] [--no-tags]\n \t  [--recurse-submodules[=<pathspec>]] [--[no-]shallow-submodules]\n-\t  [--[no-]remote-submodules] [--jobs <n>] [--sparse]\n+\t  [--[no-]remote-submodules] [--jobs <n>] [--sparse] [--[no-]reject-shallow]\n \t  [--filter=<filter>] [--] <repository>\n \t  [<directory>]\n \n@@ -149,6 +149,11 @@ objects from the source repository into a pack in the cloned repository.\n --no-checkout::\n \tNo checkout of HEAD is performed after the clone is complete.\n \n+--[no-]reject-shallow::\n+\tFail if the source repository is a shallow repository.\n+\tThe 'clone.rejectShallow' configuration variable can be used to\n+\tspecify the default.\n+\n --bare::\n \tMake a 'bare' Git repository.  That is, instead of\n \tcreating `<directory>` and placing the administrative\ndiff --git a/builtin/clone.c b/builtin/clone.c\nindex 51e844a2de0a..2a5485b72499 100644\n--- a/builtin/clone.c\n+++ b/builtin/clone.c\n@@ -50,6 +50,8 @@ static int option_no_checkout, option_bare, option_mirror, option_single_branch\n static int option_local = -1, option_no_hardlinks, option_shared;\n static int option_no_tags;\n static int option_shallow_submodules;\n+static int option_reject_shallow = -1;    /* unspecified */\n+static int config_reject_shallow = -1;    /* unspecified */\n static int deepen;\n static char *option_template, *option_depth, *option_since;\n static char *option_origin = NULL;\n@@ -90,6 +92,8 @@ static struct option builtin_clone_options[] = {\n \tOPT__VERBOSITY(&option_verbosity),\n \tOPT_BOOL(0, \"progress\", &option_progress,\n \t\t N_(\"force progress reporting\")),\n+\tOPT_BOOL(0, \"reject-shallow\", &option_reject_shallow,\n+\t\t N_(\"don't clone shallow repository\")),\n \tOPT_BOOL('n', \"no-checkout\", &option_no_checkout,\n \t\t N_(\"don't create a checkout\")),\n \tOPT_BOOL(0, \"bare\", &option_bare, N_(\"create a bare repository\")),\n@@ -858,6 +862,9 @@ static int git_clone_config(const char *k, const char *v, void *cb)\n \t\tfree(remote_name);\n \t\tremote_name = xstrdup(v);\n \t}\n+\tif (!strcmp(k, \"clone.rejectshallow\"))\n+\t\tconfig_reject_shallow = git_config_bool(k, v);\n+\n \treturn git_default_config(k, v, cb);\n }\n \n@@ -963,6 +970,7 @@ static int path_exists(const char *path)\n int cmd_clone(int argc, const char **argv, const char *prefix)\n {\n \tint is_bundle = 0, is_local;\n+\tint reject_shallow = 0;\n \tconst char *repo_name, *repo, *work_tree, *git_dir;\n \tchar *path, *dir, *display_repo = NULL;\n \tint dest_exists, real_dest_exists = 0;\n@@ -1156,6 +1164,15 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n \t */\n \tgit_config(git_clone_config, NULL);\n \n+\t/*\n+\t * If option_reject_shallow is specified from CLI option,\n+\t * ignore config_reject_shallow from git_clone_config.\n+\t */\n+\tif (config_reject_shallow != -1)\n+\t\treject_shallow = config_reject_shallow;\n+\tif (option_reject_shallow != -1)\n+\t\treject_shallow = option_reject_shallow;\n+\n \t/*\n \t * apply the remote name provided by --origin only after this second\n \t * call to git_config, to ensure it overrides all config-based values.\n@@ -1216,6 +1233,8 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n \t\tif (filter_options.choice)\n \t\t\twarning(_(\"--filter is ignored in local clones; use file:// instead.\"));\n \t\tif (!access(mkpath(\"%s/shallow\", path), F_OK)) {\n+\t\t\tif (reject_shallow)\n+\t\t\t\tdie(_(\"source repository is shallow, reject to clone.\"));\n \t\t\tif (option_local > 0)\n \t\t\t\twarning(_(\"source repository is shallow, ignoring --local\"));\n \t\t\tis_local = 0;\n@@ -1227,6 +1246,8 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n \n \ttransport_set_option(transport, TRANS_OPT_KEEP, \"yes\");\n \n+\tif (reject_shallow)\n+\t\ttransport_set_option(transport, TRANS_OPT_REJECT_SHALLOW, \"1\");\n \tif (option_depth)\n \t\ttransport_set_option(transport, TRANS_OPT_DEPTH,\n \t\t\t\t     option_depth);\ndiff --git a/fetch-pack.c b/fetch-pack.c\nindex fb04a76ca263..40392692ad07 100644\n--- a/fetch-pack.c\n+++ b/fetch-pack.c\n@@ -1129,9 +1129,11 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args,\n \tif (args->deepen)\n \t\tsetup_alternate_shallow(&shallow_lock, &alternate_shallow_file,\n \t\t\t\t\tNULL);\n-\telse if (si->nr_ours || si->nr_theirs)\n+\telse if (si->nr_ours || si->nr_theirs) {\n+\t\tif (args->reject_shallow_remote)\n+\t\t\tdie(_(\"source repository is shallow, reject to clone.\"));\n \t\talternate_shallow_file = setup_temporary_shallow(si->shallow);\n-\telse\n+\t} else\n \t\talternate_shallow_file = NULL;\n \tif (get_pack(args, fd, pack_lockfiles, NULL, sought, nr_sought,\n \t\t     &gitmodules_oids))\n@@ -1498,10 +1500,12 @@ static void receive_shallow_info(struct fetch_pack_args *args,\n \t\t * rejected (unless --update-shallow is set); do the same.\n \t\t */\n \t\tprepare_shallow_info(si, shallows);\n-\t\tif (si->nr_ours || si->nr_theirs)\n+\t\tif (si->nr_ours || si->nr_theirs) {\n+\t\t\tif (args->reject_shallow_remote)\n+\t\t\t\tdie(_(\"source repository is shallow, reject to clone.\"));\n \t\t\talternate_shallow_file =\n \t\t\t\tsetup_temporary_shallow(si->shallow);\n-\t\telse\n+\t\t} else\n \t\t\talternate_shallow_file = NULL;\n \t} else {\n \t\talternate_shallow_file = NULL;\ndiff --git a/fetch-pack.h b/fetch-pack.h\nindex 736a3dae467a..f114d7277567 100644\n--- a/fetch-pack.h\n+++ b/fetch-pack.h\n@@ -39,6 +39,7 @@ struct fetch_pack_args {\n \tunsigned self_contained_and_connected:1;\n \tunsigned cloning:1;\n \tunsigned update_shallow:1;\n+\tunsigned reject_shallow_remote:1;\n \tunsigned deepen:1;\n \n \t/*\ndiff --git a/t/t5601-clone.sh b/t/t5601-clone.sh\nindex e7e6c089554c..329ae599fd3c 100755\n--- a/t/t5601-clone.sh\n+++ b/t/t5601-clone.sh\n@@ -759,6 +759,15 @@ test_expect_success 'partial clone using HTTP' '\n \tpartial_clone \"$HTTPD_DOCUMENT_ROOT_PATH/server\" \"$HTTPD_URL/smart/server\"\n '\n \n+test_expect_success 'reject cloning shallow repository using HTTP' '\n+\ttest_when_finished \"rm -rf repo\" &&\n+\tgit clone --bare --no-local --depth=1 src \"$HTTPD_DOCUMENT_ROOT_PATH/repo.git\" &&\n+\ttest_must_fail git clone --reject-shallow $HTTPD_URL/smart/repo.git repo 2>err &&\n+\ttest_i18ngrep -e \"source repository is shallow, reject to clone.\" err &&\n+\n+\tgit clone --no-reject-shallow $HTTPD_URL/smart/repo.git repo\n+'\n+\n # DO NOT add non-httpd-specific tests here, because the last part of this\n # test script is only executed when httpd is available and enabled.\n \ndiff --git a/t/t5606-clone-options.sh b/t/t5606-clone-options.sh\nindex 428b0aac93fa..3a595c0f82c7 100755\n--- a/t/t5606-clone-options.sh\n+++ b/t/t5606-clone-options.sh\n@@ -11,7 +11,8 @@ test_expect_success 'setup' '\n \tmkdir parent &&\n \t(cd parent && git init &&\n \t echo one >file && git add file &&\n-\t git commit -m one)\n+\t git commit -m one) &&\n+\tgit clone --depth=1 --no-local parent shallow-repo\n \n '\n \n@@ -45,6 +46,30 @@ test_expect_success 'disallows --bare with --separate-git-dir' '\n \n '\n \n+test_expect_success 'reject cloning shallow repository' '\n+\ttest_when_finished \"rm -rf repo\" &&\n+\ttest_must_fail git clone --reject-shallow shallow-repo out 2>err &&\n+\ttest_i18ngrep -e \"source repository is shallow, reject to clone.\" err &&\n+\n+\tgit clone --no-reject-shallow shallow-repo repo\n+'\n+\n+test_expect_success 'reject cloning non-local shallow repository' '\n+\ttest_when_finished \"rm -rf repo\" &&\n+\ttest_must_fail git clone --reject-shallow --no-local shallow-repo out 2>err &&\n+\ttest_i18ngrep -e \"source repository is shallow, reject to clone.\" err &&\n+\n+\tgit clone --no-reject-shallow --no-local shallow-repo repo\n+'\n+\n+test_expect_success 'succeed cloning normal repository' '\n+\ttest_when_finished \"rm -rf chilad1 child2 child3 child4 \" &&\n+\tgit clone --reject-shallow parent child1 &&\n+\tgit clone --reject-shallow --no-local parent child2 &&\n+\tgit clone --no-reject-shallow parent child3 &&\n+\tgit clone --no-reject-shallow --no-local parent child4\n+'\n+\n test_expect_success 'uses \"origin\" for default remote name' '\n \n \tgit clone parent clone-default-origin &&\ndiff --git a/t/t5611-clone-config.sh b/t/t5611-clone-config.sh\nindex 9f555b87ecdf..f8625f915821 100755\n--- a/t/t5611-clone-config.sh\n+++ b/t/t5611-clone-config.sh\n@@ -95,6 +95,31 @@ test_expect_success 'clone -c remote.<remote>.fetch=<refspec> --origin=<name>' '\n \ttest_cmp expect actual\n '\n \n+test_expect_success 'set up shallow repository' '\n+\tgit clone --depth=1 --no-local . shallow-repo\n+'\n+\n+test_expect_success 'clone.rejectshallow=true should reject cloning shallow repo' '\n+\ttest_when_finished \"rm -rf out\" &&\n+\ttest_must_fail git -c clone.rejectshallow=true clone --no-local shallow-repo out 2>err &&\n+\ttest_i18ngrep -e \"source repository is shallow, reject to clone.\" err &&\n+\n+\tgit -c clone.rejectshallow=false clone --no-local shallow-repo out\n+'\n+\n+test_expect_success 'option --[no-]reject-shallow override clone.rejectshallow config' '\n+\ttest_when_finished \"rm -rf out\" &&\n+\ttest_must_fail git -c clone.rejectshallow=false clone --reject-shallow --no-local shallow-repo out 2>err &&\n+\ttest_i18ngrep -e \"source repository is shallow, reject to clone.\" err &&\n+\n+\tgit -c clone.rejectshallow=true clone --no-reject-shallow --no-local shallow-repo out\n+'\n+\n+test_expect_success 'clone.rejectshallow=true should succeed cloning normal repo' '\n+\ttest_when_finished \"rm -rf out\" &&\n+\tgit -c clone.rejectshallow=true clone --no-local . out\n+'\n+\n test_expect_success MINGW 'clone -c core.hideDotFiles' '\n \ttest_commit attributes .gitattributes \"\" &&\n \trm -rf child &&\ndiff --git a/transport.c b/transport.c\nindex 1c4ab676d1b1..b231894f9039 100644\n--- a/transport.c\n+++ b/transport.c\n@@ -236,6 +236,9 @@ static int set_git_option(struct git_transport_options *opts,\n \t\tlist_objects_filter_die_if_populated(&opts->filter_options);\n \t\tparse_list_objects_filter(&opts->filter_options, value);\n \t\treturn 0;\n+\t} else if (!strcmp(name, TRANS_OPT_REJECT_SHALLOW)) {\n+\t\topts->reject_shallow = !!value;\n+\t\treturn 0;\n \t}\n \treturn 1;\n }\n@@ -370,6 +373,7 @@ static int fetch_refs_via_pack(struct transport *transport,\n \targs.stateless_rpc = transport->stateless_rpc;\n \targs.server_options = transport->server_options;\n \targs.negotiation_tips = data->options.negotiation_tips;\n+\targs.reject_shallow_remote = transport->smart_options->reject_shallow;\n \n \tif (!data->got_remote_heads) {\n \t\tint i;\ndiff --git a/transport.h b/transport.h\nindex 24e15799e714..4d5db0a7f22b 100644\n--- a/transport.h\n+++ b/transport.h\n@@ -14,6 +14,7 @@ struct git_transport_options {\n \tunsigned check_self_contained_and_connected : 1;\n \tunsigned self_contained_and_connected : 1;\n \tunsigned update_shallow : 1;\n+\tunsigned reject_shallow : 1;\n \tunsigned deepen_relative : 1;\n \n \t/* see documentation of corresponding flag in fetch-pack.h */\n@@ -194,6 +195,9 @@ void transport_check_allowed(const char *type);\n /* Aggressively fetch annotated tags if possible */\n #define TRANS_OPT_FOLLOWTAGS \"followtags\"\n \n+/* Reject shallow repo transport */\n+#define TRANS_OPT_REJECT_SHALLOW \"rejectshallow\"\n+\n /* Accept refs that may update .git/shallow without --depth */\n #define TRANS_OPT_UPDATE_SHALLOW \"updateshallow\"\n \n\nbase-commit: a65ce7f831aa5fcc596c6d23fcde543d98b39bd7\n-- \ngitgitgadget\n"}]}