From 2d9b2f16d938e1a4a391534df37f99f1c64c8ea6 Mon Sep 17 00:00:00 2001 From: Sidney Douw Date: Thu, 1 Dec 2022 11:02:22 +0100 Subject: [PATCH 01/14] fix typo in docs --- git-discover/src/repository.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/git-discover/src/repository.rs b/git-discover/src/repository.rs index 25c0730d304..0d6cf060bb1 100644 --- a/git-discover/src/repository.rs +++ b/git-discover/src/repository.rs @@ -116,7 +116,7 @@ pub enum Kind { /// /// Note that this is merely a guess at this point as we didn't read the configuration yet. Bare, - /// A `git` repository along with a checked out files in a work tree. + /// A `git` repository along with checked out files in a work tree. WorkTree { /// If set, this is the git dir associated with this _linked_ worktree. /// If `None`, the git_dir is the `.git` directory inside the _main_ worktree we represent. From 23d847480eff1a0d26fd801dfa8ad6ed205c71d4 Mon Sep 17 00:00:00 2001 From: Sidney Douw Date: Thu, 1 Dec 2022 11:02:41 +0100 Subject: [PATCH 02/14] add test for worktree configs --- .../make_worktree_repo_with_configs.tar.xz | 3 ++ .../make_worktree_repo_with_configs.sh | 25 ++++++++++ git-repository/tests/repository/config/mod.rs | 1 + .../tests/repository/config/worktree.rs | 48 +++++++++++++++++++ 4 files changed, 77 insertions(+) create mode 100644 git-repository/tests/fixtures/generated-archives/make_worktree_repo_with_configs.tar.xz create mode 100644 git-repository/tests/fixtures/make_worktree_repo_with_configs.sh create mode 100644 git-repository/tests/repository/config/worktree.rs diff --git a/git-repository/tests/fixtures/generated-archives/make_worktree_repo_with_configs.tar.xz b/git-repository/tests/fixtures/generated-archives/make_worktree_repo_with_configs.tar.xz new file mode 100644 index 00000000000..8a3d24f6b01 --- /dev/null +++ b/git-repository/tests/fixtures/generated-archives/make_worktree_repo_with_configs.tar.xz @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ea82cbcda5a07a6e7ddde7d9e3834d1ec7e184811ccab29c7ea21d9a53705f1f +size 10564 diff --git a/git-repository/tests/fixtures/make_worktree_repo_with_configs.sh b/git-repository/tests/fixtures/make_worktree_repo_with_configs.sh new file mode 100644 index 00000000000..d3960b4cb25 --- /dev/null +++ b/git-repository/tests/fixtures/make_worktree_repo_with_configs.sh @@ -0,0 +1,25 @@ +#!/bin/bash + +set -eu -o pipefail + +git init repo + +(cd repo + touch a b c + git add . + git commit -m initial + + git worktree add ../wt-1 + git worktree add ../wt-2 + + git config extensions.worktreeConfig true + git config --worktree worktree.setting "set in the main worktree" +) + +(cd wt-1 + git config --worktree worktree.setting "set in wt-1" +) + +(cd wt-2 + git config --worktree worktree.setting "set in wt-2" +) diff --git a/git-repository/tests/repository/config/mod.rs b/git-repository/tests/repository/config/mod.rs index 5b82895d3af..5465c7fa2fd 100644 --- a/git-repository/tests/repository/config/mod.rs +++ b/git-repository/tests/repository/config/mod.rs @@ -3,3 +3,4 @@ mod identity; mod remote; #[cfg(any(feature = "blocking-network-client", feature = "async-network-client"))] mod transport_options; +mod worktree; diff --git a/git-repository/tests/repository/config/worktree.rs b/git-repository/tests/repository/config/worktree.rs new file mode 100644 index 00000000000..1b744df0c15 --- /dev/null +++ b/git-repository/tests/repository/config/worktree.rs @@ -0,0 +1,48 @@ +use git_repository::open; + +#[test] +fn with_worktree_configs() -> git_testtools::Result { + let dir = git_testtools::scripted_fixture_repo_read_only("make_worktree_repo_with_configs.sh").unwrap(); + + let base = open(dir.join("repo")).unwrap(); + let base_config = base.config_snapshot(); + + assert_eq!(base.work_dir(), Some(dir.join("repo").as_path())); + assert_eq!(base.git_dir(), dir.join("repo/.git")); + assert_eq!(base.common_dir(), dir.join("repo/.git")); + + assert_eq!( + base_config.string("worktree.setting").expect("exists").to_string(), + "set in the main worktree" + ); + + let wt1 = open(dir.join("wt-1")).unwrap(); + let wt1_config = wt1.config_snapshot(); + assert_eq!(wt1.work_dir(), Some(dir.join("wt-1").as_path())); + assert_eq!(wt1.git_dir(), dir.join("repo/.git/worktrees/wt-1").canonicalize()?); + assert_eq!( + wt1.common_dir(), + dir.join("repo/.git").canonicalize()?.join("worktrees/wt-1/../..") + ); + + assert_eq!( + wt1_config.string("worktree.setting").expect("exists").to_string(), + "set in wt-1" + ); + + let wt2 = open(dir.join("wt-2")).unwrap(); + let wt2_config = wt2.config_snapshot(); + assert_eq!(wt2.work_dir(), Some(dir.join("wt-2").as_path())); + assert_eq!(wt2.git_dir(), dir.join("repo/.git/worktrees/wt-2").canonicalize()?); + assert_eq!( + wt2.common_dir(), + dir.join("repo/.git").canonicalize()?.join("worktrees/wt-2/../..") + ); + + assert_eq!( + wt2_config.string("worktree.setting").expect("exists").to_string(), + "set in wt-2" + ); + + Ok(()) +} From 760e736931c13d155dbbe46459fe11b602084549 Mon Sep 17 00:00:00 2001 From: Sidney Douw Date: Thu, 1 Dec 2022 11:07:47 +0100 Subject: [PATCH 03/14] load worktree config if necessary --- git-repository/src/config/cache/incubate.rs | 44 ++++++++++++++------- git-repository/src/open/repository.rs | 8 +++- 2 files changed, 36 insertions(+), 16 deletions(-) diff --git a/git-repository/src/config/cache/incubate.rs b/git-repository/src/config/cache/incubate.rs index 233b4c0f3e6..401afccd0aa 100644 --- a/git-repository/src/config/cache/incubate.rs +++ b/git-repository/src/config/cache/incubate.rs @@ -15,27 +15,14 @@ pub(crate) struct StageOne { /// Initialization impl StageOne { pub fn new( + common_dir: &std::path::Path, git_dir: &std::path::Path, git_dir_trust: git_sec::Trust, lossy: Option, lenient: bool, ) -> Result { let mut buf = Vec::with_capacity(512); - let config = { - let config_path = git_dir.join("config"); - std::io::copy(&mut std::fs::File::open(&config_path)?, &mut buf)?; - - git_config::File::from_bytes_owned( - &mut buf, - git_config::file::Metadata::from(git_config::Source::Local) - .at(config_path) - .with(git_dir_trust), - git_config::file::init::Options { - includes: git_config::file::includes::Options::no_follow(), - ..util::base_options(lossy) - }, - )? - }; + let mut config = load_config(&mut buf, common_dir.join("config"), git_dir_trust, lossy)?; let is_bare = util::config_bool(&config, "core.bare", false, lenient)?; let repo_format_version = config @@ -57,6 +44,11 @@ impl StageOne { .transpose()? .unwrap_or(git_hash::Kind::Sha1); + if util::config_bool(&config, "extensions.worktreeConfig", false, lenient)? { + let wt_config = load_config(&mut buf, git_dir.join("config.worktree"), git_dir_trust, lossy)?; + config.append(wt_config); + } + let reflog = util::query_refupdates(&config, lenient)?; Ok(StageOne { git_dir_config: config, @@ -68,3 +60,25 @@ impl StageOne { }) } } + +fn load_config( + buf: &mut Vec, + config_path: std::path::PathBuf, + git_dir_trust: git_sec::Trust, + lossy: Option, +) -> Result, Error> { + std::io::copy(&mut std::fs::File::open(&config_path)?, buf)?; + + let config = git_config::File::from_bytes_owned( + buf, + git_config::file::Metadata::from(git_config::Source::Worktree) + .at(config_path) + .with(git_dir_trust), + git_config::file::init::Options { + includes: git_config::file::includes::Options::no_follow(), + ..util::base_options(lossy) + }, + )?; + + Ok(config) +} diff --git a/git-repository/src/open/repository.rs b/git-repository/src/open/repository.rs index b3a14f92571..5953a54f440 100644 --- a/git-repository/src/open/repository.rs +++ b/git-repository/src/open/repository.rs @@ -133,7 +133,13 @@ impl ThreadSafeRepository { .map(|cd| git_dir.join(cd)); let common_dir_ref = common_dir.as_deref().unwrap_or(&git_dir); - let repo_config = config::cache::StageOne::new(common_dir_ref, git_dir_trust, lossy_config, lenient_config)?; + let repo_config = config::cache::StageOne::new( + common_dir_ref, + git_dir.as_ref(), + git_dir_trust, + lossy_config, + lenient_config, + )?; let mut refs = { let reflog = repo_config.reflog.unwrap_or(git_ref::store::WriteReflog::Disable); let object_hash = repo_config.object_hash; From 7ebf229bec6075a149702273c86137f54ef721ed Mon Sep 17 00:00:00 2001 From: Sidney Douw Date: Thu, 1 Dec 2022 11:08:21 +0100 Subject: [PATCH 04/14] =?UTF-8?q?remove=20worktree=20permission=20configur?= =?UTF-8?q?ation=20option=E2=80=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit …if worktree configs exist they should always be read --- git-repository/src/repository/permissions.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/git-repository/src/repository/permissions.rs b/git-repository/src/repository/permissions.rs index aab98c6799b..970add1058c 100644 --- a/git-repository/src/repository/permissions.rs +++ b/git-repository/src/repository/permissions.rs @@ -29,9 +29,6 @@ pub struct Config { /// Whether to use the user configuration. /// This is usually `~/.gitconfig` on unix. pub user: bool, - // TODO: figure out how this really applies and provide more information here. - // Whether to use worktree configuration from `config.worktree`. - // pub worktree: bool, /// Whether to use the configuration from environment variables. pub env: bool, /// Whether to follow include files are encountered in loaded configuration, From 394aab90bdcaa1683b0318e70c455d09b1a7d4cc Mon Sep 17 00:00:00 2001 From: Sidney Douw Date: Thu, 1 Dec 2022 11:17:40 +0100 Subject: [PATCH 05/14] =?UTF-8?q?refactor=E2=80=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit …reuse buffer instead of appending configs --- git-repository/src/config/cache/incubate.rs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/git-repository/src/config/cache/incubate.rs b/git-repository/src/config/cache/incubate.rs index 401afccd0aa..c516e5d3ec6 100644 --- a/git-repository/src/config/cache/incubate.rs +++ b/git-repository/src/config/cache/incubate.rs @@ -22,7 +22,7 @@ impl StageOne { lenient: bool, ) -> Result { let mut buf = Vec::with_capacity(512); - let mut config = load_config(&mut buf, common_dir.join("config"), git_dir_trust, lossy)?; + let config = load_config(&mut buf, common_dir.join("config"), git_dir_trust, lossy)?; let is_bare = util::config_bool(&config, "core.bare", false, lenient)?; let repo_format_version = config @@ -44,10 +44,12 @@ impl StageOne { .transpose()? .unwrap_or(git_hash::Kind::Sha1); - if util::config_bool(&config, "extensions.worktreeConfig", false, lenient)? { - let wt_config = load_config(&mut buf, git_dir.join("config.worktree"), git_dir_trust, lossy)?; - config.append(wt_config); - } + let extension_worktree = util::config_bool(&config, "extensions.worktreeConfig", false, lenient)?; + let config = if extension_worktree { + load_config(&mut buf, git_dir.join("config.worktree"), git_dir_trust, lossy)? + } else { + config + }; let reflog = util::query_refupdates(&config, lenient)?; Ok(StageOne { From ae812bde55d55ce06f95ca73513d9749e876ea0e Mon Sep 17 00:00:00 2001 From: Sidney Douw Date: Thu, 1 Dec 2022 11:28:13 +0100 Subject: [PATCH 06/14] =?UTF-8?q?refactor=E2=80=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit …add shared config entry to the fixture and check it in tests --- .../make_worktree_repo_with_configs.tar.xz | 4 ++-- .../fixtures/make_worktree_repo_with_configs.sh | 2 ++ git-repository/tests/repository/config/worktree.rs | 12 ++++++++++++ 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/git-repository/tests/fixtures/generated-archives/make_worktree_repo_with_configs.tar.xz b/git-repository/tests/fixtures/generated-archives/make_worktree_repo_with_configs.tar.xz index 8a3d24f6b01..9b1344fbf5a 100644 --- a/git-repository/tests/fixtures/generated-archives/make_worktree_repo_with_configs.tar.xz +++ b/git-repository/tests/fixtures/generated-archives/make_worktree_repo_with_configs.tar.xz @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ea82cbcda5a07a6e7ddde7d9e3834d1ec7e184811ccab29c7ea21d9a53705f1f -size 10564 +oid sha256:611d290ed94289d6ed037e1ba6edfb49cdd007fb4c478147bd10f90a7ef2c515 +size 10576 diff --git a/git-repository/tests/fixtures/make_worktree_repo_with_configs.sh b/git-repository/tests/fixtures/make_worktree_repo_with_configs.sh index d3960b4cb25..f7bc178fe17 100644 --- a/git-repository/tests/fixtures/make_worktree_repo_with_configs.sh +++ b/git-repository/tests/fixtures/make_worktree_repo_with_configs.sh @@ -14,6 +14,8 @@ git init repo git config extensions.worktreeConfig true git config --worktree worktree.setting "set in the main worktree" + + git config shared.setting "set in the shared config" ) (cd wt-1 diff --git a/git-repository/tests/repository/config/worktree.rs b/git-repository/tests/repository/config/worktree.rs index 1b744df0c15..7ab707eb129 100644 --- a/git-repository/tests/repository/config/worktree.rs +++ b/git-repository/tests/repository/config/worktree.rs @@ -15,6 +15,10 @@ fn with_worktree_configs() -> git_testtools::Result { base_config.string("worktree.setting").expect("exists").to_string(), "set in the main worktree" ); + assert_eq!( + base_config.string("shared.setting").expect("exists").to_string(), + "set in the shared config" + ); let wt1 = open(dir.join("wt-1")).unwrap(); let wt1_config = wt1.config_snapshot(); @@ -29,6 +33,10 @@ fn with_worktree_configs() -> git_testtools::Result { wt1_config.string("worktree.setting").expect("exists").to_string(), "set in wt-1" ); + assert_eq!( + wt1_config.string("shared.setting").expect("exists").to_string(), + "set in the shared config" + ); let wt2 = open(dir.join("wt-2")).unwrap(); let wt2_config = wt2.config_snapshot(); @@ -43,6 +51,10 @@ fn with_worktree_configs() -> git_testtools::Result { wt2_config.string("worktree.setting").expect("exists").to_string(), "set in wt-2" ); + assert_eq!( + wt2_config.string("shared.setting").expect("exists").to_string(), + "set in the shared config" + ); Ok(()) } From 5c2b44c53feae9f23c715dbad962baaf64135963 Mon Sep 17 00:00:00 2001 From: Sidney Douw Date: Thu, 1 Dec 2022 12:55:03 +0100 Subject: [PATCH 07/14] =?UTF-8?q?exclude=20fixture=20archive=20from=20bein?= =?UTF-8?q?g=20uploaded=E2=80=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit …because creating worktrees with `git` creates a .git file in the worktree that points to the git_dir using an absolute path, so every tester has to generate that fixture from scratch. --- git-repository/tests/fixtures/generated-archives/.gitignore | 1 + .../generated-archives/make_worktree_repo_with_configs.tar.xz | 3 --- 2 files changed, 1 insertion(+), 3 deletions(-) delete mode 100644 git-repository/tests/fixtures/generated-archives/make_worktree_repo_with_configs.tar.xz diff --git a/git-repository/tests/fixtures/generated-archives/.gitignore b/git-repository/tests/fixtures/generated-archives/.gitignore index 9f4f7db5774..0a5636787b7 100644 --- a/git-repository/tests/fixtures/generated-archives/.gitignore +++ b/git-repository/tests/fixtures/generated-archives/.gitignore @@ -1,4 +1,5 @@ /make_worktree_repo.tar.xz +/make_worktree_repo_with_configs.tar /make_remote_repos.tar.xz /make_fetch_repos.tar.xz /make_core_worktree_repo.tar.xz diff --git a/git-repository/tests/fixtures/generated-archives/make_worktree_repo_with_configs.tar.xz b/git-repository/tests/fixtures/generated-archives/make_worktree_repo_with_configs.tar.xz deleted file mode 100644 index 9b1344fbf5a..00000000000 --- a/git-repository/tests/fixtures/generated-archives/make_worktree_repo_with_configs.tar.xz +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:611d290ed94289d6ed037e1ba6edfb49cdd007fb4c478147bd10f90a7ef2c515 -size 10576 From 33992ab6510c65dc97e5eb9565141b977f5b021f Mon Sep 17 00:00:00 2001 From: Sidney Douw Date: Thu, 1 Dec 2022 13:13:01 +0100 Subject: [PATCH 08/14] fix type - prevent creating a fixture archive --- git-repository/tests/fixtures/generated-archives/.gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/git-repository/tests/fixtures/generated-archives/.gitignore b/git-repository/tests/fixtures/generated-archives/.gitignore index 0a5636787b7..8895a24ab87 100644 --- a/git-repository/tests/fixtures/generated-archives/.gitignore +++ b/git-repository/tests/fixtures/generated-archives/.gitignore @@ -1,5 +1,5 @@ /make_worktree_repo.tar.xz -/make_worktree_repo_with_configs.tar +/make_worktree_repo_with_configs.tar.xz /make_remote_repos.tar.xz /make_fetch_repos.tar.xz /make_core_worktree_repo.tar.xz From bea689a97a8e42a92af7f77f7d8706cd96c6dc10 Mon Sep 17 00:00:00 2001 From: Sidney Douw Date: Thu, 1 Dec 2022 14:12:42 +0100 Subject: [PATCH 09/14] =?UTF-8?q?remove=20`canonicalize`=20calls=20from=20?= =?UTF-8?q?test=E2=80=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit …and use CARGO_MANIFEST_DIR instead --- .../tests/repository/config/worktree.rs | 34 ++++++++++++------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/git-repository/tests/repository/config/worktree.rs b/git-repository/tests/repository/config/worktree.rs index 7ab707eb129..81fdebf92f7 100644 --- a/git-repository/tests/repository/config/worktree.rs +++ b/git-repository/tests/repository/config/worktree.rs @@ -2,14 +2,16 @@ use git_repository::open; #[test] fn with_worktree_configs() -> git_testtools::Result { - let dir = git_testtools::scripted_fixture_repo_read_only("make_worktree_repo_with_configs.sh").unwrap(); + let manifest_dir = std::path::PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").unwrap()); - let base = open(dir.join("repo")).unwrap(); + let fixture_dir = git_testtools::scripted_fixture_repo_read_only("make_worktree_repo_with_configs.sh").unwrap(); + + let base = open(fixture_dir.join("repo")).unwrap(); let base_config = base.config_snapshot(); - assert_eq!(base.work_dir(), Some(dir.join("repo").as_path())); - assert_eq!(base.git_dir(), dir.join("repo/.git")); - assert_eq!(base.common_dir(), dir.join("repo/.git")); + assert_eq!(base.work_dir(), Some(fixture_dir.join("repo").as_path())); + assert_eq!(base.git_dir(), fixture_dir.join("repo/.git")); + assert_eq!(base.common_dir(), fixture_dir.join("repo/.git")); assert_eq!( base_config.string("worktree.setting").expect("exists").to_string(), @@ -20,13 +22,16 @@ fn with_worktree_configs() -> git_testtools::Result { "set in the shared config" ); - let wt1 = open(dir.join("wt-1")).unwrap(); + let wt1 = open(fixture_dir.join("wt-1")).unwrap(); let wt1_config = wt1.config_snapshot(); - assert_eq!(wt1.work_dir(), Some(dir.join("wt-1").as_path())); - assert_eq!(wt1.git_dir(), dir.join("repo/.git/worktrees/wt-1").canonicalize()?); + assert_eq!(wt1.work_dir(), Some(fixture_dir.join("wt-1").as_path())); + assert_eq!( + wt1.git_dir(), + manifest_dir.join(&fixture_dir).join("repo/.git/worktrees/wt-1") + ); assert_eq!( wt1.common_dir(), - dir.join("repo/.git").canonicalize()?.join("worktrees/wt-1/../..") + manifest_dir.join(&fixture_dir).join("repo/.git/worktrees/wt-1/../..") ); assert_eq!( @@ -38,13 +43,16 @@ fn with_worktree_configs() -> git_testtools::Result { "set in the shared config" ); - let wt2 = open(dir.join("wt-2")).unwrap(); + let wt2 = open(fixture_dir.join("wt-2")).unwrap(); let wt2_config = wt2.config_snapshot(); - assert_eq!(wt2.work_dir(), Some(dir.join("wt-2").as_path())); - assert_eq!(wt2.git_dir(), dir.join("repo/.git/worktrees/wt-2").canonicalize()?); + assert_eq!(wt2.work_dir(), Some(fixture_dir.join("wt-2").as_path())); + assert_eq!( + wt2.git_dir(), + manifest_dir.join(&fixture_dir).join("repo/.git/worktrees/wt-2") + ); assert_eq!( wt2.common_dir(), - dir.join("repo/.git").canonicalize()?.join("worktrees/wt-2/../..") + manifest_dir.join(&fixture_dir).join("repo/.git/worktrees/wt-2/../..") ); assert_eq!( From b69f21997bac7751e879608fe5b0ba08814aab4d Mon Sep 17 00:00:00 2001 From: Sidney Douw Date: Fri, 2 Dec 2022 18:14:24 +0100 Subject: [PATCH 10/14] test to check if worktree overrides shared configs --- .../fixtures/make_worktree_repo_with_configs.sh | 2 ++ git-repository/tests/repository/config/worktree.rs | 12 ++++++++++++ 2 files changed, 14 insertions(+) diff --git a/git-repository/tests/fixtures/make_worktree_repo_with_configs.sh b/git-repository/tests/fixtures/make_worktree_repo_with_configs.sh index f7bc178fe17..f1c046d7987 100644 --- a/git-repository/tests/fixtures/make_worktree_repo_with_configs.sh +++ b/git-repository/tests/fixtures/make_worktree_repo_with_configs.sh @@ -16,6 +16,7 @@ git init repo git config --worktree worktree.setting "set in the main worktree" git config shared.setting "set in the shared config" + git config override.setting "set in the shared config" ) (cd wt-1 @@ -24,4 +25,5 @@ git init repo (cd wt-2 git config --worktree worktree.setting "set in wt-2" + git config --worktree override.setting "override in wt-2" ) diff --git a/git-repository/tests/repository/config/worktree.rs b/git-repository/tests/repository/config/worktree.rs index 81fdebf92f7..af047b0e810 100644 --- a/git-repository/tests/repository/config/worktree.rs +++ b/git-repository/tests/repository/config/worktree.rs @@ -21,6 +21,10 @@ fn with_worktree_configs() -> git_testtools::Result { base_config.string("shared.setting").expect("exists").to_string(), "set in the shared config" ); + assert_eq!( + base_config.string("override.setting").expect("exists").to_string(), + "set in the shared config" + ); let wt1 = open(fixture_dir.join("wt-1")).unwrap(); let wt1_config = wt1.config_snapshot(); @@ -42,6 +46,10 @@ fn with_worktree_configs() -> git_testtools::Result { wt1_config.string("shared.setting").expect("exists").to_string(), "set in the shared config" ); + assert_eq!( + wt1_config.string("override.setting").expect("exists").to_string(), + "set in the shared config" + ); let wt2 = open(fixture_dir.join("wt-2")).unwrap(); let wt2_config = wt2.config_snapshot(); @@ -63,6 +71,10 @@ fn with_worktree_configs() -> git_testtools::Result { wt2_config.string("shared.setting").expect("exists").to_string(), "set in the shared config" ); + assert_eq!( + wt2_config.string("override.setting").expect("exists").to_string(), + "override in wt-2" + ); Ok(()) } From a191948b758ab4e06a19eef748f16a5f458fe477 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Sat, 3 Dec 2022 20:00:19 +0100 Subject: [PATCH 11/14] Assure that worktree configuration is marked as such with `Source::Worktree`. This also resets the original behaviour of reading `$COMMON_DIR/config` as `Source::Local`. --- git-repository/src/config/cache/incubate.rs | 27 +++++++++++++++------ 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/git-repository/src/config/cache/incubate.rs b/git-repository/src/config/cache/incubate.rs index c516e5d3ec6..7861b643f2d 100644 --- a/git-repository/src/config/cache/incubate.rs +++ b/git-repository/src/config/cache/incubate.rs @@ -22,7 +22,13 @@ impl StageOne { lenient: bool, ) -> Result { let mut buf = Vec::with_capacity(512); - let config = load_config(&mut buf, common_dir.join("config"), git_dir_trust, lossy)?; + let mut config = load_config( + common_dir.join("config"), + &mut buf, + git_config::Source::Local, + git_dir_trust, + lossy, + )?; let is_bare = util::config_bool(&config, "core.bare", false, lenient)?; let repo_format_version = config @@ -45,10 +51,15 @@ impl StageOne { .unwrap_or(git_hash::Kind::Sha1); let extension_worktree = util::config_bool(&config, "extensions.worktreeConfig", false, lenient)?; - let config = if extension_worktree { - load_config(&mut buf, git_dir.join("config.worktree"), git_dir_trust, lossy)? - } else { - config + if extension_worktree { + let worktree_config = load_config( + git_dir.join("config.worktree"), + &mut buf, + git_config::Source::Worktree, + git_dir_trust, + lossy, + )?; + config.append(worktree_config); }; let reflog = util::query_refupdates(&config, lenient)?; @@ -64,16 +75,18 @@ impl StageOne { } fn load_config( - buf: &mut Vec, config_path: std::path::PathBuf, + buf: &mut Vec, + source: git_config::Source, git_dir_trust: git_sec::Trust, lossy: Option, ) -> Result, Error> { + buf.clear(); std::io::copy(&mut std::fs::File::open(&config_path)?, buf)?; let config = git_config::File::from_bytes_owned( buf, - git_config::file::Metadata::from(git_config::Source::Worktree) + git_config::file::Metadata::from(source) .at(config_path) .with(git_dir_trust), git_config::file::init::Options { From 75488a7d91abb90337d42f04e86e3d1373b8c19e Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Sat, 3 Dec 2022 20:25:26 +0100 Subject: [PATCH 12/14] improve documentation about the configuration we always load --- git-repository/src/repository/permissions.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/git-repository/src/repository/permissions.rs b/git-repository/src/repository/permissions.rs index 970add1058c..b1d51364c46 100644 --- a/git-repository/src/repository/permissions.rs +++ b/git-repository/src/repository/permissions.rs @@ -9,7 +9,9 @@ pub struct Permissions { pub config: Config, } -/// Configure security relevant options when loading a git configuration. +/// Configure from which sources git configuration may be loaded. +/// +/// Note that configuration from inside of the repository is always loaded as it's definitely required for correctness. #[derive(Copy, Clone, Ord, PartialOrd, PartialEq, Eq, Debug, Hash)] pub struct Config { /// The git binary may come with configuration as part of its configuration, and if this is true (default false) From 62afb7ba87311c5b04c8cd8002308d1b44959131 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Sat, 3 Dec 2022 20:27:39 +0100 Subject: [PATCH 13/14] move tests::repository::config::worktree to `tests::repository::open::worktree` As these settings are relevant during opening and aren't queried later as part of implementations in `repository::config` or `config`. --- git-repository/tests/repository/config/mod.rs | 1 - .../tests/repository/config/worktree.rs | 80 ------------------ git-repository/tests/repository/open.rs | 83 +++++++++++++++++++ 3 files changed, 83 insertions(+), 81 deletions(-) delete mode 100644 git-repository/tests/repository/config/worktree.rs diff --git a/git-repository/tests/repository/config/mod.rs b/git-repository/tests/repository/config/mod.rs index 5465c7fa2fd..5b82895d3af 100644 --- a/git-repository/tests/repository/config/mod.rs +++ b/git-repository/tests/repository/config/mod.rs @@ -3,4 +3,3 @@ mod identity; mod remote; #[cfg(any(feature = "blocking-network-client", feature = "async-network-client"))] mod transport_options; -mod worktree; diff --git a/git-repository/tests/repository/config/worktree.rs b/git-repository/tests/repository/config/worktree.rs deleted file mode 100644 index af047b0e810..00000000000 --- a/git-repository/tests/repository/config/worktree.rs +++ /dev/null @@ -1,80 +0,0 @@ -use git_repository::open; - -#[test] -fn with_worktree_configs() -> git_testtools::Result { - let manifest_dir = std::path::PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").unwrap()); - - let fixture_dir = git_testtools::scripted_fixture_repo_read_only("make_worktree_repo_with_configs.sh").unwrap(); - - let base = open(fixture_dir.join("repo")).unwrap(); - let base_config = base.config_snapshot(); - - assert_eq!(base.work_dir(), Some(fixture_dir.join("repo").as_path())); - assert_eq!(base.git_dir(), fixture_dir.join("repo/.git")); - assert_eq!(base.common_dir(), fixture_dir.join("repo/.git")); - - assert_eq!( - base_config.string("worktree.setting").expect("exists").to_string(), - "set in the main worktree" - ); - assert_eq!( - base_config.string("shared.setting").expect("exists").to_string(), - "set in the shared config" - ); - assert_eq!( - base_config.string("override.setting").expect("exists").to_string(), - "set in the shared config" - ); - - let wt1 = open(fixture_dir.join("wt-1")).unwrap(); - let wt1_config = wt1.config_snapshot(); - assert_eq!(wt1.work_dir(), Some(fixture_dir.join("wt-1").as_path())); - assert_eq!( - wt1.git_dir(), - manifest_dir.join(&fixture_dir).join("repo/.git/worktrees/wt-1") - ); - assert_eq!( - wt1.common_dir(), - manifest_dir.join(&fixture_dir).join("repo/.git/worktrees/wt-1/../..") - ); - - assert_eq!( - wt1_config.string("worktree.setting").expect("exists").to_string(), - "set in wt-1" - ); - assert_eq!( - wt1_config.string("shared.setting").expect("exists").to_string(), - "set in the shared config" - ); - assert_eq!( - wt1_config.string("override.setting").expect("exists").to_string(), - "set in the shared config" - ); - - let wt2 = open(fixture_dir.join("wt-2")).unwrap(); - let wt2_config = wt2.config_snapshot(); - assert_eq!(wt2.work_dir(), Some(fixture_dir.join("wt-2").as_path())); - assert_eq!( - wt2.git_dir(), - manifest_dir.join(&fixture_dir).join("repo/.git/worktrees/wt-2") - ); - assert_eq!( - wt2.common_dir(), - manifest_dir.join(&fixture_dir).join("repo/.git/worktrees/wt-2/../..") - ); - - assert_eq!( - wt2_config.string("worktree.setting").expect("exists").to_string(), - "set in wt-2" - ); - assert_eq!( - wt2_config.string("shared.setting").expect("exists").to_string(), - "set in the shared config" - ); - assert_eq!( - wt2_config.string("override.setting").expect("exists").to_string(), - "override in wt-2" - ); - - Ok(()) -} diff --git a/git-repository/tests/repository/open.rs b/git-repository/tests/repository/open.rs index a0fdc013027..5cbd24e8007 100644 --- a/git-repository/tests/repository/open.rs +++ b/git-repository/tests/repository/open.rs @@ -225,3 +225,86 @@ mod with_overrides { Cow::Borrowed(s.into()) } } + +mod worktree { + use git_repository::open; + + #[test] + fn with_worktree_configs() -> git_testtools::Result { + let manifest_dir = std::path::PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").unwrap()); + + let fixture_dir = git_testtools::scripted_fixture_repo_read_only("make_worktree_repo_with_configs.sh").unwrap(); + + let base = open(fixture_dir.join("repo")).unwrap(); + let base_config = base.config_snapshot(); + + assert_eq!(base.work_dir(), Some(fixture_dir.join("repo").as_path())); + assert_eq!(base.git_dir(), fixture_dir.join("repo/.git")); + assert_eq!(base.common_dir(), fixture_dir.join("repo/.git")); + + assert_eq!( + base_config.string("worktree.setting").expect("exists").to_string(), + "set in the main worktree" + ); + assert_eq!( + base_config.string("shared.setting").expect("exists").to_string(), + "set in the shared config" + ); + assert_eq!( + base_config.string("override.setting").expect("exists").to_string(), + "set in the shared config" + ); + + let wt1 = open(fixture_dir.join("wt-1")).unwrap(); + let wt1_config = wt1.config_snapshot(); + assert_eq!(wt1.work_dir(), Some(fixture_dir.join("wt-1").as_path())); + assert_eq!( + wt1.git_dir(), + manifest_dir.join(&fixture_dir).join("repo/.git/worktrees/wt-1") + ); + assert_eq!( + wt1.common_dir(), + manifest_dir.join(&fixture_dir).join("repo/.git/worktrees/wt-1/../..") + ); + + assert_eq!( + wt1_config.string("worktree.setting").expect("exists").to_string(), + "set in wt-1" + ); + assert_eq!( + wt1_config.string("shared.setting").expect("exists").to_string(), + "set in the shared config" + ); + assert_eq!( + wt1_config.string("override.setting").expect("exists").to_string(), + "set in the shared config" + ); + + let wt2 = open(fixture_dir.join("wt-2")).unwrap(); + let wt2_config = wt2.config_snapshot(); + assert_eq!(wt2.work_dir(), Some(fixture_dir.join("wt-2").as_path())); + assert_eq!( + wt2.git_dir(), + manifest_dir.join(&fixture_dir).join("repo/.git/worktrees/wt-2") + ); + assert_eq!( + wt2.common_dir(), + manifest_dir.join(&fixture_dir).join("repo/.git/worktrees/wt-2/../..") + ); + + assert_eq!( + wt2_config.string("worktree.setting").expect("exists").to_string(), + "set in wt-2" + ); + assert_eq!( + wt2_config.string("shared.setting").expect("exists").to_string(), + "set in the shared config" + ); + assert_eq!( + wt2_config.string("override.setting").expect("exists").to_string(), + "override in wt-2" + ); + + Ok(()) + } +} From 2d83222dbf607f78acad4874580d1f007d838c13 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Sat, 3 Dec 2022 20:51:47 +0100 Subject: [PATCH 14/14] refactor This is just to keep in-code notes in the form of assertion messages, after validating each assertion one by one and forcing myself to understand them. --- git-repository/tests/repository/open.rs | 150 +++++++++++++----------- 1 file changed, 84 insertions(+), 66 deletions(-) diff --git a/git-repository/tests/repository/open.rs b/git-repository/tests/repository/open.rs index 5cbd24e8007..3768f74c5e3 100644 --- a/git-repository/tests/repository/open.rs +++ b/git-repository/tests/repository/open.rs @@ -231,79 +231,97 @@ mod worktree { #[test] fn with_worktree_configs() -> git_testtools::Result { - let manifest_dir = std::path::PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").unwrap()); + let manifest_dir = std::path::PathBuf::from(std::env::var("CARGO_MANIFEST_DIR")?); + let fixture_dir = git_testtools::scripted_fixture_repo_read_only("make_worktree_repo_with_configs.sh")?; + let worktree_base = manifest_dir.join(&fixture_dir).join("repo/.git/worktrees"); - let fixture_dir = git_testtools::scripted_fixture_repo_read_only("make_worktree_repo_with_configs.sh").unwrap(); + { + let base = open(fixture_dir.join("repo"))?; + let base_config = base.config_snapshot(); - let base = open(fixture_dir.join("repo")).unwrap(); - let base_config = base.config_snapshot(); - - assert_eq!(base.work_dir(), Some(fixture_dir.join("repo").as_path())); - assert_eq!(base.git_dir(), fixture_dir.join("repo/.git")); - assert_eq!(base.common_dir(), fixture_dir.join("repo/.git")); + assert_eq!( + base.work_dir(), + Some(fixture_dir.join("repo").as_path()), + "the main worktree" + ); + assert_eq!(base.git_dir(), fixture_dir.join("repo/.git"), "git dir and…"); + assert_eq!( + base.common_dir(), + fixture_dir.join("repo/.git"), + "…common dir are the same" + ); - assert_eq!( - base_config.string("worktree.setting").expect("exists").to_string(), - "set in the main worktree" - ); - assert_eq!( - base_config.string("shared.setting").expect("exists").to_string(), - "set in the shared config" - ); - assert_eq!( - base_config.string("override.setting").expect("exists").to_string(), - "set in the shared config" - ); + assert_eq!( + base_config.string("worktree.setting").expect("exists").as_ref(), + "set in the main worktree" + ); + assert_eq!( + base_config.string("shared.setting").expect("exists").as_ref(), + "set in the shared config" + ); + assert_eq!( + base_config.string("override.setting").expect("exists").as_ref(), + "set in the shared config" + ); + } - let wt1 = open(fixture_dir.join("wt-1")).unwrap(); - let wt1_config = wt1.config_snapshot(); - assert_eq!(wt1.work_dir(), Some(fixture_dir.join("wt-1").as_path())); - assert_eq!( - wt1.git_dir(), - manifest_dir.join(&fixture_dir).join("repo/.git/worktrees/wt-1") - ); - assert_eq!( - wt1.common_dir(), - manifest_dir.join(&fixture_dir).join("repo/.git/worktrees/wt-1/../..") - ); + { + let wt1 = open(fixture_dir.join("wt-1"))?; + let wt1_config = wt1.config_snapshot(); + assert_eq!( + wt1.work_dir(), + Some(fixture_dir.join("wt-1").as_path()), + "a linked worktree in its own location" + ); + assert_eq!( + wt1.git_dir(), + worktree_base.join("wt-1"), + "whose git-dir is within the common dir" + ); + assert_eq!( + wt1.common_dir(), + worktree_base.join("wt-1/../.."), + "the common dir is the `git-dir` of the repository with the main worktree" + ); - assert_eq!( - wt1_config.string("worktree.setting").expect("exists").to_string(), - "set in wt-1" - ); - assert_eq!( - wt1_config.string("shared.setting").expect("exists").to_string(), - "set in the shared config" - ); - assert_eq!( - wt1_config.string("override.setting").expect("exists").to_string(), - "set in the shared config" - ); + assert_eq!( + wt1_config.string("worktree.setting").expect("exists").as_ref(), + "set in wt-1" + ); + assert_eq!( + wt1_config.string("shared.setting").expect("exists").as_ref(), + "set in the shared config" + ); + assert_eq!( + wt1_config.string("override.setting").expect("exists").as_ref(), + "set in the shared config" + ); + } - let wt2 = open(fixture_dir.join("wt-2")).unwrap(); - let wt2_config = wt2.config_snapshot(); - assert_eq!(wt2.work_dir(), Some(fixture_dir.join("wt-2").as_path())); - assert_eq!( - wt2.git_dir(), - manifest_dir.join(&fixture_dir).join("repo/.git/worktrees/wt-2") - ); - assert_eq!( - wt2.common_dir(), - manifest_dir.join(&fixture_dir).join("repo/.git/worktrees/wt-2/../..") - ); + { + let wt2 = open(fixture_dir.join("wt-2"))?; + let wt2_config = wt2.config_snapshot(); + assert_eq!( + wt2.work_dir(), + Some(fixture_dir.join("wt-2").as_path()), + "another linked worktree as sibling to wt-1" + ); + assert_eq!(wt2.git_dir(), worktree_base.join("wt-2")); + assert_eq!(wt2.common_dir(), worktree_base.join("wt-2/../..")); - assert_eq!( - wt2_config.string("worktree.setting").expect("exists").to_string(), - "set in wt-2" - ); - assert_eq!( - wt2_config.string("shared.setting").expect("exists").to_string(), - "set in the shared config" - ); - assert_eq!( - wt2_config.string("override.setting").expect("exists").to_string(), - "override in wt-2" - ); + assert_eq!( + wt2_config.string("worktree.setting").expect("exists").as_ref(), + "set in wt-2" + ); + assert_eq!( + wt2_config.string("shared.setting").expect("exists").as_ref(), + "set in the shared config" + ); + assert_eq!( + wt2_config.string("override.setting").expect("exists").as_ref(), + "override in wt-2" + ); + } Ok(()) }