comparison rust/hg-core/src/repo.rs @ 46486:d7685105e504

rhg: Parse per-repository configuration Differential Revision: https://phab.mercurial-scm.org/D9964
author Simon Sapin <simon.sapin@octobus.net>
date Thu, 04 Feb 2021 15:04:53 +0100
parents f031fe1c6ede
children d8730ff51d5a
comparison
equal deleted inserted replaced
46485:f031fe1c6ede 46486:d7685105e504
1 use crate::config::Config; 1 use crate::config::{Config, ConfigError, ConfigParseError};
2 use crate::errors::{HgError, IoResultExt}; 2 use crate::errors::{HgError, IoResultExt};
3 use crate::requirements; 3 use crate::requirements;
4 use crate::utils::files::get_path_from_bytes; 4 use crate::utils::files::get_path_from_bytes;
5 use memmap::{Mmap, MmapOptions}; 5 use memmap::{Mmap, MmapOptions};
6 use std::collections::HashSet; 6 use std::collections::HashSet;
10 pub struct Repo { 10 pub struct Repo {
11 working_directory: PathBuf, 11 working_directory: PathBuf,
12 dot_hg: PathBuf, 12 dot_hg: PathBuf,
13 store: PathBuf, 13 store: PathBuf,
14 requirements: HashSet<String>, 14 requirements: HashSet<String>,
15 config: Config,
15 } 16 }
16 17
17 #[derive(Debug, derive_more::From)] 18 #[derive(Debug, derive_more::From)]
18 pub enum RepoFindError { 19 pub enum RepoError {
19 NotFoundInCurrentDirectoryOrAncestors { 20 NotFound {
20 current_directory: PathBuf, 21 current_directory: PathBuf,
21 }, 22 },
22 #[from] 23 #[from]
24 ConfigParseError(ConfigParseError),
25 #[from]
23 Other(HgError), 26 Other(HgError),
27 }
28
29 impl From<ConfigError> for RepoError {
30 fn from(error: ConfigError) -> Self {
31 match error {
32 ConfigError::Parse(error) => error.into(),
33 ConfigError::Other(error) => error.into(),
34 }
35 }
24 } 36 }
25 37
26 /// Filesystem access abstraction for the contents of a given "base" diretory 38 /// Filesystem access abstraction for the contents of a given "base" diretory
27 #[derive(Clone, Copy)] 39 #[derive(Clone, Copy)]
28 pub(crate) struct Vfs<'a> { 40 pub(crate) struct Vfs<'a> {
30 } 42 }
31 43
32 impl Repo { 44 impl Repo {
33 /// Search the current directory and its ancestores for a repository: 45 /// Search the current directory and its ancestores for a repository:
34 /// a working directory that contains a `.hg` sub-directory. 46 /// a working directory that contains a `.hg` sub-directory.
35 pub fn find(config: &Config) -> Result<Self, RepoFindError> { 47 pub fn find(config: &Config) -> Result<Self, RepoError> {
36 let current_directory = crate::utils::current_dir()?; 48 let current_directory = crate::utils::current_dir()?;
37 // ancestors() is inclusive: it first yields `current_directory` as-is. 49 // ancestors() is inclusive: it first yields `current_directory` as-is.
38 for ancestor in current_directory.ancestors() { 50 for ancestor in current_directory.ancestors() {
39 if ancestor.join(".hg").is_dir() { 51 if ancestor.join(".hg").is_dir() {
40 return Ok(Self::new_at_path(ancestor.to_owned(), config)?); 52 return Ok(Self::new_at_path(ancestor.to_owned(), config)?);
41 } 53 }
42 } 54 }
43 Err(RepoFindError::NotFoundInCurrentDirectoryOrAncestors { 55 Err(RepoError::NotFound { current_directory })
44 current_directory,
45 })
46 } 56 }
47 57
48 /// To be called after checking that `.hg` is a sub-directory 58 /// To be called after checking that `.hg` is a sub-directory
49 fn new_at_path( 59 fn new_at_path(
50 working_directory: PathBuf, 60 working_directory: PathBuf,
51 config: &Config, 61 config: &Config,
52 ) -> Result<Self, HgError> { 62 ) -> Result<Self, RepoError> {
53 let dot_hg = working_directory.join(".hg"); 63 let dot_hg = working_directory.join(".hg");
64
65 let mut repo_config_files = Vec::new();
66 repo_config_files.push(dot_hg.join("hgrc"));
67 repo_config_files.push(dot_hg.join("hgrc-not-shared"));
54 68
55 let hg_vfs = Vfs { base: &dot_hg }; 69 let hg_vfs = Vfs { base: &dot_hg };
56 let mut reqs = requirements::load_if_exists(hg_vfs)?; 70 let mut reqs = requirements::load_if_exists(hg_vfs)?;
57 let relative = 71 let relative =
58 reqs.contains(requirements::RELATIVE_SHARED_REQUIREMENT); 72 reqs.contains(requirements::RELATIVE_SHARED_REQUIREMENT);
87 } 101 }
88 if !shared_path.is_dir() { 102 if !shared_path.is_dir() {
89 return Err(HgError::corrupted(format!( 103 return Err(HgError::corrupted(format!(
90 ".hg/sharedpath points to nonexistent directory {}", 104 ".hg/sharedpath points to nonexistent directory {}",
91 shared_path.display() 105 shared_path.display()
92 ))); 106 ))
107 .into());
93 } 108 }
94 109
95 store_path = shared_path.join("store"); 110 store_path = shared_path.join("store");
96 111
97 let source_is_share_safe = 112 let source_is_share_safe =
98 requirements::load(Vfs { base: &shared_path })? 113 requirements::load(Vfs { base: &shared_path })?
99 .contains(requirements::SHARESAFE_REQUIREMENT); 114 .contains(requirements::SHARESAFE_REQUIREMENT);
100 115
101 if share_safe && !source_is_share_safe { 116 if share_safe && !source_is_share_safe {
102 return Err(match config.get(b"safe-mismatch", b"source-not-safe") { 117 return Err(match config
118 .get(b"safe-mismatch", b"source-not-safe")
119 {
103 Some(b"abort") | None => HgError::abort( 120 Some(b"abort") | None => HgError::abort(
104 "share source does not support share-safe requirement" 121 "share source does not support share-safe requirement",
105 ), 122 ),
106 _ => HgError::unsupported("share-safe downgrade") 123 _ => HgError::unsupported("share-safe downgrade"),
107 }); 124 }
125 .into());
108 } else if source_is_share_safe && !share_safe { 126 } else if source_is_share_safe && !share_safe {
109 return Err( 127 return Err(
110 match config.get(b"safe-mismatch", b"source-safe") { 128 match config.get(b"safe-mismatch", b"source-safe") {
111 Some(b"abort") | None => HgError::abort( 129 Some(b"abort") | None => HgError::abort(
112 "version mismatch: source uses share-safe \ 130 "version mismatch: source uses share-safe \
113 functionality while the current share does not", 131 functionality while the current share does not",
114 ), 132 ),
115 _ => HgError::unsupported("share-safe upgrade"), 133 _ => HgError::unsupported("share-safe upgrade"),
116 }, 134 }
135 .into(),
117 ); 136 );
118 } 137 }
119 } 138
139 if share_safe {
140 repo_config_files.insert(0, shared_path.join("hgrc"))
141 }
142 }
143
144 let repo_config = config.combine_with_repo(&repo_config_files)?;
120 145
121 let repo = Self { 146 let repo = Self {
122 requirements: reqs, 147 requirements: reqs,
123 working_directory, 148 working_directory,
124 store: store_path, 149 store: store_path,
125 dot_hg, 150 dot_hg,
151 config: repo_config,
126 }; 152 };
127 153
128 requirements::check(&repo)?; 154 requirements::check(&repo)?;
129 155
130 Ok(repo) 156 Ok(repo)
134 &self.working_directory 160 &self.working_directory
135 } 161 }
136 162
137 pub fn requirements(&self) -> &HashSet<String> { 163 pub fn requirements(&self) -> &HashSet<String> {
138 &self.requirements 164 &self.requirements
165 }
166
167 pub fn config(&self) -> &Config {
168 &self.config
139 } 169 }
140 170
141 /// For accessing repository files (in `.hg`), except for the store 171 /// For accessing repository files (in `.hg`), except for the store
142 /// (`.hg/store`). 172 /// (`.hg/store`).
143 pub(crate) fn hg_vfs(&self) -> Vfs<'_> { 173 pub(crate) fn hg_vfs(&self) -> Vfs<'_> {