diff mbox series

[v3,5/5] worktree: add `relativeWorktrees` extension

Message ID 20241025-wt_relative_paths-v3-5-8860a5321c01@pm.me (mailing list archive)
State New
Headers show
Series Optionally link worktrees with relative paths | expand

Commit Message

Caleb White Oct. 25, 2024, 8:57 p.m. UTC
A new extension, `relativeWorktrees`, is added to indicate that at least
one worktree in the repository has been linked with relative paths. This
extension is automatically set when a worktree is created or repaired
using the `--relative-paths` option, or when the
`worktree.useRelativePaths` config is set to `true`.

The `relativeWorktrees` extension ensures older Git versions do not
attempt to automatically prune worktrees with relative paths, as they
would not not recognize the paths as being valid.

Suggested-by: Junio C Hamano <gitster@pobox.com>
Signed-off-by: Caleb White <cdwhite3@pm.me>
---
 Documentation/config/extensions.txt |  6 ++++++
 repository.c                        |  1 +
 repository.h                        |  1 +
 setup.c                             |  7 +++++++
 setup.h                             |  1 +
 t/t2400-worktree-add.sh             | 13 +++++++++++++
 worktree.c                          |  8 ++++++++
 7 files changed, 37 insertions(+)
diff mbox series

Patch

diff --git a/Documentation/config/extensions.txt b/Documentation/config/extensions.txt
index 5dc569d1c9c77c15e32441493289f9c9dd5e7f0b..5cb4721a0e0ae1ed64f90492c0dc18b96473cb33 100644
--- a/Documentation/config/extensions.txt
+++ b/Documentation/config/extensions.txt
@@ -63,6 +63,12 @@  Note that this setting should only be set by linkgit:git-init[1] or
 linkgit:git-clone[1]. Trying to change it after initialization will not
 work and will produce hard-to-diagnose issues.
 
+relativeWorktrees::
+	If enabled, indicates at least one worktree has been linked with
+	relative paths. Automatically set if a worktree has been created or
+	repaired with either the `--relative-paths` option or with the
+	`worktree.useRelativePaths` config set to `true`.
+
 worktreeConfig::
 	If enabled, then worktrees will load config settings from the
 	`$GIT_DIR/config.worktree` file in addition to the
diff --git a/repository.c b/repository.c
index f988b8ae68a6a29792e7f2c980a02bd0e388a3b9..1a6a62bbd03a5dc4fdade3eb45ea2696968abc23 100644
--- a/repository.c
+++ b/repository.c
@@ -283,6 +283,7 @@  int repo_init(struct repository *repo,
 	repo_set_compat_hash_algo(repo, format.compat_hash_algo);
 	repo_set_ref_storage_format(repo, format.ref_storage_format);
 	repo->repository_format_worktree_config = format.worktree_config;
+	repo->repository_format_relative_worktrees = format.relative_worktrees;
 
 	/* take ownership of format.partial_clone */
 	repo->repository_format_partial_clone = format.partial_clone;
diff --git a/repository.h b/repository.h
index 24a66a496a6ff516ce06d47b7329b3d36eb701ca..c4c92b2ab9c9e3b425dc2974636e33d1f4089c69 100644
--- a/repository.h
+++ b/repository.h
@@ -150,6 +150,7 @@  struct repository {
 
 	/* Configurations */
 	int repository_format_worktree_config;
+	int repository_format_relative_worktrees;
 
 	/* Indicate if a repository has a different 'commondir' from 'gitdir' */
 	unsigned different_commondir:1;
diff --git a/setup.c b/setup.c
index 7b648de0279116b381eea46800ad130606926103..6bf56cf72c4b46a95f46f9b3901f7e77d702cec7 100644
--- a/setup.c
+++ b/setup.c
@@ -683,6 +683,9 @@  static enum extension_result handle_extension(const char *var,
 				     "extensions.refstorage", value);
 		data->ref_storage_format = format;
 		return EXTENSION_OK;
+	} else if (!strcmp(ext, "relativeworktrees")) {
+		data->relative_worktrees = git_config_bool(var, value);
+		return EXTENSION_OK;
 	}
 	return EXTENSION_UNKNOWN;
 }
