Subject: [PATCH 1/3] worktree: add --recurse-submodules flag to worktree add

From: Jimmy Aguilar Mena <[email-masked]>
Date: Thu, 16 Apr 2026 18:34:14 +0200

git worktree add leaves submodules untouched: the linked worktree is
checked out but its submodule working trees are empty.  Users must run
"git submodule update --init" by hand afterwards.

Add a --recurse-submodules flag that does this automatically.  After
checkout_worktree() succeeds, a child "git submodule update --init
--recursive" is run with its working directory set to the new worktree
path so that $GIT_DIR is resolved correctly.

The flag is incompatible with --no-checkout (nothing is checked out, so
there is nowhere to put submodule working trees) and --orphan (the
branch has no commits and therefore no submodule configuration to
follow).

Signed-off-by: Jimmy Aguilar Mena <[email-masked]>
---
  builtin/worktree.c | 23 +++++++++++++++++++++++
  1 file changed, 23 insertions(+)

diff --git a/builtin/worktree.c b/builtin/worktree.c
index 4fd6f7575f..f3dacb0e5c 100644
--- a/builtin/worktree.c
+++ b/builtin/worktree.c
@@ -123,6 +123,7 @@ struct add_opts {
  	int checkout;
  	int orphan;
  	int relative_paths;
+	int recurse_submodules;
  	const char *keep_locked;
  };
  
@@ -593,6 +594,20 @@ static int add_worktree(const char *path, const char *refname,
  	    (ret = checkout_worktree(opts, &child_env)))
  		goto done;
  
+	if (!ret && opts->checkout && opts->recurse_submodules) {
+		struct child_process cp = CHILD_PROCESS_INIT;
+		cp.git_cmd = 1;
+		cp.dir = path;
+		strvec_pushl(&cp.args, "submodule", "update",
+			     "--init", "--recursive", NULL);
+		if (opts->quiet)
+			strvec_push(&cp.args, "--quiet");
+		strvec_pushv(&cp.env, child_env.v);
+		ret = run_command(&cp);
+		if (ret)
+			goto done;
+	}
+
  	is_junk = 0;
  	FREE_AND_NULL(junk_work_tree);
  	FREE_AND_NULL(junk_git_dir);
@@ -823,6 +838,8 @@ static int add(int ac, const char **av, const char *prefix,
  			 N_("try to match the new branch name with a remote-tracking branch")),
  		OPT_BOOL(0, "relative-paths", &opts.relative_paths,
  			 N_("use relative paths for worktrees")),
+		OPT_BOOL(0, "recurse-submodules", &opts.recurse_submodules,
+			 N_("initialize submodules in the new worktree")),
  		OPT_END()
  	};
  	int ret;
@@ -842,6 +859,12 @@ static int add(int ac, const char **av, const char *prefix,
  	if (opts.orphan && !opts.checkout)
  		die(_("options '%s' and '%s' cannot be used together"),
  		    "--orphan", "--no-checkout");
+	if (opts.recurse_submodules && !opts.checkout)
+		die(_("options '%s' and '%s' cannot be used together"),
+		    "--recurse-submodules", "--no-checkout");
+	if (opts.recurse_submodules && opts.orphan)
+		die(_("options '%s' and '%s' cannot be used together"),
+		    "--recurse-submodules", "--orphan");
  	if (opts.orphan && ac == 2)
  		die(_("option '%s' and commit-ish cannot be used together"),
  		    "--orphan");