diff --git a/Documentation/git-worktree.txt b/Documentation/git-worktree.txt
index 9e862fbcf79..0f0642ac039 100644
--- a/Documentation/git-worktree.txt
+++ b/Documentation/git-worktree.txt
@@ -10,6 +10,7 @@ SYNOPSIS
 --------
 [verse]
 'git worktree add' [-f] [--detach] [--checkout] [--lock [--reason <string>]] [-b <new-branch>] <path> [<commit-ish>]
+'git worktree init-worktree-config'
 'git worktree list' [-v | --porcelain]
 'git worktree lock' [--reason <string>] <worktree>
 'git worktree move' <worktree> <new-path>
@@ -92,6 +93,14 @@ checked out in the new working tree, if it's not checked out anywhere
 else, otherwise the command will refuse to create the working tree (unless
 `--force` is used).
 
+init-worktree-config::
+
+Initialize config settings to enable worktree-specific config settings.
+This will set `core.repositoryFormatversion=1` and enable
+`extensions.worktreeConfig`, which might cause some third-party tools from
+being able to operate on your repository. See CONFIGURATION FILE for more
+details.
+
 list::
 
 List details of each working tree.  The main working tree is listed first,
@@ -290,10 +299,10 @@ already present in the config file, they will be applied to the main
 working trees only.
 
 In order to have configuration specific to working trees, you can turn
-on the `worktreeConfig` extension, e.g.:
+on the `worktreeConfig` extension, using this command:
 
 ------------