@@ -1854,6 +1857,8 @@  const char *setup_git_directory_gently(int *nongit_ok)
 						    repo_fmt.ref_storage_format);
 			the_repository->repository_format_worktree_config =
 				repo_fmt.worktree_config;
+			the_repository->repository_format_relative_worktrees =
+				repo_fmt.relative_worktrees;
 			/* take ownership of repo_fmt.partial_clone */
 			the_repository->repository_format_partial_clone =
 				repo_fmt.partial_clone;
@@ -1950,6 +1955,8 @@  void check_repository_format(struct repository_format *fmt)
 				    fmt->ref_storage_format);
 	the_repository->repository_format_worktree_config =
 		fmt->worktree_config;
+	the_repository->repository_format_relative_worktrees =
+		fmt->relative_worktrees;
 	the_repository->repository_format_partial_clone =
 		xstrdup_or_null(fmt->partial_clone);
 	clear_repository_format(&repo_fmt);
diff --git a/setup.h b/setup.h
index e496ab3e4de580c2d9f95a7ea0eaf90e0d41b070..18dc3b73686ce28fac2fe04282ce95f8bf3e6b74 100644
--- a/setup.h
+++ b/setup.h
@@ -129,6 +129,7 @@  struct repository_format {
 	int precious_objects;
 	char *partial_clone; /* value of extensions.partialclone */
 	int worktree_config;
+	int relative_worktrees;
 	int is_bare;
 	int hash_algo;
 	int compat_hash_algo;
diff --git a/t/t2400-worktree-add.sh b/t/t2400-worktree-add.sh
index 630c13230b3cc762ce8d943e22be8891aa2b1871..d36d8a4db0e924877787697579544f10f92dc0cf 100755
--- a/t/t2400-worktree-add.sh
+++ b/t/t2400-worktree-add.sh
@@ -1248,4 +1248,17 @@  test_expect_success 'move repo without breaking relative internal links' '
 	)
 '
 
+test_expect_success 'relative worktree sets extension config' '
+	test_when_finished "rm -rf repo" &&
+	git init repo &&
+	git -C repo commit --allow-empty -m base &&
+	git -C repo worktree add --relative-paths ./foo &&
+	git -C repo config get core.repositoryformatversion >actual &&
+	echo 1 >expected &&
+	test_cmp expected actual &&
+	git -C repo config get extensions.relativeworktrees >actual &&
+	echo true >expected &&
+	test_cmp expected actual
+'
+
 test_done
diff --git a/worktree.c b/worktree.c
index 0d1685b892fcddf74a91304424a94e5ee847388b..41ebddb495d146e302fedefdb03e0d2637fb2831 100644
--- a/worktree.c
+++ b/worktree.c
@@ -1025,6 +1025,14 @@  void write_worktree_linking_files(struct strbuf dotgit, struct strbuf gitdir)
 	strbuf_strip_suffix(&repo, "/gitdir");
 	strbuf_realpath(&repo, repo.buf, 1);
 
+	if (use_relative_paths && !the_repository->repository_format_relative_worktrees) {
+		if (upgrade_repository_format(1) < 0)
+			die(_("unable to upgrade repository format to support relative worktrees"));
+		if (git_config_set_gently("extensions.relativeWorktrees", "true"))
+			die(_("unable to set extensions.relativeWorktrees setting"));
+		the_repository->repository_format_relative_worktrees = 1;
+	}
+
 	if (use_relative_paths) {
 		write_file(gitdir.buf, "%s/.git", relative_path(path.buf, repo.buf, &tmp));
 		write_file(dotgit.buf, "gitdir: %s", relative_path(repo.buf, path.buf, &tmp));