From patchwork Thu Feb 6 06:33:29 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Toon Claes X-Patchwork-Id: 13962286 Received: from out-182.mta0.migadu.com (out-182.mta0.migadu.com [91.218.175.182]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 46067148FF5 for ; Thu, 6 Feb 2025 06:33:51 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=91.218.175.182 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1738823636; cv=none; b=kCbVr4+WYDWOS5czyGIzbFbeqBfpXiDw3wkhu8wVQm5WLMqYF7LKOWPqPsFjUygC6cDLsr6wjIxaqNkyXl7LQUL+Fsja+4iXWp9FZk6+vcG88jJeSUqlEeHYWWeKYm2bfiToLUSvo0Lf2l3yd4ryaBh62VR9MrmKGFpD2d6fpfk= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1738823636; c=relaxed/simple; bh=tUnRtj8/LDYFMykWUhbQGewNnZEcJHZ1ERsxlpMkCUQ=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=CwLbZSzOCfhpHKky9r6PezGYanXFR+pkYArsAwhrA9QFchCCGVxWeeDP7Bl3uVQdVUpK5xUt2rUtboB/XXyFj14nYR/3Z6HLOPszRikE3UEUPM2Q/N4Cc37LV3/k9jWOyjtq6Kmq7aLNC0YxA1NxShhmVjHRG+9stymghtl7IbE= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=iotcl.com; spf=fail smtp.mailfrom=iotcl.com; dkim=pass (1024-bit key) header.d=iotcl.com header.i=@iotcl.com header.b=rNUp5sIF; arc=none smtp.client-ip=91.218.175.182 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=iotcl.com Authentication-Results: smtp.subspace.kernel.org; spf=fail smtp.mailfrom=iotcl.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=iotcl.com header.i=@iotcl.com header.b="rNUp5sIF" X-Report-Abuse: Please report any abuse attempt to abuse@migadu.com and include these headers. DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=iotcl.com; s=key1; t=1738823629; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=uAcsC8dqdAPyNFqywfsdUkFSTvPozPyX/NhH/fdUWeM=; b=rNUp5sIFNhUS9MfsYZg3PK6QzBysfZhn6MjDcga8eCBWj6kDAzTWsr3oCrgi0vhIeRm6vK 7m9JOr/stVRWwbq1/FLKulkncCmgJhxYkIQEDd4SlJN6E/R53Lg6OU3/Xs3KrpTpGnBJO4 hD9bGSWPgeUNZqbON0QOD4OMXR9nTDg= From: Toon Claes Date: Thu, 06 Feb 2025 07:33:29 +0100 Subject: [PATCH v7 1/7] clone: cut down on global variables in clone.c Precedence: bulk X-Mailing-List: git@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Message-Id: <20250206-toon-clone-refs-v7-1-4622b7392202@iotcl.com> References: <20250206-toon-clone-refs-v7-0-4622b7392202@iotcl.com> In-Reply-To: <20250206-toon-clone-refs-v7-0-4622b7392202@iotcl.com> To: git@vger.kernel.org Cc: Kristoffer Haugsbakk , =?utf-8?q?Micha?= =?utf-8?q?l_Such=C3=A1nek?= , Patrick Steinhardt , Jeff King , Junio C Hamano , Toon Claes X-Migadu-Flow: FLOW_OUT In clone.c the `struct option` which is used to parse the input options for git-clone(1) is a global variable. Due to this, many variables that are used to parse the value into, are also global. Make `builtin_clone_options` a local variable in cmd_clone() and carry along all variables that are only used in that function. Signed-off-by: Toon Claes --- builtin/clone.c | 195 +++++++++++++++++++++++++++++--------------------------- 1 file changed, 101 insertions(+), 94 deletions(-) diff --git a/builtin/clone.c b/builtin/clone.c index fd001d800c635e46bbc7027a8fdda2a8c9fbf069..5ed0802f1d0ddebaf512aac93bf8c8b340494323 100644 --- a/builtin/clone.c +++ b/builtin/clone.c @@ -56,42 +56,22 @@ * - dropping use-separate-remote and no-separate-remote compatibility * */ -static const char * const builtin_clone_usage[] = { - N_("git clone [] [--] []"), - NULL -}; static int option_no_checkout, option_bare, option_mirror, option_single_branch = -1; static int option_local = -1, option_no_hardlinks, option_shared; static int option_no_tags; static int option_shallow_submodules; -static int option_reject_shallow = -1; /* unspecified */ static int config_reject_shallow = -1; /* unspecified */ -static int deepen; -static char *option_template, *option_depth, *option_since; -static char *option_origin = NULL; static char *remote_name = NULL; static char *option_branch = NULL; -static struct string_list option_not = STRING_LIST_INIT_NODUP; -static const char *real_git_dir; -static const char *ref_format; -static const char *option_upload_pack = "git-upload-pack"; static int option_verbosity; -static int option_progress = -1; -static int option_sparse_checkout; -static enum transport_family family; -static struct string_list option_config = STRING_LIST_INIT_NODUP; static struct string_list option_required_reference = STRING_LIST_INIT_NODUP; static struct string_list option_optional_reference = STRING_LIST_INIT_NODUP; -static int option_dissociate; static int max_jobs = -1; static struct string_list option_recurse_submodules = STRING_LIST_INIT_NODUP; static struct list_objects_filter_options filter_options = LIST_OBJECTS_FILTER_INIT; -static int option_filter_submodules = -1; /* unspecified */ static int config_filter_submodules = -1; /* unspecified */ -static struct string_list server_options = STRING_LIST_INIT_NODUP; static int option_remote_submodules; -static const char *bundle_uri; static int recurse_submodules_cb(const struct option *opt, const char *arg, int unset) @@ -107,78 +87,6 @@ static int recurse_submodules_cb(const struct option *opt, return 0; } -static struct option builtin_clone_options[] = { - OPT__VERBOSITY(&option_verbosity), - OPT_BOOL(0, "progress", &option_progress, - N_("force progress reporting")), - OPT_BOOL(0, "reject-shallow", &option_reject_shallow, - N_("don't clone shallow repository")), - OPT_BOOL('n', "no-checkout", &option_no_checkout, - N_("don't create a checkout")), - OPT_BOOL(0, "bare", &option_bare, N_("create a bare repository")), - OPT_HIDDEN_BOOL(0, "naked", &option_bare, - N_("create a bare repository")), - OPT_BOOL(0, "mirror", &option_mirror, - N_("create a mirror repository (implies --bare)")), - OPT_BOOL('l', "local", &option_local, - N_("to clone from a local repository")), - OPT_BOOL(0, "no-hardlinks", &option_no_hardlinks, - N_("don't use local hardlinks, always copy")), - OPT_BOOL('s', "shared", &option_shared, - N_("setup as shared repository")), - { OPTION_CALLBACK, 0, "recurse-submodules", &option_recurse_submodules, - N_("pathspec"), N_("initialize submodules in the clone"), - PARSE_OPT_OPTARG, recurse_submodules_cb, (intptr_t)"." }, - OPT_ALIAS(0, "recursive", "recurse-submodules"), - OPT_INTEGER('j', "jobs", &max_jobs, - N_("number of submodules cloned in parallel")), - OPT_STRING(0, "template", &option_template, N_("template-directory"), - N_("directory from which templates will be used")), - OPT_STRING_LIST(0, "reference", &option_required_reference, N_("repo"), - N_("reference repository")), - OPT_STRING_LIST(0, "reference-if-able", &option_optional_reference, - N_("repo"), N_("reference repository")), - OPT_BOOL(0, "dissociate", &option_dissociate, - N_("use --reference only while cloning")), - OPT_STRING('o', "origin", &option_origin, N_("name"), - N_("use instead of 'origin' to track upstream")), - OPT_STRING('b', "branch", &option_branch, N_("branch"), - N_("checkout instead of the remote's HEAD")), - OPT_STRING('u', "upload-pack", &option_upload_pack, N_("path"), - N_("path to git-upload-pack on the remote")), - OPT_STRING(0, "depth", &option_depth, N_("depth"), - N_("create a shallow clone of that depth")), - OPT_STRING(0, "shallow-since", &option_since, N_("time"), - N_("create a shallow clone since a specific time")), - OPT_STRING_LIST(0, "shallow-exclude", &option_not, N_("ref"), - N_("deepen history of shallow clone, excluding ref")), - OPT_BOOL(0, "single-branch", &option_single_branch, - N_("clone only one branch, HEAD or --branch")), - OPT_BOOL(0, "no-tags", &option_no_tags, - N_("don't clone any tags, and make later fetches not to follow them")), - OPT_BOOL(0, "shallow-submodules", &option_shallow_submodules, - N_("any cloned submodules will be shallow")), - OPT_STRING(0, "separate-git-dir", &real_git_dir, N_("gitdir"), - N_("separate git dir from working tree")), - OPT_STRING(0, "ref-format", &ref_format, N_("format"), - N_("specify the reference format to use")), - OPT_STRING_LIST('c', "config", &option_config, N_("key=value"), - N_("set config inside the new repository")), - OPT_STRING_LIST(0, "server-option", &server_options, - N_("server-specific"), N_("option to transmit")), - OPT_IPVERSION(&family), - OPT_PARSE_LIST_OBJECTS_FILTER(&filter_options), - OPT_BOOL(0, "also-filter-submodules", &option_filter_submodules, - N_("apply partial clone filters to submodules")), - OPT_BOOL(0, "remote-submodules", &option_remote_submodules, - N_("any cloned submodules will use their remote-tracking branch")), - OPT_BOOL(0, "sparse", &option_sparse_checkout, - N_("initialize sparse-checkout file to include only files at root")), - OPT_STRING(0, "bundle-uri", &bundle_uri, - N_("uri"), N_("a URI for downloading bundles before fetching from origin remote")), - OPT_END() -}; - static const char *get_repo_path_1(struct strbuf *path, int *is_bundle) { static const char *suffix[] = { "/.git", "", ".git/.git", ".git" }; @@ -989,10 +897,103 @@ int cmd_clone(int argc, int hash_algo; enum ref_storage_format ref_storage_format = REF_STORAGE_FORMAT_UNKNOWN; const int do_not_override_repo_unix_permissions = -1; + int option_reject_shallow = -1; /* unspecified */ + int deepen = 0; + char *option_template = NULL, *option_depth = NULL, *option_since = NULL; + char *option_origin = NULL; + struct string_list option_not = STRING_LIST_INIT_NODUP; + const char *real_git_dir = NULL; + const char *ref_format = NULL; + const char *option_upload_pack = "git-upload-pack"; + int option_progress = -1; + int option_sparse_checkout = 0; + enum transport_family family = TRANSPORT_FAMILY_ALL; + struct string_list option_config = STRING_LIST_INIT_DUP; + int option_dissociate = 0; + int option_filter_submodules = -1; /* unspecified */ + struct string_list server_options = STRING_LIST_INIT_NODUP; + const char *bundle_uri = NULL; struct transport_ls_refs_options transport_ls_refs_options = TRANSPORT_LS_REFS_OPTIONS_INIT; + struct option builtin_clone_options[] = { + OPT__VERBOSITY(&option_verbosity), + OPT_BOOL(0, "progress", &option_progress, + N_("force progress reporting")), + OPT_BOOL(0, "reject-shallow", &option_reject_shallow, + N_("don't clone shallow repository")), + OPT_BOOL('n', "no-checkout", &option_no_checkout, + N_("don't create a checkout")), + OPT_BOOL(0, "bare", &option_bare, N_("create a bare repository")), + OPT_HIDDEN_BOOL(0, "naked", &option_bare, + N_("create a bare repository")), + OPT_BOOL(0, "mirror", &option_mirror, + N_("create a mirror repository (implies --bare)")), + OPT_BOOL('l', "local", &option_local, + N_("to clone from a local repository")), + OPT_BOOL(0, "no-hardlinks", &option_no_hardlinks, + N_("don't use local hardlinks, always copy")), + OPT_BOOL('s', "shared", &option_shared, + N_("setup as shared repository")), + { OPTION_CALLBACK, 0, "recurse-submodules", &option_recurse_submodules, + N_("pathspec"), N_("initialize submodules in the clone"), + PARSE_OPT_OPTARG, recurse_submodules_cb, (intptr_t)"." }, + OPT_ALIAS(0, "recursive", "recurse-submodules"), + OPT_INTEGER('j', "jobs", &max_jobs, + N_("number of submodules cloned in parallel")), + OPT_STRING(0, "template", &option_template, N_("template-directory"), + N_("directory from which templates will be used")), + OPT_STRING_LIST(0, "reference", &option_required_reference, N_("repo"), + N_("reference repository")), + OPT_STRING_LIST(0, "reference-if-able", &option_optional_reference, + N_("repo"), N_("reference repository")), + OPT_BOOL(0, "dissociate", &option_dissociate, + N_("use --reference only while cloning")), + OPT_STRING('o', "origin", &option_origin, N_("name"), + N_("use instead of 'origin' to track upstream")), + OPT_STRING('b', "branch", &option_branch, N_("branch"), + N_("checkout instead of the remote's HEAD")), + OPT_STRING('u', "upload-pack", &option_upload_pack, N_("path"), + N_("path to git-upload-pack on the remote")), + OPT_STRING(0, "depth", &option_depth, N_("depth"), + N_("create a shallow clone of that depth")), + OPT_STRING(0, "shallow-since", &option_since, N_("time"), + N_("create a shallow clone since a specific time")), + OPT_STRING_LIST(0, "shallow-exclude", &option_not, N_("ref"), + N_("deepen history of shallow clone, excluding ref")), + OPT_BOOL(0, "single-branch", &option_single_branch, + N_("clone only one branch, HEAD or --branch")), + OPT_BOOL(0, "no-tags", &option_no_tags, + N_("don't clone any tags, and make later fetches not to follow them")), + OPT_BOOL(0, "shallow-submodules", &option_shallow_submodules, + N_("any cloned submodules will be shallow")), + OPT_STRING(0, "separate-git-dir", &real_git_dir, N_("gitdir"), + N_("separate git dir from working tree")), + OPT_STRING(0, "ref-format", &ref_format, N_("format"), + N_("specify the reference format to use")), + OPT_STRING_LIST('c', "config", &option_config, N_("key=value"), + N_("set config inside the new repository")), + OPT_STRING_LIST(0, "server-option", &server_options, + N_("server-specific"), N_("option to transmit")), + OPT_IPVERSION(&family), + OPT_PARSE_LIST_OBJECTS_FILTER(&filter_options), + OPT_BOOL(0, "also-filter-submodules", &option_filter_submodules, + N_("apply partial clone filters to submodules")), + OPT_BOOL(0, "remote-submodules", &option_remote_submodules, + N_("any cloned submodules will use their remote-tracking branch")), + OPT_BOOL(0, "sparse", &option_sparse_checkout, + N_("initialize sparse-checkout file to include only files at root")), + OPT_STRING(0, "bundle-uri", &bundle_uri, + N_("uri"), N_("a URI for downloading bundles before fetching from origin remote")), + OPT_END() + }; + + const char * const builtin_clone_usage[] = { + N_("git clone [] [--] []"), + NULL + }; + packet_trace_identity("clone"); git_config(git_clone_config, NULL); @@ -1138,8 +1139,8 @@ int cmd_clone(int argc, for_each_string_list_item(item, &option_recurse_submodules) { strbuf_addf(&sb, "submodule.active=%s", item->string); - string_list_append(&option_config, - strbuf_detach(&sb, NULL)); + string_list_append(&option_config, sb.buf); + strbuf_reset(&sb); } if (!git_config_get_bool("submodule.stickyRecursiveClone", &val) && @@ -1161,6 +1162,8 @@ int cmd_clone(int argc, string_list_append(&option_config, "submodule.alternateErrorStrategy=info"); } + + strbuf_release(&sb); } /* @@ -1578,6 +1581,10 @@ int cmd_clone(int argc, err = checkout(submodule_progress, filter_submodules, ref_storage_format); + string_list_clear(&option_not, 0); + string_list_clear(&option_config, 0); + string_list_clear(&server_options, 0); + free(remote_name); strbuf_release(&reflog_msg); strbuf_release(&branch_top); From patchwork Thu Feb 6 06:33:30 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Toon Claes X-Patchwork-Id: 13962287 Received: from out-185.mta1.migadu.com (out-185.mta1.migadu.com [95.215.58.185]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 297EF224AEA for ; Thu, 6 Feb 2025 06:33:56 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=95.215.58.185 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1738823639; cv=none; b=uBypxjUzMNip6I6ef0wkcxkSnDj58qM1tQxEj4DXQ/gcKVUIpR3+BkirkHkv9mcCHGzmMiBS7R3dwOmlGGDRzq9RJ7GGV723ZFz0cwu7zZNERjbNYCjtgAj6uu11/o1UG0JFhC7EZErVB3Q7uqzUzamBlaFjj3ca5jzTYiqaJwU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1738823639; c=relaxed/simple; bh=DB7Dh1AsEzH2VIao0gHpEm2C/pRrg3sNB+XuScCa4u8=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=A2xSuBDBJ2WVLRVTIQyYnqFzD+mJaJ1MCUl6oKqzmO9ALcwE48N7ES9EoiKDP/5Ti/rYYiEfBhGjpvjcOP4lULp8nHM6We3kQpdfxSgAiT2VNVgIMpCnHwpHxnnZPrws5WBOtWm7Q3MGh1F9UnItRZwyoT7pozQ/TLFEpTbbW0I= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=iotcl.com; spf=fail smtp.mailfrom=iotcl.com; dkim=pass (1024-bit key) header.d=iotcl.com header.i=@iotcl.com header.b=J+RrBMwF; arc=none smtp.client-ip=95.215.58.185 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=iotcl.com Authentication-Results: smtp.subspace.kernel.org; spf=fail smtp.mailfrom=iotcl.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=iotcl.com header.i=@iotcl.com header.b="J+RrBMwF" X-Report-Abuse: Please report any abuse attempt to abuse@migadu.com and include these headers. DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=iotcl.com; s=key1; t=1738823635; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=s/8I1F1/B70GdkroRtnDo8OXE6LKQ/lQvrZDOwPt7tc=; b=J+RrBMwFMgS+Pz9U1C8ZjzrcVQ6WETMdyzLJhFm95VK0vQSbqX9ziPKWEkrxE1YqvsXmCi Rffmm7FfBTDEOceh3Hteb4BKgi+j9Y2Ads3Wc3pYmM/RPCHo9AtX+AtQip32OL3LoKJ3EZ 4m/ga1kj7JwGRVzkyXIB0YRmc3Q2UmM= From: Toon Claes Date: Thu, 06 Feb 2025 07:33:30 +0100 Subject: [PATCH v7 2/7] clone: make it possible to specify --tags Precedence: bulk X-Mailing-List: git@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Message-Id: <20250206-toon-clone-refs-v7-2-4622b7392202@iotcl.com> References: <20250206-toon-clone-refs-v7-0-4622b7392202@iotcl.com> In-Reply-To: <20250206-toon-clone-refs-v7-0-4622b7392202@iotcl.com> To: git@vger.kernel.org Cc: Kristoffer Haugsbakk , =?utf-8?q?Micha?= =?utf-8?q?l_Such=C3=A1nek?= , Patrick Steinhardt , Jeff King , Junio C Hamano , Toon Claes X-Migadu-Flow: FLOW_OUT Option --no-tags was added in 0dab2468ee (clone: add a --no-tags option to clone without tags, 2017-04-26). At the time there was no need to support --tags as well, although there was some conversation about it[1]. To simplify the code and to prepare for future commits, invert the flag internally. Functionally there is no change, because the flag is default-enabled passing `--tags` has no effect, so there's no need to add tests for this. [1]: https://lore.kernel.org/git/CAGZ79kbHuMpiavJ90kQLEL_AR0BEyArcZoEWAjPPhOFacN16YQ@mail.gmail.com/ Signed-off-by: Toon Claes --- Documentation/git-clone.txt | 17 ++++++++++------- builtin/clone.c | 14 +++++++------- 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/Documentation/git-clone.txt b/Documentation/git-clone.txt index de8d8f58930ecff305f79480b13ddce10cd96c60..8d0476f6dcaf6fed7ccd48a20398556dd4e20722 100644 --- a/Documentation/git-clone.txt +++ b/Documentation/git-clone.txt @@ -13,7 +13,7 @@ git clone [--template=] [-l] [-s] [--no-hardlinks] [-q] [-n] [--bare] [--mirror] [-o ] [-b ] [-u ] [--reference ] [--dissociate] [--separate-git-dir ] - [--depth ] [--[no-]single-branch] [--no-tags] + [--depth ] [--[no-]single-branch] [--[no-]tags] [--recurse-submodules[=]] [--[no-]shallow-submodules] [--[no-]remote-submodules] [--jobs ] [--sparse] [--[no-]reject-shallow] [--filter=] [--also-filter-submodules]] [--] @@ -273,12 +273,15 @@ corresponding `--mirror` and `--no-tags` options instead. branch when `--single-branch` clone was made, no remote-tracking branch is created. -`--no-tags`:: - Don't clone any tags, and set - `remote..tagOpt=--no-tags` in the config, ensuring - that future `git pull` and `git fetch` operations won't follow - any tags. Subsequent explicit tag fetches will still work, - (see linkgit:git-fetch[1]). +`--[no-]tags`:: + Control whether or not tags will be cloned. When `--no-tags` is + given, the option will be become permanent by setting the + `remote..tagOpt=--no-tags` configuration. This ensures that + future `git pull` and `git fetch` won't follow any tags. Subsequent + explicit tag fetches will still work (see linkgit:git-fetch[1]). + + By default, tags are cloned and passing `--tags` is thus typically a + no-op, unless it cancels out a previous `--no-tags`. + Can be used in conjunction with `--single-branch` to clone and maintain a branch with no references other than a single cloned diff --git a/builtin/clone.c b/builtin/clone.c index 5ed0802f1d0ddebaf512aac93bf8c8b340494323..69d1ad029dfa84a2f7136fa4a3c4c8a594b179c4 100644 --- a/builtin/clone.c +++ b/builtin/clone.c @@ -59,7 +59,7 @@ static int option_no_checkout, option_bare, option_mirror, option_single_branch = -1; static int option_local = -1, option_no_hardlinks, option_shared; -static int option_no_tags; +static int option_tags = 1; /* default enabled */ static int option_shallow_submodules; static int config_reject_shallow = -1; /* unspecified */ static char *remote_name = NULL; @@ -470,7 +470,7 @@ static struct ref *wanted_peer_refs(const struct ref *refs, get_fetch_map(refs, &refspec->items[i], &tail, 0); } - if (!option_mirror && !option_single_branch && !option_no_tags) + if (!option_mirror && !option_single_branch && option_tags) get_fetch_map(refs, &tag_refspec, &tail, 0); refspec_item_clear(&tag_refspec); @@ -562,7 +562,7 @@ static void update_remote_refs(const struct ref *refs, if (refs) { write_remote_refs(mapped_refs); - if (option_single_branch && !option_no_tags) + if (option_single_branch && option_tags) write_followtags(refs, msg); } @@ -964,8 +964,8 @@ int cmd_clone(int argc, N_("deepen history of shallow clone, excluding ref")), OPT_BOOL(0, "single-branch", &option_single_branch, N_("clone only one branch, HEAD or --branch")), - OPT_BOOL(0, "no-tags", &option_no_tags, - N_("don't clone any tags, and make later fetches not to follow them")), + OPT_BOOL(0, "tags", &option_tags, + N_("clone tags, and make later fetches not to follow them")), OPT_BOOL(0, "shallow-submodules", &option_shallow_submodules, N_("any cloned submodules will be shallow")), OPT_STRING(0, "separate-git-dir", &real_git_dir, N_("gitdir"), @@ -1296,7 +1296,7 @@ int cmd_clone(int argc, git_config_set(key.buf, repo); strbuf_reset(&key); - if (option_no_tags) { + if (!option_tags) { strbuf_addf(&key, "remote.%s.tagOpt", remote_name); git_config_set(key.buf, "--no-tags"); strbuf_reset(&key); @@ -1389,7 +1389,7 @@ int cmd_clone(int argc, if (option_branch) expand_ref_prefix(&transport_ls_refs_options.ref_prefixes, option_branch); - if (!option_no_tags) + if (option_tags) strvec_push(&transport_ls_refs_options.ref_prefixes, "refs/tags/"); From patchwork Thu Feb 6 06:33:31 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Toon Claes X-Patchwork-Id: 13962288 Received: from out-173.mta0.migadu.com (out-173.mta0.migadu.com [91.218.175.173]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 3BFF82144A8 for ; Thu, 6 Feb 2025 06:34:04 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=91.218.175.173 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1738823648; cv=none; b=fQeJkiHEFnL4aMyTsYnmKn3jIN5lPOv0hVFr34uiQwsGXNuEp/HuQHlREFa3uk4hAd6JZ+ieFUo+NSP3P8/ToQHPAQ5XgJd5Bo1yre9E4MfQBYWtvB/2xxgTiqWdRyGuUX5m+WrJW621o6olgA4U1lZMqRabfAJdhKSbePNDZ6Y= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1738823648; c=relaxed/simple; bh=WPIfQUijgkgLnVirjLfDKr8BCdS5wGqpyEK65MIiQTs=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=HuMeSH+0+Lh34L8wQcqc3jRdA8vp4HDsLj62rSekj7If3yT4QVp+NrIcSHXoFW6L8dlAfymzmm6gav8AAxgyIXrX/MxaGzeGux5MUocEo8YJbB1Pt779+62kdq/y4mq6KQH5FtjWFdk8rBQJVpG8seftfxviqA+HtHQx+Dq/n28= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=iotcl.com; spf=fail smtp.mailfrom=iotcl.com; dkim=pass (1024-bit key) header.d=iotcl.com header.i=@iotcl.com header.b=hdfItJDd; arc=none smtp.client-ip=91.218.175.173 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=iotcl.com Authentication-Results: smtp.subspace.kernel.org; spf=fail smtp.mailfrom=iotcl.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=iotcl.com header.i=@iotcl.com header.b="hdfItJDd" X-Report-Abuse: Please report any abuse attempt to abuse@migadu.com and include these headers. DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=iotcl.com; s=key1; t=1738823638; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=GhiBbp0As8MrORVTe77mTMEMF+yFM7KnVjyaDRbeK40=; b=hdfItJDdhvswm8BpkjEJeJrWoYTIvhvm7Xpf0EvCilAa8vjyAKzqbKw1TUTDWf2LmTyIhu GK9Baom+t6EdbzTVW2EhisE+9lS5DIPfy2hPdjuuESpgMcQUSJiIA5X6EkaQVjr+fP33V1 fxpGBq5z4Xbxyr3a1wy3Iyl0wZPhOTw= From: Toon Claes Date: Thu, 06 Feb 2025 07:33:31 +0100 Subject: [PATCH v7 3/7] clone: refactor wanted_peer_refs() Precedence: bulk X-Mailing-List: git@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Message-Id: <20250206-toon-clone-refs-v7-3-4622b7392202@iotcl.com> References: <20250206-toon-clone-refs-v7-0-4622b7392202@iotcl.com> In-Reply-To: <20250206-toon-clone-refs-v7-0-4622b7392202@iotcl.com> To: git@vger.kernel.org Cc: Kristoffer Haugsbakk , =?utf-8?q?Micha?= =?utf-8?q?l_Such=C3=A1nek?= , Patrick Steinhardt , Jeff King , Junio C Hamano , Toon Claes X-Migadu-Flow: FLOW_OUT The function wanted_peer_refs() is used to map the refs returned by the server to refs we will save in our clone. Over time this function grown to be very complex. Refactor it. Previously, there was a separate code path for when `option_single_branch` was set. It resulted in duplicated code and deeper nested conditions. After this refactor the code path for when `option_single_branch` is truthy modifies `refs` and then falls through to the common code path. This approach relies on the `refspec` being set correctly and thus only mapping refs that are relevant. Signed-off-by: Toon Claes --- builtin/clone.c | 39 +++++++++++++++------------------------ 1 file changed, 15 insertions(+), 24 deletions(-) diff --git a/builtin/clone.c b/builtin/clone.c index 69d1ad029dfa84a2f7136fa4a3c4c8a594b179c4..5efa2bbceb42b230f723660ea963ca1253888235 100644 --- a/builtin/clone.c +++ b/builtin/clone.c @@ -434,46 +434,37 @@ static struct ref *wanted_peer_refs(const struct ref *refs, { struct ref *head = copy_ref(find_ref_by_name(refs, "HEAD")); struct ref *local_refs = head; - struct ref **tail = head ? &head->next : &local_refs; + struct ref **tail = local_refs ? &local_refs->next : &local_refs; struct refspec_item tag_refspec; + struct ref *to_free = NULL; refspec_item_init(&tag_refspec, TAG_REFSPEC, 0); if (option_single_branch) { - struct ref *remote_head = NULL; - if (!option_branch) - remote_head = guess_remote_head(head, refs, 0); + refs = to_free = guess_remote_head(head, refs, 0); else { free_one_ref(head); local_refs = head = NULL; tail = &local_refs; - remote_head = copy_ref(find_remote_branch(refs, option_branch)); - } - - if (!remote_head && option_branch) - warning(_("Could not find remote branch %s to clone."), - option_branch); - else { - int i; - for (i = 0; i < refspec->nr; i++) - get_fetch_map(remote_head, &refspec->items[i], - &tail, 0); - - /* if --branch=tag, pull the requested tag explicitly */ - get_fetch_map(remote_head, &tag_refspec, &tail, 0); + refs = to_free = copy_ref(find_remote_branch(refs, option_branch)); } - free_refs(remote_head); - } else { - int i; - for (i = 0; i < refspec->nr; i++) - get_fetch_map(refs, &refspec->items[i], &tail, 0); } - if (!option_mirror && !option_single_branch && option_tags) + for (size_t i = 0; i < refspec->nr; i++) + get_fetch_map(refs, &refspec->items[i], &tail, 0); + + /* + * Grab all refs that match the TAG_REFSPEC. Any tags we don't care + * about won't be present in `refs` anyway. + * Except with option --mirror, where we grab all refs already. + */ + if (!option_mirror) get_fetch_map(refs, &tag_refspec, &tail, 0); + free_one_ref(to_free); refspec_item_clear(&tag_refspec); + return local_refs; } From patchwork Thu Feb 6 06:33:32 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Toon Claes X-Patchwork-Id: 13962289 Received: from out-182.mta0.migadu.com (out-182.mta0.migadu.com [91.218.175.182]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id DE4DF19BBA for ; Thu, 6 Feb 2025 06:34:07 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=91.218.175.182 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1738823649; cv=none; b=hUNBeQYf73Ll/Y3cCBI7B1DUrjoihQFmYkgTP41n0HU2YA2ephCsZ8BHCMw+4xhVVdzONj8wO5FxpYrTm+pbD42Oz62s4QcpvhRhves16IYzYZzoY/n3mjRteGV0oRXF30GyvFpjMGULQHWLK3MgiMgV+aY3U1pdtP0K9CaEdns= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1738823649; c=relaxed/simple; bh=lgBLB697+FvzKO4KojqRjDrIjd1KI0R1zccyQDFF91s=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=rCOivXaLsxMZLVgxmLxp/ZItS0I/TB4rqo+xCbOXIqhX647tInIGSJ18tK8wrunB1MPT9GTXdtN+uA5aiuBTa9FWD6LgkMTZgetGyNUSGgWh7k1ONZ7jHVFcqAv7l0fb1W2eotKURzjUp5HfimafMgDLDl91XUWz0krLldgJIXU= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=iotcl.com; spf=fail smtp.mailfrom=iotcl.com; dkim=pass (1024-bit key) header.d=iotcl.com header.i=@iotcl.com header.b=2E0ROOGw; arc=none smtp.client-ip=91.218.175.182 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=iotcl.com Authentication-Results: smtp.subspace.kernel.org; spf=fail smtp.mailfrom=iotcl.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=iotcl.com header.i=@iotcl.com header.b="2E0ROOGw" X-Report-Abuse: Please report any abuse attempt to abuse@migadu.com and include these headers. DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=iotcl.com; s=key1; t=1738823646; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=psL1avMTJ1yHFAPZmmDLfT8J+X1MaflHkvHAuR1+WVY=; b=2E0ROOGw6bxeOcevwSkxFGLWXsMC+mEz3Pl2t7SUlr+fusGzjBvRvXKe6YyCi1mRVCx2Y6 PiIIc9Riggtx4HBrPCoTw5fc0/wHY66Sr/uX8MyZa8wS/+Cx8tCUWGsE9QIxSfdjgDMtqU lazrk0FivTLJVIg245Son5SoMK6I+kA= From: Toon Claes Date: Thu, 06 Feb 2025 07:33:32 +0100 Subject: [PATCH v7 4/7] clone: add tags refspec earlier to fetch refspec Precedence: bulk X-Mailing-List: git@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Message-Id: <20250206-toon-clone-refs-v7-4-4622b7392202@iotcl.com> References: <20250206-toon-clone-refs-v7-0-4622b7392202@iotcl.com> In-Reply-To: <20250206-toon-clone-refs-v7-0-4622b7392202@iotcl.com> To: git@vger.kernel.org Cc: Kristoffer Haugsbakk , =?utf-8?q?Micha?= =?utf-8?q?l_Such=C3=A1nek?= , Patrick Steinhardt , Jeff King , Junio C Hamano , Toon Claes X-Migadu-Flow: FLOW_OUT In clone.c we call refspec_ref_prefixes() to copy the fetch refspecs from the `remote->fetch` refspec into `ref_prefixes` of `transport_ls_refs_options`. Afterwards we add the tags prefix `refs/tags/` prefix as well. At a later point, in wanted_peer_refs() we process refs using both `remote->fetch` and `TAG_REFSPEC`. Simplify the code by appending `TAG_REFSPEC` to `remote->fetch` before calling refspec_ref_prefixes(). To be able to do this, we set `option_tags` to 0 when --mirror is given. This is because --mirror mirrors (hence the name) all the refs, including tags and they do not need to be treated separately. Signed-off-by: Toon Claes --- builtin/clone.c | 27 +++++++++++---------------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/builtin/clone.c b/builtin/clone.c index 5efa2bbceb42b230f723660ea963ca1253888235..ef4af1f3e6bf0a33c13ed874587b35aba14fe43c 100644 --- a/builtin/clone.c +++ b/builtin/clone.c @@ -435,11 +435,8 @@ static struct ref *wanted_peer_refs(const struct ref *refs, struct ref *head = copy_ref(find_ref_by_name(refs, "HEAD")); struct ref *local_refs = head; struct ref **tail = local_refs ? &local_refs->next : &local_refs; - struct refspec_item tag_refspec; struct ref *to_free = NULL; - refspec_item_init(&tag_refspec, TAG_REFSPEC, 0); - if (option_single_branch) { if (!option_branch) refs = to_free = guess_remote_head(head, refs, 0); @@ -454,16 +451,7 @@ static struct ref *wanted_peer_refs(const struct ref *refs, for (size_t i = 0; i < refspec->nr; i++) get_fetch_map(refs, &refspec->items[i], &tail, 0); - /* - * Grab all refs that match the TAG_REFSPEC. Any tags we don't care - * about won't be present in `refs` anyway. - * Except with option --mirror, where we grab all refs already. - */ - if (!option_mirror) - get_fetch_map(refs, &tag_refspec, &tail, 0); - free_one_ref(to_free); - refspec_item_clear(&tag_refspec); return local_refs; } @@ -1011,8 +999,10 @@ int cmd_clone(int argc, die(_("unknown ref storage format '%s'"), ref_format); } - if (option_mirror) + if (option_mirror) { option_bare = 1; + option_tags = 0; + } if (option_bare) { if (real_git_dir) @@ -1375,14 +1365,19 @@ int cmd_clone(int argc, transport->smart_options->check_self_contained_and_connected = 1; strvec_push(&transport_ls_refs_options.ref_prefixes, "HEAD"); + + if (option_tags || option_branch) + /* + * Add tags refspec when user asked for tags (implicitly) or + * specified --branch, whose argument might be a tag. + */ + refspec_append(&remote->fetch, TAG_REFSPEC); + refspec_ref_prefixes(&remote->fetch, &transport_ls_refs_options.ref_prefixes); if (option_branch) expand_ref_prefix(&transport_ls_refs_options.ref_prefixes, option_branch); - if (option_tags) - strvec_push(&transport_ls_refs_options.ref_prefixes, - "refs/tags/"); refs = transport_get_remote_refs(transport, &transport_ls_refs_options); From patchwork Thu Feb 6 06:33:33 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Toon Claes X-Patchwork-Id: 13962299 Received: from out-188.mta1.migadu.com (out-188.mta1.migadu.com [95.215.58.188]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 926C419BBA for ; Thu, 6 Feb 2025 06:34:11 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=95.215.58.188 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1738823654; cv=none; b=svtTlSWwWvLIyc8YPjMtGL49+L04xscpbw1/c4yP9+dAJJF2zvrMkuBDwPWJTlZraOFgDHIVdoDVMsQ5rY/xBOTPUbzVTFZNJqF3kLwqFahDU4EdOMUqSRQeSl4Vzv1OT8olomWadeoxUCQ15G6v1SxIJtXSI209oOarN7DKFKk= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1738823654; c=relaxed/simple; bh=bs1gkyPkUfDNeLh45zXl4dcXDQ809hJu4bxdlyHU8uo=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=OBr6IDN/G0NZoHon9mKHPOoEMgKnGSelQDe8lT5iE/KM/bixIS2caaXKCJ44GzJh3TbpgNoCcqZrM2v1qfGifZoynOfoHXSi9wlt7x8sQA9bXrfh0LgUxvQOP5ASXj9b0+gnYoyLzyW3IqL8Cmn7qV0j4Z6z4u4qG7dc7AVJE5Q= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=iotcl.com; spf=fail smtp.mailfrom=iotcl.com; dkim=pass (1024-bit key) header.d=iotcl.com header.i=@iotcl.com header.b=uJKtcrQV; arc=none smtp.client-ip=95.215.58.188 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=iotcl.com Authentication-Results: smtp.subspace.kernel.org; spf=fail smtp.mailfrom=iotcl.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=iotcl.com header.i=@iotcl.com header.b="uJKtcrQV" X-Report-Abuse: Please report any abuse attempt to abuse@migadu.com and include these headers. DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=iotcl.com; s=key1; t=1738823650; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=fzv5Ko9hQst6aa/WKkKw4GXaTzJEI0Kmorzo5IwTop4=; b=uJKtcrQV8Jt8H/ltFrICmatDzY1Xt2lzmH/yeo1PdFq9NHdjz4+/PDfDIxd/o3z2Z/sG7k FlvfTYtos73TuRbZP+Vtfo9HdAquIp0dad/PFJYtv2YXJG0R3ZfkgUSeulCVkIegb3vSLT r8/363Bq4aGOnElQZ3G/IO/5xTrqCAA= From: Toon Claes Date: Thu, 06 Feb 2025 07:33:33 +0100 Subject: [PATCH v7 5/7] clone: introduce struct clone_opts in builtin/clone.c Precedence: bulk X-Mailing-List: git@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Message-Id: <20250206-toon-clone-refs-v7-5-4622b7392202@iotcl.com> References: <20250206-toon-clone-refs-v7-0-4622b7392202@iotcl.com> In-Reply-To: <20250206-toon-clone-refs-v7-0-4622b7392202@iotcl.com> To: git@vger.kernel.org Cc: Kristoffer Haugsbakk , =?utf-8?q?Micha?= =?utf-8?q?l_Such=C3=A1nek?= , Patrick Steinhardt , Jeff King , Junio C Hamano , Toon Claes X-Migadu-Flow: FLOW_OUT There is a lot of state stored in global variables in builtin/clone.c. In the long run we'd like to remove many of those. Introduce `struct clone_opts` in this file. This struct will be used to contain all details needed to perform the clone. The struct object can be thrown around to all the functions that need these details. The first field we're adding is `wants_head`. In some scenarios (specifically when both `--single-branch` and `--branch` are given) we are not interested in `HEAD` on the remote. The field `wants_head` in `struct clone_opts` will hold this information. We could have put `option_branch` and `option_single_branch` into that struct instead, but in a following commit we'll be using `wants_head` as well. Signed-off-by: Toon Claes --- builtin/clone.c | 44 +++++++++++++++++++++++++++++--------------- remote.c | 2 +- remote.h | 5 +++++ 3 files changed, 35 insertions(+), 16 deletions(-) diff --git a/builtin/clone.c b/builtin/clone.c index ef4af1f3e6bf0a33c13ed874587b35aba14fe43c..1d421c8f758e37a7219d2da680c7ef8699016171 100644 --- a/builtin/clone.c +++ b/builtin/clone.c @@ -57,6 +57,13 @@ * */ +struct clone_opts { + int wants_head; +}; +#define CLONE_OPTS_INIT { \ + .wants_head = 1 /* default enabled */ \ +} + static int option_no_checkout, option_bare, option_mirror, option_single_branch = -1; static int option_local = -1, option_no_hardlinks, option_shared; static int option_tags = 1; /* default enabled */ @@ -429,23 +436,24 @@ static struct ref *find_remote_branch(const struct ref *refs, const char *branch return ref; } -static struct ref *wanted_peer_refs(const struct ref *refs, - struct refspec *refspec) +static struct ref *wanted_peer_refs(struct clone_opts *opts, + const struct ref *refs, + struct refspec *refspec) { - struct ref *head = copy_ref(find_ref_by_name(refs, "HEAD")); - struct ref *local_refs = head; - struct ref **tail = local_refs ? &local_refs->next : &local_refs; + struct ref *local_refs = NULL; + struct ref **tail = &local_refs; struct ref *to_free = NULL; - if (option_single_branch) { - if (!option_branch) + if (opts->wants_head) { + struct ref *head = copy_ref(find_ref_by_name(refs, "HEAD")); + if (head) + tail_link_ref(head, &tail); + if (option_single_branch) refs = to_free = guess_remote_head(head, refs, 0); - else { - free_one_ref(head); - local_refs = head = NULL; - tail = &local_refs; - refs = to_free = copy_ref(find_remote_branch(refs, option_branch)); - } + } else if (option_single_branch) { + local_refs = NULL; + tail = &local_refs; + refs = to_free = copy_ref(find_remote_branch(refs, option_branch)); } for (size_t i = 0; i < refspec->nr; i++) @@ -893,6 +901,8 @@ int cmd_clone(int argc, struct string_list server_options = STRING_LIST_INIT_NODUP; const char *bundle_uri = NULL; + struct clone_opts opts = CLONE_OPTS_INIT; + struct transport_ls_refs_options transport_ls_refs_options = TRANSPORT_LS_REFS_OPTIONS_INIT; @@ -1343,9 +1353,13 @@ int cmd_clone(int argc, if (option_not.nr) transport_set_option(transport, TRANS_OPT_DEEPEN_NOT, (const char *)&option_not); - if (option_single_branch) + if (option_single_branch) { transport_set_option(transport, TRANS_OPT_FOLLOWTAGS, "1"); + if (option_branch) + opts.wants_head = 0; + } + if (option_upload_pack) transport_set_option(transport, TRANS_OPT_UPLOADPACK, option_upload_pack); @@ -1454,7 +1468,7 @@ int cmd_clone(int argc, } if (refs) - mapped_refs = wanted_peer_refs(refs, &remote->fetch); + mapped_refs = wanted_peer_refs(&opts, refs, &remote->fetch); if (mapped_refs) { /* diff --git a/remote.c b/remote.c index 1779f0e7bbb8b88965f2fedf407e50ed20ea7a13..69d8c43ea694f1b9a7699e5d5a49bfc169058b8e 100644 --- a/remote.c +++ b/remote.c @@ -1260,7 +1260,7 @@ int count_refspec_match(const char *pattern, } } -static void tail_link_ref(struct ref *ref, struct ref ***tail) +void tail_link_ref(struct ref *ref, struct ref ***tail) { **tail = ref; while (ref->next) diff --git a/remote.h b/remote.h index a19353f68999f5440db7bf5f91dd4be8bcc1d8a5..ce3e7c8512981d0ac5db2da508c4fbe64cede961 100644 --- a/remote.h +++ b/remote.h @@ -221,6 +221,11 @@ struct ref *alloc_ref(const char *name); struct ref *copy_ref(const struct ref *ref); struct ref *copy_ref_list(const struct ref *ref); int count_refspec_match(const char *, struct ref *refs, struct ref **matched_ref); +/* + * Put a ref in the tail and prepare tail for adding another one. + * *tail is the pointer to the tail of the list of refs. + */ +void tail_link_ref(struct ref *ref, struct ref ***tail); int check_ref_type(const struct ref *ref, int flags); From patchwork Thu Feb 6 06:33:34 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Toon Claes X-Patchwork-Id: 13962300 Received: from out-182.mta0.migadu.com (out-182.mta0.migadu.com [91.218.175.182]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 013E119BBA for ; Thu, 6 Feb 2025 06:34:17 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=91.218.175.182 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1738823660; cv=none; b=BAxkUTwrTrQQdrA8VT/ZkGCQCPAiRDIAwibqcDrU9pWuJatHEj+pn9Vu/heWCu5SE88a3t6gJ5ZxHJPpzza2c/REyX1na7MsPynfRf1hA/3UBBLqLq6ipSy3RK8efvMd/oDtgn19mzZ3If1+JmUAVmItWp+OPw5Kdx4p/JSuXk4= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1738823660; c=relaxed/simple; bh=WxC5JRLtG3mnKIGr/yhqPYVJD/jif99bS962f2VCCcs=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=ryxkNhlyh2CHL9+oxM5VmIwiJyGbBA/EwXNrQ3ZSNe1J/c/1u4tQCoTnu9xKZwTFP2VyhVu0LQ0+08qheSOvnU18wKtYXiWQUC/anK35yu9U2M6QwRTezVmq//zp+mqK+8Xo1rdTp97ZcvrfbGmu3WH+ddOuu42IFIvk5rX2bdc= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=iotcl.com; spf=fail smtp.mailfrom=iotcl.com; dkim=pass (1024-bit key) header.d=iotcl.com header.i=@iotcl.com header.b=Oa7zBlfP; arc=none smtp.client-ip=91.218.175.182 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=iotcl.com Authentication-Results: smtp.subspace.kernel.org; spf=fail smtp.mailfrom=iotcl.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=iotcl.com header.i=@iotcl.com header.b="Oa7zBlfP" X-Report-Abuse: Please report any abuse attempt to abuse@migadu.com and include these headers. DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=iotcl.com; s=key1; t=1738823653; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=TQuMm0VceDjoTQnImSzjoHHrHcGqYqPtkv28jATe10I=; b=Oa7zBlfP3cmRoiGrf0tqpew//PSV2yocOMLuClSgUz7o6K7lSqsSbeYwGI2KuWiDLNscl7 8B7YIYZyiU0CTclK+SDyOmpGCu/sqJghFCpNcE2V0vbwRMuf7o8I36cGdpdnpW90MIDf8T qhGf0EtgMXAy8wyDC2VNAuYI6BWR1gw= From: Toon Claes Date: Thu, 06 Feb 2025 07:33:34 +0100 Subject: [PATCH v7 6/7] parse-options: introduce die_for_incompatible_opt2() Precedence: bulk X-Mailing-List: git@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Message-Id: <20250206-toon-clone-refs-v7-6-4622b7392202@iotcl.com> References: <20250206-toon-clone-refs-v7-0-4622b7392202@iotcl.com> In-Reply-To: <20250206-toon-clone-refs-v7-0-4622b7392202@iotcl.com> To: git@vger.kernel.org Cc: Kristoffer Haugsbakk , =?utf-8?q?Micha?= =?utf-8?q?l_Such=C3=A1nek?= , Patrick Steinhardt , Jeff King , Junio C Hamano , Toon Claes X-Migadu-Flow: FLOW_OUT The functions die_for_incompatible_opt3() and die_for_incompatible_opt4() already exist to die whenever a user specifies three or four options respectively that are not compatible. Introduce die_for_incompatible_opt2() which dies when two options that are incompatible are set. Signed-off-by: Toon Claes --- builtin/replay.c | 7 ++++--- parse-options.h | 9 +++++++++ 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/builtin/replay.c b/builtin/replay.c index 1afc6d1ee0cb738fa7fa3f2b5c8ce0dd7802e7da..032c172b65ece4e2c3b275ffdde22a54c55a3933 100644 --- a/builtin/replay.c +++ b/builtin/replay.c @@ -163,9 +163,10 @@ static void determine_replay_mode(struct rev_cmdline_info *cmd_info, get_ref_information(cmd_info, &rinfo); if (!rinfo.positive_refexprs) die(_("need some commits to replay")); - if (onto_name && *advance_name) - die(_("--onto and --advance are incompatible")); - else if (onto_name) { + + die_for_incompatible_opt2(!!onto_name, "--onto", + !!*advance_name, "--advance"); + if (onto_name) { *onto = peel_committish(onto_name); if (rinfo.positive_refexprs < strset_get_size(&rinfo.positive_refs)) diff --git a/parse-options.h b/parse-options.h index 39f088625494f20dea96b9a9cbe986916773bf60..fca944d9a93d643d984c58de2ead9154c8b16c94 100644 --- a/parse-options.h +++ b/parse-options.h @@ -436,6 +436,15 @@ static inline void die_for_incompatible_opt3(int opt1, const char *opt1_name, 0, ""); } +static inline void die_for_incompatible_opt2(int opt1, const char *opt1_name, + int opt2, const char *opt2_name) +{ + die_for_incompatible_opt4(opt1, opt1_name, + opt2, opt2_name, + 0, "", + 0, ""); +} + /* * Use these assertions for callbacks that expect to be called with NONEG and * NOARG respectively, and do not otherwise handle the "unset" and "arg" From patchwork Thu Feb 6 06:33:35 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Toon Claes X-Patchwork-Id: 13962301 Received: from out-188.mta1.migadu.com (out-188.mta1.migadu.com [95.215.58.188]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id EE4172248B0 for ; Thu, 6 Feb 2025 06:34:20 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=95.215.58.188 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1738823664; cv=none; b=kjhqAr4cA3PMUJAUHwU4m99glL8KaV1B+lklRc5yTkikhu4ty69a5RfY+3++crKW5zbB3ZKg72v1wzp/yc4y2cGfuRvakTkmyWoCAEdM3uNf0kHotiF6BEGbw4pFYa2rm7GhAGhw0hX+PKZTN1sYmChV7jjkhoC5eZgspX+HXc8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1738823664; c=relaxed/simple; bh=WKcb98Eh7MwvTezGQantLKNH5NGEyAELaHe0EqekbNU=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=dYtxKZWKNKtZ7kNeuTJ/DAcWirdA5I4rWMXVrdvGSm+8CAmkGCvXwfjQAytWhcuT4X1NOpzXiXqgVNCupN0Y6Na2hJmu8rjLRdJdQehenS8YzVWHfuAIeNMhI4GO3YXgdqhNdIu+uOUgbXOddfCgKenw09SWSXpAqpEsNfO6ozo= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=iotcl.com; spf=fail smtp.mailfrom=iotcl.com; dkim=pass (1024-bit key) header.d=iotcl.com header.i=@iotcl.com header.b=3ie4iaxS; arc=none smtp.client-ip=95.215.58.188 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=iotcl.com Authentication-Results: smtp.subspace.kernel.org; spf=fail smtp.mailfrom=iotcl.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=iotcl.com header.i=@iotcl.com header.b="3ie4iaxS" X-Report-Abuse: Please report any abuse attempt to abuse@migadu.com and include these headers. DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=iotcl.com; s=key1; t=1738823658; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=jkQCsMhoNRyeL4jhWGHChPGHJI2Ne7YaOK7XlTm/wmI=; b=3ie4iaxS7xVJQf8dfNWlj3lUpY66upl1xLVCDVDhwYdmHyvfb9cPzOkn279Ura9zxVKJqB uFYlxe0sVTP6+DIN1p25FV/10/e/4idMLVrISn37U3ryNholX9Hr84qYzC55kWjcxq4uGV NRotG9/DVYb3D6ygYA7YxNX2ism0Cvc= From: Toon Claes Date: Thu, 06 Feb 2025 07:33:35 +0100 Subject: [PATCH v7 7/7] builtin/clone: teach git-clone(1) the --revision= option Precedence: bulk X-Mailing-List: git@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Message-Id: <20250206-toon-clone-refs-v7-7-4622b7392202@iotcl.com> References: <20250206-toon-clone-refs-v7-0-4622b7392202@iotcl.com> In-Reply-To: <20250206-toon-clone-refs-v7-0-4622b7392202@iotcl.com> To: git@vger.kernel.org Cc: Kristoffer Haugsbakk , =?utf-8?q?Micha?= =?utf-8?q?l_Such=C3=A1nek?= , Patrick Steinhardt , Jeff King , Junio C Hamano , Toon Claes X-Migadu-Flow: FLOW_OUT The git-clone(1) command has the option `--branch` that allows the user to select the branch they want HEAD to point to. In a non-bare repository this also checks out that branch. Option `--branch` also accepts a tag. When a tag name is provided, the commit this tag points to is checked out and HEAD is detached. Thus `--branch` can be used to clone a repository and check out a ref kept under `refs/heads` or `refs/tags`. But some other refs might be in use as well. For example Git forges might use refs like `refs/pull/` and `refs/merge-requests/` to track pull/merge requests. These refs cannot be selected upon git-clone(1). Add option `--revision` to git-clone(1). This option accepts a fully qualified reference, or a hexadecimal commit ID. This enables the user to clone and check out any revision they want. `--revision` can be used in conjunction with `--depth` to do a minimal clone that only contains the blob and tree for a single revision. This can be useful for automated tests running in CI systems. Using option `--branch` and `--single-branch` together is a similar scenario, but serves a different purpose. Using these two options, a singlet remote tracking branch is created and the fetch refspec is set up so git-fetch(1) will receive updates on that branch from the remote. This allows the user work on that single branch. Option `--revision` on contrary detaches HEAD, creates no tracking branches, and writes no fetch refspec. Signed-off-by: Toon Claes --- Documentation/git-clone.txt | 9 ++++ builtin/clone.c | 57 ++++++++++++++++---- t/meson.build | 1 + t/t5621-clone-revision.sh | 123 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 179 insertions(+), 11 deletions(-) diff --git a/Documentation/git-clone.txt b/Documentation/git-clone.txt index 8d0476f6dcaf6fed7ccd48a20398556dd4e20722..1069d56e7126c006c14c6b4579547f79d7fe4fb5 100644 --- a/Documentation/git-clone.txt +++ b/Documentation/git-clone.txt @@ -221,6 +221,15 @@ objects from the source repository into a pack in the cloned repository. `--branch` can also take tags and detaches the `HEAD` at that commit in the resulting repository. +`--revision=`:: + Create a new repository, and fetch the history leading to the given + revision __ (and nothing else), without making any remote-tracking + branch, and without making any local branch, and detach `HEAD` to + __. The argument can be a ref name (e.g. `refs/heads/main` or + `refs/tags/v1.0`) that peels down to a commit, or a hexadecimal object + name. + This option is incompatible with `--branch` and `--mirror`. + `-u` __:: `--upload-pack` __:: When given, and the repository to clone from is accessed diff --git a/builtin/clone.c b/builtin/clone.c index 1d421c8f758e37a7219d2da680c7ef8699016171..f9a2ecbe9cc944793203d722b42573e779575c5a 100644 --- a/builtin/clone.c +++ b/builtin/clone.c @@ -59,6 +59,7 @@ struct clone_opts { int wants_head; + int detach; }; #define CLONE_OPTS_INIT { \ .wants_head = 1 /* default enabled */ \ @@ -565,11 +566,11 @@ static void update_remote_refs(const struct ref *refs, } } -static void update_head(const struct ref *our, const struct ref *remote, +static void update_head(struct clone_opts *opts, const struct ref *our, const struct ref *remote, const char *unborn, const char *msg) { const char *head; - if (our && skip_prefix(our->name, "refs/heads/", &head)) { + if (our && !opts->detach && skip_prefix(our->name, "refs/heads/", &head)) { /* Local default branch link */ if (refs_update_symref(get_main_ref_store(the_repository), "HEAD", our->name, NULL) < 0) die(_("unable to update HEAD")); @@ -580,8 +581,9 @@ static void update_head(const struct ref *our, const struct ref *remote, install_branch_config(0, head, remote_name, our->name); } } else if (our) { - struct commit *c = lookup_commit_reference(the_repository, - &our->old_oid); + struct commit *c = lookup_commit_or_die(&our->old_oid, + our->name); + /* --branch specifies a non-branch (i.e. tags), detach HEAD */ refs_update_ref(get_main_ref_store(the_repository), msg, "HEAD", &c->object.oid, NULL, REF_NO_DEREF, @@ -900,6 +902,7 @@ int cmd_clone(int argc, int option_filter_submodules = -1; /* unspecified */ struct string_list server_options = STRING_LIST_INIT_NODUP; const char *bundle_uri = NULL; + char *option_rev = NULL; struct clone_opts opts = CLONE_OPTS_INIT; @@ -943,6 +946,8 @@ int cmd_clone(int argc, N_("use instead of 'origin' to track upstream")), OPT_STRING('b', "branch", &option_branch, N_("branch"), N_("checkout instead of the remote's HEAD")), + OPT_STRING(0, "revision", &option_rev, N_("rev"), + N_("clone single revision and check out")), OPT_STRING('u', "upload-pack", &option_upload_pack, N_("path"), N_("path to git-upload-pack on the remote")), OPT_STRING(0, "depth", &option_depth, N_("depth"), @@ -1279,7 +1284,7 @@ int cmd_clone(int argc, strbuf_addstr(&branch_top, src_ref_prefix); git_config_set("core.bare", "true"); - } else { + } else if (!option_rev) { strbuf_addf(&branch_top, "refs/remotes/%s/", remote_name); } @@ -1298,8 +1303,9 @@ int cmd_clone(int argc, remote = remote_get_early(remote_name); - refspec_appendf(&remote->fetch, "+%s*:%s*", src_ref_prefix, - branch_top.buf); + if (!option_rev) + refspec_appendf(&remote->fetch, "+%s*:%s*", src_ref_prefix, + branch_top.buf); path = get_repo_path(remote->url.v[0], &is_bundle); is_local = option_local != 0 && path && !is_bundle; @@ -1342,6 +1348,11 @@ int cmd_clone(int argc, transport_set_option(transport, TRANS_OPT_KEEP, "yes"); + die_for_incompatible_opt2(!!option_rev, "--revision", + !!option_branch, "--branch"); + die_for_incompatible_opt2(!!option_rev, "--revision", + option_mirror, "--mirror"); + if (reject_shallow) transport_set_option(transport, TRANS_OPT_REJECT_SHALLOW, "1"); if (option_depth) @@ -1378,7 +1389,14 @@ int cmd_clone(int argc, if (transport->smart_options && !deepen && !filter_options.choice) transport->smart_options->check_self_contained_and_connected = 1; - strvec_push(&transport_ls_refs_options.ref_prefixes, "HEAD"); + if (option_rev) { + option_tags = 0; + option_single_branch = 0; + opts.wants_head = 0; + opts.detach = 1; + + refspec_append(&remote->fetch, option_rev); + } if (option_tags || option_branch) /* @@ -1393,6 +1411,17 @@ int cmd_clone(int argc, expand_ref_prefix(&transport_ls_refs_options.ref_prefixes, option_branch); + /* + * As part of transport_get_remote_refs() the server tells us the hash + * algorithm, which we require to initialize the repo. But calling that + * function without any ref prefix, will cause the server to announce + * all known refs. If the argument passed to --revision was a hex oid, + * ref_prefixes will be empty so we fall back to asking about HEAD to + * reduce traffic from the server. + */ + if (opts.wants_head || transport_ls_refs_options.ref_prefixes.nr == 0) + strvec_push(&transport_ls_refs_options.ref_prefixes, "HEAD"); + refs = transport_get_remote_refs(transport, &transport_ls_refs_options); /* @@ -1501,6 +1530,11 @@ int cmd_clone(int argc, if (!our_head_points_at) die(_("Remote branch %s not found in upstream %s"), option_branch, remote_name); + } else if (option_rev) { + our_head_points_at = mapped_refs; + if (!our_head_points_at) + die(_("Remote revision %s not found in upstream %s"), + option_rev, remote_name); } else if (remote_head_points_at) { our_head_points_at = remote_head_points_at; } else if (remote_head) { @@ -1539,8 +1573,9 @@ int cmd_clone(int argc, free(to_free); } - write_refspec_config(src_ref_prefix, our_head_points_at, - remote_head_points_at, &branch_top); + if (!option_rev) + write_refspec_config(src_ref_prefix, our_head_points_at, + remote_head_points_at, &branch_top); if (filter_options.choice) partial_clone_register(remote_name, &filter_options); @@ -1556,7 +1591,7 @@ int cmd_clone(int argc, branch_top.buf, reflog_msg.buf, transport, !is_local); - update_head(our_head_points_at, remote_head, unborn_head, reflog_msg.buf); + update_head(&opts, our_head_points_at, remote_head, unborn_head, reflog_msg.buf); /* * We want to show progress for recursive submodule clones iff diff --git a/t/meson.build b/t/meson.build index 35f25ca4a1d960564190288e9456620a46ccc80a..b5f917926b61de379b6cef45e5f750912422a7d1 100644 --- a/t/meson.build +++ b/t/meson.build @@ -721,6 +721,7 @@ integration_tests = [ 't5617-clone-submodules-remote.sh', 't5618-alternate-refs.sh', 't5619-clone-local-ambiguous-transport.sh', + 't5621-clone-revision.sh', 't5700-protocol-v1.sh', 't5701-git-serve.sh', 't5702-protocol-v2.sh', diff --git a/t/t5621-clone-revision.sh b/t/t5621-clone-revision.sh new file mode 100755 index 0000000000000000000000000000000000000000..d4889a954e6300e0e327ebe7dfcf73569d966829 --- /dev/null +++ b/t/t5621-clone-revision.sh @@ -0,0 +1,123 @@ +#!/bin/sh + +test_description='tests for git clone --revision' +GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main +export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME + +TEST_PASSES_SANITIZE_LEAK=true +. ./test-lib.sh + +test_expect_success 'setup' ' + test_commit --no-tag "initial commit" README "Hello" && + test_commit --annotate "second commit" README "Hello world" v1.0 && + test_commit --no-tag "third commit" README "Hello world!" && + git switch -c feature v1.0 && + test_commit --no-tag "feature commit" README "Hello world!" && + git switch main +' + +test_expect_success 'clone with --revision being a branch' ' + test_when_finished "rm -rf dst" && + git clone --revision=refs/heads/feature . dst && + git rev-parse refs/heads/feature >expect && + git -C dst rev-parse HEAD >actual && + test_must_fail git -C dst symbolic-ref -q HEAD >/dev/null && + test_cmp expect actual && + git -C dst for-each-ref refs >expect && + test_must_be_empty expect && + test_must_fail git -C dst config remote.origin.fetch +' + +test_expect_success 'clone with --depth and --revision being a branch' ' + test_when_finished "rm -rf dst" && + git clone --no-local --depth=1 --revision=refs/heads/feature . dst && + git rev-parse refs/heads/feature >expect && + git -C dst rev-parse HEAD >actual && + test_must_fail git -C dst symbolic-ref -q HEAD >/dev/null && + test_cmp expect actual && + git -C dst for-each-ref refs >expect && + test_must_be_empty expect && + test_must_fail git -C dst config remote.origin.fetch && + git -C dst rev-list HEAD >actual && + test_line_count = 1 actual +' + +test_expect_success 'clone with --revision being a tag' ' + test_when_finished "rm -rf dst" && + git clone --revision=refs/tags/v1.0 . dst && + git rev-parse refs/tags/v1.0^{} >expect && + git -C dst rev-parse HEAD >actual && + test_must_fail git -C dst symbolic-ref -q HEAD >/dev/null && + test_cmp expect actual && + git -C dst for-each-ref refs >expect && + test_must_be_empty expect && + test_must_fail git -C dst config remote.origin.fetch +' + +test_expect_success 'clone with --revision being HEAD' ' + test_when_finished "rm -rf dst" && + git clone --revision=HEAD . dst && + git rev-parse HEAD >expect && + git -C dst rev-parse HEAD >actual && + test_must_fail git -C dst symbolic-ref -q HEAD >/dev/null && + test_cmp expect actual && + git -C dst for-each-ref refs >expect && + test_must_be_empty expect && + test_must_fail git -C dst config remote.origin.fetch +' + +test_expect_success 'clone with --revision being a raw commit hash' ' + test_when_finished "rm -rf dst" && + oid=$(git rev-parse refs/heads/feature) && + git clone --revision=$oid . dst && + echo $oid >expect && + git -C dst rev-parse HEAD >actual && + test_must_fail git -C dst symbolic-ref -q HEAD >/dev/null && + test_cmp expect actual && + git -C dst for-each-ref refs >expect && + test_must_be_empty expect && + test_must_fail git -C dst config remote.origin.fetch +' + +test_expect_success 'clone with --revision and --bare' ' + test_when_finished "rm -rf dst" && + git clone --revision=refs/heads/main --bare . dst && + oid=$(git rev-parse refs/heads/main) && + git -C dst cat-file -t $oid >actual && + echo "commit" >expect && + test_cmp expect actual && + git -C dst for-each-ref refs >expect && + test_must_be_empty expect && + test_must_fail git -C dst config remote.origin.fetch +' + +test_expect_success 'clone with --revision being a short raw commit hash' ' + test_when_finished "rm -rf dst" && + oid=$(git rev-parse --short refs/heads/feature) && + test_must_fail git clone --revision=$oid . dst 2>err && + test_grep "fatal: Remote revision $oid not found in upstream origin" err +' + +test_expect_success 'clone with --revision being a tree hash' ' + test_when_finished "rm -rf dst" && + oid=$(git rev-parse refs/heads/feature^{tree}) && + test_must_fail git clone --revision=$oid . dst 2>err && + test_grep "error: object $oid is a tree, not a commit" err +' + +test_expect_success 'clone with --revision being the parent of a ref fails' ' + test_when_finished "rm -rf dst" && + test_must_fail git clone --revision=refs/heads/main^ . dst +' + +test_expect_success 'clone with --revision and --branch fails' ' + test_when_finished "rm -rf dst" && + test_must_fail git clone --revision=refs/heads/main --branch=main . dst +' + +test_expect_success 'clone with --revision and --mirror fails' ' + test_when_finished "rm -rf dst" && + test_must_fail git clone --revision=refs/heads/main --mirror . dst +' + +test_done