-$ git config extensions.worktreeConfig true
+$ git worktree init-worktree-config
 ------------
 
 In this mode, specific configuration stays in the path pointed by `git
@@ -303,9 +312,12 @@ versions will refuse to access repositories with this extension.
 
 Note that in this file, the exception for `core.bare` and `core.worktree`
 is gone. If they exist in `$GIT_DIR/config`, you must move
-them to the `config.worktree` of the main working tree. You may also
-take this opportunity to review and move other configuration that you
-do not want to share to all working trees:
+them to the `config.worktree` of the main working tree. These keys are
+moved automatically when you use the `git worktree init-worktree-config`
+command.
+
+You may also take this opportunity to review and move other configuration
+that you do not want to share to all working trees:
 
  - `core.worktree` and `core.bare` should never be shared
 
diff --git a/builtin/worktree.c b/builtin/worktree.c
index a57fcd0f3c5..937ee6fc38b 100644
--- a/builtin/worktree.c
+++ b/builtin/worktree.c
@@ -17,6 +17,7 @@
 
 static const char * const worktree_usage[] = {
 	N_("git worktree add [<options>] <path> [<commit-ish>]"),
+	N_("git worktree init-worktree-config"),
 	N_("git worktree list [<options>]"),
 	N_("git worktree lock [<options>] <path>"),
 	N_("git worktree move <worktree> <new-path>"),
@@ -1031,6 +1032,85 @@ static int repair(int ac, const char **av, const char *prefix)
 	return rc;
 }
 
+static int move_config_setting(const char *key, const char *value,
+			       const char *from_file, const char *to_file)
+{
+	if (git_config_set_in_file_gently(to_file, key, value))
+		return error(_("unable to set %s in '%s'"), key, to_file);
+	if (git_config_set_in_file_gently(from_file, key, NULL))
+		return error(_("unable to unset %s in '%s'"), key, from_file);
+	return 0;
+}
+
+static int init_worktree_config(int ac, const char **av, const char *prefix)
+{
+	struct repository *r = the_repository;
+	struct option options[] = {
+		OPT_END()
+	};
+	int res = 0;
+	int bare = 0;
+	struct config_set cs = { 0 };
+	const char *core_worktree;
+	char *common_config_file = xstrfmt("%s/config", r->commondir);
+	char *main_worktree_file = xstrfmt("%s/config.worktree", r->commondir);
+
+	/* Report error on any arguments */
+	ac = parse_options(ac, av, prefix, options, worktree_usage, 0);
+	if (ac)
+		usage_with_options(worktree_usage, options);
+
+	git_configset_init(&cs);
+	git_configset_add_file(&cs, common_config_file);
+
+	/*
+	 * If the format and extension are already enabled, then we can
+	 * skip the upgrade process.
+	 */
+	if (repository_format_worktree_config)
+		return 0;
+
+	if (upgrade_repository_format(r, 1) < 0) {
+		res = error(_("unable to upgrade repository format to enable worktreeConfig"));
+		goto cleanup;
+	}
+	if ((res = git_config_set_gently("extensions.worktreeConfig", "true"))) {
+		error(_("failed to set extensions.worktreeConfig setting"));
+		goto cleanup;
+	}
+
+	/*
+	 * If core.bare is true in the common config file, then we need to
+	 * move it to the base worktree's config file or it will break all
+	 * worktrees. If it is false, then leave it in place because it
+	 * _could_ be negating a global core.bare=true.
+	 */
+	if (!git_configset_get_bool(&cs, "core.bare", &bare) && bare) {
+		if ((res = move_config_setting("core.bare", "true",
+					       common_config_file,
+					       main_worktree_file)))
+			goto cleanup;
+	}
+	/*
+	 * If core.worktree is set, then the base worktree is located
+	 * somewhere different than the parent of the common Git dir.
+	 * Relocate that value to avoid breaking all worktrees with this
+	 * upgrade to worktree config.
+	 */
+	if (!git_configset_get_string_tmp(&cs, "core.worktree", &core_worktree)) {
+		if ((res = move_config_setting("core.worktree", core_worktree,
+					       common_config_file,
+					       main_worktree_file)))
+			goto cleanup;
+	}
+
+cleanup:
+	git_configset_clear(&cs);
+	free(common_config_file);
+	free(main_worktree_file);
+	return res;
+}
+
 int cmd_worktree(int ac, const char **av, const char *prefix)
 {
 	struct option options[] = {
@@ -1045,6 +1125,8 @@ int cmd_worktree(int ac, const char **av, const char *prefix)
 		prefix = "";
 	if (!strcmp(av[1], "add"))
 		return add(ac - 1, av + 1, prefix);
+	if (!strcmp(av[1], "init-worktree-config"))
+		return init_worktree_config(ac - 1, av + 1, prefix);
 	if (!strcmp(av[1], "prune"))
 		return prune(ac - 1, av + 1, prefix);
 	if (!strcmp(av[1], "list"))
diff --git a/t/t2407-worktree-init-worktree-config.sh b/t/t2407-worktree-init-worktree-config.sh
new file mode 100755
index 00000000000..b3bd0fa1322
--- /dev/null
+++ b/t/t2407-worktree-init-worktree-config.sh
@@ -0,0 +1,68 @@
+#!/bin/sh
+
+test_description='test git worktree init-worktree-config'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+	git init base &&
+	test_commit -C base commit &&
+	git -C base worktree add --detach worktree
+'
+
+reset_config_when_finished () {
+	test_when_finished git -C base config --unset core.repositoryFormatVersion &&
+	test_when_finished git -C base config --unset extensions.worktreeConfig &&
+	rm -rf base/.git/config.worktree &&
+	rm -rf base/.git/worktrees/worktree/config.worktree
+}
+
+test_expect_success 'upgrades repo format and adds extension' '
+	reset_config_when_finished &&
+	git -C base worktree init-worktree-config >out 2>err &&
+	test_must_be_empty out &&
+	test_must_be_empty err &&
+	test_cmp_config -C base 1 core.repositoryFormatVersion &&
+	test_cmp_config -C base true extensions.worktreeConfig
+'
+
+test_expect_success 'relocates core.worktree' '
+	reset_config_when_finished &&
+	mkdir dir &&
+	git -C base config core.worktree ../../dir &&
+	git -C base worktree init-worktree-config >out 2>err &&
+	test_must_be_empty out &&
+	test_must_be_empty err &&
+	test_cmp_config -C base 1 core.repositoryFormatVersion &&
+	test_cmp_config -C base true extensions.worktreeConfig &&
+	test_cmp_config -C base ../../dir core.worktree &&
+	test_must_fail git -C worktree core.worktree
+'
+
+test_expect_success 'relocates core.bare' '
+	reset_config_when_finished &&
+	git -C base config core.bare true &&
+	git -C base worktree init-worktree-config >out 2>err &&
+	test_must_be_empty out &&
+	test_must_be_empty err &&
+	test_cmp_config -C base 1 core.repositoryFormatVersion &&
+	test_cmp_config -C base true extensions.worktreeConfig &&
+	test_cmp_config -C base true core.bare &&
+	test_must_fail git -C worktree core.bare
+'
+
+test_expect_success 'skips upgrade is already upgraded' '
+	reset_config_when_finished &&
+	git -C base worktree init-worktree-config &&
+	git -C base config core.bare true &&
+
+	# this should be a no-op, even though core.bare
+	# makes the worktree be broken.
+	git -C base worktree init-worktree-config >out 2>err &&
+	test_must_be_empty out &&
+	test_must_be_empty err &&
+	test_must_fail git -C base config --worktree core.bare &&
+	git -C base config core.bare
+'
+
+test_done
