The path to git repositories is well-known as they either end in `.git` or `.../.git`. If this is not the case, by default we append `/.git` to the path. With this new option enabled, no path transformations apply to open the given path as is, which is preferable if you know it's a non-standard git repository folder name.
Unified · Split
| @@ -23,6 +23,7 @@ pub struct Options { · 22 unmodified lines | ||
| 23 | 23 | pub(crate) bail_if_untrusted: bool, |
| 24 | 24 | pub(crate) api_config_overrides: Vec<BString>, |
| 25 | 25 | pub(crate) cli_config_overrides: Vec<BString>, |
| 26 | + pub(crate) open_path_as_is: bool, | |
| 26 | 27 | /// Internal to pass an already obtained CWD on to where it may also be used. This avoids the CWD being queried more than once per repo. |
| 27 | 28 | pub(crate) current_dir: Option<PathBuf>, |
| 28 | 29 | } |
| @@ -13,6 +13,7 @@ impl Default for Options { · 12 unmodified lines | ||
| 13 | 13 | lossy_config: None, |
| 14 | 14 | lenient_config: true, |
| 15 | 15 | bail_if_untrusted: false, |
| 16 | + open_path_as_is: false, | |
| 16 | 17 | api_config_overrides: Vec::new(), |
| 17 | 18 | cli_config_overrides: Vec::new(), |
| 18 | 19 | current_dir: None, |
| @@ -69,6 +70,15 @@ impl Options { · 50 unmodified lines | ||
| 69 | 70 | self |
| 70 | 71 | } |
| 71 | 72 | |
| 73 | + /// If `true`, default `false`, we will not modify the incoming path to open to assure it is a `.git` directory. | |
| 74 | + /// | |
| 75 | + /// If `false`, we will try to open the input directory as is, even though it doesn't appear to be a `git` repository | |
| 76 | + /// due to the lack of `.git` suffix or because its basename is not `.git` as in `worktree/.git`. | |
| 77 | + pub fn open_path_as_is(mut self, enable: bool) -> Self { | |
| 78 | + self.open_path_as_is = enable; | |
| 79 | + self | |
| 80 | + } | |
| 81 | + | |
| 72 | 82 | /// Set the trust level of the `.git` directory we are about to open. |
| 73 | 83 | /// |
| 74 | 84 | /// This can be set manually to force trust even though otherwise it might |
| @@ -146,6 +156,7 @@ impl git_sec::trust::DefaultForLevel for Options { · 71 unmodified lines | ||
| 146 | 156 | lossy_config: None, |
| 147 | 157 | bail_if_untrusted: false, |
| 148 | 158 | lenient_config: true, |
| 159 | + open_path_as_is: false, | |
| 149 | 160 | api_config_overrides: Vec::new(), |
| 150 | 161 | cli_config_overrides: Vec::new(), |
| 151 | 162 | current_dir: None, |
| @@ -157,6 +168,7 @@ impl git_sec::trust::DefaultForLevel for Options { · 5 unmodified lines | ||
| 157 | 168 | filter_config_section: Some(config::section::is_trusted), |
| 158 | 169 | bail_if_untrusted: false, |
| 159 | 170 | lenient_config: true, |
| 171 | + open_path_as_is: false, | |
| 160 | 172 | lossy_config: None, |
| 161 | 173 | api_config_overrides: Vec::new(), |
| 162 | 174 | cli_config_overrides: Vec::new(), |
| @@ -48,13 +48,26 @@ impl ThreadSafeRepository { · 47 unmodified lines | ||
| 48 | 48 | pub fn open_opts(path: impl Into<PathBuf>, mut options: Options) -> Result<Self, Error> { |
| 49 | 49 | let (path, kind) = { |
| 50 | 50 | let path = path.into(); |
| 51 | − match git_discover::is_git(&path) { | |
| 52 | − Ok(kind) => (path, kind), | |
| 53 | − Err(_err) => { | |
| 54 | − let git_dir = path.join(git_discover::DOT_GIT_DIR); | |
| 55 | − git_discover::is_git(&git_dir) | |
| 56 | − .map(|kind| (git_dir, kind)) | |
| 57 | − .map_err(|err| Error::NotARepository { source: err, path })? | |
| 51 | + let looks_like_git_dir = | |
| 52 | + path.ends_with(git_discover::DOT_GIT_DIR) || path.extension() == Some(std::ffi::OsStr::new("git")); | |
| 53 | + let candidate = if !options.open_path_as_is && !looks_like_git_dir { | |
| 54 | + Cow::Owned(path.join(git_discover::DOT_GIT_DIR)) | |
| 55 | + } else { | |
| 56 | + Cow::Borrowed(&path) | |
| 57 | + }; | |
| 58 | + match git_discover::is_git(candidate.as_ref()) { | |
| 59 | + Ok(kind) => (candidate.into_owned(), kind), | |
| 60 | + Err(err) => { | |
| 61 | + if options.open_path_as_is || matches!(candidate, Cow::Borrowed(_)) { | |
| 62 | + return Err(Error::NotARepository { | |
| 63 | + source: err, | |
| 64 | + path: candidate.into_owned(), | |
| 65 | + }); | |
| 66 | + } | |
| 67 | + match git_discover::is_git(&path) { | |
| 68 | + Ok(kind) => (path, kind), | |
| 69 | + Err(err) => return Err(Error::NotARepository { source: err, path }), | |
| 70 | + } | |
| 58 | 71 | } |
| 59 | 72 | } |
| 60 | 73 | }; |
| @@ -128,6 +141,7 @@ impl ThreadSafeRepository { · 67 unmodified lines | ||
| 128 | 141 | lossy_config, |
| 129 | 142 | lenient_config, |
| 130 | 143 | bail_if_untrusted, |
| 144 | + open_path_as_is: _, | |
| 131 | 145 | permissions: Permissions { ref env, config }, |
| 132 | 146 | ref api_config_overrides, |
| 133 | 147 | ref cli_config_overrides, |
| @@ -46,6 +46,34 @@ mod not_a_repository { · 45 unmodified lines | ||
| 46 | 46 | } |
| 47 | 47 | } |
| 48 | 48 | |
| 49 | +mod open_path_as_is { | |
| 50 | + use crate::util::{named_subrepo_opts, repo_opts}; | |
| 51 | + use git_repository as git; | |
| 52 | + | |
| 53 | + fn open_path_as_is() -> git::open::Options { | |
| 54 | + git::open::Options::isolated().open_path_as_is(true) | |
| 55 | + } | |
| 56 | + | |
| 57 | + #[test] | |
| 58 | + fn bare_repos_open_normally() -> crate::Result { | |
| 59 | + assert!(named_subrepo_opts("make_basic_repo.sh", "bare.git", open_path_as_is())?.is_bare()); | |
| 60 | + Ok(()) | |
| 61 | + } | |
| 62 | + | |
| 63 | + #[test] | |
| 64 | + fn worktrees_cannot_be_opened() -> crate::Result { | |
| 65 | + let err = repo_opts("make_basic_repo.sh", open_path_as_is()).unwrap_err(); | |
| 66 | + assert!(matches!(err, git::open::Error::NotARepository { .. })); | |
| 67 | + Ok(()) | |
| 68 | + } | |
| 69 | + | |
| 70 | + #[test] | |
| 71 | + fn git_dir_within_worktrees_open_normally() -> crate::Result { | |
| 72 | + assert!(!named_subrepo_opts("make_basic_repo.sh", ".git", open_path_as_is())?.is_bare()); | |
| 73 | + Ok(()) | |
| 74 | + } | |
| 75 | +} | |
| 76 | + | |
| 49 | 77 | mod submodules { |
| 50 | 78 | use std::path::Path; |
| 51 | 79 | |
| @@ -18,6 +18,11 @@ pub fn repo(name: &str) -> Result<ThreadSafeRepository> { · 17 unmodified lines | ||
| 18 | 18 | Ok(ThreadSafeRepository::open_opts(repo_path, restricted())?) |
| 19 | 19 | } |
| 20 | 20 | |
| 21 | +pub fn repo_opts(name: &str, opts: open::Options) -> std::result::Result<ThreadSafeRepository, open::Error> { | |
| 22 | + let repo_path = git_testtools::scripted_fixture_read_only(name).unwrap(); | |
| 23 | + ThreadSafeRepository::open_opts(repo_path, opts) | |
| 24 | +} | |
| 25 | + | |
| 21 | 26 | pub fn named_repo(name: &str) -> Result<Repository> { |
| 22 | 27 | let repo_path = git_testtools::scripted_fixture_read_only(name)?; |
| 23 | 28 | Ok(ThreadSafeRepository::open_opts(repo_path, restricted())?.to_thread_local()) |