All of lore.kernel.org
 help / color / mirror / Atom feed
From: Jimmy Aguilar Mena <kratsbinovish@gmail.com>
To: git@vger.kernel.org
Subject: [PATCH 2/3] worktree: add --recurse-submodules flag to worktree add
Date: Thu, 16 Apr 2026 18:35:36 +0200	[thread overview]
Message-ID: <aeEPk7m5gwPnmMUZ@RTX> (raw)

When "git submodule update --init" is run inside a linked worktree,
submodule_name_to_gitdir() returns a per-worktree path:

   $GIT_COMMON_DIR/worktrees/<id>/modules/<name>

rather than the main worktree's:

   $GIT_COMMON_DIR/modules/<name>

If the main worktree already has the submodule cloned, the per-worktree
gitdir does not yet exist and clone_submodule() falls through to fetch
from the remote URL -- wasting bandwidth and disk space when all the
objects are already present locally.

Detect this case: when the target sm_gitdir does not exist, differs
from the common-dir path, and the common-dir path is a valid git
directory, skip the remote clone and instead run:

   git clone --local --no-checkout --separate-git-dir <sm_gitdir> \
             <common_sm_gitdir> <working_tree_path>

"--local" makes Git use hardlinks for pack files and loose objects, so
no extra disk space is consumed for data that is already present.  The
per-worktree gitdir gets its own HEAD, index, and config, just like any
other worktree, while sharing the object store with the main worktree's
submodule repository.  Mutable per-worktree files (HEAD, refs, index,
config) start out as hardlinks but become independent on the first write
via Git's atomic rename(2) approach.

Signed-off-by: Jimmy Aguilar Mena <kratsbinovish@gmail.com>
---
  builtin/submodule--helper.c | 54 +++++++++++++++++++++++++++++++++++++
  1 file changed, 54 insertions(+)

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 2f589e3b37..2da59e8c93 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -1914,6 +1914,59 @@ static int clone_submodule(const struct module_clone_data *clone_data,
  		clone_data_path = to_free = xstrfmt("%s/%s", repo_get_work_tree(the_repository),
  						    clone_data->path);
  
+	/*
+	 * If we are populating a submodule in a linked worktree and the main
+	 * worktree already has this submodule cloned, reuse its objects via a
+	 * local clone (hardlinks) instead of fetching from the network.  The
+	 * common-dir path "$GIT_COMMON_DIR/modules/<name>" is where the main
+	 * worktree stores the submodule gitdir; the per-worktree path returned
+	 * by submodule_name_to_gitdir() diverges from it only in linked
+	 * worktrees.
+	 */
+	{
+		struct strbuf common_sm_gitdir = STRBUF_INIT;
+		strbuf_addf(&common_sm_gitdir, "%s/modules/%s",
+			    the_repository->commondir, clone_data->name);
+
+		if (!file_exists(sm_gitdir) &&
+		    strcmp(sm_gitdir, common_sm_gitdir.buf) &&
+		    is_git_directory(common_sm_gitdir.buf)) {
+			/*
+			 * Main worktree has the submodule; reuse it locally.
+			 * git clone --local creates hardlinks for pack files so
+			 * no extra disk space is needed for existing objects.
+			 */
+			if (clone_data->require_init && !stat(clone_data_path, &st) &&
+			    !is_empty_dir(clone_data_path))
+				die(_("directory not empty: '%s'"), clone_data_path);
+
+			if (safe_create_leading_directories_const(the_repository, sm_gitdir) < 0)
+				die(_("could not create directory '%s'"), sm_gitdir);
+
+			strvec_push(&cp.args, "clone");
+			strvec_push(&cp.args, "--local");
+			strvec_push(&cp.args, "--no-checkout");
+			if (clone_data->quiet)
+				strvec_push(&cp.args, "--quiet");
+			strvec_pushl(&cp.args, "--separate-git-dir", sm_gitdir, NULL);
+			strvec_push(&cp.args, "--");
+			strvec_push(&cp.args, common_sm_gitdir.buf);
+			strvec_push(&cp.args, clone_data_path);
+
+			cp.git_cmd = 1;
+			prepare_submodule_repo_env(&cp.env);
+			cp.no_stdin = 1;
+
+			if (run_command(&cp))
+				die(_("local clone of '%s' into submodule path '%s' failed"),
+				    common_sm_gitdir.buf, clone_data_path);
+
+			strbuf_release(&common_sm_gitdir);
+			goto connect_wt;
+		}
+		strbuf_release(&common_sm_gitdir);
+	}
+
  	if (!file_exists(sm_gitdir)) {
  		if (clone_data->require_init && !stat(clone_data_path, &st) &&
  		    !is_empty_dir(clone_data_path))
@@ -2005,6 +2058,7 @@ static int clone_submodule(const struct module_clone_data *clone_data,
  		    sm_gitdir);
  	}
  
+connect_wt:
  	connect_work_tree_and_git_dir(clone_data_path, sm_gitdir, 0);
  
  	p = repo_submodule_path(the_repository, clone_data_path, "config");



             reply	other threads:[~2026-04-16 16:35 UTC|newest]

Thread overview: 2+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-04-16 16:35 Jimmy Aguilar Mena [this message]
2026-04-16 17:27 ` [PATCH 2/3] worktree: add --recurse-submodules flag to worktree add Junio C Hamano

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=aeEPk7m5gwPnmMUZ@RTX \
    --to=kratsbinovish@gmail.com \
    --cc=CAPSFGa8uu9CEEPH3XVjfN5VEOfcnb2p8YgXVuansjKc0S2S_tA@mail.gmail.com \
    --cc=git@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.