From patchwork Mon Oct 28 19:09:37 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Caleb White X-Patchwork-Id: 13853925 Received: from mail-4322.protonmail.ch (mail-4322.protonmail.ch [185.70.43.22]) (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 A66541E32DC for ; Mon, 28 Oct 2024 19:09:43 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=185.70.43.22 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1730142586; cv=none; b=mEdLur6IMXslMP3AEeL6nHtVXE4uC8w13S32xpcEUYxG5aM/qYL/k76lCR33K/TJjrMquYL5T8oS2MESvi0xcK2NS7wS40UafxFTpWOkRGGRm5yaKU+OQzhrWLpU36+d6FLVmRHFmEm/B1fzYWSqDoi7t6zTY2MZ6iFc+sIge/c= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1730142586; c=relaxed/simple; bh=x5lquUQKna9XXorWiydNkCgMJXafcGe6yfA/JEfSeLw=; h=Date:To:From:Cc:Subject:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=brjg+sGvuo8bo3vozx1/YysFGuPvDiR+VnNAix+LvmY5s2V5FaRRTWqxuM453p3slpY+XZEeaAafBJcSIyzZBUKhRJrBJxb7I76HAYxljpgZ6uM1YIX267aC0fOheunkLsctzsW8orlGi4AkSKMzfYqTlApFYcVvx1Yw2xD30Eg= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=pm.me; spf=pass smtp.mailfrom=pm.me; dkim=pass (2048-bit key) header.d=pm.me header.i=@pm.me header.b=eAYhmRn5; arc=none smtp.client-ip=185.70.43.22 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=pm.me Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=pm.me Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=pm.me header.i=@pm.me header.b="eAYhmRn5" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=pm.me; s=protonmail3; t=1730142581; x=1730401781; bh=uX5jOI7q0BNxL4ut/6Ug0DKM0Eq/+S0J/E2Uyd/RZzU=; h=Date:To:From:Cc:Subject:Message-ID:In-Reply-To:References: Feedback-ID:From:To:Cc:Date:Subject:Reply-To:Feedback-ID: Message-ID:BIMI-Selector; b=eAYhmRn5kVkwS27NTrCSqUCn9pt9Ime0N82d0BuXVEQSNa2rifTIBU4ux2LVlIhVl VDpOPStDcWoBvWJUsHZN252x3A3vIQyKOzBiZye8Bh0BGwBkzNn2Gk6ZgIagB9Nb+4 CikrySXfGit6bbmlYXj51UdCTBXGe77JaSl7/Xvyb8w517uWWY6R222x36RZwPLtD3 kdR83Zg4wo79twCGVGCNvnXzTwWW7wFYwSN5xucza2vC0udq6nwLyf2L4pjREsOB5+ Q4Nsh+YmX/vVDldlDQHWTSwiXL2K1CjaFL/6Tu8kdaECvhea70GO5G5wUZYD4/sBUW 1sA2D5ysingrw== Date: Mon, 28 Oct 2024 19:09:37 +0000 To: git@vger.kernel.org From: Caleb White Cc: Taylor Blau , Junio C Hamano , Eric Sunshine , Caleb White Subject: [PATCH v2 1/5] worktree: add CLI/config options for relative path linking Message-ID: <20241028-wt_relative_options-v2-1-33a5021bd7bb@pm.me> In-Reply-To: <20241028-wt_relative_options-v2-0-33a5021bd7bb@pm.me> References: <20241028-wt_relative_options-v2-0-33a5021bd7bb@pm.me> Feedback-ID: 31210263:user:proton X-Pm-Message-ID: 68f1954a783edb6b243ca18b3f2539c396b77539 Precedence: bulk X-Mailing-List: git@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 This patch introduces the `--[no-]relative-paths` CLI option for `git worktree {add, move, repair}` commands, as well as the `worktree.useRelativePaths` configuration setting. When enabled, these options allow worktrees to be linked using relative paths, enhancing portability across environments where absolute paths may differ (e.g., containerized setups, shared network drives). Git still creates absolute paths by default, but these options allow users to opt-in to relative paths if desired. Using the `--relative-paths` option with `worktree {move, repair}` will convert absolute paths to relative ones, while `--no-relative-paths` does the reverse. For cases where users want consistency in path handling, the config option `worktree.useRelativePaths` provides a persistent setting. In response to reviewer feedback from the initial patch series[1], this revision includes slight refactoring for improved maintainability and clarity in the code base. [1]: https://lore.kernel.org/git/20241007-wt_relative_paths-v3-0-622cf18c45eb@pm.me Suggested-by: Junio C Hamano Signed-off-by: Caleb White --- Documentation/config/worktree.txt | 5 +++++ Documentation/git-worktree.txt | 12 ++++++++++++ builtin/worktree.c | 9 +++++++++ t/t2408-worktree-relative.sh | 39 --------------------------------------- worktree.c | 17 ++++++++++------- worktree.h | 2 ++ 6 files changed, 38 insertions(+), 46 deletions(-) diff --git a/Documentation/config/worktree.txt b/Documentation/config/worktree.txt index 048e349482df6c892055720eb53cdcd6c327b6ed..44b783c2774dc5ff65e3fa232b0c25cd5254876b 100644 --- a/Documentation/config/worktree.txt +++ b/Documentation/config/worktree.txt @@ -7,3 +7,8 @@ worktree.guessRemote:: such a branch exists, it is checked out and set as "upstream" for the new branch. If no such match can be found, it falls back to creating a new branch from the current HEAD. +worktree.useRelativePaths:: + If set to `true`, worktrees will be linked to the repository using + relative paths rather than using absolute paths. This is particularly + useful for setups where the repository and worktrees may be moved between + different locations or environments. diff --git a/Documentation/git-worktree.txt b/Documentation/git-worktree.txt index 70437c815f13852bd2eb862176b8b933e6de0acf..975dc3c46d480480457ec4857988a6b8bc67b647 100644 --- a/Documentation/git-worktree.txt +++ b/Documentation/git-worktree.txt @@ -216,6 +216,18 @@ To remove a locked worktree, specify `--force` twice. This can also be set up as the default behaviour by using the `worktree.guessRemote` config option. +--[no-]relative-paths:: + Worktrees will be linked to the repository using relative paths + rather than using absolute paths. This is particularly useful for setups + where the repository and worktrees may be moved between different + locations or environments. ++ +With `repair`, the linking files will be updated if there's an absolute/relative +mismatch, even if the links are correct. ++ +This can also be set up as the default behaviour by using the +`worktree.useRelativePaths` config option. + --[no-]track:: When creating a new branch, if `` is a branch, mark it as "upstream" from the new branch. This is the diff --git a/builtin/worktree.c b/builtin/worktree.c index dae63dedf4cac2621f51f95a39aa456b33acd894..c1130be5890c905c0b648782a834eb8dfcd79ba5 100644 --- a/builtin/worktree.c +++ b/builtin/worktree.c @@ -134,6 +134,9 @@ static int git_worktree_config(const char *var, const char *value, if (!strcmp(var, "worktree.guessremote")) { guess_remote = git_config_bool(var, value); return 0; + } else if (!strcmp(var, "worktree.userelativepaths")) { + use_relative_paths = git_config_bool(var, value); + return 0; } return git_default_config(var, value, ctx, cb); @@ -796,6 +799,8 @@ static int add(int ac, const char **av, const char *prefix) PARSE_OPT_NOARG | PARSE_OPT_OPTARG), OPT_BOOL(0, "guess-remote", &guess_remote, N_("try to match the new branch name with a remote-tracking branch")), + OPT_BOOL(0, "relative-paths", &use_relative_paths, + N_("use relative paths for worktrees")), OPT_END() }; int ret; @@ -1189,6 +1194,8 @@ static int move_worktree(int ac, const char **av, const char *prefix) OPT__FORCE(&force, N_("force move even if worktree is dirty or locked"), PARSE_OPT_NOCOMPLETE), + OPT_BOOL(0, "relative-paths", &use_relative_paths, + N_("use relative paths for worktrees")), OPT_END() }; struct worktree **worktrees, *wt; @@ -1382,6 +1389,8 @@ static int repair(int ac, const char **av, const char *prefix) const char **p; const char *self[] = { ".", NULL }; struct option options[] = { + OPT_BOOL(0, "relative-paths", &use_relative_paths, + N_("use relative paths for worktrees")), OPT_END() }; int rc = 0; diff --git a/t/t2408-worktree-relative.sh b/t/t2408-worktree-relative.sh deleted file mode 100755 index a3136db7e28cb20926ff44211e246ce625a6e51a..0000000000000000000000000000000000000000 --- a/t/t2408-worktree-relative.sh +++ /dev/null @@ -1,39 +0,0 @@ -#!/bin/sh - -test_description='test worktrees linked with relative paths' - -TEST_PASSES_SANITIZE_LEAK=true -. ./test-lib.sh - -test_expect_success 'links worktrees with relative paths' ' - test_when_finished rm -rf repo && - git init repo && - ( - cd repo && - test_commit initial && - git worktree add wt1 && - echo "../../../wt1/.git" >expected_gitdir && - cat .git/worktrees/wt1/gitdir >actual_gitdir && - echo "gitdir: ../.git/worktrees/wt1" >expected_git && - cat wt1/.git >actual_git && - test_cmp expected_gitdir actual_gitdir && - test_cmp expected_git actual_git - ) -' - -test_expect_success 'move repo without breaking relative internal links' ' - test_when_finished rm -rf repo moved && - git init repo && - ( - cd repo && - test_commit initial && - git worktree add wt1 && - cd .. && - mv repo moved && - cd moved/wt1 && - git status >out 2>err && - test_must_be_empty err - ) -' - -test_done diff --git a/worktree.c b/worktree.c index 77ff484d3ec48c547ee4e3d958dfa28a52c1eaa7..de5c5e53a5f2a758ddf470b5d6a9ad6c66247181 100644 --- a/worktree.c +++ b/worktree.c @@ -14,6 +14,8 @@ #include "wt-status.h" #include "config.h" +int use_relative_paths = 0; + void free_worktree(struct worktree *worktree) { if (!worktree) @@ -111,9 +113,9 @@ struct worktree *get_linked_worktree(const char *id, strbuf_strip_suffix(&worktree_path, "/.git"); if (!is_absolute_path(worktree_path.buf)) { - strbuf_strip_suffix(&path, "gitdir"); - strbuf_addbuf(&path, &worktree_path); - strbuf_realpath_forgiving(&worktree_path, path.buf, 0); + strbuf_strip_suffix(&path, "gitdir"); + strbuf_addbuf(&path, &worktree_path); + strbuf_realpath_forgiving(&worktree_path, path.buf, 0); } CALLOC_ARRAY(worktree, 1); @@ -725,12 +727,15 @@ static int is_main_worktree_path(const char *path) * won't know which /worktrees//gitdir to repair. However, we may * be able to infer the gitdir by manually reading /path/to/worktree/.git, * extracting the , and checking if /worktrees/ exists. + * + * Returns -1 on failure and strbuf.len on success. */ static int infer_backlink(const char *gitfile, struct strbuf *inferred) { struct strbuf actual = STRBUF_INIT; const char *id; + strbuf_reset(inferred); if (strbuf_read_file(&actual, gitfile, 0) < 0) goto error; if (!starts_with(actual.buf, "gitdir:")) @@ -741,18 +746,16 @@ static int infer_backlink(const char *gitfile, struct strbuf *inferred) id++; /* advance past '/' to point at */ if (!*id) goto error; - strbuf_reset(inferred); strbuf_git_common_path(inferred, the_repository, "worktrees/%s", id); if (!is_directory(inferred->buf)) goto error; strbuf_release(&actual); - return 1; - + return inferred->len; error: strbuf_release(&actual); strbuf_reset(inferred); /* clear invalid path */ - return 0; + return -1; } /* diff --git a/worktree.h b/worktree.h index e96118621638667d87c5d7e0452ed10bd1ddf606..37e65d508ed23d3e7a29850bb938285072a3aaa6 100644 --- a/worktree.h +++ b/worktree.h @@ -5,6 +5,8 @@ struct strbuf; +extern int use_relative_paths; + struct worktree { /* The repository this worktree belongs to. */ struct repository *repo;