History

feat: `open::Options::open_path_as_is()` allows to avoid 'smart opening' to try the path verbatim.

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.

Sebastian Thiel committed 3 years ago

Commit ca84c871 parent 68ba9a05 files · +67 −7Browse files

Unified · Split

5 files
  1. git-repository/src/open/mod.rs +1 −0
  2. git-repository/src/open/options.rs +12 −0
  3. git-repository/src/open/repository.rs +21 −7
  4. git-repository/tests/repository/open.rs +28 −0
  5. git-repository/tests/util/mod.rs +5 −0
git-repository/src/open/mod.rs+1−0
@@ -23,6 +23,7 @@ pub struct Options { · 22 unmodified lines
23 pub(crate) bail_if_untrusted: bool,23 pub(crate) bail_if_untrusted: bool,
24 pub(crate) api_config_overrides: Vec<BString>,24 pub(crate) api_config_overrides: Vec<BString>,
25 pub(crate) cli_config_overrides: Vec<BString>,25 pub(crate) cli_config_overrides: Vec<BString>,
26 pub(crate) open_path_as_is: bool,
26 /// 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 /// 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 pub(crate) current_dir: Option<PathBuf>,28 pub(crate) current_dir: Option<PathBuf>,
28}29}
git-repository/src/open/options.rs+12−0
@@ -13,6 +13,7 @@ impl Default for Options { · 12 unmodified lines
13 lossy_config: None,13 lossy_config: None,
14 lenient_config: true,14 lenient_config: true,
15 bail_if_untrusted: false,15 bail_if_untrusted: false,
16 open_path_as_is: false,
16 api_config_overrides: Vec::new(),17 api_config_overrides: Vec::new(),
17 cli_config_overrides: Vec::new(),18 cli_config_overrides: Vec::new(),
18 current_dir: None,19 current_dir: None,
@@ -69,6 +70,15 @@ impl Options { · 50 unmodified lines
69 self70 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 /// Set the trust level of the `.git` directory we are about to open.82 /// Set the trust level of the `.git` directory we are about to open.
73 ///83 ///
74 /// This can be set manually to force trust even though otherwise it might84 /// 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 lossy_config: None,156 lossy_config: None,
147 bail_if_untrusted: false,157 bail_if_untrusted: false,
148 lenient_config: true,158 lenient_config: true,
159 open_path_as_is: false,
149 api_config_overrides: Vec::new(),160 api_config_overrides: Vec::new(),
150 cli_config_overrides: Vec::new(),161 cli_config_overrides: Vec::new(),
151 current_dir: None,162 current_dir: None,
@@ -157,6 +168,7 @@ impl git_sec::trust::DefaultForLevel for Options { · 5 unmodified lines
157 filter_config_section: Some(config::section::is_trusted),168 filter_config_section: Some(config::section::is_trusted),
158 bail_if_untrusted: false,169 bail_if_untrusted: false,
159 lenient_config: true,170 lenient_config: true,
171 open_path_as_is: false,
160 lossy_config: None,172 lossy_config: None,
161 api_config_overrides: Vec::new(),173 api_config_overrides: Vec::new(),
162 cli_config_overrides: Vec::new(),174 cli_config_overrides: Vec::new(),
git-repository/src/open/repository.rs+21−7
@@ -48,13 +48,26 @@ impl ThreadSafeRepository { · 47 unmodified lines
48 pub fn open_opts(path: impl Into<PathBuf>, mut options: Options) -> Result<Self, Error> {48 pub fn open_opts(path: impl Into<PathBuf>, mut options: Options) -> Result<Self, Error> {
49 let (path, kind) = {49 let (path, kind) = {
50 let path = path.into();50 let path = path.into();
51 match git_discover::is_git(&path) {51 let looks_like_git_dir =
52 Ok(kind) => (path, kind),52 path.ends_with(git_discover::DOT_GIT_DIR) || path.extension() == Some(std::ffi::OsStr::new("git"));
53 Err(_err) => {53 let candidate = if !options.open_path_as_is && !looks_like_git_dir {
54 let git_dir = path.join(git_discover::DOT_GIT_DIR);54 Cow::Owned(path.join(git_discover::DOT_GIT_DIR))
55 git_discover::is_git(&git_dir)55 } else {
56 .map(|kind| (git_dir, kind))56 Cow::Borrowed(&path)
57 .map_err(|err| Error::NotARepository { source: err, 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 lossy_config,141 lossy_config,
129 lenient_config,142 lenient_config,
130 bail_if_untrusted,143 bail_if_untrusted,
144 open_path_as_is: _,
131 permissions: Permissions { ref env, config },145 permissions: Permissions { ref env, config },
132 ref api_config_overrides,146 ref api_config_overrides,
133 ref cli_config_overrides,147 ref cli_config_overrides,
git-repository/tests/repository/open.rs+28−0
@@ -46,6 +46,34 @@ mod not_a_repository { · 45 unmodified lines
46 }46 }
47}47}
48 48
49mod 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
49mod submodules {77mod submodules {
50 use std::path::Path;78 use std::path::Path;
51 79
git-repository/tests/util/mod.rs+5−0
@@ -18,6 +18,11 @@ pub fn repo(name: &str) -> Result<ThreadSafeRepository> { · 17 unmodified lines
18 Ok(ThreadSafeRepository::open_opts(repo_path, restricted())?)18 Ok(ThreadSafeRepository::open_opts(repo_path, restricted())?)
19}19}
20 20
21pub 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
21pub fn named_repo(name: &str) -> Result<Repository> {26pub fn named_repo(name: &str) -> Result<Repository> {
22 let repo_path = git_testtools::scripted_fixture_read_only(name)?;27 let repo_path = git_testtools::scripted_fixture_read_only(name)?;
23 Ok(ThreadSafeRepository::open_opts(repo_path, restricted())?.to_thread_local())28 Ok(ThreadSafeRepository::open_opts(repo_path, restricted())?.to_thread_local())

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t