Do not stop at ancestors of our refs when deepening during fetching. This is because when performing such a fetch, shallow entries may have been updated; the client can reasonably assume that the existing refs are connected up to the old shallow points, but not the new. This was noticed when a user tried to deepen an existing repository by fetching with --no-shallow from a server that did not send all necessary objects - the connectivity check as run by "git fetch" succeeded, but a subsequent "git fsck" failed. Signed-off-by: Jonathan Tan <jonathantanmy@xxxxxxxxxx> --- builtin/fetch.c | 5 ++++- connected.c | 6 ++++-- connected.h | 7 +++++++ t/t5537-fetch-shallow.sh | 23 +++++++++++++++++++++++ 4 files changed, 38 insertions(+), 3 deletions(-) diff --git a/builtin/fetch.c b/builtin/fetch.c index ea5b9669ad..2cfd13342f 100644 --- a/builtin/fetch.c +++ b/builtin/fetch.c @@ -780,6 +780,7 @@ static int store_updated_refs(const char *raw_url, const char *remote_name, const char *filename = dry_run ? "/dev/null" : git_path_fetch_head(); int want_status; int summary_width = transport_summary_width(ref_map); + struct check_connected_options opt = CHECK_CONNECTED_INIT; fp = fopen(filename, "a"); if (!fp) @@ -791,7 +792,9 @@ static int store_updated_refs(const char *raw_url, const char *remote_name, url = xstrdup("foreign"); rm = ref_map; - if (check_connected(iterate_ref_map, &rm, NULL)) { + if (deepen) + opt.is_deepening_fetch = 1; + if (check_connected(iterate_ref_map, &rm, &opt)) { rc = error(_("%s did not send all necessary objects\n"), url); goto abort; } diff --git a/connected.c b/connected.c index 91feb78815..1bba888eff 100644 --- a/connected.c +++ b/connected.c @@ -58,8 +58,10 @@ int check_connected(oid_iterate_fn fn, void *cb_data, argv_array_push(&rev_list.args, "--stdin"); if (repository_format_partial_clone) argv_array_push(&rev_list.args, "--exclude-promisor-objects"); - argv_array_push(&rev_list.args, "--not"); - argv_array_push(&rev_list.args, "--all"); + if (!opt->is_deepening_fetch) { + argv_array_push(&rev_list.args, "--not"); + argv_array_push(&rev_list.args, "--all"); + } argv_array_push(&rev_list.args, "--quiet"); if (opt->progress) argv_array_pushf(&rev_list.args, "--progress=%s", diff --git a/connected.h b/connected.h index a53f03a61a..322dc76372 100644 --- a/connected.h +++ b/connected.h @@ -38,6 +38,13 @@ struct check_connected_options { * Insert these variables into the environment of the child process. */ const char **env; + + /* + * If non-zero, check the ancestry chain completely, not stopping at + * any existing ref. This is necessary when deepening existing refs + * during a fetch. + */ + unsigned is_deepening_fetch : 1; }; #define CHECK_CONNECTED_INIT { 0 } diff --git a/t/t5537-fetch-shallow.sh b/t/t5537-fetch-shallow.sh index df8d2f095a..ac4beac96b 100755 --- a/t/t5537-fetch-shallow.sh +++ b/t/t5537-fetch-shallow.sh @@ -186,4 +186,27 @@ EOF test_cmp expect actual ' +test_expect_success 'shallow fetches check connectivity without stopping at existing refs' ' + cp -R .git server.git && + + # Normally, the connectivity check stops at ancestors of existing refs. + git init client && + GIT_TRACE="$(pwd)/trace" git -C client fetch "$(pwd)/server.git" && + grep "run_command: git rev-list" trace >rev-list-command && + grep -e "--not --all" rev-list-command && + + # But it does not for a shallow fetch... + rm -rf client trace && + git init client && + GIT_TRACE="$(pwd)/trace" git -C client fetch --depth=1 "$(pwd)/server.git" && + grep "run_command: git rev-list" trace >rev-list-command && + ! grep -e "--not --all" rev-list-command && + + # ...and when deepening. + rm trace && + GIT_TRACE="$(pwd)/trace" git -C client fetch --unshallow "$(pwd)/server.git" && + grep "run_command: git rev-list" trace >rev-list-command && + ! grep -e "--not --all" rev-list-command +' + test_done -- 2.18.0.rc2.346.g013aa6912e-goog