##// END OF EJS Templates
rhg: Sub-repositories are not supported...
Simon Sapin -
r49341:7f633432 default
parent child Browse files
Show More
@@ -1,532 +1,540 b''
1 use crate::changelog::Changelog;
1 use crate::changelog::Changelog;
2 use crate::config::{Config, ConfigError, ConfigParseError};
2 use crate::config::{Config, ConfigError, ConfigParseError};
3 use crate::dirstate::DirstateParents;
3 use crate::dirstate::DirstateParents;
4 use crate::dirstate_tree::dirstate_map::DirstateMap;
4 use crate::dirstate_tree::dirstate_map::DirstateMap;
5 use crate::dirstate_tree::on_disk::Docket as DirstateDocket;
5 use crate::dirstate_tree::on_disk::Docket as DirstateDocket;
6 use crate::dirstate_tree::owning::OwningDirstateMap;
6 use crate::dirstate_tree::owning::OwningDirstateMap;
7 use crate::errors::HgResultExt;
7 use crate::errors::HgResultExt;
8 use crate::errors::{HgError, IoResultExt};
8 use crate::errors::{HgError, IoResultExt};
9 use crate::exit_codes;
9 use crate::exit_codes;
10 use crate::lock::{try_with_lock_no_wait, LockError};
10 use crate::lock::{try_with_lock_no_wait, LockError};
11 use crate::manifest::{Manifest, Manifestlog};
11 use crate::manifest::{Manifest, Manifestlog};
12 use crate::revlog::filelog::Filelog;
12 use crate::revlog::filelog::Filelog;
13 use crate::revlog::revlog::RevlogError;
13 use crate::revlog::revlog::RevlogError;
14 use crate::utils::files::get_path_from_bytes;
14 use crate::utils::files::get_path_from_bytes;
15 use crate::utils::hg_path::HgPath;
15 use crate::utils::hg_path::HgPath;
16 use crate::utils::SliceExt;
16 use crate::utils::SliceExt;
17 use crate::vfs::{is_dir, is_file, Vfs};
17 use crate::vfs::{is_dir, is_file, Vfs};
18 use crate::{requirements, NodePrefix};
18 use crate::{requirements, NodePrefix};
19 use crate::{DirstateError, Revision};
19 use crate::{DirstateError, Revision};
20 use std::cell::{Ref, RefCell, RefMut};
20 use std::cell::{Ref, RefCell, RefMut};
21 use std::collections::HashSet;
21 use std::collections::HashSet;
22 use std::io::Seek;
22 use std::io::Seek;
23 use std::io::SeekFrom;
23 use std::io::SeekFrom;
24 use std::io::Write as IoWrite;
24 use std::io::Write as IoWrite;
25 use std::path::{Path, PathBuf};
25 use std::path::{Path, PathBuf};
26
26
27 /// A repository on disk
27 /// A repository on disk
28 pub struct Repo {
28 pub struct Repo {
29 working_directory: PathBuf,
29 working_directory: PathBuf,
30 dot_hg: PathBuf,
30 dot_hg: PathBuf,
31 store: PathBuf,
31 store: PathBuf,
32 requirements: HashSet<String>,
32 requirements: HashSet<String>,
33 config: Config,
33 config: Config,
34 dirstate_parents: LazyCell<DirstateParents, HgError>,
34 dirstate_parents: LazyCell<DirstateParents, HgError>,
35 dirstate_data_file_uuid: LazyCell<Option<Vec<u8>>, HgError>,
35 dirstate_data_file_uuid: LazyCell<Option<Vec<u8>>, HgError>,
36 dirstate_map: LazyCell<OwningDirstateMap, DirstateError>,
36 dirstate_map: LazyCell<OwningDirstateMap, DirstateError>,
37 changelog: LazyCell<Changelog, HgError>,
37 changelog: LazyCell<Changelog, HgError>,
38 manifestlog: LazyCell<Manifestlog, HgError>,
38 manifestlog: LazyCell<Manifestlog, HgError>,
39 }
39 }
40
40
41 #[derive(Debug, derive_more::From)]
41 #[derive(Debug, derive_more::From)]
42 pub enum RepoError {
42 pub enum RepoError {
43 NotFound {
43 NotFound {
44 at: PathBuf,
44 at: PathBuf,
45 },
45 },
46 #[from]
46 #[from]
47 ConfigParseError(ConfigParseError),
47 ConfigParseError(ConfigParseError),
48 #[from]
48 #[from]
49 Other(HgError),
49 Other(HgError),
50 }
50 }
51
51
52 impl From<ConfigError> for RepoError {
52 impl From<ConfigError> for RepoError {
53 fn from(error: ConfigError) -> Self {
53 fn from(error: ConfigError) -> Self {
54 match error {
54 match error {
55 ConfigError::Parse(error) => error.into(),
55 ConfigError::Parse(error) => error.into(),
56 ConfigError::Other(error) => error.into(),
56 ConfigError::Other(error) => error.into(),
57 }
57 }
58 }
58 }
59 }
59 }
60
60
61 impl Repo {
61 impl Repo {
62 /// tries to find nearest repository root in current working directory or
62 /// tries to find nearest repository root in current working directory or
63 /// its ancestors
63 /// its ancestors
64 pub fn find_repo_root() -> Result<PathBuf, RepoError> {
64 pub fn find_repo_root() -> Result<PathBuf, RepoError> {
65 let current_directory = crate::utils::current_dir()?;
65 let current_directory = crate::utils::current_dir()?;
66 // ancestors() is inclusive: it first yields `current_directory`
66 // ancestors() is inclusive: it first yields `current_directory`
67 // as-is.
67 // as-is.
68 for ancestor in current_directory.ancestors() {
68 for ancestor in current_directory.ancestors() {
69 if is_dir(ancestor.join(".hg"))? {
69 if is_dir(ancestor.join(".hg"))? {
70 return Ok(ancestor.to_path_buf());
70 return Ok(ancestor.to_path_buf());
71 }
71 }
72 }
72 }
73 return Err(RepoError::NotFound {
73 return Err(RepoError::NotFound {
74 at: current_directory,
74 at: current_directory,
75 });
75 });
76 }
76 }
77
77
78 /// Find a repository, either at the given path (which must contain a `.hg`
78 /// Find a repository, either at the given path (which must contain a `.hg`
79 /// sub-directory) or by searching the current directory and its
79 /// sub-directory) or by searching the current directory and its
80 /// ancestors.
80 /// ancestors.
81 ///
81 ///
82 /// A method with two very different "modes" like this usually a code smell
82 /// A method with two very different "modes" like this usually a code smell
83 /// to make two methods instead, but in this case an `Option` is what rhg
83 /// to make two methods instead, but in this case an `Option` is what rhg
84 /// sub-commands get from Clap for the `-R` / `--repository` CLI argument.
84 /// sub-commands get from Clap for the `-R` / `--repository` CLI argument.
85 /// Having two methods would just move that `if` to almost all callers.
85 /// Having two methods would just move that `if` to almost all callers.
86 pub fn find(
86 pub fn find(
87 config: &Config,
87 config: &Config,
88 explicit_path: Option<PathBuf>,
88 explicit_path: Option<PathBuf>,
89 ) -> Result<Self, RepoError> {
89 ) -> Result<Self, RepoError> {
90 if let Some(root) = explicit_path {
90 if let Some(root) = explicit_path {
91 if is_dir(root.join(".hg"))? {
91 if is_dir(root.join(".hg"))? {
92 Self::new_at_path(root.to_owned(), config)
92 Self::new_at_path(root.to_owned(), config)
93 } else if is_file(&root)? {
93 } else if is_file(&root)? {
94 Err(HgError::unsupported("bundle repository").into())
94 Err(HgError::unsupported("bundle repository").into())
95 } else {
95 } else {
96 Err(RepoError::NotFound {
96 Err(RepoError::NotFound {
97 at: root.to_owned(),
97 at: root.to_owned(),
98 })
98 })
99 }
99 }
100 } else {
100 } else {
101 let root = Self::find_repo_root()?;
101 let root = Self::find_repo_root()?;
102 Self::new_at_path(root, config)
102 Self::new_at_path(root, config)
103 }
103 }
104 }
104 }
105
105
106 /// To be called after checking that `.hg` is a sub-directory
106 /// To be called after checking that `.hg` is a sub-directory
107 fn new_at_path(
107 fn new_at_path(
108 working_directory: PathBuf,
108 working_directory: PathBuf,
109 config: &Config,
109 config: &Config,
110 ) -> Result<Self, RepoError> {
110 ) -> Result<Self, RepoError> {
111 let dot_hg = working_directory.join(".hg");
111 let dot_hg = working_directory.join(".hg");
112
112
113 let mut repo_config_files = Vec::new();
113 let mut repo_config_files = Vec::new();
114 repo_config_files.push(dot_hg.join("hgrc"));
114 repo_config_files.push(dot_hg.join("hgrc"));
115 repo_config_files.push(dot_hg.join("hgrc-not-shared"));
115 repo_config_files.push(dot_hg.join("hgrc-not-shared"));
116
116
117 let hg_vfs = Vfs { base: &dot_hg };
117 let hg_vfs = Vfs { base: &dot_hg };
118 let mut reqs = requirements::load_if_exists(hg_vfs)?;
118 let mut reqs = requirements::load_if_exists(hg_vfs)?;
119 let relative =
119 let relative =
120 reqs.contains(requirements::RELATIVE_SHARED_REQUIREMENT);
120 reqs.contains(requirements::RELATIVE_SHARED_REQUIREMENT);
121 let shared =
121 let shared =
122 reqs.contains(requirements::SHARED_REQUIREMENT) || relative;
122 reqs.contains(requirements::SHARED_REQUIREMENT) || relative;
123
123
124 // From `mercurial/localrepo.py`:
124 // From `mercurial/localrepo.py`:
125 //
125 //
126 // if .hg/requires contains the sharesafe requirement, it means
126 // if .hg/requires contains the sharesafe requirement, it means
127 // there exists a `.hg/store/requires` too and we should read it
127 // there exists a `.hg/store/requires` too and we should read it
128 // NOTE: presence of SHARESAFE_REQUIREMENT imply that store requirement
128 // NOTE: presence of SHARESAFE_REQUIREMENT imply that store requirement
129 // is present. We never write SHARESAFE_REQUIREMENT for a repo if store
129 // is present. We never write SHARESAFE_REQUIREMENT for a repo if store
130 // is not present, refer checkrequirementscompat() for that
130 // is not present, refer checkrequirementscompat() for that
131 //
131 //
132 // However, if SHARESAFE_REQUIREMENT is not present, it means that the
132 // However, if SHARESAFE_REQUIREMENT is not present, it means that the
133 // repository was shared the old way. We check the share source
133 // repository was shared the old way. We check the share source
134 // .hg/requires for SHARESAFE_REQUIREMENT to detect whether the
134 // .hg/requires for SHARESAFE_REQUIREMENT to detect whether the
135 // current repository needs to be reshared
135 // current repository needs to be reshared
136 let share_safe = reqs.contains(requirements::SHARESAFE_REQUIREMENT);
136 let share_safe = reqs.contains(requirements::SHARESAFE_REQUIREMENT);
137
137
138 let store_path;
138 let store_path;
139 if !shared {
139 if !shared {
140 store_path = dot_hg.join("store");
140 store_path = dot_hg.join("store");
141 } else {
141 } else {
142 let bytes = hg_vfs.read("sharedpath")?;
142 let bytes = hg_vfs.read("sharedpath")?;
143 let mut shared_path =
143 let mut shared_path =
144 get_path_from_bytes(bytes.trim_end_matches(|b| b == b'\n'))
144 get_path_from_bytes(bytes.trim_end_matches(|b| b == b'\n'))
145 .to_owned();
145 .to_owned();
146 if relative {
146 if relative {
147 shared_path = dot_hg.join(shared_path)
147 shared_path = dot_hg.join(shared_path)
148 }
148 }
149 if !is_dir(&shared_path)? {
149 if !is_dir(&shared_path)? {
150 return Err(HgError::corrupted(format!(
150 return Err(HgError::corrupted(format!(
151 ".hg/sharedpath points to nonexistent directory {}",
151 ".hg/sharedpath points to nonexistent directory {}",
152 shared_path.display()
152 shared_path.display()
153 ))
153 ))
154 .into());
154 .into());
155 }
155 }
156
156
157 store_path = shared_path.join("store");
157 store_path = shared_path.join("store");
158
158
159 let source_is_share_safe =
159 let source_is_share_safe =
160 requirements::load(Vfs { base: &shared_path })?
160 requirements::load(Vfs { base: &shared_path })?
161 .contains(requirements::SHARESAFE_REQUIREMENT);
161 .contains(requirements::SHARESAFE_REQUIREMENT);
162
162
163 if share_safe && !source_is_share_safe {
163 if share_safe && !source_is_share_safe {
164 return Err(match config
164 return Err(match config
165 .get(b"share", b"safe-mismatch.source-not-safe")
165 .get(b"share", b"safe-mismatch.source-not-safe")
166 {
166 {
167 Some(b"abort") | None => HgError::abort(
167 Some(b"abort") | None => HgError::abort(
168 "abort: share source does not support share-safe requirement\n\
168 "abort: share source does not support share-safe requirement\n\
169 (see `hg help config.format.use-share-safe` for more information)",
169 (see `hg help config.format.use-share-safe` for more information)",
170 exit_codes::ABORT,
170 exit_codes::ABORT,
171 ),
171 ),
172 _ => HgError::unsupported("share-safe downgrade"),
172 _ => HgError::unsupported("share-safe downgrade"),
173 }
173 }
174 .into());
174 .into());
175 } else if source_is_share_safe && !share_safe {
175 } else if source_is_share_safe && !share_safe {
176 return Err(
176 return Err(
177 match config.get(b"share", b"safe-mismatch.source-safe") {
177 match config.get(b"share", b"safe-mismatch.source-safe") {
178 Some(b"abort") | None => HgError::abort(
178 Some(b"abort") | None => HgError::abort(
179 "abort: version mismatch: source uses share-safe \
179 "abort: version mismatch: source uses share-safe \
180 functionality while the current share does not\n\
180 functionality while the current share does not\n\
181 (see `hg help config.format.use-share-safe` for more information)",
181 (see `hg help config.format.use-share-safe` for more information)",
182 exit_codes::ABORT,
182 exit_codes::ABORT,
183 ),
183 ),
184 _ => HgError::unsupported("share-safe upgrade"),
184 _ => HgError::unsupported("share-safe upgrade"),
185 }
185 }
186 .into(),
186 .into(),
187 );
187 );
188 }
188 }
189
189
190 if share_safe {
190 if share_safe {
191 repo_config_files.insert(0, shared_path.join("hgrc"))
191 repo_config_files.insert(0, shared_path.join("hgrc"))
192 }
192 }
193 }
193 }
194 if share_safe {
194 if share_safe {
195 reqs.extend(requirements::load(Vfs { base: &store_path })?);
195 reqs.extend(requirements::load(Vfs { base: &store_path })?);
196 }
196 }
197
197
198 let repo_config = if std::env::var_os("HGRCSKIPREPO").is_none() {
198 let repo_config = if std::env::var_os("HGRCSKIPREPO").is_none() {
199 config.combine_with_repo(&repo_config_files)?
199 config.combine_with_repo(&repo_config_files)?
200 } else {
200 } else {
201 config.clone()
201 config.clone()
202 };
202 };
203
203
204 let repo = Self {
204 let repo = Self {
205 requirements: reqs,
205 requirements: reqs,
206 working_directory,
206 working_directory,
207 store: store_path,
207 store: store_path,
208 dot_hg,
208 dot_hg,
209 config: repo_config,
209 config: repo_config,
210 dirstate_parents: LazyCell::new(Self::read_dirstate_parents),
210 dirstate_parents: LazyCell::new(Self::read_dirstate_parents),
211 dirstate_data_file_uuid: LazyCell::new(
211 dirstate_data_file_uuid: LazyCell::new(
212 Self::read_dirstate_data_file_uuid,
212 Self::read_dirstate_data_file_uuid,
213 ),
213 ),
214 dirstate_map: LazyCell::new(Self::new_dirstate_map),
214 dirstate_map: LazyCell::new(Self::new_dirstate_map),
215 changelog: LazyCell::new(Changelog::open),
215 changelog: LazyCell::new(Changelog::open),
216 manifestlog: LazyCell::new(Manifestlog::open),
216 manifestlog: LazyCell::new(Manifestlog::open),
217 };
217 };
218
218
219 requirements::check(&repo)?;
219 requirements::check(&repo)?;
220
220
221 Ok(repo)
221 Ok(repo)
222 }
222 }
223
223
224 pub fn working_directory_path(&self) -> &Path {
224 pub fn working_directory_path(&self) -> &Path {
225 &self.working_directory
225 &self.working_directory
226 }
226 }
227
227
228 pub fn requirements(&self) -> &HashSet<String> {
228 pub fn requirements(&self) -> &HashSet<String> {
229 &self.requirements
229 &self.requirements
230 }
230 }
231
231
232 pub fn config(&self) -> &Config {
232 pub fn config(&self) -> &Config {
233 &self.config
233 &self.config
234 }
234 }
235
235
236 /// For accessing repository files (in `.hg`), except for the store
236 /// For accessing repository files (in `.hg`), except for the store
237 /// (`.hg/store`).
237 /// (`.hg/store`).
238 pub fn hg_vfs(&self) -> Vfs<'_> {
238 pub fn hg_vfs(&self) -> Vfs<'_> {
239 Vfs { base: &self.dot_hg }
239 Vfs { base: &self.dot_hg }
240 }
240 }
241
241
242 /// For accessing repository store files (in `.hg/store`)
242 /// For accessing repository store files (in `.hg/store`)
243 pub fn store_vfs(&self) -> Vfs<'_> {
243 pub fn store_vfs(&self) -> Vfs<'_> {
244 Vfs { base: &self.store }
244 Vfs { base: &self.store }
245 }
245 }
246
246
247 /// For accessing the working copy
247 /// For accessing the working copy
248 pub fn working_directory_vfs(&self) -> Vfs<'_> {
248 pub fn working_directory_vfs(&self) -> Vfs<'_> {
249 Vfs {
249 Vfs {
250 base: &self.working_directory,
250 base: &self.working_directory,
251 }
251 }
252 }
252 }
253
253
254 pub fn try_with_wlock_no_wait<R>(
254 pub fn try_with_wlock_no_wait<R>(
255 &self,
255 &self,
256 f: impl FnOnce() -> R,
256 f: impl FnOnce() -> R,
257 ) -> Result<R, LockError> {
257 ) -> Result<R, LockError> {
258 try_with_lock_no_wait(self.hg_vfs(), "wlock", f)
258 try_with_lock_no_wait(self.hg_vfs(), "wlock", f)
259 }
259 }
260
260
261 pub fn has_dirstate_v2(&self) -> bool {
261 pub fn has_dirstate_v2(&self) -> bool {
262 self.requirements
262 self.requirements
263 .contains(requirements::DIRSTATE_V2_REQUIREMENT)
263 .contains(requirements::DIRSTATE_V2_REQUIREMENT)
264 }
264 }
265
265
266 pub fn has_sparse(&self) -> bool {
266 pub fn has_sparse(&self) -> bool {
267 self.requirements.contains(requirements::SPARSE_REQUIREMENT)
267 self.requirements.contains(requirements::SPARSE_REQUIREMENT)
268 }
268 }
269
269
270 pub fn has_narrow(&self) -> bool {
270 pub fn has_narrow(&self) -> bool {
271 self.requirements.contains(requirements::NARROW_REQUIREMENT)
271 self.requirements.contains(requirements::NARROW_REQUIREMENT)
272 }
272 }
273
273
274 fn dirstate_file_contents(&self) -> Result<Vec<u8>, HgError> {
274 fn dirstate_file_contents(&self) -> Result<Vec<u8>, HgError> {
275 Ok(self
275 Ok(self
276 .hg_vfs()
276 .hg_vfs()
277 .read("dirstate")
277 .read("dirstate")
278 .io_not_found_as_none()?
278 .io_not_found_as_none()?
279 .unwrap_or(Vec::new()))
279 .unwrap_or(Vec::new()))
280 }
280 }
281
281
282 pub fn dirstate_parents(&self) -> Result<DirstateParents, HgError> {
282 pub fn dirstate_parents(&self) -> Result<DirstateParents, HgError> {
283 Ok(*self.dirstate_parents.get_or_init(self)?)
283 Ok(*self.dirstate_parents.get_or_init(self)?)
284 }
284 }
285
285
286 fn read_dirstate_parents(&self) -> Result<DirstateParents, HgError> {
286 fn read_dirstate_parents(&self) -> Result<DirstateParents, HgError> {
287 let dirstate = self.dirstate_file_contents()?;
287 let dirstate = self.dirstate_file_contents()?;
288 let parents = if dirstate.is_empty() {
288 let parents = if dirstate.is_empty() {
289 if self.has_dirstate_v2() {
289 if self.has_dirstate_v2() {
290 self.dirstate_data_file_uuid.set(None);
290 self.dirstate_data_file_uuid.set(None);
291 }
291 }
292 DirstateParents::NULL
292 DirstateParents::NULL
293 } else if self.has_dirstate_v2() {
293 } else if self.has_dirstate_v2() {
294 let docket =
294 let docket =
295 crate::dirstate_tree::on_disk::read_docket(&dirstate)?;
295 crate::dirstate_tree::on_disk::read_docket(&dirstate)?;
296 self.dirstate_data_file_uuid
296 self.dirstate_data_file_uuid
297 .set(Some(docket.uuid.to_owned()));
297 .set(Some(docket.uuid.to_owned()));
298 docket.parents()
298 docket.parents()
299 } else {
299 } else {
300 crate::dirstate::parsers::parse_dirstate_parents(&dirstate)?
300 crate::dirstate::parsers::parse_dirstate_parents(&dirstate)?
301 .clone()
301 .clone()
302 };
302 };
303 self.dirstate_parents.set(parents);
303 self.dirstate_parents.set(parents);
304 Ok(parents)
304 Ok(parents)
305 }
305 }
306
306
307 fn read_dirstate_data_file_uuid(
307 fn read_dirstate_data_file_uuid(
308 &self,
308 &self,
309 ) -> Result<Option<Vec<u8>>, HgError> {
309 ) -> Result<Option<Vec<u8>>, HgError> {
310 assert!(
310 assert!(
311 self.has_dirstate_v2(),
311 self.has_dirstate_v2(),
312 "accessing dirstate data file ID without dirstate-v2"
312 "accessing dirstate data file ID without dirstate-v2"
313 );
313 );
314 let dirstate = self.dirstate_file_contents()?;
314 let dirstate = self.dirstate_file_contents()?;
315 if dirstate.is_empty() {
315 if dirstate.is_empty() {
316 self.dirstate_parents.set(DirstateParents::NULL);
316 self.dirstate_parents.set(DirstateParents::NULL);
317 Ok(None)
317 Ok(None)
318 } else {
318 } else {
319 let docket =
319 let docket =
320 crate::dirstate_tree::on_disk::read_docket(&dirstate)?;
320 crate::dirstate_tree::on_disk::read_docket(&dirstate)?;
321 self.dirstate_parents.set(docket.parents());
321 self.dirstate_parents.set(docket.parents());
322 Ok(Some(docket.uuid.to_owned()))
322 Ok(Some(docket.uuid.to_owned()))
323 }
323 }
324 }
324 }
325
325
326 fn new_dirstate_map(&self) -> Result<OwningDirstateMap, DirstateError> {
326 fn new_dirstate_map(&self) -> Result<OwningDirstateMap, DirstateError> {
327 let dirstate_file_contents = self.dirstate_file_contents()?;
327 let dirstate_file_contents = self.dirstate_file_contents()?;
328 if dirstate_file_contents.is_empty() {
328 if dirstate_file_contents.is_empty() {
329 self.dirstate_parents.set(DirstateParents::NULL);
329 self.dirstate_parents.set(DirstateParents::NULL);
330 if self.has_dirstate_v2() {
330 if self.has_dirstate_v2() {
331 self.dirstate_data_file_uuid.set(None);
331 self.dirstate_data_file_uuid.set(None);
332 }
332 }
333 Ok(OwningDirstateMap::new_empty(Vec::new()))
333 Ok(OwningDirstateMap::new_empty(Vec::new()))
334 } else if self.has_dirstate_v2() {
334 } else if self.has_dirstate_v2() {
335 let docket = crate::dirstate_tree::on_disk::read_docket(
335 let docket = crate::dirstate_tree::on_disk::read_docket(
336 &dirstate_file_contents,
336 &dirstate_file_contents,
337 )?;
337 )?;
338 self.dirstate_parents.set(docket.parents());
338 self.dirstate_parents.set(docket.parents());
339 self.dirstate_data_file_uuid
339 self.dirstate_data_file_uuid
340 .set(Some(docket.uuid.to_owned()));
340 .set(Some(docket.uuid.to_owned()));
341 let data_size = docket.data_size();
341 let data_size = docket.data_size();
342 let metadata = docket.tree_metadata();
342 let metadata = docket.tree_metadata();
343 let mut map = if let Some(data_mmap) = self
343 let mut map = if let Some(data_mmap) = self
344 .hg_vfs()
344 .hg_vfs()
345 .mmap_open(docket.data_filename())
345 .mmap_open(docket.data_filename())
346 .io_not_found_as_none()?
346 .io_not_found_as_none()?
347 {
347 {
348 OwningDirstateMap::new_empty(data_mmap)
348 OwningDirstateMap::new_empty(data_mmap)
349 } else {
349 } else {
350 OwningDirstateMap::new_empty(Vec::new())
350 OwningDirstateMap::new_empty(Vec::new())
351 };
351 };
352 let (on_disk, placeholder) = map.get_pair_mut();
352 let (on_disk, placeholder) = map.get_pair_mut();
353 *placeholder = DirstateMap::new_v2(on_disk, data_size, metadata)?;
353 *placeholder = DirstateMap::new_v2(on_disk, data_size, metadata)?;
354 Ok(map)
354 Ok(map)
355 } else {
355 } else {
356 let mut map = OwningDirstateMap::new_empty(dirstate_file_contents);
356 let mut map = OwningDirstateMap::new_empty(dirstate_file_contents);
357 let (on_disk, placeholder) = map.get_pair_mut();
357 let (on_disk, placeholder) = map.get_pair_mut();
358 let (inner, parents) = DirstateMap::new_v1(on_disk)?;
358 let (inner, parents) = DirstateMap::new_v1(on_disk)?;
359 self.dirstate_parents
359 self.dirstate_parents
360 .set(parents.unwrap_or(DirstateParents::NULL));
360 .set(parents.unwrap_or(DirstateParents::NULL));
361 *placeholder = inner;
361 *placeholder = inner;
362 Ok(map)
362 Ok(map)
363 }
363 }
364 }
364 }
365
365
366 pub fn dirstate_map(
366 pub fn dirstate_map(
367 &self,
367 &self,
368 ) -> Result<Ref<OwningDirstateMap>, DirstateError> {
368 ) -> Result<Ref<OwningDirstateMap>, DirstateError> {
369 self.dirstate_map.get_or_init(self)
369 self.dirstate_map.get_or_init(self)
370 }
370 }
371
371
372 pub fn dirstate_map_mut(
372 pub fn dirstate_map_mut(
373 &self,
373 &self,
374 ) -> Result<RefMut<OwningDirstateMap>, DirstateError> {
374 ) -> Result<RefMut<OwningDirstateMap>, DirstateError> {
375 self.dirstate_map.get_mut_or_init(self)
375 self.dirstate_map.get_mut_or_init(self)
376 }
376 }
377
377
378 pub fn changelog(&self) -> Result<Ref<Changelog>, HgError> {
378 pub fn changelog(&self) -> Result<Ref<Changelog>, HgError> {
379 self.changelog.get_or_init(self)
379 self.changelog.get_or_init(self)
380 }
380 }
381
381
382 pub fn changelog_mut(&self) -> Result<RefMut<Changelog>, HgError> {
382 pub fn changelog_mut(&self) -> Result<RefMut<Changelog>, HgError> {
383 self.changelog.get_mut_or_init(self)
383 self.changelog.get_mut_or_init(self)
384 }
384 }
385
385
386 pub fn manifestlog(&self) -> Result<Ref<Manifestlog>, HgError> {
386 pub fn manifestlog(&self) -> Result<Ref<Manifestlog>, HgError> {
387 self.manifestlog.get_or_init(self)
387 self.manifestlog.get_or_init(self)
388 }
388 }
389
389
390 pub fn manifestlog_mut(&self) -> Result<RefMut<Manifestlog>, HgError> {
390 pub fn manifestlog_mut(&self) -> Result<RefMut<Manifestlog>, HgError> {
391 self.manifestlog.get_mut_or_init(self)
391 self.manifestlog.get_mut_or_init(self)
392 }
392 }
393
393
394 /// Returns the manifest of the *changeset* with the given node ID
394 /// Returns the manifest of the *changeset* with the given node ID
395 pub fn manifest_for_node(
395 pub fn manifest_for_node(
396 &self,
396 &self,
397 node: impl Into<NodePrefix>,
397 node: impl Into<NodePrefix>,
398 ) -> Result<Manifest, RevlogError> {
398 ) -> Result<Manifest, RevlogError> {
399 self.manifestlog()?.data_for_node(
399 self.manifestlog()?.data_for_node(
400 self.changelog()?
400 self.changelog()?
401 .data_for_node(node.into())?
401 .data_for_node(node.into())?
402 .manifest_node()?
402 .manifest_node()?
403 .into(),
403 .into(),
404 )
404 )
405 }
405 }
406
406
407 /// Returns the manifest of the *changeset* with the given revision number
407 /// Returns the manifest of the *changeset* with the given revision number
408 pub fn manifest_for_rev(
408 pub fn manifest_for_rev(
409 &self,
409 &self,
410 revision: Revision,
410 revision: Revision,
411 ) -> Result<Manifest, RevlogError> {
411 ) -> Result<Manifest, RevlogError> {
412 self.manifestlog()?.data_for_node(
412 self.manifestlog()?.data_for_node(
413 self.changelog()?
413 self.changelog()?
414 .data_for_rev(revision)?
414 .data_for_rev(revision)?
415 .manifest_node()?
415 .manifest_node()?
416 .into(),
416 .into(),
417 )
417 )
418 }
418 }
419
419
420 pub fn has_subrepos(&self) -> Result<bool, DirstateError> {
421 if let Some(entry) = self.dirstate_map()?.get(HgPath::new(".hgsub"))? {
422 Ok(entry.state().is_tracked())
423 } else {
424 Ok(false)
425 }
426 }
427
420 pub fn filelog(&self, path: &HgPath) -> Result<Filelog, HgError> {
428 pub fn filelog(&self, path: &HgPath) -> Result<Filelog, HgError> {
421 Filelog::open(self, path)
429 Filelog::open(self, path)
422 }
430 }
423
431
424 /// Write to disk any updates that were made through `dirstate_map_mut`.
432 /// Write to disk any updates that were made through `dirstate_map_mut`.
425 ///
433 ///
426 /// The "wlock" must be held while calling this.
434 /// The "wlock" must be held while calling this.
427 /// See for example `try_with_wlock_no_wait`.
435 /// See for example `try_with_wlock_no_wait`.
428 ///
436 ///
429 /// TODO: have a `WritableRepo` type only accessible while holding the
437 /// TODO: have a `WritableRepo` type only accessible while holding the
430 /// lock?
438 /// lock?
431 pub fn write_dirstate(&self) -> Result<(), DirstateError> {
439 pub fn write_dirstate(&self) -> Result<(), DirstateError> {
432 let map = self.dirstate_map()?;
440 let map = self.dirstate_map()?;
433 // TODO: Maintain a `DirstateMap::dirty` flag, and return early here if
441 // TODO: Maintain a `DirstateMap::dirty` flag, and return early here if
434 // it’s unset
442 // it’s unset
435 let parents = self.dirstate_parents()?;
443 let parents = self.dirstate_parents()?;
436 let packed_dirstate = if self.has_dirstate_v2() {
444 let packed_dirstate = if self.has_dirstate_v2() {
437 let uuid = self.dirstate_data_file_uuid.get_or_init(self)?;
445 let uuid = self.dirstate_data_file_uuid.get_or_init(self)?;
438 let mut uuid = uuid.as_ref();
446 let mut uuid = uuid.as_ref();
439 let can_append = uuid.is_some();
447 let can_append = uuid.is_some();
440 let (data, tree_metadata, append) = map.pack_v2(can_append)?;
448 let (data, tree_metadata, append) = map.pack_v2(can_append)?;
441 if !append {
449 if !append {
442 uuid = None
450 uuid = None
443 }
451 }
444 let uuid = if let Some(uuid) = uuid {
452 let uuid = if let Some(uuid) = uuid {
445 std::str::from_utf8(uuid)
453 std::str::from_utf8(uuid)
446 .map_err(|_| {
454 .map_err(|_| {
447 HgError::corrupted("non-UTF-8 dirstate data file ID")
455 HgError::corrupted("non-UTF-8 dirstate data file ID")
448 })?
456 })?
449 .to_owned()
457 .to_owned()
450 } else {
458 } else {
451 DirstateDocket::new_uid()
459 DirstateDocket::new_uid()
452 };
460 };
453 let data_filename = format!("dirstate.{}", uuid);
461 let data_filename = format!("dirstate.{}", uuid);
454 let data_filename = self.hg_vfs().join(data_filename);
462 let data_filename = self.hg_vfs().join(data_filename);
455 let mut options = std::fs::OpenOptions::new();
463 let mut options = std::fs::OpenOptions::new();
456 if append {
464 if append {
457 options.append(true);
465 options.append(true);
458 } else {
466 } else {
459 options.write(true).create_new(true);
467 options.write(true).create_new(true);
460 }
468 }
461 let data_size = (|| {
469 let data_size = (|| {
462 // TODO: loop and try another random ID if !append and this
470 // TODO: loop and try another random ID if !append and this
463 // returns `ErrorKind::AlreadyExists`? Collision chance of two
471 // returns `ErrorKind::AlreadyExists`? Collision chance of two
464 // random IDs is one in 2**32
472 // random IDs is one in 2**32
465 let mut file = options.open(&data_filename)?;
473 let mut file = options.open(&data_filename)?;
466 file.write_all(&data)?;
474 file.write_all(&data)?;
467 file.flush()?;
475 file.flush()?;
468 // TODO: use https://doc.rust-lang.org/std/io/trait.Seek.html#method.stream_position when we require Rust 1.51+
476 // TODO: use https://doc.rust-lang.org/std/io/trait.Seek.html#method.stream_position when we require Rust 1.51+
469 file.seek(SeekFrom::Current(0))
477 file.seek(SeekFrom::Current(0))
470 })()
478 })()
471 .when_writing_file(&data_filename)?;
479 .when_writing_file(&data_filename)?;
472 DirstateDocket::serialize(
480 DirstateDocket::serialize(
473 parents,
481 parents,
474 tree_metadata,
482 tree_metadata,
475 data_size,
483 data_size,
476 uuid.as_bytes(),
484 uuid.as_bytes(),
477 )
485 )
478 .map_err(|_: std::num::TryFromIntError| {
486 .map_err(|_: std::num::TryFromIntError| {
479 HgError::corrupted("overflow in dirstate docket serialization")
487 HgError::corrupted("overflow in dirstate docket serialization")
480 })?
488 })?
481 } else {
489 } else {
482 map.pack_v1(parents)?
490 map.pack_v1(parents)?
483 };
491 };
484 self.hg_vfs().atomic_write("dirstate", &packed_dirstate)?;
492 self.hg_vfs().atomic_write("dirstate", &packed_dirstate)?;
485 Ok(())
493 Ok(())
486 }
494 }
487 }
495 }
488
496
489 /// Lazily-initialized component of `Repo` with interior mutability
497 /// Lazily-initialized component of `Repo` with interior mutability
490 ///
498 ///
491 /// This differs from `OnceCell` in that the value can still be "deinitialized"
499 /// This differs from `OnceCell` in that the value can still be "deinitialized"
492 /// later by setting its inner `Option` to `None`.
500 /// later by setting its inner `Option` to `None`.
493 struct LazyCell<T, E> {
501 struct LazyCell<T, E> {
494 value: RefCell<Option<T>>,
502 value: RefCell<Option<T>>,
495 // `Fn`s that don’t capture environment are zero-size, so this box does
503 // `Fn`s that don’t capture environment are zero-size, so this box does
496 // not allocate:
504 // not allocate:
497 init: Box<dyn Fn(&Repo) -> Result<T, E>>,
505 init: Box<dyn Fn(&Repo) -> Result<T, E>>,
498 }
506 }
499
507
500 impl<T, E> LazyCell<T, E> {
508 impl<T, E> LazyCell<T, E> {
501 fn new(init: impl Fn(&Repo) -> Result<T, E> + 'static) -> Self {
509 fn new(init: impl Fn(&Repo) -> Result<T, E> + 'static) -> Self {
502 Self {
510 Self {
503 value: RefCell::new(None),
511 value: RefCell::new(None),
504 init: Box::new(init),
512 init: Box::new(init),
505 }
513 }
506 }
514 }
507
515
508 fn set(&self, value: T) {
516 fn set(&self, value: T) {
509 *self.value.borrow_mut() = Some(value)
517 *self.value.borrow_mut() = Some(value)
510 }
518 }
511
519
512 fn get_or_init(&self, repo: &Repo) -> Result<Ref<T>, E> {
520 fn get_or_init(&self, repo: &Repo) -> Result<Ref<T>, E> {
513 let mut borrowed = self.value.borrow();
521 let mut borrowed = self.value.borrow();
514 if borrowed.is_none() {
522 if borrowed.is_none() {
515 drop(borrowed);
523 drop(borrowed);
516 // Only use `borrow_mut` if it is really needed to avoid panic in
524 // Only use `borrow_mut` if it is really needed to avoid panic in
517 // case there is another outstanding borrow but mutation is not
525 // case there is another outstanding borrow but mutation is not
518 // needed.
526 // needed.
519 *self.value.borrow_mut() = Some((self.init)(repo)?);
527 *self.value.borrow_mut() = Some((self.init)(repo)?);
520 borrowed = self.value.borrow()
528 borrowed = self.value.borrow()
521 }
529 }
522 Ok(Ref::map(borrowed, |option| option.as_ref().unwrap()))
530 Ok(Ref::map(borrowed, |option| option.as_ref().unwrap()))
523 }
531 }
524
532
525 fn get_mut_or_init(&self, repo: &Repo) -> Result<RefMut<T>, E> {
533 fn get_mut_or_init(&self, repo: &Repo) -> Result<RefMut<T>, E> {
526 let mut borrowed = self.value.borrow_mut();
534 let mut borrowed = self.value.borrow_mut();
527 if borrowed.is_none() {
535 if borrowed.is_none() {
528 *borrowed = Some((self.init)(repo)?);
536 *borrowed = Some((self.init)(repo)?);
529 }
537 }
530 Ok(RefMut::map(borrowed, |option| option.as_mut().unwrap()))
538 Ok(RefMut::map(borrowed, |option| option.as_mut().unwrap()))
531 }
539 }
532 }
540 }
@@ -1,671 +1,678 b''
1 extern crate log;
1 extern crate log;
2 use crate::error::CommandError;
2 use crate::error::CommandError;
3 use crate::ui::Ui;
3 use crate::ui::Ui;
4 use clap::App;
4 use clap::App;
5 use clap::AppSettings;
5 use clap::AppSettings;
6 use clap::Arg;
6 use clap::Arg;
7 use clap::ArgMatches;
7 use clap::ArgMatches;
8 use format_bytes::{format_bytes, join};
8 use format_bytes::{format_bytes, join};
9 use hg::config::{Config, ConfigSource};
9 use hg::config::{Config, ConfigSource};
10 use hg::exit_codes;
10 use hg::exit_codes;
11 use hg::repo::{Repo, RepoError};
11 use hg::repo::{Repo, RepoError};
12 use hg::utils::files::{get_bytes_from_os_str, get_path_from_bytes};
12 use hg::utils::files::{get_bytes_from_os_str, get_path_from_bytes};
13 use hg::utils::SliceExt;
13 use hg::utils::SliceExt;
14 use std::collections::HashSet;
14 use std::collections::HashSet;
15 use std::ffi::OsString;
15 use std::ffi::OsString;
16 use std::path::PathBuf;
16 use std::path::PathBuf;
17 use std::process::Command;
17 use std::process::Command;
18
18
19 mod blackbox;
19 mod blackbox;
20 mod error;
20 mod error;
21 mod ui;
21 mod ui;
22 pub mod utils {
22 pub mod utils {
23 pub mod path_utils;
23 pub mod path_utils;
24 }
24 }
25
25
26 fn main_with_result(
26 fn main_with_result(
27 process_start_time: &blackbox::ProcessStartTime,
27 process_start_time: &blackbox::ProcessStartTime,
28 ui: &ui::Ui,
28 ui: &ui::Ui,
29 repo: Result<&Repo, &NoRepoInCwdError>,
29 repo: Result<&Repo, &NoRepoInCwdError>,
30 config: &Config,
30 config: &Config,
31 ) -> Result<(), CommandError> {
31 ) -> Result<(), CommandError> {
32 check_unsupported(config, ui)?;
32 check_unsupported(config, repo, ui)?;
33
33
34 let app = App::new("rhg")
34 let app = App::new("rhg")
35 .global_setting(AppSettings::AllowInvalidUtf8)
35 .global_setting(AppSettings::AllowInvalidUtf8)
36 .global_setting(AppSettings::DisableVersion)
36 .global_setting(AppSettings::DisableVersion)
37 .setting(AppSettings::SubcommandRequired)
37 .setting(AppSettings::SubcommandRequired)
38 .setting(AppSettings::VersionlessSubcommands)
38 .setting(AppSettings::VersionlessSubcommands)
39 .arg(
39 .arg(
40 Arg::with_name("repository")
40 Arg::with_name("repository")
41 .help("repository root directory")
41 .help("repository root directory")
42 .short("-R")
42 .short("-R")
43 .long("--repository")
43 .long("--repository")
44 .value_name("REPO")
44 .value_name("REPO")
45 .takes_value(true)
45 .takes_value(true)
46 // Both ok: `hg -R ./foo log` or `hg log -R ./foo`
46 // Both ok: `hg -R ./foo log` or `hg log -R ./foo`
47 .global(true),
47 .global(true),
48 )
48 )
49 .arg(
49 .arg(
50 Arg::with_name("config")
50 Arg::with_name("config")
51 .help("set/override config option (use 'section.name=value')")
51 .help("set/override config option (use 'section.name=value')")
52 .long("--config")
52 .long("--config")
53 .value_name("CONFIG")
53 .value_name("CONFIG")
54 .takes_value(true)
54 .takes_value(true)
55 .global(true)
55 .global(true)
56 // Ok: `--config section.key1=val --config section.key2=val2`
56 // Ok: `--config section.key1=val --config section.key2=val2`
57 .multiple(true)
57 .multiple(true)
58 // Not ok: `--config section.key1=val section.key2=val2`
58 // Not ok: `--config section.key1=val section.key2=val2`
59 .number_of_values(1),
59 .number_of_values(1),
60 )
60 )
61 .arg(
61 .arg(
62 Arg::with_name("cwd")
62 Arg::with_name("cwd")
63 .help("change working directory")
63 .help("change working directory")
64 .long("--cwd")
64 .long("--cwd")
65 .value_name("DIR")
65 .value_name("DIR")
66 .takes_value(true)
66 .takes_value(true)
67 .global(true),
67 .global(true),
68 )
68 )
69 .version("0.0.1");
69 .version("0.0.1");
70 let app = add_subcommand_args(app);
70 let app = add_subcommand_args(app);
71
71
72 let matches = app.clone().get_matches_safe()?;
72 let matches = app.clone().get_matches_safe()?;
73
73
74 let (subcommand_name, subcommand_matches) = matches.subcommand();
74 let (subcommand_name, subcommand_matches) = matches.subcommand();
75
75
76 // Mercurial allows users to define "defaults" for commands, fallback
76 // Mercurial allows users to define "defaults" for commands, fallback
77 // if a default is detected for the current command
77 // if a default is detected for the current command
78 let defaults = config.get_str(b"defaults", subcommand_name.as_bytes());
78 let defaults = config.get_str(b"defaults", subcommand_name.as_bytes());
79 if defaults?.is_some() {
79 if defaults?.is_some() {
80 let msg = "`defaults` config set";
80 let msg = "`defaults` config set";
81 return Err(CommandError::unsupported(msg));
81 return Err(CommandError::unsupported(msg));
82 }
82 }
83
83
84 for prefix in ["pre", "post", "fail"].iter() {
84 for prefix in ["pre", "post", "fail"].iter() {
85 // Mercurial allows users to define generic hooks for commands,
85 // Mercurial allows users to define generic hooks for commands,
86 // fallback if any are detected
86 // fallback if any are detected
87 let item = format!("{}-{}", prefix, subcommand_name);
87 let item = format!("{}-{}", prefix, subcommand_name);
88 let hook_for_command = config.get_str(b"hooks", item.as_bytes())?;
88 let hook_for_command = config.get_str(b"hooks", item.as_bytes())?;
89 if hook_for_command.is_some() {
89 if hook_for_command.is_some() {
90 let msg = format!("{}-{} hook defined", prefix, subcommand_name);
90 let msg = format!("{}-{} hook defined", prefix, subcommand_name);
91 return Err(CommandError::unsupported(msg));
91 return Err(CommandError::unsupported(msg));
92 }
92 }
93 }
93 }
94 let run = subcommand_run_fn(subcommand_name)
94 let run = subcommand_run_fn(subcommand_name)
95 .expect("unknown subcommand name from clap despite AppSettings::SubcommandRequired");
95 .expect("unknown subcommand name from clap despite AppSettings::SubcommandRequired");
96 let subcommand_args = subcommand_matches
96 let subcommand_args = subcommand_matches
97 .expect("no subcommand arguments from clap despite AppSettings::SubcommandRequired");
97 .expect("no subcommand arguments from clap despite AppSettings::SubcommandRequired");
98
98
99 let invocation = CliInvocation {
99 let invocation = CliInvocation {
100 ui,
100 ui,
101 subcommand_args,
101 subcommand_args,
102 config,
102 config,
103 repo,
103 repo,
104 };
104 };
105
105
106 if let Ok(repo) = repo {
106 if let Ok(repo) = repo {
107 // We don't support subrepos, fallback if the subrepos file is present
107 // We don't support subrepos, fallback if the subrepos file is present
108 if repo.working_directory_vfs().join(".hgsub").exists() {
108 if repo.working_directory_vfs().join(".hgsub").exists() {
109 let msg = "subrepos (.hgsub is present)";
109 let msg = "subrepos (.hgsub is present)";
110 return Err(CommandError::unsupported(msg));
110 return Err(CommandError::unsupported(msg));
111 }
111 }
112 }
112 }
113
113
114 if config.is_extension_enabled(b"blackbox") {
114 if config.is_extension_enabled(b"blackbox") {
115 let blackbox =
115 let blackbox =
116 blackbox::Blackbox::new(&invocation, process_start_time)?;
116 blackbox::Blackbox::new(&invocation, process_start_time)?;
117 blackbox.log_command_start();
117 blackbox.log_command_start();
118 let result = run(&invocation);
118 let result = run(&invocation);
119 blackbox.log_command_end(exit_code(
119 blackbox.log_command_end(exit_code(
120 &result,
120 &result,
121 // TODO: show a warning or combine with original error if
121 // TODO: show a warning or combine with original error if
122 // `get_bool` returns an error
122 // `get_bool` returns an error
123 config
123 config
124 .get_bool(b"ui", b"detailed-exit-code")
124 .get_bool(b"ui", b"detailed-exit-code")
125 .unwrap_or(false),
125 .unwrap_or(false),
126 ));
126 ));
127 result
127 result
128 } else {
128 } else {
129 run(&invocation)
129 run(&invocation)
130 }
130 }
131 }
131 }
132
132
133 fn main() {
133 fn main() {
134 // Run this first, before we find out if the blackbox extension is even
134 // Run this first, before we find out if the blackbox extension is even
135 // enabled, in order to include everything in-between in the duration
135 // enabled, in order to include everything in-between in the duration
136 // measurements. Reading config files can be slow if they’re on NFS.
136 // measurements. Reading config files can be slow if they’re on NFS.
137 let process_start_time = blackbox::ProcessStartTime::now();
137 let process_start_time = blackbox::ProcessStartTime::now();
138
138
139 env_logger::init();
139 env_logger::init();
140 let ui = ui::Ui::new();
140 let ui = ui::Ui::new();
141
141
142 let early_args = EarlyArgs::parse(std::env::args_os());
142 let early_args = EarlyArgs::parse(std::env::args_os());
143
143
144 let initial_current_dir = early_args.cwd.map(|cwd| {
144 let initial_current_dir = early_args.cwd.map(|cwd| {
145 let cwd = get_path_from_bytes(&cwd);
145 let cwd = get_path_from_bytes(&cwd);
146 std::env::current_dir()
146 std::env::current_dir()
147 .and_then(|initial| {
147 .and_then(|initial| {
148 std::env::set_current_dir(cwd)?;
148 std::env::set_current_dir(cwd)?;
149 Ok(initial)
149 Ok(initial)
150 })
150 })
151 .unwrap_or_else(|error| {
151 .unwrap_or_else(|error| {
152 exit(
152 exit(
153 &None,
153 &None,
154 &ui,
154 &ui,
155 OnUnsupported::Abort,
155 OnUnsupported::Abort,
156 Err(CommandError::abort(format!(
156 Err(CommandError::abort(format!(
157 "abort: {}: '{}'",
157 "abort: {}: '{}'",
158 error,
158 error,
159 cwd.display()
159 cwd.display()
160 ))),
160 ))),
161 false,
161 false,
162 )
162 )
163 })
163 })
164 });
164 });
165
165
166 let mut non_repo_config =
166 let mut non_repo_config =
167 Config::load_non_repo().unwrap_or_else(|error| {
167 Config::load_non_repo().unwrap_or_else(|error| {
168 // Normally this is decided based on config, but we don’t have that
168 // Normally this is decided based on config, but we don’t have that
169 // available. As of this writing config loading never returns an
169 // available. As of this writing config loading never returns an
170 // "unsupported" error but that is not enforced by the type system.
170 // "unsupported" error but that is not enforced by the type system.
171 let on_unsupported = OnUnsupported::Abort;
171 let on_unsupported = OnUnsupported::Abort;
172
172
173 exit(
173 exit(
174 &initial_current_dir,
174 &initial_current_dir,
175 &ui,
175 &ui,
176 on_unsupported,
176 on_unsupported,
177 Err(error.into()),
177 Err(error.into()),
178 false,
178 false,
179 )
179 )
180 });
180 });
181
181
182 non_repo_config
182 non_repo_config
183 .load_cli_args_config(early_args.config)
183 .load_cli_args_config(early_args.config)
184 .unwrap_or_else(|error| {
184 .unwrap_or_else(|error| {
185 exit(
185 exit(
186 &initial_current_dir,
186 &initial_current_dir,
187 &ui,
187 &ui,
188 OnUnsupported::from_config(&non_repo_config),
188 OnUnsupported::from_config(&non_repo_config),
189 Err(error.into()),
189 Err(error.into()),
190 non_repo_config
190 non_repo_config
191 .get_bool(b"ui", b"detailed-exit-code")
191 .get_bool(b"ui", b"detailed-exit-code")
192 .unwrap_or(false),
192 .unwrap_or(false),
193 )
193 )
194 });
194 });
195
195
196 if let Some(repo_path_bytes) = &early_args.repo {
196 if let Some(repo_path_bytes) = &early_args.repo {
197 lazy_static::lazy_static! {
197 lazy_static::lazy_static! {
198 static ref SCHEME_RE: regex::bytes::Regex =
198 static ref SCHEME_RE: regex::bytes::Regex =
199 // Same as `_matchscheme` in `mercurial/util.py`
199 // Same as `_matchscheme` in `mercurial/util.py`
200 regex::bytes::Regex::new("^[a-zA-Z0-9+.\\-]+:").unwrap();
200 regex::bytes::Regex::new("^[a-zA-Z0-9+.\\-]+:").unwrap();
201 }
201 }
202 if SCHEME_RE.is_match(&repo_path_bytes) {
202 if SCHEME_RE.is_match(&repo_path_bytes) {
203 exit(
203 exit(
204 &initial_current_dir,
204 &initial_current_dir,
205 &ui,
205 &ui,
206 OnUnsupported::from_config(&non_repo_config),
206 OnUnsupported::from_config(&non_repo_config),
207 Err(CommandError::UnsupportedFeature {
207 Err(CommandError::UnsupportedFeature {
208 message: format_bytes!(
208 message: format_bytes!(
209 b"URL-like --repository {}",
209 b"URL-like --repository {}",
210 repo_path_bytes
210 repo_path_bytes
211 ),
211 ),
212 }),
212 }),
213 // TODO: show a warning or combine with original error if
213 // TODO: show a warning or combine with original error if
214 // `get_bool` returns an error
214 // `get_bool` returns an error
215 non_repo_config
215 non_repo_config
216 .get_bool(b"ui", b"detailed-exit-code")
216 .get_bool(b"ui", b"detailed-exit-code")
217 .unwrap_or(false),
217 .unwrap_or(false),
218 )
218 )
219 }
219 }
220 }
220 }
221 let repo_arg = early_args.repo.unwrap_or(Vec::new());
221 let repo_arg = early_args.repo.unwrap_or(Vec::new());
222 let repo_path: Option<PathBuf> = {
222 let repo_path: Option<PathBuf> = {
223 if repo_arg.is_empty() {
223 if repo_arg.is_empty() {
224 None
224 None
225 } else {
225 } else {
226 let local_config = {
226 let local_config = {
227 if std::env::var_os("HGRCSKIPREPO").is_none() {
227 if std::env::var_os("HGRCSKIPREPO").is_none() {
228 // TODO: handle errors from find_repo_root
228 // TODO: handle errors from find_repo_root
229 if let Ok(current_dir_path) = Repo::find_repo_root() {
229 if let Ok(current_dir_path) = Repo::find_repo_root() {
230 let config_files = vec![
230 let config_files = vec![
231 ConfigSource::AbsPath(
231 ConfigSource::AbsPath(
232 current_dir_path.join(".hg/hgrc"),
232 current_dir_path.join(".hg/hgrc"),
233 ),
233 ),
234 ConfigSource::AbsPath(
234 ConfigSource::AbsPath(
235 current_dir_path.join(".hg/hgrc-not-shared"),
235 current_dir_path.join(".hg/hgrc-not-shared"),
236 ),
236 ),
237 ];
237 ];
238 // TODO: handle errors from
238 // TODO: handle errors from
239 // `load_from_explicit_sources`
239 // `load_from_explicit_sources`
240 Config::load_from_explicit_sources(config_files).ok()
240 Config::load_from_explicit_sources(config_files).ok()
241 } else {
241 } else {
242 None
242 None
243 }
243 }
244 } else {
244 } else {
245 None
245 None
246 }
246 }
247 };
247 };
248
248
249 let non_repo_config_val = {
249 let non_repo_config_val = {
250 let non_repo_val = non_repo_config.get(b"paths", &repo_arg);
250 let non_repo_val = non_repo_config.get(b"paths", &repo_arg);
251 match &non_repo_val {
251 match &non_repo_val {
252 Some(val) if val.len() > 0 => home::home_dir()
252 Some(val) if val.len() > 0 => home::home_dir()
253 .unwrap_or_else(|| PathBuf::from("~"))
253 .unwrap_or_else(|| PathBuf::from("~"))
254 .join(get_path_from_bytes(val))
254 .join(get_path_from_bytes(val))
255 .canonicalize()
255 .canonicalize()
256 // TODO: handle error and make it similar to python
256 // TODO: handle error and make it similar to python
257 // implementation maybe?
257 // implementation maybe?
258 .ok(),
258 .ok(),
259 _ => None,
259 _ => None,
260 }
260 }
261 };
261 };
262
262
263 let config_val = match &local_config {
263 let config_val = match &local_config {
264 None => non_repo_config_val,
264 None => non_repo_config_val,
265 Some(val) => {
265 Some(val) => {
266 let local_config_val = val.get(b"paths", &repo_arg);
266 let local_config_val = val.get(b"paths", &repo_arg);
267 match &local_config_val {
267 match &local_config_val {
268 Some(val) if val.len() > 0 => {
268 Some(val) if val.len() > 0 => {
269 // presence of a local_config assures that
269 // presence of a local_config assures that
270 // current_dir
270 // current_dir
271 // wont result in an Error
271 // wont result in an Error
272 let canpath = hg::utils::current_dir()
272 let canpath = hg::utils::current_dir()
273 .unwrap()
273 .unwrap()
274 .join(get_path_from_bytes(val))
274 .join(get_path_from_bytes(val))
275 .canonicalize();
275 .canonicalize();
276 canpath.ok().or(non_repo_config_val)
276 canpath.ok().or(non_repo_config_val)
277 }
277 }
278 _ => non_repo_config_val,
278 _ => non_repo_config_val,
279 }
279 }
280 }
280 }
281 };
281 };
282 config_val.or(Some(get_path_from_bytes(&repo_arg).to_path_buf()))
282 config_val.or(Some(get_path_from_bytes(&repo_arg).to_path_buf()))
283 }
283 }
284 };
284 };
285
285
286 let repo_result = match Repo::find(&non_repo_config, repo_path.to_owned())
286 let repo_result = match Repo::find(&non_repo_config, repo_path.to_owned())
287 {
287 {
288 Ok(repo) => Ok(repo),
288 Ok(repo) => Ok(repo),
289 Err(RepoError::NotFound { at }) if repo_path.is_none() => {
289 Err(RepoError::NotFound { at }) if repo_path.is_none() => {
290 // Not finding a repo is not fatal yet, if `-R` was not given
290 // Not finding a repo is not fatal yet, if `-R` was not given
291 Err(NoRepoInCwdError { cwd: at })
291 Err(NoRepoInCwdError { cwd: at })
292 }
292 }
293 Err(error) => exit(
293 Err(error) => exit(
294 &initial_current_dir,
294 &initial_current_dir,
295 &ui,
295 &ui,
296 OnUnsupported::from_config(&non_repo_config),
296 OnUnsupported::from_config(&non_repo_config),
297 Err(error.into()),
297 Err(error.into()),
298 // TODO: show a warning or combine with original error if
298 // TODO: show a warning or combine with original error if
299 // `get_bool` returns an error
299 // `get_bool` returns an error
300 non_repo_config
300 non_repo_config
301 .get_bool(b"ui", b"detailed-exit-code")
301 .get_bool(b"ui", b"detailed-exit-code")
302 .unwrap_or(false),
302 .unwrap_or(false),
303 ),
303 ),
304 };
304 };
305
305
306 let config = if let Ok(repo) = &repo_result {
306 let config = if let Ok(repo) = &repo_result {
307 repo.config()
307 repo.config()
308 } else {
308 } else {
309 &non_repo_config
309 &non_repo_config
310 };
310 };
311 let on_unsupported = OnUnsupported::from_config(config);
311 let on_unsupported = OnUnsupported::from_config(config);
312
312
313 let result = main_with_result(
313 let result = main_with_result(
314 &process_start_time,
314 &process_start_time,
315 &ui,
315 &ui,
316 repo_result.as_ref(),
316 repo_result.as_ref(),
317 config,
317 config,
318 );
318 );
319 exit(
319 exit(
320 &initial_current_dir,
320 &initial_current_dir,
321 &ui,
321 &ui,
322 on_unsupported,
322 on_unsupported,
323 result,
323 result,
324 // TODO: show a warning or combine with original error if `get_bool`
324 // TODO: show a warning or combine with original error if `get_bool`
325 // returns an error
325 // returns an error
326 config
326 config
327 .get_bool(b"ui", b"detailed-exit-code")
327 .get_bool(b"ui", b"detailed-exit-code")
328 .unwrap_or(false),
328 .unwrap_or(false),
329 )
329 )
330 }
330 }
331
331
332 fn exit_code(
332 fn exit_code(
333 result: &Result<(), CommandError>,
333 result: &Result<(), CommandError>,
334 use_detailed_exit_code: bool,
334 use_detailed_exit_code: bool,
335 ) -> i32 {
335 ) -> i32 {
336 match result {
336 match result {
337 Ok(()) => exit_codes::OK,
337 Ok(()) => exit_codes::OK,
338 Err(CommandError::Abort {
338 Err(CommandError::Abort {
339 message: _,
339 message: _,
340 detailed_exit_code,
340 detailed_exit_code,
341 }) => {
341 }) => {
342 if use_detailed_exit_code {
342 if use_detailed_exit_code {
343 *detailed_exit_code
343 *detailed_exit_code
344 } else {
344 } else {
345 exit_codes::ABORT
345 exit_codes::ABORT
346 }
346 }
347 }
347 }
348 Err(CommandError::Unsuccessful) => exit_codes::UNSUCCESSFUL,
348 Err(CommandError::Unsuccessful) => exit_codes::UNSUCCESSFUL,
349
349
350 // Exit with a specific code and no error message to let a potential
350 // Exit with a specific code and no error message to let a potential
351 // wrapper script fallback to Python-based Mercurial.
351 // wrapper script fallback to Python-based Mercurial.
352 Err(CommandError::UnsupportedFeature { .. }) => {
352 Err(CommandError::UnsupportedFeature { .. }) => {
353 exit_codes::UNIMPLEMENTED
353 exit_codes::UNIMPLEMENTED
354 }
354 }
355 }
355 }
356 }
356 }
357
357
358 fn exit(
358 fn exit(
359 initial_current_dir: &Option<PathBuf>,
359 initial_current_dir: &Option<PathBuf>,
360 ui: &Ui,
360 ui: &Ui,
361 mut on_unsupported: OnUnsupported,
361 mut on_unsupported: OnUnsupported,
362 result: Result<(), CommandError>,
362 result: Result<(), CommandError>,
363 use_detailed_exit_code: bool,
363 use_detailed_exit_code: bool,
364 ) -> ! {
364 ) -> ! {
365 if let (
365 if let (
366 OnUnsupported::Fallback { executable },
366 OnUnsupported::Fallback { executable },
367 Err(CommandError::UnsupportedFeature { .. }),
367 Err(CommandError::UnsupportedFeature { .. }),
368 ) = (&on_unsupported, &result)
368 ) = (&on_unsupported, &result)
369 {
369 {
370 let mut args = std::env::args_os();
370 let mut args = std::env::args_os();
371 let executable = match executable {
371 let executable = match executable {
372 None => {
372 None => {
373 exit_no_fallback(
373 exit_no_fallback(
374 ui,
374 ui,
375 OnUnsupported::Abort,
375 OnUnsupported::Abort,
376 Err(CommandError::abort(
376 Err(CommandError::abort(
377 "abort: 'rhg.on-unsupported=fallback' without \
377 "abort: 'rhg.on-unsupported=fallback' without \
378 'rhg.fallback-executable' set.",
378 'rhg.fallback-executable' set.",
379 )),
379 )),
380 false,
380 false,
381 );
381 );
382 }
382 }
383 Some(executable) => executable,
383 Some(executable) => executable,
384 };
384 };
385 let executable_path = get_path_from_bytes(&executable);
385 let executable_path = get_path_from_bytes(&executable);
386 let this_executable = args.next().expect("exepcted argv[0] to exist");
386 let this_executable = args.next().expect("exepcted argv[0] to exist");
387 if executable_path == &PathBuf::from(this_executable) {
387 if executable_path == &PathBuf::from(this_executable) {
388 // Avoid spawning infinitely many processes until resource
388 // Avoid spawning infinitely many processes until resource
389 // exhaustion.
389 // exhaustion.
390 let _ = ui.write_stderr(&format_bytes!(
390 let _ = ui.write_stderr(&format_bytes!(
391 b"Blocking recursive fallback. The 'rhg.fallback-executable = {}' config \
391 b"Blocking recursive fallback. The 'rhg.fallback-executable = {}' config \
392 points to `rhg` itself.\n",
392 points to `rhg` itself.\n",
393 executable
393 executable
394 ));
394 ));
395 on_unsupported = OnUnsupported::Abort
395 on_unsupported = OnUnsupported::Abort
396 } else {
396 } else {
397 // `args` is now `argv[1..]` since we’ve already consumed
397 // `args` is now `argv[1..]` since we’ve already consumed
398 // `argv[0]`
398 // `argv[0]`
399 let mut command = Command::new(executable_path);
399 let mut command = Command::new(executable_path);
400 command.args(args);
400 command.args(args);
401 if let Some(initial) = initial_current_dir {
401 if let Some(initial) = initial_current_dir {
402 command.current_dir(initial);
402 command.current_dir(initial);
403 }
403 }
404 let result = command.status();
404 let result = command.status();
405 match result {
405 match result {
406 Ok(status) => std::process::exit(
406 Ok(status) => std::process::exit(
407 status.code().unwrap_or(exit_codes::ABORT),
407 status.code().unwrap_or(exit_codes::ABORT),
408 ),
408 ),
409 Err(error) => {
409 Err(error) => {
410 let _ = ui.write_stderr(&format_bytes!(
410 let _ = ui.write_stderr(&format_bytes!(
411 b"tried to fall back to a '{}' sub-process but got error {}\n",
411 b"tried to fall back to a '{}' sub-process but got error {}\n",
412 executable, format_bytes::Utf8(error)
412 executable, format_bytes::Utf8(error)
413 ));
413 ));
414 on_unsupported = OnUnsupported::Abort
414 on_unsupported = OnUnsupported::Abort
415 }
415 }
416 }
416 }
417 }
417 }
418 }
418 }
419 exit_no_fallback(ui, on_unsupported, result, use_detailed_exit_code)
419 exit_no_fallback(ui, on_unsupported, result, use_detailed_exit_code)
420 }
420 }
421
421
422 fn exit_no_fallback(
422 fn exit_no_fallback(
423 ui: &Ui,
423 ui: &Ui,
424 on_unsupported: OnUnsupported,
424 on_unsupported: OnUnsupported,
425 result: Result<(), CommandError>,
425 result: Result<(), CommandError>,
426 use_detailed_exit_code: bool,
426 use_detailed_exit_code: bool,
427 ) -> ! {
427 ) -> ! {
428 match &result {
428 match &result {
429 Ok(_) => {}
429 Ok(_) => {}
430 Err(CommandError::Unsuccessful) => {}
430 Err(CommandError::Unsuccessful) => {}
431 Err(CommandError::Abort {
431 Err(CommandError::Abort {
432 message,
432 message,
433 detailed_exit_code: _,
433 detailed_exit_code: _,
434 }) => {
434 }) => {
435 if !message.is_empty() {
435 if !message.is_empty() {
436 // Ignore errors when writing to stderr, we’re already exiting
436 // Ignore errors when writing to stderr, we’re already exiting
437 // with failure code so there’s not much more we can do.
437 // with failure code so there’s not much more we can do.
438 let _ = ui.write_stderr(&format_bytes!(b"{}\n", message));
438 let _ = ui.write_stderr(&format_bytes!(b"{}\n", message));
439 }
439 }
440 }
440 }
441 Err(CommandError::UnsupportedFeature { message }) => {
441 Err(CommandError::UnsupportedFeature { message }) => {
442 match on_unsupported {
442 match on_unsupported {
443 OnUnsupported::Abort => {
443 OnUnsupported::Abort => {
444 let _ = ui.write_stderr(&format_bytes!(
444 let _ = ui.write_stderr(&format_bytes!(
445 b"unsupported feature: {}\n",
445 b"unsupported feature: {}\n",
446 message
446 message
447 ));
447 ));
448 }
448 }
449 OnUnsupported::AbortSilent => {}
449 OnUnsupported::AbortSilent => {}
450 OnUnsupported::Fallback { .. } => unreachable!(),
450 OnUnsupported::Fallback { .. } => unreachable!(),
451 }
451 }
452 }
452 }
453 }
453 }
454 std::process::exit(exit_code(&result, use_detailed_exit_code))
454 std::process::exit(exit_code(&result, use_detailed_exit_code))
455 }
455 }
456
456
457 macro_rules! subcommands {
457 macro_rules! subcommands {
458 ($( $command: ident )+) => {
458 ($( $command: ident )+) => {
459 mod commands {
459 mod commands {
460 $(
460 $(
461 pub mod $command;
461 pub mod $command;
462 )+
462 )+
463 }
463 }
464
464
465 fn add_subcommand_args<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b> {
465 fn add_subcommand_args<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b> {
466 app
466 app
467 $(
467 $(
468 .subcommand(commands::$command::args())
468 .subcommand(commands::$command::args())
469 )+
469 )+
470 }
470 }
471
471
472 pub type RunFn = fn(&CliInvocation) -> Result<(), CommandError>;
472 pub type RunFn = fn(&CliInvocation) -> Result<(), CommandError>;
473
473
474 fn subcommand_run_fn(name: &str) -> Option<RunFn> {
474 fn subcommand_run_fn(name: &str) -> Option<RunFn> {
475 match name {
475 match name {
476 $(
476 $(
477 stringify!($command) => Some(commands::$command::run),
477 stringify!($command) => Some(commands::$command::run),
478 )+
478 )+
479 _ => None,
479 _ => None,
480 }
480 }
481 }
481 }
482 };
482 };
483 }
483 }
484
484
485 subcommands! {
485 subcommands! {
486 cat
486 cat
487 debugdata
487 debugdata
488 debugrequirements
488 debugrequirements
489 debugignorerhg
489 debugignorerhg
490 files
490 files
491 root
491 root
492 config
492 config
493 status
493 status
494 }
494 }
495
495
496 pub struct CliInvocation<'a> {
496 pub struct CliInvocation<'a> {
497 ui: &'a Ui,
497 ui: &'a Ui,
498 subcommand_args: &'a ArgMatches<'a>,
498 subcommand_args: &'a ArgMatches<'a>,
499 config: &'a Config,
499 config: &'a Config,
500 /// References inside `Result` is a bit peculiar but allow
500 /// References inside `Result` is a bit peculiar but allow
501 /// `invocation.repo?` to work out with `&CliInvocation` since this
501 /// `invocation.repo?` to work out with `&CliInvocation` since this
502 /// `Result` type is `Copy`.
502 /// `Result` type is `Copy`.
503 repo: Result<&'a Repo, &'a NoRepoInCwdError>,
503 repo: Result<&'a Repo, &'a NoRepoInCwdError>,
504 }
504 }
505
505
506 struct NoRepoInCwdError {
506 struct NoRepoInCwdError {
507 cwd: PathBuf,
507 cwd: PathBuf,
508 }
508 }
509
509
510 /// CLI arguments to be parsed "early" in order to be able to read
510 /// CLI arguments to be parsed "early" in order to be able to read
511 /// configuration before using Clap. Ideally we would also use Clap for this,
511 /// configuration before using Clap. Ideally we would also use Clap for this,
512 /// see <https://github.com/clap-rs/clap/discussions/2366>.
512 /// see <https://github.com/clap-rs/clap/discussions/2366>.
513 ///
513 ///
514 /// These arguments are still declared when we do use Clap later, so that Clap
514 /// These arguments are still declared when we do use Clap later, so that Clap
515 /// does not return an error for their presence.
515 /// does not return an error for their presence.
516 struct EarlyArgs {
516 struct EarlyArgs {
517 /// Values of all `--config` arguments. (Possibly none)
517 /// Values of all `--config` arguments. (Possibly none)
518 config: Vec<Vec<u8>>,
518 config: Vec<Vec<u8>>,
519 /// Value of the `-R` or `--repository` argument, if any.
519 /// Value of the `-R` or `--repository` argument, if any.
520 repo: Option<Vec<u8>>,
520 repo: Option<Vec<u8>>,
521 /// Value of the `--cwd` argument, if any.
521 /// Value of the `--cwd` argument, if any.
522 cwd: Option<Vec<u8>>,
522 cwd: Option<Vec<u8>>,
523 }
523 }
524
524
525 impl EarlyArgs {
525 impl EarlyArgs {
526 fn parse(args: impl IntoIterator<Item = OsString>) -> Self {
526 fn parse(args: impl IntoIterator<Item = OsString>) -> Self {
527 let mut args = args.into_iter().map(get_bytes_from_os_str);
527 let mut args = args.into_iter().map(get_bytes_from_os_str);
528 let mut config = Vec::new();
528 let mut config = Vec::new();
529 let mut repo = None;
529 let mut repo = None;
530 let mut cwd = None;
530 let mut cwd = None;
531 // Use `while let` instead of `for` so that we can also call
531 // Use `while let` instead of `for` so that we can also call
532 // `args.next()` inside the loop.
532 // `args.next()` inside the loop.
533 while let Some(arg) = args.next() {
533 while let Some(arg) = args.next() {
534 if arg == b"--config" {
534 if arg == b"--config" {
535 if let Some(value) = args.next() {
535 if let Some(value) = args.next() {
536 config.push(value)
536 config.push(value)
537 }
537 }
538 } else if let Some(value) = arg.drop_prefix(b"--config=") {
538 } else if let Some(value) = arg.drop_prefix(b"--config=") {
539 config.push(value.to_owned())
539 config.push(value.to_owned())
540 }
540 }
541
541
542 if arg == b"--cwd" {
542 if arg == b"--cwd" {
543 if let Some(value) = args.next() {
543 if let Some(value) = args.next() {
544 cwd = Some(value)
544 cwd = Some(value)
545 }
545 }
546 } else if let Some(value) = arg.drop_prefix(b"--cwd=") {
546 } else if let Some(value) = arg.drop_prefix(b"--cwd=") {
547 cwd = Some(value.to_owned())
547 cwd = Some(value.to_owned())
548 }
548 }
549
549
550 if arg == b"--repository" || arg == b"-R" {
550 if arg == b"--repository" || arg == b"-R" {
551 if let Some(value) = args.next() {
551 if let Some(value) = args.next() {
552 repo = Some(value)
552 repo = Some(value)
553 }
553 }
554 } else if let Some(value) = arg.drop_prefix(b"--repository=") {
554 } else if let Some(value) = arg.drop_prefix(b"--repository=") {
555 repo = Some(value.to_owned())
555 repo = Some(value.to_owned())
556 } else if let Some(value) = arg.drop_prefix(b"-R") {
556 } else if let Some(value) = arg.drop_prefix(b"-R") {
557 repo = Some(value.to_owned())
557 repo = Some(value.to_owned())
558 }
558 }
559 }
559 }
560 Self { config, repo, cwd }
560 Self { config, repo, cwd }
561 }
561 }
562 }
562 }
563
563
564 /// What to do when encountering some unsupported feature.
564 /// What to do when encountering some unsupported feature.
565 ///
565 ///
566 /// See `HgError::UnsupportedFeature` and `CommandError::UnsupportedFeature`.
566 /// See `HgError::UnsupportedFeature` and `CommandError::UnsupportedFeature`.
567 enum OnUnsupported {
567 enum OnUnsupported {
568 /// Print an error message describing what feature is not supported,
568 /// Print an error message describing what feature is not supported,
569 /// and exit with code 252.
569 /// and exit with code 252.
570 Abort,
570 Abort,
571 /// Silently exit with code 252.
571 /// Silently exit with code 252.
572 AbortSilent,
572 AbortSilent,
573 /// Try running a Python implementation
573 /// Try running a Python implementation
574 Fallback { executable: Option<Vec<u8>> },
574 Fallback { executable: Option<Vec<u8>> },
575 }
575 }
576
576
577 impl OnUnsupported {
577 impl OnUnsupported {
578 const DEFAULT: Self = OnUnsupported::Abort;
578 const DEFAULT: Self = OnUnsupported::Abort;
579
579
580 fn from_config(config: &Config) -> Self {
580 fn from_config(config: &Config) -> Self {
581 match config
581 match config
582 .get(b"rhg", b"on-unsupported")
582 .get(b"rhg", b"on-unsupported")
583 .map(|value| value.to_ascii_lowercase())
583 .map(|value| value.to_ascii_lowercase())
584 .as_deref()
584 .as_deref()
585 {
585 {
586 Some(b"abort") => OnUnsupported::Abort,
586 Some(b"abort") => OnUnsupported::Abort,
587 Some(b"abort-silent") => OnUnsupported::AbortSilent,
587 Some(b"abort-silent") => OnUnsupported::AbortSilent,
588 Some(b"fallback") => OnUnsupported::Fallback {
588 Some(b"fallback") => OnUnsupported::Fallback {
589 executable: config
589 executable: config
590 .get(b"rhg", b"fallback-executable")
590 .get(b"rhg", b"fallback-executable")
591 .map(|x| x.to_owned()),
591 .map(|x| x.to_owned()),
592 },
592 },
593 None => Self::DEFAULT,
593 None => Self::DEFAULT,
594 Some(_) => {
594 Some(_) => {
595 // TODO: warn about unknown config value
595 // TODO: warn about unknown config value
596 Self::DEFAULT
596 Self::DEFAULT
597 }
597 }
598 }
598 }
599 }
599 }
600 }
600 }
601
601
602 /// The `*` extension is an edge-case for config sub-options that apply to all
602 /// The `*` extension is an edge-case for config sub-options that apply to all
603 /// extensions. For now, only `:required` exists, but that may change in the
603 /// extensions. For now, only `:required` exists, but that may change in the
604 /// future.
604 /// future.
605 const SUPPORTED_EXTENSIONS: &[&[u8]] =
605 const SUPPORTED_EXTENSIONS: &[&[u8]] =
606 &[b"blackbox", b"share", b"sparse", b"narrow", b"*"];
606 &[b"blackbox", b"share", b"sparse", b"narrow", b"*"];
607
607
608 fn check_extensions(config: &Config) -> Result<(), CommandError> {
608 fn check_extensions(config: &Config) -> Result<(), CommandError> {
609 let enabled: HashSet<&[u8]> = config
609 let enabled: HashSet<&[u8]> = config
610 .get_section_keys(b"extensions")
610 .get_section_keys(b"extensions")
611 .into_iter()
611 .into_iter()
612 .map(|extension| {
612 .map(|extension| {
613 // Ignore extension suboptions. Only `required` exists for now.
613 // Ignore extension suboptions. Only `required` exists for now.
614 // `rhg` either supports an extension or doesn't, so it doesn't
614 // `rhg` either supports an extension or doesn't, so it doesn't
615 // make sense to consider the loading of an extension.
615 // make sense to consider the loading of an extension.
616 extension.split_2(b':').unwrap_or((extension, b"")).0
616 extension.split_2(b':').unwrap_or((extension, b"")).0
617 })
617 })
618 .collect();
618 .collect();
619
619
620 let mut unsupported = enabled;
620 let mut unsupported = enabled;
621 for supported in SUPPORTED_EXTENSIONS {
621 for supported in SUPPORTED_EXTENSIONS {
622 unsupported.remove(supported);
622 unsupported.remove(supported);
623 }
623 }
624
624
625 if let Some(ignored_list) = config.get_list(b"rhg", b"ignored-extensions")
625 if let Some(ignored_list) = config.get_list(b"rhg", b"ignored-extensions")
626 {
626 {
627 for ignored in ignored_list {
627 for ignored in ignored_list {
628 unsupported.remove(ignored.as_slice());
628 unsupported.remove(ignored.as_slice());
629 }
629 }
630 }
630 }
631
631
632 if unsupported.is_empty() {
632 if unsupported.is_empty() {
633 Ok(())
633 Ok(())
634 } else {
634 } else {
635 Err(CommandError::UnsupportedFeature {
635 Err(CommandError::UnsupportedFeature {
636 message: format_bytes!(
636 message: format_bytes!(
637 b"extensions: {} (consider adding them to 'rhg.ignored-extensions' config)",
637 b"extensions: {} (consider adding them to 'rhg.ignored-extensions' config)",
638 join(unsupported, b", ")
638 join(unsupported, b", ")
639 ),
639 ),
640 })
640 })
641 }
641 }
642 }
642 }
643
643
644 fn check_unsupported(
644 fn check_unsupported(
645 config: &Config,
645 config: &Config,
646 repo: Result<&Repo, &NoRepoInCwdError>,
646 ui: &ui::Ui,
647 ui: &ui::Ui,
647 ) -> Result<(), CommandError> {
648 ) -> Result<(), CommandError> {
648 check_extensions(config)?;
649 check_extensions(config)?;
649
650
650 if std::env::var_os("HG_PENDING").is_some() {
651 if std::env::var_os("HG_PENDING").is_some() {
651 // TODO: only if the value is `== repo.working_directory`?
652 // TODO: only if the value is `== repo.working_directory`?
652 // What about relative v.s. absolute paths?
653 // What about relative v.s. absolute paths?
653 Err(CommandError::unsupported("$HG_PENDING"))?
654 Err(CommandError::unsupported("$HG_PENDING"))?
654 }
655 }
655
656
657 if let Ok(repo) = repo {
658 if repo.has_subrepos()? {
659 Err(CommandError::unsupported("sub-repositories"))?
660 }
661 }
662
656 if config.has_non_empty_section(b"encode") {
663 if config.has_non_empty_section(b"encode") {
657 Err(CommandError::unsupported("[encode] config"))?
664 Err(CommandError::unsupported("[encode] config"))?
658 }
665 }
659
666
660 if config.has_non_empty_section(b"decode") {
667 if config.has_non_empty_section(b"decode") {
661 Err(CommandError::unsupported("[decode] config"))?
668 Err(CommandError::unsupported("[decode] config"))?
662 }
669 }
663
670
664 if let Some(color) = config.get(b"ui", b"color") {
671 if let Some(color) = config.get(b"ui", b"color") {
665 if (color == b"always" || color == b"debug") && !ui.plain() {
672 if (color == b"always" || color == b"debug") && !ui.plain() {
666 Err(CommandError::unsupported("colored output"))?
673 Err(CommandError::unsupported("colored output"))?
667 }
674 }
668 }
675 }
669
676
670 Ok(())
677 Ok(())
671 }
678 }
@@ -1,1174 +1,1170 b''
1 TODO: fix rhg bugs that make this test fail when status is enabled
2 $ unset RHG_STATUS
3
4
5 $ cat >> $HGRCPATH <<EOF
1 $ cat >> $HGRCPATH <<EOF
6 > [extdiff]
2 > [extdiff]
7 > # for portability:
3 > # for portability:
8 > pdiff = sh "$RUNTESTDIR/pdiff"
4 > pdiff = sh "$RUNTESTDIR/pdiff"
9 > [progress]
5 > [progress]
10 > disable=False
6 > disable=False
11 > assume-tty = 1
7 > assume-tty = 1
12 > delay = 0
8 > delay = 0
13 > # set changedelay really large so we don't see nested topics
9 > # set changedelay really large so we don't see nested topics
14 > changedelay = 30000
10 > changedelay = 30000
15 > format = topic bar number
11 > format = topic bar number
16 > refresh = 0
12 > refresh = 0
17 > width = 60
13 > width = 60
18 > EOF
14 > EOF
19
15
20 Preparing the subrepository 'sub2'
16 Preparing the subrepository 'sub2'
21
17
22 $ hg init sub2
18 $ hg init sub2
23 $ echo sub2 > sub2/sub2
19 $ echo sub2 > sub2/sub2
24 $ hg add -R sub2
20 $ hg add -R sub2
25 adding sub2/sub2
21 adding sub2/sub2
26 $ hg commit -R sub2 -m "sub2 import"
22 $ hg commit -R sub2 -m "sub2 import"
27
23
28 Preparing the 'sub1' repo which depends on the subrepo 'sub2'
24 Preparing the 'sub1' repo which depends on the subrepo 'sub2'
29
25
30 $ hg init sub1
26 $ hg init sub1
31 $ echo sub1 > sub1/sub1
27 $ echo sub1 > sub1/sub1
32 $ echo "sub2 = ../sub2" > sub1/.hgsub
28 $ echo "sub2 = ../sub2" > sub1/.hgsub
33 $ hg clone sub2 sub1/sub2
29 $ hg clone sub2 sub1/sub2
34 \r (no-eol) (esc)
30 \r (no-eol) (esc)
35 linking [======> ] 1/6\r (no-eol) (esc)
31 linking [======> ] 1/6\r (no-eol) (esc)
36 linking [==============> ] 2/6\r (no-eol) (esc)
32 linking [==============> ] 2/6\r (no-eol) (esc)
37 linking [=====================> ] 3/6\r (no-eol) (esc)
33 linking [=====================> ] 3/6\r (no-eol) (esc)
38 linking [=============================> ] 4/6\r (no-eol) (esc)
34 linking [=============================> ] 4/6\r (no-eol) (esc)
39 linking [====================================> ] 5/6\r (no-eol) (esc)
35 linking [====================================> ] 5/6\r (no-eol) (esc)
40 linking [============================================>] 6/6\r (no-eol) (esc)
36 linking [============================================>] 6/6\r (no-eol) (esc)
41 \r (no-eol) (esc)
37 \r (no-eol) (esc)
42 \r (no-eol) (esc)
38 \r (no-eol) (esc)
43 updating [===========================================>] 1/1\r (no-eol) (esc)
39 updating [===========================================>] 1/1\r (no-eol) (esc)
44 \r (no-eol) (esc)
40 \r (no-eol) (esc)
45 updating to branch default
41 updating to branch default
46 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
42 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
47 $ hg add -R sub1
43 $ hg add -R sub1
48 adding sub1/.hgsub
44 adding sub1/.hgsub
49 adding sub1/sub1
45 adding sub1/sub1
50 $ hg commit -R sub1 -m "sub1 import"
46 $ hg commit -R sub1 -m "sub1 import"
51
47
52 Preparing the 'main' repo which depends on the subrepo 'sub1'
48 Preparing the 'main' repo which depends on the subrepo 'sub1'
53
49
54 $ hg init main
50 $ hg init main
55 $ echo main > main/main
51 $ echo main > main/main
56 $ echo "sub1 = ../sub1" > main/.hgsub
52 $ echo "sub1 = ../sub1" > main/.hgsub
57 $ hg clone sub1 main/sub1
53 $ hg clone sub1 main/sub1
58 \r (no-eol) (esc)
54 \r (no-eol) (esc)
59 linking [====> ] 1/8\r (no-eol) (esc)
55 linking [====> ] 1/8\r (no-eol) (esc)
60 linking [==========> ] 2/8\r (no-eol) (esc)
56 linking [==========> ] 2/8\r (no-eol) (esc)
61 linking [===============> ] 3/8\r (no-eol) (esc)
57 linking [===============> ] 3/8\r (no-eol) (esc)
62 linking [=====================> ] 4/8\r (no-eol) (esc)
58 linking [=====================> ] 4/8\r (no-eol) (esc)
63 linking [===========================> ] 5/8\r (no-eol) (esc)
59 linking [===========================> ] 5/8\r (no-eol) (esc)
64 linking [================================> ] 6/8\r (no-eol) (esc)
60 linking [================================> ] 6/8\r (no-eol) (esc)
65 linking [======================================> ] 7/8\r (no-eol) (esc)
61 linking [======================================> ] 7/8\r (no-eol) (esc)
66 linking [============================================>] 8/8\r (no-eol) (esc)
62 linking [============================================>] 8/8\r (no-eol) (esc)
67 \r (no-eol) (esc)
63 \r (no-eol) (esc)
68 \r (no-eol) (esc)
64 \r (no-eol) (esc)
69 updating [===========================================>] 3/3\r (no-eol) (esc)
65 updating [===========================================>] 3/3\r (no-eol) (esc)
70 \r (no-eol) (esc)
66 \r (no-eol) (esc)
71 \r (no-eol) (esc)
67 \r (no-eol) (esc)
72 linking [======> ] 1/6\r (no-eol) (esc)
68 linking [======> ] 1/6\r (no-eol) (esc)
73 linking [==============> ] 2/6\r (no-eol) (esc)
69 linking [==============> ] 2/6\r (no-eol) (esc)
74 linking [=====================> ] 3/6\r (no-eol) (esc)
70 linking [=====================> ] 3/6\r (no-eol) (esc)
75 linking [=============================> ] 4/6\r (no-eol) (esc)
71 linking [=============================> ] 4/6\r (no-eol) (esc)
76 linking [====================================> ] 5/6\r (no-eol) (esc)
72 linking [====================================> ] 5/6\r (no-eol) (esc)
77 linking [============================================>] 6/6\r (no-eol) (esc)
73 linking [============================================>] 6/6\r (no-eol) (esc)
78 updating [===========================================>] 1/1\r (no-eol) (esc)
74 updating [===========================================>] 1/1\r (no-eol) (esc)
79 \r (no-eol) (esc)
75 \r (no-eol) (esc)
80 updating to branch default
76 updating to branch default
81 cloning subrepo sub2 from $TESTTMP/sub2
77 cloning subrepo sub2 from $TESTTMP/sub2
82 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
78 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
83 $ hg add -R main
79 $ hg add -R main
84 adding main/.hgsub
80 adding main/.hgsub
85 adding main/main
81 adding main/main
86 $ hg commit -R main -m "main import"
82 $ hg commit -R main -m "main import"
87
83
88 #if serve
84 #if serve
89
85
90 Unfortunately, subrepos not at their nominal location cannot be cloned. But
86 Unfortunately, subrepos not at their nominal location cannot be cloned. But
91 they are still served from their location within the local repository. The only
87 they are still served from their location within the local repository. The only
92 reason why 'main' can be cloned via the filesystem is because 'sub1' and 'sub2'
88 reason why 'main' can be cloned via the filesystem is because 'sub1' and 'sub2'
93 are also available as siblings of 'main'.
89 are also available as siblings of 'main'.
94
90
95 $ hg serve -R main --debug -S -p $HGPORT -d --pid-file=hg1.pid -E error.log -A access.log
91 $ hg serve -R main --debug -S -p $HGPORT -d --pid-file=hg1.pid -E error.log -A access.log
96 adding = $TESTTMP/main
92 adding = $TESTTMP/main
97 adding sub1 = $TESTTMP/main/sub1
93 adding sub1 = $TESTTMP/main/sub1
98 adding sub1/sub2 = $TESTTMP/main/sub1/sub2
94 adding sub1/sub2 = $TESTTMP/main/sub1/sub2
99 listening at http://*:$HGPORT/ (bound to *:$HGPORT) (glob) (?)
95 listening at http://*:$HGPORT/ (bound to *:$HGPORT) (glob) (?)
100 adding = $TESTTMP/main (?)
96 adding = $TESTTMP/main (?)
101 adding sub1 = $TESTTMP/main/sub1 (?)
97 adding sub1 = $TESTTMP/main/sub1 (?)
102 adding sub1/sub2 = $TESTTMP/main/sub1/sub2 (?)
98 adding sub1/sub2 = $TESTTMP/main/sub1/sub2 (?)
103 $ cat hg1.pid >> $DAEMON_PIDS
99 $ cat hg1.pid >> $DAEMON_PIDS
104
100
105 $ hg clone http://localhost:$HGPORT httpclone --config progress.disable=True
101 $ hg clone http://localhost:$HGPORT httpclone --config progress.disable=True
106 requesting all changes
102 requesting all changes
107 adding changesets
103 adding changesets
108 adding manifests
104 adding manifests
109 adding file changes
105 adding file changes
110 added 1 changesets with 3 changes to 3 files
106 added 1 changesets with 3 changes to 3 files
111 new changesets 7f491f53a367
107 new changesets 7f491f53a367
112 updating to branch default
108 updating to branch default
113 cloning subrepo sub1 from http://localhost:$HGPORT/../sub1
109 cloning subrepo sub1 from http://localhost:$HGPORT/../sub1
114 abort: HTTP Error 404: Not Found
110 abort: HTTP Error 404: Not Found
115 [100]
111 [100]
116
112
117 $ cat access.log
113 $ cat access.log
118 * "GET /?cmd=capabilities HTTP/1.1" 200 - (glob)
114 * "GET /?cmd=capabilities HTTP/1.1" 200 - (glob)
119 * "GET /?cmd=batch HTTP/1.1" 200 - * (glob)
115 * "GET /?cmd=batch HTTP/1.1" 200 - * (glob)
120 * "GET /?cmd=getbundle HTTP/1.1" 200 - * (glob)
116 * "GET /?cmd=getbundle HTTP/1.1" 200 - * (glob)
121 * "GET /../sub1?cmd=capabilities HTTP/1.1" 404 - (glob)
117 * "GET /../sub1?cmd=capabilities HTTP/1.1" 404 - (glob)
122 $ cat error.log
118 $ cat error.log
123
119
124 $ killdaemons.py
120 $ killdaemons.py
125 $ rm hg1.pid error.log access.log
121 $ rm hg1.pid error.log access.log
126 #endif
122 #endif
127
123
128 Cleaning both repositories, just as a clone -U
124 Cleaning both repositories, just as a clone -U
129
125
130 $ hg up -C -R sub2 null
126 $ hg up -C -R sub2 null
131 \r (no-eol) (esc)
127 \r (no-eol) (esc)
132 updating [===========================================>] 1/1\r (no-eol) (esc)
128 updating [===========================================>] 1/1\r (no-eol) (esc)
133 \r (no-eol) (esc)
129 \r (no-eol) (esc)
134 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
130 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
135 $ hg up -C -R sub1 null
131 $ hg up -C -R sub1 null
136 \r (no-eol) (esc)
132 \r (no-eol) (esc)
137 updating [===========================================>] 1/1\r (no-eol) (esc)
133 updating [===========================================>] 1/1\r (no-eol) (esc)
138 \r (no-eol) (esc)
134 \r (no-eol) (esc)
139 \r (no-eol) (esc)
135 \r (no-eol) (esc)
140 updating [===========================================>] 3/3\r (no-eol) (esc)
136 updating [===========================================>] 3/3\r (no-eol) (esc)
141 \r (no-eol) (esc)
137 \r (no-eol) (esc)
142 0 files updated, 0 files merged, 3 files removed, 0 files unresolved
138 0 files updated, 0 files merged, 3 files removed, 0 files unresolved
143 $ hg up -C -R main null
139 $ hg up -C -R main null
144 \r (no-eol) (esc)
140 \r (no-eol) (esc)
145 updating [===========================================>] 1/1\r (no-eol) (esc)
141 updating [===========================================>] 1/1\r (no-eol) (esc)
146 \r (no-eol) (esc)
142 \r (no-eol) (esc)
147 \r (no-eol) (esc)
143 \r (no-eol) (esc)
148 updating [===========================================>] 3/3\r (no-eol) (esc)
144 updating [===========================================>] 3/3\r (no-eol) (esc)
149 \r (no-eol) (esc)
145 \r (no-eol) (esc)
150 \r (no-eol) (esc)
146 \r (no-eol) (esc)
151 updating [===========================================>] 3/3\r (no-eol) (esc)
147 updating [===========================================>] 3/3\r (no-eol) (esc)
152 \r (no-eol) (esc)
148 \r (no-eol) (esc)
153 0 files updated, 0 files merged, 3 files removed, 0 files unresolved
149 0 files updated, 0 files merged, 3 files removed, 0 files unresolved
154 $ rm -rf main/sub1
150 $ rm -rf main/sub1
155 $ rm -rf sub1/sub2
151 $ rm -rf sub1/sub2
156
152
157 Clone main
153 Clone main
158
154
159 $ hg --config extensions.largefiles= clone main cloned
155 $ hg --config extensions.largefiles= clone main cloned
160 \r (no-eol) (esc)
156 \r (no-eol) (esc)
161 linking [====> ] 1/8\r (no-eol) (esc)
157 linking [====> ] 1/8\r (no-eol) (esc)
162 linking [==========> ] 2/8\r (no-eol) (esc)
158 linking [==========> ] 2/8\r (no-eol) (esc)
163 linking [===============> ] 3/8\r (no-eol) (esc)
159 linking [===============> ] 3/8\r (no-eol) (esc)
164 linking [=====================> ] 4/8\r (no-eol) (esc)
160 linking [=====================> ] 4/8\r (no-eol) (esc)
165 linking [===========================> ] 5/8\r (no-eol) (esc)
161 linking [===========================> ] 5/8\r (no-eol) (esc)
166 linking [================================> ] 6/8\r (no-eol) (esc)
162 linking [================================> ] 6/8\r (no-eol) (esc)
167 linking [======================================> ] 7/8\r (no-eol) (esc)
163 linking [======================================> ] 7/8\r (no-eol) (esc)
168 linking [============================================>] 8/8\r (no-eol) (esc)
164 linking [============================================>] 8/8\r (no-eol) (esc)
169 \r (no-eol) (esc)
165 \r (no-eol) (esc)
170 \r (no-eol) (esc)
166 \r (no-eol) (esc)
171 updating [===========================================>] 3/3\r (no-eol) (esc)
167 updating [===========================================>] 3/3\r (no-eol) (esc)
172 \r (no-eol) (esc)
168 \r (no-eol) (esc)
173 \r (no-eol) (esc)
169 \r (no-eol) (esc)
174 linking [====> ] 1/8\r (no-eol) (esc)
170 linking [====> ] 1/8\r (no-eol) (esc)
175 linking [==========> ] 2/8\r (no-eol) (esc)
171 linking [==========> ] 2/8\r (no-eol) (esc)
176 linking [===============> ] 3/8\r (no-eol) (esc)
172 linking [===============> ] 3/8\r (no-eol) (esc)
177 linking [=====================> ] 4/8\r (no-eol) (esc)
173 linking [=====================> ] 4/8\r (no-eol) (esc)
178 linking [===========================> ] 5/8\r (no-eol) (esc)
174 linking [===========================> ] 5/8\r (no-eol) (esc)
179 linking [================================> ] 6/8\r (no-eol) (esc)
175 linking [================================> ] 6/8\r (no-eol) (esc)
180 linking [======================================> ] 7/8\r (no-eol) (esc)
176 linking [======================================> ] 7/8\r (no-eol) (esc)
181 linking [============================================>] 8/8\r (no-eol) (esc)
177 linking [============================================>] 8/8\r (no-eol) (esc)
182 updating [===========================================>] 3/3\r (no-eol) (esc)
178 updating [===========================================>] 3/3\r (no-eol) (esc)
183 \r (no-eol) (esc)
179 \r (no-eol) (esc)
184 \r (no-eol) (esc)
180 \r (no-eol) (esc)
185 linking [======> ] 1/6\r (no-eol) (esc)
181 linking [======> ] 1/6\r (no-eol) (esc)
186 linking [==============> ] 2/6\r (no-eol) (esc)
182 linking [==============> ] 2/6\r (no-eol) (esc)
187 linking [=====================> ] 3/6\r (no-eol) (esc)
183 linking [=====================> ] 3/6\r (no-eol) (esc)
188 linking [=============================> ] 4/6\r (no-eol) (esc)
184 linking [=============================> ] 4/6\r (no-eol) (esc)
189 linking [====================================> ] 5/6\r (no-eol) (esc)
185 linking [====================================> ] 5/6\r (no-eol) (esc)
190 linking [============================================>] 6/6\r (no-eol) (esc)
186 linking [============================================>] 6/6\r (no-eol) (esc)
191 updating [===========================================>] 1/1\r (no-eol) (esc)
187 updating [===========================================>] 1/1\r (no-eol) (esc)
192 \r (no-eol) (esc)
188 \r (no-eol) (esc)
193 updating to branch default
189 updating to branch default
194 cloning subrepo sub1 from $TESTTMP/sub1
190 cloning subrepo sub1 from $TESTTMP/sub1
195 cloning subrepo sub1/sub2 from $TESTTMP/sub2
191 cloning subrepo sub1/sub2 from $TESTTMP/sub2
196 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
192 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
197
193
198 Largefiles is NOT enabled in the clone if the source repo doesn't require it
194 Largefiles is NOT enabled in the clone if the source repo doesn't require it
199 $ grep largefiles cloned/.hg/hgrc
195 $ grep largefiles cloned/.hg/hgrc
200 [1]
196 [1]
201
197
202 Checking cloned repo ids
198 Checking cloned repo ids
203
199
204 $ printf "cloned " ; hg id -R cloned
200 $ printf "cloned " ; hg id -R cloned
205 cloned 7f491f53a367 tip
201 cloned 7f491f53a367 tip
206 $ printf "cloned/sub1 " ; hg id -R cloned/sub1
202 $ printf "cloned/sub1 " ; hg id -R cloned/sub1
207 cloned/sub1 fc3b4ce2696f tip
203 cloned/sub1 fc3b4ce2696f tip
208 $ printf "cloned/sub1/sub2 " ; hg id -R cloned/sub1/sub2
204 $ printf "cloned/sub1/sub2 " ; hg id -R cloned/sub1/sub2
209 cloned/sub1/sub2 c57a0840e3ba tip
205 cloned/sub1/sub2 c57a0840e3ba tip
210
206
211 debugsub output for main and sub1
207 debugsub output for main and sub1
212
208
213 $ hg debugsub -R cloned
209 $ hg debugsub -R cloned
214 path sub1
210 path sub1
215 source ../sub1
211 source ../sub1
216 revision fc3b4ce2696f7741438c79207583768f2ce6b0dd
212 revision fc3b4ce2696f7741438c79207583768f2ce6b0dd
217 $ hg debugsub -R cloned/sub1
213 $ hg debugsub -R cloned/sub1
218 path sub2
214 path sub2
219 source ../sub2
215 source ../sub2
220 revision c57a0840e3badd667ef3c3ef65471609acb2ba3c
216 revision c57a0840e3badd667ef3c3ef65471609acb2ba3c
221
217
222 Modifying deeply nested 'sub2'
218 Modifying deeply nested 'sub2'
223
219
224 $ echo modified > cloned/sub1/sub2/sub2
220 $ echo modified > cloned/sub1/sub2/sub2
225 $ hg commit --subrepos -m "deep nested modif should trigger a commit" -R cloned
221 $ hg commit --subrepos -m "deep nested modif should trigger a commit" -R cloned
226 committing subrepository sub1
222 committing subrepository sub1
227 committing subrepository sub1/sub2
223 committing subrepository sub1/sub2
228
224
229 Checking modified node ids
225 Checking modified node ids
230
226
231 $ printf "cloned " ; hg id -R cloned
227 $ printf "cloned " ; hg id -R cloned
232 cloned ffe6649062fe tip
228 cloned ffe6649062fe tip
233 $ printf "cloned/sub1 " ; hg id -R cloned/sub1
229 $ printf "cloned/sub1 " ; hg id -R cloned/sub1
234 cloned/sub1 2ecb03bf44a9 tip
230 cloned/sub1 2ecb03bf44a9 tip
235 $ printf "cloned/sub1/sub2 " ; hg id -R cloned/sub1/sub2
231 $ printf "cloned/sub1/sub2 " ; hg id -R cloned/sub1/sub2
236 cloned/sub1/sub2 53dd3430bcaf tip
232 cloned/sub1/sub2 53dd3430bcaf tip
237
233
238 debugsub output for main and sub1
234 debugsub output for main and sub1
239
235
240 $ hg debugsub -R cloned
236 $ hg debugsub -R cloned
241 path sub1
237 path sub1
242 source ../sub1
238 source ../sub1
243 revision 2ecb03bf44a94e749e8669481dd9069526ce7cb9
239 revision 2ecb03bf44a94e749e8669481dd9069526ce7cb9
244 $ hg debugsub -R cloned/sub1
240 $ hg debugsub -R cloned/sub1
245 path sub2
241 path sub2
246 source ../sub2
242 source ../sub2
247 revision 53dd3430bcaf5ab4a7c48262bcad6d441f510487
243 revision 53dd3430bcaf5ab4a7c48262bcad6d441f510487
248
244
249 Check that deep archiving works
245 Check that deep archiving works
250
246
251 $ cd cloned
247 $ cd cloned
252 $ echo 'test' > sub1/sub2/test.txt
248 $ echo 'test' > sub1/sub2/test.txt
253 $ hg --config extensions.largefiles=! add sub1/sub2/test.txt
249 $ hg --config extensions.largefiles=! add sub1/sub2/test.txt
254 $ mkdir sub1/sub2/folder
250 $ mkdir sub1/sub2/folder
255 $ echo 'subfolder' > sub1/sub2/folder/test.txt
251 $ echo 'subfolder' > sub1/sub2/folder/test.txt
256 $ hg ci -ASm "add test.txt"
252 $ hg ci -ASm "add test.txt"
257 adding sub1/sub2/folder/test.txt
253 adding sub1/sub2/folder/test.txt
258 committing subrepository sub1
254 committing subrepository sub1
259 committing subrepository sub1/sub2
255 committing subrepository sub1/sub2
260
256
261 $ rm -r main
257 $ rm -r main
262 $ hg archive -S -qr 'wdir()' ../wdir
258 $ hg archive -S -qr 'wdir()' ../wdir
263 $ cat ../wdir/.hg_archival.txt
259 $ cat ../wdir/.hg_archival.txt
264 repo: 7f491f53a367861f47ee64a80eb997d1f341b77a
260 repo: 7f491f53a367861f47ee64a80eb997d1f341b77a
265 node: 9bb10eebee29dc0f1201dcf5977b811a540255fd+
261 node: 9bb10eebee29dc0f1201dcf5977b811a540255fd+
266 branch: default
262 branch: default
267 latesttag: null
263 latesttag: null
268 latesttagdistance: 4
264 latesttagdistance: 4
269 changessincelatesttag: 4
265 changessincelatesttag: 4
270 $ hg update -Cq .
266 $ hg update -Cq .
271
267
272 A deleted subrepo file is flagged as dirty, like the top level repo
268 A deleted subrepo file is flagged as dirty, like the top level repo
273
269
274 $ rm -r ../wdir sub1/sub2/folder/test.txt
270 $ rm -r ../wdir sub1/sub2/folder/test.txt
275 $ hg archive -S -qr 'wdir()' ../wdir
271 $ hg archive -S -qr 'wdir()' ../wdir
276 $ cat ../wdir/.hg_archival.txt
272 $ cat ../wdir/.hg_archival.txt
277 repo: 7f491f53a367861f47ee64a80eb997d1f341b77a
273 repo: 7f491f53a367861f47ee64a80eb997d1f341b77a
278 node: 9bb10eebee29dc0f1201dcf5977b811a540255fd+
274 node: 9bb10eebee29dc0f1201dcf5977b811a540255fd+
279 branch: default
275 branch: default
280 latesttag: null
276 latesttag: null
281 latesttagdistance: 4
277 latesttagdistance: 4
282 changessincelatesttag: 4
278 changessincelatesttag: 4
283 $ hg update -Cq .
279 $ hg update -Cq .
284 $ rm -r ../wdir
280 $ rm -r ../wdir
285
281
286 $ hg archive -S -qr 'wdir()' ../wdir \
282 $ hg archive -S -qr 'wdir()' ../wdir \
287 > --config 'experimental.archivemetatemplate=archived {node|short}\n'
283 > --config 'experimental.archivemetatemplate=archived {node|short}\n'
288 $ cat ../wdir/.hg_archival.txt
284 $ cat ../wdir/.hg_archival.txt
289 archived ffffffffffff
285 archived ffffffffffff
290 $ rm -r ../wdir
286 $ rm -r ../wdir
291
287
292 .. but first take a detour through some deep removal testing
288 .. but first take a detour through some deep removal testing
293
289
294 $ hg remove -S -I 're:.*.txt' .
290 $ hg remove -S -I 're:.*.txt' .
295 \r (no-eol) (esc)
291 \r (no-eol) (esc)
296 searching [==========================================>] 1/1\r (no-eol) (esc)
292 searching [==========================================>] 1/1\r (no-eol) (esc)
297 searching [==========================================>] 1/1\r (no-eol) (esc)
293 searching [==========================================>] 1/1\r (no-eol) (esc)
298 \r (no-eol) (esc)
294 \r (no-eol) (esc)
299 \r (no-eol) (esc)
295 \r (no-eol) (esc)
300 deleting [=====================> ] 1/2\r (no-eol) (esc)
296 deleting [=====================> ] 1/2\r (no-eol) (esc)
301 \r (no-eol) (esc)
297 \r (no-eol) (esc)
302 \r (no-eol) (esc)
298 \r (no-eol) (esc)
303 deleting [===========================================>] 2/2\r (no-eol) (esc)
299 deleting [===========================================>] 2/2\r (no-eol) (esc)
304 \r (no-eol) (esc)
300 \r (no-eol) (esc)
305 removing sub1/sub2/folder/test.txt
301 removing sub1/sub2/folder/test.txt
306 removing sub1/sub2/test.txt
302 removing sub1/sub2/test.txt
307 $ hg status -S
303 $ hg status -S
308 R sub1/sub2/folder/test.txt
304 R sub1/sub2/folder/test.txt
309 R sub1/sub2/test.txt
305 R sub1/sub2/test.txt
310 $ hg update -Cq
306 $ hg update -Cq
311 $ hg remove -I 're:.*.txt' sub1
307 $ hg remove -I 're:.*.txt' sub1
312 \r (no-eol) (esc)
308 \r (no-eol) (esc)
313 searching [==========================================>] 1/1\r (no-eol) (esc)
309 searching [==========================================>] 1/1\r (no-eol) (esc)
314 \r (no-eol) (esc)
310 \r (no-eol) (esc)
315 \r (no-eol) (esc)
311 \r (no-eol) (esc)
316 deleting [===========================================>] 1/1\r (no-eol) (esc)
312 deleting [===========================================>] 1/1\r (no-eol) (esc)
317 \r (no-eol) (esc)
313 \r (no-eol) (esc)
318 $ hg status -S
314 $ hg status -S
319 $ hg remove sub1/sub2/folder/test.txt
315 $ hg remove sub1/sub2/folder/test.txt
320 \r (no-eol) (esc)
316 \r (no-eol) (esc)
321 searching [==========================================>] 1/1\r (no-eol) (esc)
317 searching [==========================================>] 1/1\r (no-eol) (esc)
322 searching [==========================================>] 1/1\r (no-eol) (esc)
318 searching [==========================================>] 1/1\r (no-eol) (esc)
323 \r (no-eol) (esc)
319 \r (no-eol) (esc)
324 \r (no-eol) (esc)
320 \r (no-eol) (esc)
325 deleting [===========================================>] 1/1\r (no-eol) (esc)
321 deleting [===========================================>] 1/1\r (no-eol) (esc)
326 \r (no-eol) (esc)
322 \r (no-eol) (esc)
327 \r (no-eol) (esc)
323 \r (no-eol) (esc)
328 deleting [===========================================>] 1/1\r (no-eol) (esc)
324 deleting [===========================================>] 1/1\r (no-eol) (esc)
329 \r (no-eol) (esc)
325 \r (no-eol) (esc)
330 \r (no-eol) (esc)
326 \r (no-eol) (esc)
331 deleting [===========================================>] 1/1\r (no-eol) (esc)
327 deleting [===========================================>] 1/1\r (no-eol) (esc)
332 \r (no-eol) (esc)
328 \r (no-eol) (esc)
333 $ hg remove sub1/.hgsubstate
329 $ hg remove sub1/.hgsubstate
334 \r (no-eol) (esc)
330 \r (no-eol) (esc)
335 searching [==========================================>] 1/1\r (no-eol) (esc)
331 searching [==========================================>] 1/1\r (no-eol) (esc)
336 \r (no-eol) (esc)
332 \r (no-eol) (esc)
337 \r (no-eol) (esc)
333 \r (no-eol) (esc)
338 deleting [===========================================>] 1/1\r (no-eol) (esc)
334 deleting [===========================================>] 1/1\r (no-eol) (esc)
339 \r (no-eol) (esc)
335 \r (no-eol) (esc)
340 \r (no-eol) (esc)
336 \r (no-eol) (esc)
341 deleting [===========================================>] 1/1\r (no-eol) (esc)
337 deleting [===========================================>] 1/1\r (no-eol) (esc)
342 \r (no-eol) (esc)
338 \r (no-eol) (esc)
343 $ mv sub1/.hgsub sub1/x.hgsub
339 $ mv sub1/.hgsub sub1/x.hgsub
344 $ hg status -S
340 $ hg status -S
345 warning: subrepo spec file 'sub1/.hgsub' not found
341 warning: subrepo spec file 'sub1/.hgsub' not found
346 R sub1/.hgsubstate
342 R sub1/.hgsubstate
347 R sub1/sub2/folder/test.txt
343 R sub1/sub2/folder/test.txt
348 ! sub1/.hgsub
344 ! sub1/.hgsub
349 ? sub1/x.hgsub
345 ? sub1/x.hgsub
350 $ hg status -R sub1
346 $ hg status -R sub1
351 warning: subrepo spec file 'sub1/.hgsub' not found
347 warning: subrepo spec file 'sub1/.hgsub' not found
352 R .hgsubstate
348 R .hgsubstate
353 ! .hgsub
349 ! .hgsub
354 ? x.hgsub
350 ? x.hgsub
355 $ mv sub1/x.hgsub sub1/.hgsub
351 $ mv sub1/x.hgsub sub1/.hgsub
356 $ hg update -Cq
352 $ hg update -Cq
357 $ touch sub1/foo
353 $ touch sub1/foo
358 $ hg forget sub1/sub2/folder/test.txt
354 $ hg forget sub1/sub2/folder/test.txt
359 $ rm sub1/sub2/test.txt
355 $ rm sub1/sub2/test.txt
360
356
361 Test relative path printing + subrepos
357 Test relative path printing + subrepos
362 $ mkdir -p foo/bar
358 $ mkdir -p foo/bar
363 $ cd foo
359 $ cd foo
364 $ touch bar/abc
360 $ touch bar/abc
365 $ hg addremove -S ..
361 $ hg addremove -S ..
366 \r (no-eol) (esc)
362 \r (no-eol) (esc)
367 searching for exact renames [========================>] 1/1\r (no-eol) (esc)
363 searching for exact renames [========================>] 1/1\r (no-eol) (esc)
368 \r (no-eol) (esc)
364 \r (no-eol) (esc)
369 adding ../sub1/sub2/folder/test.txt
365 adding ../sub1/sub2/folder/test.txt
370 removing ../sub1/sub2/test.txt
366 removing ../sub1/sub2/test.txt
371 adding ../sub1/foo
367 adding ../sub1/foo
372 adding bar/abc
368 adding bar/abc
373 $ cd ..
369 $ cd ..
374 $ hg status -S
370 $ hg status -S
375 A foo/bar/abc
371 A foo/bar/abc
376 A sub1/foo
372 A sub1/foo
377 R sub1/sub2/test.txt
373 R sub1/sub2/test.txt
378
374
379 Archive wdir() with subrepos
375 Archive wdir() with subrepos
380 $ hg rm main
376 $ hg rm main
381 \r (no-eol) (esc)
377 \r (no-eol) (esc)
382 deleting [===========================================>] 1/1\r (no-eol) (esc)
378 deleting [===========================================>] 1/1\r (no-eol) (esc)
383 \r (no-eol) (esc)
379 \r (no-eol) (esc)
384 $ hg archive -S -r 'wdir()' ../wdir
380 $ hg archive -S -r 'wdir()' ../wdir
385 \r (no-eol) (esc)
381 \r (no-eol) (esc)
386 archiving [ ] 0/3\r (no-eol) (esc)
382 archiving [ ] 0/3\r (no-eol) (esc)
387 archiving [=============> ] 1/3\r (no-eol) (esc)
383 archiving [=============> ] 1/3\r (no-eol) (esc)
388 archiving [===========================> ] 2/3\r (no-eol) (esc)
384 archiving [===========================> ] 2/3\r (no-eol) (esc)
389 archiving [==========================================>] 3/3\r (no-eol) (esc)
385 archiving [==========================================>] 3/3\r (no-eol) (esc)
390 \r (no-eol) (esc)
386 \r (no-eol) (esc)
391 \r (no-eol) (esc)
387 \r (no-eol) (esc)
392 archiving (sub1) [ ] 0/4\r (no-eol) (esc)
388 archiving (sub1) [ ] 0/4\r (no-eol) (esc)
393 archiving (sub1) [========> ] 1/4\r (no-eol) (esc)
389 archiving (sub1) [========> ] 1/4\r (no-eol) (esc)
394 archiving (sub1) [=================> ] 2/4\r (no-eol) (esc)
390 archiving (sub1) [=================> ] 2/4\r (no-eol) (esc)
395 archiving (sub1) [==========================> ] 3/4\r (no-eol) (esc)
391 archiving (sub1) [==========================> ] 3/4\r (no-eol) (esc)
396 archiving (sub1) [===================================>] 4/4\r (no-eol) (esc)
392 archiving (sub1) [===================================>] 4/4\r (no-eol) (esc)
397 \r (no-eol) (esc)
393 \r (no-eol) (esc)
398 \r (no-eol) (esc)
394 \r (no-eol) (esc)
399 archiving (sub1/sub2) [ ] 0/2\r (no-eol) (esc)
395 archiving (sub1/sub2) [ ] 0/2\r (no-eol) (esc)
400 archiving (sub1/sub2) [==============> ] 1/2\r (no-eol) (esc)
396 archiving (sub1/sub2) [==============> ] 1/2\r (no-eol) (esc)
401 archiving (sub1/sub2) [==============================>] 2/2\r (no-eol) (esc)
397 archiving (sub1/sub2) [==============================>] 2/2\r (no-eol) (esc)
402 \r (no-eol) (esc)
398 \r (no-eol) (esc)
403 $ diff -r . ../wdir | egrep -v '\.hg$|^Common subdirectories:'
399 $ diff -r . ../wdir | egrep -v '\.hg$|^Common subdirectories:'
404 Only in ../wdir: .hg_archival.txt
400 Only in ../wdir: .hg_archival.txt
405
401
406 $ find ../wdir -type f | sort
402 $ find ../wdir -type f | sort
407 ../wdir/.hg_archival.txt
403 ../wdir/.hg_archival.txt
408 ../wdir/.hgsub
404 ../wdir/.hgsub
409 ../wdir/.hgsubstate
405 ../wdir/.hgsubstate
410 ../wdir/foo/bar/abc
406 ../wdir/foo/bar/abc
411 ../wdir/sub1/.hgsub
407 ../wdir/sub1/.hgsub
412 ../wdir/sub1/.hgsubstate
408 ../wdir/sub1/.hgsubstate
413 ../wdir/sub1/foo
409 ../wdir/sub1/foo
414 ../wdir/sub1/sub1
410 ../wdir/sub1/sub1
415 ../wdir/sub1/sub2/folder/test.txt
411 ../wdir/sub1/sub2/folder/test.txt
416 ../wdir/sub1/sub2/sub2
412 ../wdir/sub1/sub2/sub2
417
413
418 $ cat ../wdir/.hg_archival.txt
414 $ cat ../wdir/.hg_archival.txt
419 repo: 7f491f53a367861f47ee64a80eb997d1f341b77a
415 repo: 7f491f53a367861f47ee64a80eb997d1f341b77a
420 node: 9bb10eebee29dc0f1201dcf5977b811a540255fd+
416 node: 9bb10eebee29dc0f1201dcf5977b811a540255fd+
421 branch: default
417 branch: default
422 latesttag: null
418 latesttag: null
423 latesttagdistance: 4
419 latesttagdistance: 4
424 changessincelatesttag: 4
420 changessincelatesttag: 4
425
421
426 Attempting to archive 'wdir()' with a missing file is handled gracefully
422 Attempting to archive 'wdir()' with a missing file is handled gracefully
427 $ rm sub1/sub1
423 $ rm sub1/sub1
428 $ rm -r ../wdir
424 $ rm -r ../wdir
429 $ hg archive -v -S -r 'wdir()' ../wdir
425 $ hg archive -v -S -r 'wdir()' ../wdir
430 \r (no-eol) (esc)
426 \r (no-eol) (esc)
431 archiving [ ] 0/3\r (no-eol) (esc)
427 archiving [ ] 0/3\r (no-eol) (esc)
432 archiving [=============> ] 1/3\r (no-eol) (esc)
428 archiving [=============> ] 1/3\r (no-eol) (esc)
433 archiving [===========================> ] 2/3\r (no-eol) (esc)
429 archiving [===========================> ] 2/3\r (no-eol) (esc)
434 archiving [==========================================>] 3/3\r (no-eol) (esc)
430 archiving [==========================================>] 3/3\r (no-eol) (esc)
435 \r (no-eol) (esc)
431 \r (no-eol) (esc)
436 \r (no-eol) (esc)
432 \r (no-eol) (esc)
437 archiving (sub1) [ ] 0/3\r (no-eol) (esc)
433 archiving (sub1) [ ] 0/3\r (no-eol) (esc)
438 archiving (sub1) [===========> ] 1/3\r (no-eol) (esc)
434 archiving (sub1) [===========> ] 1/3\r (no-eol) (esc)
439 archiving (sub1) [=======================> ] 2/3\r (no-eol) (esc)
435 archiving (sub1) [=======================> ] 2/3\r (no-eol) (esc)
440 archiving (sub1) [===================================>] 3/3\r (no-eol) (esc)
436 archiving (sub1) [===================================>] 3/3\r (no-eol) (esc)
441 \r (no-eol) (esc)
437 \r (no-eol) (esc)
442 \r (no-eol) (esc)
438 \r (no-eol) (esc)
443 archiving (sub1/sub2) [ ] 0/2\r (no-eol) (esc)
439 archiving (sub1/sub2) [ ] 0/2\r (no-eol) (esc)
444 archiving (sub1/sub2) [==============> ] 1/2\r (no-eol) (esc)
440 archiving (sub1/sub2) [==============> ] 1/2\r (no-eol) (esc)
445 archiving (sub1/sub2) [==============================>] 2/2\r (no-eol) (esc)
441 archiving (sub1/sub2) [==============================>] 2/2\r (no-eol) (esc)
446 \r (no-eol) (esc)
442 \r (no-eol) (esc)
447 $ find ../wdir -type f | sort
443 $ find ../wdir -type f | sort
448 ../wdir/.hg_archival.txt
444 ../wdir/.hg_archival.txt
449 ../wdir/.hgsub
445 ../wdir/.hgsub
450 ../wdir/.hgsubstate
446 ../wdir/.hgsubstate
451 ../wdir/foo/bar/abc
447 ../wdir/foo/bar/abc
452 ../wdir/sub1/.hgsub
448 ../wdir/sub1/.hgsub
453 ../wdir/sub1/.hgsubstate
449 ../wdir/sub1/.hgsubstate
454 ../wdir/sub1/foo
450 ../wdir/sub1/foo
455 ../wdir/sub1/sub2/folder/test.txt
451 ../wdir/sub1/sub2/folder/test.txt
456 ../wdir/sub1/sub2/sub2
452 ../wdir/sub1/sub2/sub2
457
453
458 Continue relative path printing + subrepos
454 Continue relative path printing + subrepos
459 $ hg update -Cq
455 $ hg update -Cq
460 $ rm -r ../wdir
456 $ rm -r ../wdir
461 $ hg archive -S -r 'wdir()' ../wdir
457 $ hg archive -S -r 'wdir()' ../wdir
462 \r (no-eol) (esc)
458 \r (no-eol) (esc)
463 archiving [ ] 0/3\r (no-eol) (esc)
459 archiving [ ] 0/3\r (no-eol) (esc)
464 archiving [=============> ] 1/3\r (no-eol) (esc)
460 archiving [=============> ] 1/3\r (no-eol) (esc)
465 archiving [===========================> ] 2/3\r (no-eol) (esc)
461 archiving [===========================> ] 2/3\r (no-eol) (esc)
466 archiving [==========================================>] 3/3\r (no-eol) (esc)
462 archiving [==========================================>] 3/3\r (no-eol) (esc)
467 \r (no-eol) (esc)
463 \r (no-eol) (esc)
468 \r (no-eol) (esc)
464 \r (no-eol) (esc)
469 archiving (sub1) [ ] 0/3\r (no-eol) (esc)
465 archiving (sub1) [ ] 0/3\r (no-eol) (esc)
470 archiving (sub1) [===========> ] 1/3\r (no-eol) (esc)
466 archiving (sub1) [===========> ] 1/3\r (no-eol) (esc)
471 archiving (sub1) [=======================> ] 2/3\r (no-eol) (esc)
467 archiving (sub1) [=======================> ] 2/3\r (no-eol) (esc)
472 archiving (sub1) [===================================>] 3/3\r (no-eol) (esc)
468 archiving (sub1) [===================================>] 3/3\r (no-eol) (esc)
473 \r (no-eol) (esc)
469 \r (no-eol) (esc)
474 \r (no-eol) (esc)
470 \r (no-eol) (esc)
475 archiving (sub1/sub2) [ ] 0/3\r (no-eol) (esc)
471 archiving (sub1/sub2) [ ] 0/3\r (no-eol) (esc)
476 archiving (sub1/sub2) [=========> ] 1/3\r (no-eol) (esc)
472 archiving (sub1/sub2) [=========> ] 1/3\r (no-eol) (esc)
477 archiving (sub1/sub2) [===================> ] 2/3\r (no-eol) (esc)
473 archiving (sub1/sub2) [===================> ] 2/3\r (no-eol) (esc)
478 archiving (sub1/sub2) [==============================>] 3/3\r (no-eol) (esc)
474 archiving (sub1/sub2) [==============================>] 3/3\r (no-eol) (esc)
479 \r (no-eol) (esc)
475 \r (no-eol) (esc)
480 $ cat ../wdir/.hg_archival.txt
476 $ cat ../wdir/.hg_archival.txt
481 repo: 7f491f53a367861f47ee64a80eb997d1f341b77a
477 repo: 7f491f53a367861f47ee64a80eb997d1f341b77a
482 node: 9bb10eebee29dc0f1201dcf5977b811a540255fd
478 node: 9bb10eebee29dc0f1201dcf5977b811a540255fd
483 branch: default
479 branch: default
484 latesttag: null
480 latesttag: null
485 latesttagdistance: 4
481 latesttagdistance: 4
486 changessincelatesttag: 4
482 changessincelatesttag: 4
487
483
488 $ touch sub1/sub2/folder/bar
484 $ touch sub1/sub2/folder/bar
489 $ hg addremove sub1/sub2
485 $ hg addremove sub1/sub2
490 adding sub1/sub2/folder/bar
486 adding sub1/sub2/folder/bar
491 $ hg status -S
487 $ hg status -S
492 A sub1/sub2/folder/bar
488 A sub1/sub2/folder/bar
493 ? foo/bar/abc
489 ? foo/bar/abc
494 ? sub1/foo
490 ? sub1/foo
495 $ hg update -Cq
491 $ hg update -Cq
496 $ hg addremove sub1
492 $ hg addremove sub1
497 adding sub1/sub2/folder/bar
493 adding sub1/sub2/folder/bar
498 adding sub1/foo
494 adding sub1/foo
499 $ hg update -Cq
495 $ hg update -Cq
500 $ rm sub1/sub2/folder/test.txt
496 $ rm sub1/sub2/folder/test.txt
501 $ rm sub1/sub2/test.txt
497 $ rm sub1/sub2/test.txt
502 $ hg ci -ASm "remove test.txt"
498 $ hg ci -ASm "remove test.txt"
503 adding sub1/sub2/folder/bar
499 adding sub1/sub2/folder/bar
504 removing sub1/sub2/folder/test.txt
500 removing sub1/sub2/folder/test.txt
505 removing sub1/sub2/test.txt
501 removing sub1/sub2/test.txt
506 adding sub1/foo
502 adding sub1/foo
507 adding foo/bar/abc
503 adding foo/bar/abc
508 committing subrepository sub1
504 committing subrepository sub1
509 committing subrepository sub1/sub2
505 committing subrepository sub1/sub2
510
506
511 $ hg forget sub1/sub2/sub2
507 $ hg forget sub1/sub2/sub2
512 $ echo x > sub1/sub2/x.txt
508 $ echo x > sub1/sub2/x.txt
513 $ hg add sub1/sub2/x.txt
509 $ hg add sub1/sub2/x.txt
514
510
515 Files sees uncommitted adds and removes in subrepos
511 Files sees uncommitted adds and removes in subrepos
516 $ hg files -S
512 $ hg files -S
517 .hgsub
513 .hgsub
518 .hgsubstate
514 .hgsubstate
519 foo/bar/abc
515 foo/bar/abc
520 main
516 main
521 sub1/.hgsub
517 sub1/.hgsub
522 sub1/.hgsubstate
518 sub1/.hgsubstate
523 sub1/foo
519 sub1/foo
524 sub1/sub1
520 sub1/sub1
525 sub1/sub2/folder/bar
521 sub1/sub2/folder/bar
526 sub1/sub2/x.txt
522 sub1/sub2/x.txt
527
523
528 $ hg files -S "set:eol('dos') or eol('unix') or size('<= 0')"
524 $ hg files -S "set:eol('dos') or eol('unix') or size('<= 0')"
529 .hgsub
525 .hgsub
530 .hgsubstate
526 .hgsubstate
531 foo/bar/abc
527 foo/bar/abc
532 main
528 main
533 sub1/.hgsub
529 sub1/.hgsub
534 sub1/.hgsubstate
530 sub1/.hgsubstate
535 sub1/foo
531 sub1/foo
536 sub1/sub1
532 sub1/sub1
537 sub1/sub2/folder/bar
533 sub1/sub2/folder/bar
538 sub1/sub2/x.txt
534 sub1/sub2/x.txt
539
535
540 $ hg files -r '.^' -S "set:eol('dos') or eol('unix')"
536 $ hg files -r '.^' -S "set:eol('dos') or eol('unix')"
541 .hgsub
537 .hgsub
542 .hgsubstate
538 .hgsubstate
543 main
539 main
544 sub1/.hgsub
540 sub1/.hgsub
545 sub1/.hgsubstate
541 sub1/.hgsubstate
546 sub1/sub1
542 sub1/sub1
547 sub1/sub2/folder/test.txt
543 sub1/sub2/folder/test.txt
548 sub1/sub2/sub2
544 sub1/sub2/sub2
549 sub1/sub2/test.txt
545 sub1/sub2/test.txt
550
546
551 $ hg files sub1
547 $ hg files sub1
552 sub1/.hgsub
548 sub1/.hgsub
553 sub1/.hgsubstate
549 sub1/.hgsubstate
554 sub1/foo
550 sub1/foo
555 sub1/sub1
551 sub1/sub1
556 sub1/sub2/folder/bar
552 sub1/sub2/folder/bar
557 sub1/sub2/x.txt
553 sub1/sub2/x.txt
558
554
559 $ hg files sub1/sub2
555 $ hg files sub1/sub2
560 sub1/sub2/folder/bar
556 sub1/sub2/folder/bar
561 sub1/sub2/x.txt
557 sub1/sub2/x.txt
562
558
563 $ hg files
559 $ hg files
564 .hgsub
560 .hgsub
565 .hgsubstate
561 .hgsubstate
566 foo/bar/abc
562 foo/bar/abc
567 main
563 main
568
564
569 $ hg files -S -r '.^' sub1/sub2/folder
565 $ hg files -S -r '.^' sub1/sub2/folder
570 sub1/sub2/folder/test.txt
566 sub1/sub2/folder/test.txt
571
567
572 $ hg files -S -r '.^' sub1/sub2/missing
568 $ hg files -S -r '.^' sub1/sub2/missing
573 sub1/sub2/missing: no such file in rev 78026e779ea6
569 sub1/sub2/missing: no such file in rev 78026e779ea6
574 [1]
570 [1]
575
571
576 $ hg files -r '.^' sub1/
572 $ hg files -r '.^' sub1/
577 sub1/.hgsub
573 sub1/.hgsub
578 sub1/.hgsubstate
574 sub1/.hgsubstate
579 sub1/sub1
575 sub1/sub1
580 sub1/sub2/folder/test.txt
576 sub1/sub2/folder/test.txt
581 sub1/sub2/sub2
577 sub1/sub2/sub2
582 sub1/sub2/test.txt
578 sub1/sub2/test.txt
583
579
584 $ hg files -r '.^' sub1/sub2
580 $ hg files -r '.^' sub1/sub2
585 sub1/sub2/folder/test.txt
581 sub1/sub2/folder/test.txt
586 sub1/sub2/sub2
582 sub1/sub2/sub2
587 sub1/sub2/test.txt
583 sub1/sub2/test.txt
588
584
589 $ hg rollback -q
585 $ hg rollback -q
590 $ hg up -Cq
586 $ hg up -Cq
591
587
592 $ hg --config extensions.largefiles=! archive -S ../archive_all
588 $ hg --config extensions.largefiles=! archive -S ../archive_all
593 \r (no-eol) (esc)
589 \r (no-eol) (esc)
594 archiving [ ] 0/3\r (no-eol) (esc)
590 archiving [ ] 0/3\r (no-eol) (esc)
595 archiving [=============> ] 1/3\r (no-eol) (esc)
591 archiving [=============> ] 1/3\r (no-eol) (esc)
596 archiving [===========================> ] 2/3\r (no-eol) (esc)
592 archiving [===========================> ] 2/3\r (no-eol) (esc)
597 archiving [==========================================>] 3/3\r (no-eol) (esc)
593 archiving [==========================================>] 3/3\r (no-eol) (esc)
598 \r (no-eol) (esc)
594 \r (no-eol) (esc)
599 \r (no-eol) (esc)
595 \r (no-eol) (esc)
600 archiving (sub1) [ ] 0/3\r (no-eol) (esc)
596 archiving (sub1) [ ] 0/3\r (no-eol) (esc)
601 archiving (sub1) [===========> ] 1/3\r (no-eol) (esc)
597 archiving (sub1) [===========> ] 1/3\r (no-eol) (esc)
602 archiving (sub1) [=======================> ] 2/3\r (no-eol) (esc)
598 archiving (sub1) [=======================> ] 2/3\r (no-eol) (esc)
603 archiving (sub1) [===================================>] 3/3\r (no-eol) (esc)
599 archiving (sub1) [===================================>] 3/3\r (no-eol) (esc)
604 \r (no-eol) (esc)
600 \r (no-eol) (esc)
605 \r (no-eol) (esc)
601 \r (no-eol) (esc)
606 archiving (sub1/sub2) [ ] 0/3\r (no-eol) (esc)
602 archiving (sub1/sub2) [ ] 0/3\r (no-eol) (esc)
607 archiving (sub1/sub2) [=========> ] 1/3\r (no-eol) (esc)
603 archiving (sub1/sub2) [=========> ] 1/3\r (no-eol) (esc)
608 archiving (sub1/sub2) [===================> ] 2/3\r (no-eol) (esc)
604 archiving (sub1/sub2) [===================> ] 2/3\r (no-eol) (esc)
609 archiving (sub1/sub2) [==============================>] 3/3\r (no-eol) (esc)
605 archiving (sub1/sub2) [==============================>] 3/3\r (no-eol) (esc)
610 \r (no-eol) (esc)
606 \r (no-eol) (esc)
611 $ find ../archive_all | sort
607 $ find ../archive_all | sort
612 ../archive_all
608 ../archive_all
613 ../archive_all/.hg_archival.txt
609 ../archive_all/.hg_archival.txt
614 ../archive_all/.hgsub
610 ../archive_all/.hgsub
615 ../archive_all/.hgsubstate
611 ../archive_all/.hgsubstate
616 ../archive_all/main
612 ../archive_all/main
617 ../archive_all/sub1
613 ../archive_all/sub1
618 ../archive_all/sub1/.hgsub
614 ../archive_all/sub1/.hgsub
619 ../archive_all/sub1/.hgsubstate
615 ../archive_all/sub1/.hgsubstate
620 ../archive_all/sub1/sub1
616 ../archive_all/sub1/sub1
621 ../archive_all/sub1/sub2
617 ../archive_all/sub1/sub2
622 ../archive_all/sub1/sub2/folder
618 ../archive_all/sub1/sub2/folder
623 ../archive_all/sub1/sub2/folder/test.txt
619 ../archive_all/sub1/sub2/folder/test.txt
624 ../archive_all/sub1/sub2/sub2
620 ../archive_all/sub1/sub2/sub2
625 ../archive_all/sub1/sub2/test.txt
621 ../archive_all/sub1/sub2/test.txt
626
622
627 Check that archive -X works in deep subrepos
623 Check that archive -X works in deep subrepos
628
624
629 $ hg --config extensions.largefiles=! archive -S -X '**test*' ../archive_exclude
625 $ hg --config extensions.largefiles=! archive -S -X '**test*' ../archive_exclude
630 \r (no-eol) (esc)
626 \r (no-eol) (esc)
631 archiving [ ] 0/3\r (no-eol) (esc)
627 archiving [ ] 0/3\r (no-eol) (esc)
632 archiving [=============> ] 1/3\r (no-eol) (esc)
628 archiving [=============> ] 1/3\r (no-eol) (esc)
633 archiving [===========================> ] 2/3\r (no-eol) (esc)
629 archiving [===========================> ] 2/3\r (no-eol) (esc)
634 archiving [==========================================>] 3/3\r (no-eol) (esc)
630 archiving [==========================================>] 3/3\r (no-eol) (esc)
635 \r (no-eol) (esc)
631 \r (no-eol) (esc)
636 \r (no-eol) (esc)
632 \r (no-eol) (esc)
637 archiving (sub1) [ ] 0/3\r (no-eol) (esc)
633 archiving (sub1) [ ] 0/3\r (no-eol) (esc)
638 archiving (sub1) [===========> ] 1/3\r (no-eol) (esc)
634 archiving (sub1) [===========> ] 1/3\r (no-eol) (esc)
639 archiving (sub1) [=======================> ] 2/3\r (no-eol) (esc)
635 archiving (sub1) [=======================> ] 2/3\r (no-eol) (esc)
640 archiving (sub1) [===================================>] 3/3\r (no-eol) (esc)
636 archiving (sub1) [===================================>] 3/3\r (no-eol) (esc)
641 \r (no-eol) (esc)
637 \r (no-eol) (esc)
642 \r (no-eol) (esc)
638 \r (no-eol) (esc)
643 archiving (sub1/sub2) [ ] 0/1\r (no-eol) (esc)
639 archiving (sub1/sub2) [ ] 0/1\r (no-eol) (esc)
644 archiving (sub1/sub2) [==============================>] 1/1\r (no-eol) (esc)
640 archiving (sub1/sub2) [==============================>] 1/1\r (no-eol) (esc)
645 \r (no-eol) (esc)
641 \r (no-eol) (esc)
646 $ find ../archive_exclude | sort
642 $ find ../archive_exclude | sort
647 ../archive_exclude
643 ../archive_exclude
648 ../archive_exclude/.hg_archival.txt
644 ../archive_exclude/.hg_archival.txt
649 ../archive_exclude/.hgsub
645 ../archive_exclude/.hgsub
650 ../archive_exclude/.hgsubstate
646 ../archive_exclude/.hgsubstate
651 ../archive_exclude/main
647 ../archive_exclude/main
652 ../archive_exclude/sub1
648 ../archive_exclude/sub1
653 ../archive_exclude/sub1/.hgsub
649 ../archive_exclude/sub1/.hgsub
654 ../archive_exclude/sub1/.hgsubstate
650 ../archive_exclude/sub1/.hgsubstate
655 ../archive_exclude/sub1/sub1
651 ../archive_exclude/sub1/sub1
656 ../archive_exclude/sub1/sub2
652 ../archive_exclude/sub1/sub2
657 ../archive_exclude/sub1/sub2/sub2
653 ../archive_exclude/sub1/sub2/sub2
658
654
659 $ hg --config extensions.largefiles=! archive -S -I '**test*' ../archive_include
655 $ hg --config extensions.largefiles=! archive -S -I '**test*' ../archive_include
660 \r (no-eol) (esc)
656 \r (no-eol) (esc)
661 archiving (sub1) [ <=> ] 0\r (no-eol) (esc)
657 archiving (sub1) [ <=> ] 0\r (no-eol) (esc)
662 \r (no-eol) (esc)
658 \r (no-eol) (esc)
663 \r (no-eol) (esc)
659 \r (no-eol) (esc)
664 archiving (sub1/sub2) [ ] 0/2\r (no-eol) (esc)
660 archiving (sub1/sub2) [ ] 0/2\r (no-eol) (esc)
665 archiving (sub1/sub2) [==============> ] 1/2\r (no-eol) (esc)
661 archiving (sub1/sub2) [==============> ] 1/2\r (no-eol) (esc)
666 archiving (sub1/sub2) [==============================>] 2/2\r (no-eol) (esc)
662 archiving (sub1/sub2) [==============================>] 2/2\r (no-eol) (esc)
667 \r (no-eol) (esc)
663 \r (no-eol) (esc)
668 $ find ../archive_include | sort
664 $ find ../archive_include | sort
669 ../archive_include
665 ../archive_include
670 ../archive_include/sub1
666 ../archive_include/sub1
671 ../archive_include/sub1/sub2
667 ../archive_include/sub1/sub2
672 ../archive_include/sub1/sub2/folder
668 ../archive_include/sub1/sub2/folder
673 ../archive_include/sub1/sub2/folder/test.txt
669 ../archive_include/sub1/sub2/folder/test.txt
674 ../archive_include/sub1/sub2/test.txt
670 ../archive_include/sub1/sub2/test.txt
675
671
676 Check that deep archive works with largefiles (which overrides hgsubrepo impl)
672 Check that deep archive works with largefiles (which overrides hgsubrepo impl)
677 This also tests the repo.ui regression in 43fb170a23bd, and that lf subrepo
673 This also tests the repo.ui regression in 43fb170a23bd, and that lf subrepo
678 subrepos are archived properly.
674 subrepos are archived properly.
679 Note that add --large through a subrepo currently adds the file as a normal file
675 Note that add --large through a subrepo currently adds the file as a normal file
680
676
681 $ echo "large" > sub1/sub2/large.bin
677 $ echo "large" > sub1/sub2/large.bin
682 $ hg --config extensions.largefiles= add --large -R sub1/sub2 sub1/sub2/large.bin
678 $ hg --config extensions.largefiles= add --large -R sub1/sub2 sub1/sub2/large.bin
683 $ echo "large" > large.bin
679 $ echo "large" > large.bin
684 $ hg --config extensions.largefiles= add --large large.bin
680 $ hg --config extensions.largefiles= add --large large.bin
685 $ hg --config extensions.largefiles= ci -S -m "add large files"
681 $ hg --config extensions.largefiles= ci -S -m "add large files"
686 committing subrepository sub1
682 committing subrepository sub1
687 committing subrepository sub1/sub2
683 committing subrepository sub1/sub2
688
684
689 $ hg --config extensions.largefiles= archive -S ../archive_lf
685 $ hg --config extensions.largefiles= archive -S ../archive_lf
690 $ find ../archive_lf | sort
686 $ find ../archive_lf | sort
691 ../archive_lf
687 ../archive_lf
692 ../archive_lf/.hg_archival.txt
688 ../archive_lf/.hg_archival.txt
693 ../archive_lf/.hgsub
689 ../archive_lf/.hgsub
694 ../archive_lf/.hgsubstate
690 ../archive_lf/.hgsubstate
695 ../archive_lf/large.bin
691 ../archive_lf/large.bin
696 ../archive_lf/main
692 ../archive_lf/main
697 ../archive_lf/sub1
693 ../archive_lf/sub1
698 ../archive_lf/sub1/.hgsub
694 ../archive_lf/sub1/.hgsub
699 ../archive_lf/sub1/.hgsubstate
695 ../archive_lf/sub1/.hgsubstate
700 ../archive_lf/sub1/sub1
696 ../archive_lf/sub1/sub1
701 ../archive_lf/sub1/sub2
697 ../archive_lf/sub1/sub2
702 ../archive_lf/sub1/sub2/folder
698 ../archive_lf/sub1/sub2/folder
703 ../archive_lf/sub1/sub2/folder/test.txt
699 ../archive_lf/sub1/sub2/folder/test.txt
704 ../archive_lf/sub1/sub2/large.bin
700 ../archive_lf/sub1/sub2/large.bin
705 ../archive_lf/sub1/sub2/sub2
701 ../archive_lf/sub1/sub2/sub2
706 ../archive_lf/sub1/sub2/test.txt
702 ../archive_lf/sub1/sub2/test.txt
707 $ rm -rf ../archive_lf
703 $ rm -rf ../archive_lf
708
704
709 Exclude large files from main and sub-sub repo
705 Exclude large files from main and sub-sub repo
710
706
711 $ hg --config extensions.largefiles= archive -S -X '**.bin' ../archive_lf
707 $ hg --config extensions.largefiles= archive -S -X '**.bin' ../archive_lf
712 $ find ../archive_lf | sort
708 $ find ../archive_lf | sort
713 ../archive_lf
709 ../archive_lf
714 ../archive_lf/.hg_archival.txt
710 ../archive_lf/.hg_archival.txt
715 ../archive_lf/.hgsub
711 ../archive_lf/.hgsub
716 ../archive_lf/.hgsubstate
712 ../archive_lf/.hgsubstate
717 ../archive_lf/main
713 ../archive_lf/main
718 ../archive_lf/sub1
714 ../archive_lf/sub1
719 ../archive_lf/sub1/.hgsub
715 ../archive_lf/sub1/.hgsub
720 ../archive_lf/sub1/.hgsubstate
716 ../archive_lf/sub1/.hgsubstate
721 ../archive_lf/sub1/sub1
717 ../archive_lf/sub1/sub1
722 ../archive_lf/sub1/sub2
718 ../archive_lf/sub1/sub2
723 ../archive_lf/sub1/sub2/folder
719 ../archive_lf/sub1/sub2/folder
724 ../archive_lf/sub1/sub2/folder/test.txt
720 ../archive_lf/sub1/sub2/folder/test.txt
725 ../archive_lf/sub1/sub2/sub2
721 ../archive_lf/sub1/sub2/sub2
726 ../archive_lf/sub1/sub2/test.txt
722 ../archive_lf/sub1/sub2/test.txt
727 $ rm -rf ../archive_lf
723 $ rm -rf ../archive_lf
728
724
729 Exclude normal files from main and sub-sub repo
725 Exclude normal files from main and sub-sub repo
730
726
731 $ hg --config extensions.largefiles= archive -S -X '**.txt' -p '.' ../archive_lf.tgz
727 $ hg --config extensions.largefiles= archive -S -X '**.txt' -p '.' ../archive_lf.tgz
732 $ tar -tzf ../archive_lf.tgz | sort
728 $ tar -tzf ../archive_lf.tgz | sort
733 .hgsub
729 .hgsub
734 .hgsubstate
730 .hgsubstate
735 large.bin
731 large.bin
736 main
732 main
737 sub1/.hgsub
733 sub1/.hgsub
738 sub1/.hgsubstate
734 sub1/.hgsubstate
739 sub1/sub1
735 sub1/sub1
740 sub1/sub2/large.bin
736 sub1/sub2/large.bin
741 sub1/sub2/sub2
737 sub1/sub2/sub2
742
738
743 Include normal files from within a largefiles subrepo
739 Include normal files from within a largefiles subrepo
744
740
745 $ hg --config extensions.largefiles= archive -S -I '**.txt' ../archive_lf
741 $ hg --config extensions.largefiles= archive -S -I '**.txt' ../archive_lf
746 $ find ../archive_lf | sort
742 $ find ../archive_lf | sort
747 ../archive_lf
743 ../archive_lf
748 ../archive_lf/.hg_archival.txt
744 ../archive_lf/.hg_archival.txt
749 ../archive_lf/sub1
745 ../archive_lf/sub1
750 ../archive_lf/sub1/sub2
746 ../archive_lf/sub1/sub2
751 ../archive_lf/sub1/sub2/folder
747 ../archive_lf/sub1/sub2/folder
752 ../archive_lf/sub1/sub2/folder/test.txt
748 ../archive_lf/sub1/sub2/folder/test.txt
753 ../archive_lf/sub1/sub2/test.txt
749 ../archive_lf/sub1/sub2/test.txt
754 $ rm -rf ../archive_lf
750 $ rm -rf ../archive_lf
755
751
756 Include large files from within a largefiles subrepo
752 Include large files from within a largefiles subrepo
757
753
758 $ hg --config extensions.largefiles= archive -S -I '**.bin' ../archive_lf
754 $ hg --config extensions.largefiles= archive -S -I '**.bin' ../archive_lf
759 $ find ../archive_lf | sort
755 $ find ../archive_lf | sort
760 ../archive_lf
756 ../archive_lf
761 ../archive_lf/large.bin
757 ../archive_lf/large.bin
762 ../archive_lf/sub1
758 ../archive_lf/sub1
763 ../archive_lf/sub1/sub2
759 ../archive_lf/sub1/sub2
764 ../archive_lf/sub1/sub2/large.bin
760 ../archive_lf/sub1/sub2/large.bin
765 $ rm -rf ../archive_lf
761 $ rm -rf ../archive_lf
766
762
767 Find an exact largefile match in a largefiles subrepo
763 Find an exact largefile match in a largefiles subrepo
768
764
769 $ hg --config extensions.largefiles= archive -S -I 'sub1/sub2/large.bin' ../archive_lf
765 $ hg --config extensions.largefiles= archive -S -I 'sub1/sub2/large.bin' ../archive_lf
770 $ find ../archive_lf | sort
766 $ find ../archive_lf | sort
771 ../archive_lf
767 ../archive_lf
772 ../archive_lf/sub1
768 ../archive_lf/sub1
773 ../archive_lf/sub1/sub2
769 ../archive_lf/sub1/sub2
774 ../archive_lf/sub1/sub2/large.bin
770 ../archive_lf/sub1/sub2/large.bin
775 $ rm -rf ../archive_lf
771 $ rm -rf ../archive_lf
776
772
777 The local repo enables largefiles if a largefiles repo is cloned
773 The local repo enables largefiles if a largefiles repo is cloned
778
774
779 $ hg showconfig extensions
775 $ hg showconfig extensions
780 extensions.largefiles=
776 extensions.largefiles=
781
777
782 $ hg --config extensions.largefiles= clone -qU . ../lfclone
778 $ hg --config extensions.largefiles= clone -qU . ../lfclone
783 $ grep largefiles ../lfclone/.hg/requires
779 $ grep largefiles ../lfclone/.hg/requires
784 largefiles
780 largefiles
785
781
786 Find an exact match to a standin (should archive nothing)
782 Find an exact match to a standin (should archive nothing)
787 $ hg --config extensions.largefiles= archive -S -I 'sub/sub2/.hglf/large.bin' ../archive_lf
783 $ hg --config extensions.largefiles= archive -S -I 'sub/sub2/.hglf/large.bin' ../archive_lf
788 $ find ../archive_lf 2> /dev/null | sort
784 $ find ../archive_lf 2> /dev/null | sort
789
785
790 $ cat >> $HGRCPATH <<EOF
786 $ cat >> $HGRCPATH <<EOF
791 > [extensions]
787 > [extensions]
792 > largefiles=
788 > largefiles=
793 > [largefiles]
789 > [largefiles]
794 > patterns=glob:**.dat
790 > patterns=glob:**.dat
795 > EOF
791 > EOF
796
792
797 Test forget through a deep subrepo with the largefiles extension, both a
793 Test forget through a deep subrepo with the largefiles extension, both a
798 largefile and a normal file. Then a largefile that hasn't been committed yet.
794 largefile and a normal file. Then a largefile that hasn't been committed yet.
799 $ touch sub1/sub2/untracked.txt
795 $ touch sub1/sub2/untracked.txt
800 $ touch sub1/sub2/large.dat
796 $ touch sub1/sub2/large.dat
801 $ hg forget sub1/sub2/large.bin sub1/sub2/test.txt sub1/sub2/untracked.txt
797 $ hg forget sub1/sub2/large.bin sub1/sub2/test.txt sub1/sub2/untracked.txt
802 not removing sub1/sub2/untracked.txt: file is already untracked
798 not removing sub1/sub2/untracked.txt: file is already untracked
803 [1]
799 [1]
804 $ hg add --large --dry-run -v sub1/sub2/untracked.txt
800 $ hg add --large --dry-run -v sub1/sub2/untracked.txt
805 adding sub1/sub2/untracked.txt as a largefile
801 adding sub1/sub2/untracked.txt as a largefile
806 $ hg add --large -v sub1/sub2/untracked.txt
802 $ hg add --large -v sub1/sub2/untracked.txt
807 adding sub1/sub2/untracked.txt as a largefile
803 adding sub1/sub2/untracked.txt as a largefile
808 $ hg add --normal -v sub1/sub2/large.dat
804 $ hg add --normal -v sub1/sub2/large.dat
809 adding sub1/sub2/large.dat
805 adding sub1/sub2/large.dat
810 $ hg forget -v sub1/sub2/untracked.txt
806 $ hg forget -v sub1/sub2/untracked.txt
811 removing sub1/sub2/untracked.txt
807 removing sub1/sub2/untracked.txt
812 $ hg status -S
808 $ hg status -S
813 A sub1/sub2/large.dat
809 A sub1/sub2/large.dat
814 R sub1/sub2/large.bin
810 R sub1/sub2/large.bin
815 R sub1/sub2/test.txt
811 R sub1/sub2/test.txt
816 ? foo/bar/abc
812 ? foo/bar/abc
817 ? sub1/sub2/untracked.txt
813 ? sub1/sub2/untracked.txt
818 ? sub1/sub2/x.txt
814 ? sub1/sub2/x.txt
819 $ hg add sub1/sub2
815 $ hg add sub1/sub2
820
816
821 $ hg archive -S -r 'wdir()' ../wdir2
817 $ hg archive -S -r 'wdir()' ../wdir2
822 $ diff -r . ../wdir2 | egrep -v '\.hg$|^Common subdirectories:'
818 $ diff -r . ../wdir2 | egrep -v '\.hg$|^Common subdirectories:'
823 Only in ../wdir2: .hg_archival.txt
819 Only in ../wdir2: .hg_archival.txt
824 Only in .: .hglf
820 Only in .: .hglf
825 Only in .: foo
821 Only in .: foo
826 Only in ./sub1/sub2: large.bin
822 Only in ./sub1/sub2: large.bin
827 Only in ./sub1/sub2: test.txt
823 Only in ./sub1/sub2: test.txt
828 Only in ./sub1/sub2: untracked.txt
824 Only in ./sub1/sub2: untracked.txt
829 Only in ./sub1/sub2: x.txt
825 Only in ./sub1/sub2: x.txt
830 $ find ../wdir2 -type f | sort
826 $ find ../wdir2 -type f | sort
831 ../wdir2/.hg_archival.txt
827 ../wdir2/.hg_archival.txt
832 ../wdir2/.hgsub
828 ../wdir2/.hgsub
833 ../wdir2/.hgsubstate
829 ../wdir2/.hgsubstate
834 ../wdir2/large.bin
830 ../wdir2/large.bin
835 ../wdir2/main
831 ../wdir2/main
836 ../wdir2/sub1/.hgsub
832 ../wdir2/sub1/.hgsub
837 ../wdir2/sub1/.hgsubstate
833 ../wdir2/sub1/.hgsubstate
838 ../wdir2/sub1/sub1
834 ../wdir2/sub1/sub1
839 ../wdir2/sub1/sub2/folder/test.txt
835 ../wdir2/sub1/sub2/folder/test.txt
840 ../wdir2/sub1/sub2/large.dat
836 ../wdir2/sub1/sub2/large.dat
841 ../wdir2/sub1/sub2/sub2
837 ../wdir2/sub1/sub2/sub2
842 $ hg status -S -mac -n | sort
838 $ hg status -S -mac -n | sort
843 .hgsub
839 .hgsub
844 .hgsubstate
840 .hgsubstate
845 large.bin
841 large.bin
846 main
842 main
847 sub1/.hgsub
843 sub1/.hgsub
848 sub1/.hgsubstate
844 sub1/.hgsubstate
849 sub1/sub1
845 sub1/sub1
850 sub1/sub2/folder/test.txt
846 sub1/sub2/folder/test.txt
851 sub1/sub2/large.dat
847 sub1/sub2/large.dat
852 sub1/sub2/sub2
848 sub1/sub2/sub2
853
849
854 $ hg ci -Sqm 'forget testing'
850 $ hg ci -Sqm 'forget testing'
855
851
856 Test 'wdir()' modified file archiving with largefiles
852 Test 'wdir()' modified file archiving with largefiles
857 $ echo 'mod' > main
853 $ echo 'mod' > main
858 $ echo 'mod' > large.bin
854 $ echo 'mod' > large.bin
859 $ echo 'mod' > sub1/sub2/large.dat
855 $ echo 'mod' > sub1/sub2/large.dat
860 $ hg archive -S -r 'wdir()' ../wdir3
856 $ hg archive -S -r 'wdir()' ../wdir3
861 $ diff -r . ../wdir3 | egrep -v '\.hg$|^Common subdirectories'
857 $ diff -r . ../wdir3 | egrep -v '\.hg$|^Common subdirectories'
862 Only in ../wdir3: .hg_archival.txt
858 Only in ../wdir3: .hg_archival.txt
863 Only in .: .hglf
859 Only in .: .hglf
864 Only in .: foo
860 Only in .: foo
865 Only in ./sub1/sub2: large.bin
861 Only in ./sub1/sub2: large.bin
866 Only in ./sub1/sub2: test.txt
862 Only in ./sub1/sub2: test.txt
867 Only in ./sub1/sub2: untracked.txt
863 Only in ./sub1/sub2: untracked.txt
868 Only in ./sub1/sub2: x.txt
864 Only in ./sub1/sub2: x.txt
869 $ find ../wdir3 -type f | sort
865 $ find ../wdir3 -type f | sort
870 ../wdir3/.hg_archival.txt
866 ../wdir3/.hg_archival.txt
871 ../wdir3/.hgsub
867 ../wdir3/.hgsub
872 ../wdir3/.hgsubstate
868 ../wdir3/.hgsubstate
873 ../wdir3/large.bin
869 ../wdir3/large.bin
874 ../wdir3/main
870 ../wdir3/main
875 ../wdir3/sub1/.hgsub
871 ../wdir3/sub1/.hgsub
876 ../wdir3/sub1/.hgsubstate
872 ../wdir3/sub1/.hgsubstate
877 ../wdir3/sub1/sub1
873 ../wdir3/sub1/sub1
878 ../wdir3/sub1/sub2/folder/test.txt
874 ../wdir3/sub1/sub2/folder/test.txt
879 ../wdir3/sub1/sub2/large.dat
875 ../wdir3/sub1/sub2/large.dat
880 ../wdir3/sub1/sub2/sub2
876 ../wdir3/sub1/sub2/sub2
881 $ hg up -Cq
877 $ hg up -Cq
882
878
883 Test issue4330: commit a directory where only normal files have changed
879 Test issue4330: commit a directory where only normal files have changed
884 $ touch foo/bar/large.dat
880 $ touch foo/bar/large.dat
885 $ hg add --large foo/bar/large.dat
881 $ hg add --large foo/bar/large.dat
886 $ hg ci -m 'add foo/bar/large.dat'
882 $ hg ci -m 'add foo/bar/large.dat'
887 $ touch a.txt
883 $ touch a.txt
888 $ touch a.dat
884 $ touch a.dat
889 $ hg add -v foo/bar/abc a.txt a.dat
885 $ hg add -v foo/bar/abc a.txt a.dat
890 adding a.dat as a largefile
886 adding a.dat as a largefile
891 adding a.txt
887 adding a.txt
892 adding foo/bar/abc
888 adding foo/bar/abc
893 $ hg ci -m 'dir commit with only normal file deltas' foo/bar
889 $ hg ci -m 'dir commit with only normal file deltas' foo/bar
894 $ hg status
890 $ hg status
895 A a.dat
891 A a.dat
896 A a.txt
892 A a.txt
897
893
898 Test a directory commit with a changed largefile and a changed normal file
894 Test a directory commit with a changed largefile and a changed normal file
899 $ echo changed > foo/bar/large.dat
895 $ echo changed > foo/bar/large.dat
900 $ echo changed > foo/bar/abc
896 $ echo changed > foo/bar/abc
901 $ hg ci -m 'dir commit with normal and lf file deltas' foo
897 $ hg ci -m 'dir commit with normal and lf file deltas' foo
902 $ hg status
898 $ hg status
903 A a.dat
899 A a.dat
904 A a.txt
900 A a.txt
905
901
906 $ hg ci -m "add a.*"
902 $ hg ci -m "add a.*"
907 $ hg mv a.dat b.dat
903 $ hg mv a.dat b.dat
908 $ hg mv foo/bar/abc foo/bar/def
904 $ hg mv foo/bar/abc foo/bar/def
909 $ hg status -C
905 $ hg status -C
910 A b.dat
906 A b.dat
911 a.dat
907 a.dat
912 A foo/bar/def
908 A foo/bar/def
913 foo/bar/abc
909 foo/bar/abc
914 R a.dat
910 R a.dat
915 R foo/bar/abc
911 R foo/bar/abc
916
912
917 $ hg ci -m "move large and normal"
913 $ hg ci -m "move large and normal"
918 $ hg status -C --rev '.^' --rev .
914 $ hg status -C --rev '.^' --rev .
919 A b.dat
915 A b.dat
920 a.dat
916 a.dat
921 A foo/bar/def
917 A foo/bar/def
922 foo/bar/abc
918 foo/bar/abc
923 R a.dat
919 R a.dat
924 R foo/bar/abc
920 R foo/bar/abc
925
921
926
922
927 $ echo foo > main
923 $ echo foo > main
928 $ hg ci -m "mod parent only"
924 $ hg ci -m "mod parent only"
929 $ hg init sub3
925 $ hg init sub3
930 $ echo "sub3 = sub3" >> .hgsub
926 $ echo "sub3 = sub3" >> .hgsub
931 $ echo xyz > sub3/a.txt
927 $ echo xyz > sub3/a.txt
932 $ hg add sub3/a.txt
928 $ hg add sub3/a.txt
933 $ hg ci -Sm "add sub3"
929 $ hg ci -Sm "add sub3"
934 committing subrepository sub3
930 committing subrepository sub3
935 $ cat .hgsub | grep -v sub3 > .hgsub1
931 $ cat .hgsub | grep -v sub3 > .hgsub1
936 $ mv .hgsub1 .hgsub
932 $ mv .hgsub1 .hgsub
937 $ hg ci -m "remove sub3"
933 $ hg ci -m "remove sub3"
938
934
939 $ hg log -r "subrepo()" --style compact
935 $ hg log -r "subrepo()" --style compact
940 0 7f491f53a367 1970-01-01 00:00 +0000 test
936 0 7f491f53a367 1970-01-01 00:00 +0000 test
941 main import
937 main import
942
938
943 1 ffe6649062fe 1970-01-01 00:00 +0000 test
939 1 ffe6649062fe 1970-01-01 00:00 +0000 test
944 deep nested modif should trigger a commit
940 deep nested modif should trigger a commit
945
941
946 2 9bb10eebee29 1970-01-01 00:00 +0000 test
942 2 9bb10eebee29 1970-01-01 00:00 +0000 test
947 add test.txt
943 add test.txt
948
944
949 3 7c64f035294f 1970-01-01 00:00 +0000 test
945 3 7c64f035294f 1970-01-01 00:00 +0000 test
950 add large files
946 add large files
951
947
952 4 f734a59e2e35 1970-01-01 00:00 +0000 test
948 4 f734a59e2e35 1970-01-01 00:00 +0000 test
953 forget testing
949 forget testing
954
950
955 11 9685a22af5db 1970-01-01 00:00 +0000 test
951 11 9685a22af5db 1970-01-01 00:00 +0000 test
956 add sub3
952 add sub3
957
953
958 12[tip] 2e0485b475b9 1970-01-01 00:00 +0000 test
954 12[tip] 2e0485b475b9 1970-01-01 00:00 +0000 test
959 remove sub3
955 remove sub3
960
956
961 $ hg log -r "subrepo('sub3')" --style compact
957 $ hg log -r "subrepo('sub3')" --style compact
962 11 9685a22af5db 1970-01-01 00:00 +0000 test
958 11 9685a22af5db 1970-01-01 00:00 +0000 test
963 add sub3
959 add sub3
964
960
965 12[tip] 2e0485b475b9 1970-01-01 00:00 +0000 test
961 12[tip] 2e0485b475b9 1970-01-01 00:00 +0000 test
966 remove sub3
962 remove sub3
967
963
968 $ hg log -r "subrepo('bogus')" --style compact
964 $ hg log -r "subrepo('bogus')" --style compact
969
965
970
966
971 Test .hgsubstate in the R state
967 Test .hgsubstate in the R state
972
968
973 $ hg rm .hgsub .hgsubstate
969 $ hg rm .hgsub .hgsubstate
974 \r (no-eol) (esc)
970 \r (no-eol) (esc)
975 deleting [=====================> ] 1/2\r (no-eol) (esc)
971 deleting [=====================> ] 1/2\r (no-eol) (esc)
976 deleting [===========================================>] 2/2\r (no-eol) (esc)
972 deleting [===========================================>] 2/2\r (no-eol) (esc)
977 \r (no-eol) (esc)
973 \r (no-eol) (esc)
978 $ hg ci -m 'trash subrepo tracking'
974 $ hg ci -m 'trash subrepo tracking'
979
975
980 $ hg log -r "subrepo('re:sub\d+')" --style compact
976 $ hg log -r "subrepo('re:sub\d+')" --style compact
981 0 7f491f53a367 1970-01-01 00:00 +0000 test
977 0 7f491f53a367 1970-01-01 00:00 +0000 test
982 main import
978 main import
983
979
984 1 ffe6649062fe 1970-01-01 00:00 +0000 test
980 1 ffe6649062fe 1970-01-01 00:00 +0000 test
985 deep nested modif should trigger a commit
981 deep nested modif should trigger a commit
986
982
987 2 9bb10eebee29 1970-01-01 00:00 +0000 test
983 2 9bb10eebee29 1970-01-01 00:00 +0000 test
988 add test.txt
984 add test.txt
989
985
990 3 7c64f035294f 1970-01-01 00:00 +0000 test
986 3 7c64f035294f 1970-01-01 00:00 +0000 test
991 add large files
987 add large files
992
988
993 4 f734a59e2e35 1970-01-01 00:00 +0000 test
989 4 f734a59e2e35 1970-01-01 00:00 +0000 test
994 forget testing
990 forget testing
995
991
996 11 9685a22af5db 1970-01-01 00:00 +0000 test
992 11 9685a22af5db 1970-01-01 00:00 +0000 test
997 add sub3
993 add sub3
998
994
999 12 2e0485b475b9 1970-01-01 00:00 +0000 test
995 12 2e0485b475b9 1970-01-01 00:00 +0000 test
1000 remove sub3
996 remove sub3
1001
997
1002 13[tip] a68b2c361653 1970-01-01 00:00 +0000 test
998 13[tip] a68b2c361653 1970-01-01 00:00 +0000 test
1003 trash subrepo tracking
999 trash subrepo tracking
1004
1000
1005
1001
1006 Restore the trashed subrepo tracking
1002 Restore the trashed subrepo tracking
1007
1003
1008 $ hg rollback -q
1004 $ hg rollback -q
1009 $ hg update -Cq .
1005 $ hg update -Cq .
1010
1006
1011 Interaction with extdiff, largefiles and subrepos
1007 Interaction with extdiff, largefiles and subrepos
1012
1008
1013 $ hg --config extensions.extdiff= pdiff -S
1009 $ hg --config extensions.extdiff= pdiff -S
1014
1010
1015 $ hg --config extensions.extdiff= pdiff -r '.^' -S
1011 $ hg --config extensions.extdiff= pdiff -r '.^' -S
1016 \r (no-eol) (esc)
1012 \r (no-eol) (esc)
1017 archiving [ ] 0/2\r (no-eol) (esc)
1013 archiving [ ] 0/2\r (no-eol) (esc)
1018 archiving [====================> ] 1/2\r (no-eol) (esc)
1014 archiving [====================> ] 1/2\r (no-eol) (esc)
1019 archiving [==========================================>] 2/2\r (no-eol) (esc)
1015 archiving [==========================================>] 2/2\r (no-eol) (esc)
1020 \r (no-eol) (esc)
1016 \r (no-eol) (esc)
1021 \r (no-eol) (esc)
1017 \r (no-eol) (esc)
1022 archiving (sub1) [ <=> ] 0\r (no-eol) (esc)
1018 archiving (sub1) [ <=> ] 0\r (no-eol) (esc)
1023 \r (no-eol) (esc)
1019 \r (no-eol) (esc)
1024 \r (no-eol) (esc)
1020 \r (no-eol) (esc)
1025 archiving (sub1/sub2) [ <=> ] 0\r (no-eol) (esc)
1021 archiving (sub1/sub2) [ <=> ] 0\r (no-eol) (esc)
1026 \r (no-eol) (esc)
1022 \r (no-eol) (esc)
1027 \r (no-eol) (esc)
1023 \r (no-eol) (esc)
1028 archiving (sub3) [ <=> ] 0\r (no-eol) (esc)
1024 archiving (sub3) [ <=> ] 0\r (no-eol) (esc)
1029 \r (no-eol) (esc)
1025 \r (no-eol) (esc)
1030 \r (no-eol) (esc)
1026 \r (no-eol) (esc)
1031 archiving [ ] 0/2\r (no-eol) (esc)
1027 archiving [ ] 0/2\r (no-eol) (esc)
1032 archiving [====================> ] 1/2\r (no-eol) (esc)
1028 archiving [====================> ] 1/2\r (no-eol) (esc)
1033 archiving [==========================================>] 2/2\r (no-eol) (esc)
1029 archiving [==========================================>] 2/2\r (no-eol) (esc)
1034 \r (no-eol) (esc)
1030 \r (no-eol) (esc)
1035 \r (no-eol) (esc)
1031 \r (no-eol) (esc)
1036 archiving (sub1) [ <=> ] 0\r (no-eol) (esc)
1032 archiving (sub1) [ <=> ] 0\r (no-eol) (esc)
1037 \r (no-eol) (esc)
1033 \r (no-eol) (esc)
1038 \r (no-eol) (esc)
1034 \r (no-eol) (esc)
1039 archiving (sub1/sub2) [ <=> ] 0\r (no-eol) (esc)
1035 archiving (sub1/sub2) [ <=> ] 0\r (no-eol) (esc)
1040 \r (no-eol) (esc)
1036 \r (no-eol) (esc)
1041 diff -Nru cloned.*/.hgsub cloned/.hgsub (glob)
1037 diff -Nru cloned.*/.hgsub cloned/.hgsub (glob)
1042 --- cloned.*/.hgsub * (glob)
1038 --- cloned.*/.hgsub * (glob)
1043 +++ cloned/.hgsub * (glob)
1039 +++ cloned/.hgsub * (glob)
1044 @@ -1,2 +1* @@ (glob)
1040 @@ -1,2 +1* @@ (glob)
1045 sub1 = ../sub1
1041 sub1 = ../sub1
1046 -sub3 = sub3
1042 -sub3 = sub3
1047 diff -Nru cloned.*/.hgsubstate cloned/.hgsubstate (glob)
1043 diff -Nru cloned.*/.hgsubstate cloned/.hgsubstate (glob)
1048 --- cloned.*/.hgsubstate * (glob)
1044 --- cloned.*/.hgsubstate * (glob)
1049 +++ cloned/.hgsubstate * (glob)
1045 +++ cloned/.hgsubstate * (glob)
1050 @@ -1,2 +1* @@ (glob)
1046 @@ -1,2 +1* @@ (glob)
1051 7a36fa02b66e61f27f3d4a822809f159479b8ab2 sub1
1047 7a36fa02b66e61f27f3d4a822809f159479b8ab2 sub1
1052 -b1a26de6f2a045a9f079323693614ee322f1ff7e sub3
1048 -b1a26de6f2a045a9f079323693614ee322f1ff7e sub3
1053 [1]
1049 [1]
1054
1050
1055 $ hg --config extensions.extdiff= pdiff -r 0 -r '.^' -S
1051 $ hg --config extensions.extdiff= pdiff -r 0 -r '.^' -S
1056 \r (no-eol) (esc)
1052 \r (no-eol) (esc)
1057 archiving [ ] 0/3\r (no-eol) (esc)
1053 archiving [ ] 0/3\r (no-eol) (esc)
1058 archiving [=============> ] 1/3\r (no-eol) (esc)
1054 archiving [=============> ] 1/3\r (no-eol) (esc)
1059 archiving [===========================> ] 2/3\r (no-eol) (esc)
1055 archiving [===========================> ] 2/3\r (no-eol) (esc)
1060 archiving [==========================================>] 3/3\r (no-eol) (esc)
1056 archiving [==========================================>] 3/3\r (no-eol) (esc)
1061 \r (no-eol) (esc)
1057 \r (no-eol) (esc)
1062 \r (no-eol) (esc)
1058 \r (no-eol) (esc)
1063 archiving (sub1) [ ] 0/1\r (no-eol) (esc)
1059 archiving (sub1) [ ] 0/1\r (no-eol) (esc)
1064 archiving (sub1) [===================================>] 1/1\r (no-eol) (esc)
1060 archiving (sub1) [===================================>] 1/1\r (no-eol) (esc)
1065 \r (no-eol) (esc)
1061 \r (no-eol) (esc)
1066 \r (no-eol) (esc)
1062 \r (no-eol) (esc)
1067 archiving (sub1/sub2) [ ] 0/1\r (no-eol) (esc)
1063 archiving (sub1/sub2) [ ] 0/1\r (no-eol) (esc)
1068 archiving (sub1/sub2) [==============================>] 1/1\r (no-eol) (esc)
1064 archiving (sub1/sub2) [==============================>] 1/1\r (no-eol) (esc)
1069 \r (no-eol) (esc)
1065 \r (no-eol) (esc)
1070 \r (no-eol) (esc)
1066 \r (no-eol) (esc)
1071 archiving [ ] 0/8\r (no-eol) (esc)
1067 archiving [ ] 0/8\r (no-eol) (esc)
1072 archiving [====> ] 1/8\r (no-eol) (esc)
1068 archiving [====> ] 1/8\r (no-eol) (esc)
1073 archiving [=========> ] 2/8\r (no-eol) (esc)
1069 archiving [=========> ] 2/8\r (no-eol) (esc)
1074 archiving [===============> ] 3/8\r (no-eol) (esc)
1070 archiving [===============> ] 3/8\r (no-eol) (esc)
1075 archiving [====================> ] 4/8\r (no-eol) (esc)
1071 archiving [====================> ] 4/8\r (no-eol) (esc)
1076 archiving [=========================> ] 5/8\r (no-eol) (esc)
1072 archiving [=========================> ] 5/8\r (no-eol) (esc)
1077 archiving [===============================> ] 6/8\r (no-eol) (esc)
1073 archiving [===============================> ] 6/8\r (no-eol) (esc)
1078 archiving [====================================> ] 7/8\r (no-eol) (esc)
1074 archiving [====================================> ] 7/8\r (no-eol) (esc)
1079 archiving [==========================================>] 8/8\r (no-eol) (esc)
1075 archiving [==========================================>] 8/8\r (no-eol) (esc)
1080 \r (no-eol) (esc)
1076 \r (no-eol) (esc)
1081 \r (no-eol) (esc)
1077 \r (no-eol) (esc)
1082 archiving (sub1) [ ] 0/1\r (no-eol) (esc)
1078 archiving (sub1) [ ] 0/1\r (no-eol) (esc)
1083 archiving (sub1) [===================================>] 1/1\r (no-eol) (esc)
1079 archiving (sub1) [===================================>] 1/1\r (no-eol) (esc)
1084 \r (no-eol) (esc)
1080 \r (no-eol) (esc)
1085 \r (no-eol) (esc)
1081 \r (no-eol) (esc)
1086 archiving (sub1/sub2) [ ] 0/3\r (no-eol) (esc)
1082 archiving (sub1/sub2) [ ] 0/3\r (no-eol) (esc)
1087 archiving (sub1/sub2) [=========> ] 1/3\r (no-eol) (esc)
1083 archiving (sub1/sub2) [=========> ] 1/3\r (no-eol) (esc)
1088 archiving (sub1/sub2) [===================> ] 2/3\r (no-eol) (esc)
1084 archiving (sub1/sub2) [===================> ] 2/3\r (no-eol) (esc)
1089 archiving (sub1/sub2) [==============================>] 3/3\r (no-eol) (esc)
1085 archiving (sub1/sub2) [==============================>] 3/3\r (no-eol) (esc)
1090 \r (no-eol) (esc)
1086 \r (no-eol) (esc)
1091 \r (no-eol) (esc)
1087 \r (no-eol) (esc)
1092 archiving (sub3) [ ] 0/1\r (no-eol) (esc)
1088 archiving (sub3) [ ] 0/1\r (no-eol) (esc)
1093 archiving (sub3) [===================================>] 1/1\r (no-eol) (esc)
1089 archiving (sub3) [===================================>] 1/1\r (no-eol) (esc)
1094 \r (no-eol) (esc)
1090 \r (no-eol) (esc)
1095 diff -Nru cloned.*/.hglf/b.dat cloned.*/.hglf/b.dat (glob)
1091 diff -Nru cloned.*/.hglf/b.dat cloned.*/.hglf/b.dat (glob)
1096 --- cloned.*/.hglf/b.dat * (glob)
1092 --- cloned.*/.hglf/b.dat * (glob)
1097 +++ cloned.*/.hglf/b.dat * (glob)
1093 +++ cloned.*/.hglf/b.dat * (glob)
1098 @@ -*,0 +1* @@ (glob)
1094 @@ -*,0 +1* @@ (glob)
1099 +da39a3ee5e6b4b0d3255bfef95601890afd80709
1095 +da39a3ee5e6b4b0d3255bfef95601890afd80709
1100 diff -Nru cloned.*/.hglf/foo/bar/large.dat cloned.*/.hglf/foo/bar/large.dat (glob)
1096 diff -Nru cloned.*/.hglf/foo/bar/large.dat cloned.*/.hglf/foo/bar/large.dat (glob)
1101 --- cloned.*/.hglf/foo/bar/large.dat * (glob)
1097 --- cloned.*/.hglf/foo/bar/large.dat * (glob)
1102 +++ cloned.*/.hglf/foo/bar/large.dat * (glob)
1098 +++ cloned.*/.hglf/foo/bar/large.dat * (glob)
1103 @@ -*,0 +1* @@ (glob)
1099 @@ -*,0 +1* @@ (glob)
1104 +2f6933b5ee0f5fdd823d9717d8729f3c2523811b
1100 +2f6933b5ee0f5fdd823d9717d8729f3c2523811b
1105 diff -Nru cloned.*/.hglf/large.bin cloned.*/.hglf/large.bin (glob)
1101 diff -Nru cloned.*/.hglf/large.bin cloned.*/.hglf/large.bin (glob)
1106 --- cloned.*/.hglf/large.bin * (glob)
1102 --- cloned.*/.hglf/large.bin * (glob)
1107 +++ cloned.*/.hglf/large.bin * (glob)
1103 +++ cloned.*/.hglf/large.bin * (glob)
1108 @@ -*,0 +1* @@ (glob)
1104 @@ -*,0 +1* @@ (glob)
1109 +7f7097b041ccf68cc5561e9600da4655d21c6d18
1105 +7f7097b041ccf68cc5561e9600da4655d21c6d18
1110 diff -Nru cloned.*/.hgsub cloned.*/.hgsub (glob)
1106 diff -Nru cloned.*/.hgsub cloned.*/.hgsub (glob)
1111 --- cloned.*/.hgsub * (glob)
1107 --- cloned.*/.hgsub * (glob)
1112 +++ cloned.*/.hgsub * (glob)
1108 +++ cloned.*/.hgsub * (glob)
1113 @@ -1* +1,2 @@ (glob)
1109 @@ -1* +1,2 @@ (glob)
1114 sub1 = ../sub1
1110 sub1 = ../sub1
1115 +sub3 = sub3
1111 +sub3 = sub3
1116 diff -Nru cloned.*/.hgsubstate cloned.*/.hgsubstate (glob)
1112 diff -Nru cloned.*/.hgsubstate cloned.*/.hgsubstate (glob)
1117 --- cloned.*/.hgsubstate * (glob)
1113 --- cloned.*/.hgsubstate * (glob)
1118 +++ cloned.*/.hgsubstate * (glob)
1114 +++ cloned.*/.hgsubstate * (glob)
1119 @@ -1* +1,2 @@ (glob)
1115 @@ -1* +1,2 @@ (glob)
1120 -fc3b4ce2696f7741438c79207583768f2ce6b0dd sub1
1116 -fc3b4ce2696f7741438c79207583768f2ce6b0dd sub1
1121 +7a36fa02b66e61f27f3d4a822809f159479b8ab2 sub1
1117 +7a36fa02b66e61f27f3d4a822809f159479b8ab2 sub1
1122 +b1a26de6f2a045a9f079323693614ee322f1ff7e sub3
1118 +b1a26de6f2a045a9f079323693614ee322f1ff7e sub3
1123 diff -Nru cloned.*/foo/bar/def cloned.*/foo/bar/def (glob)
1119 diff -Nru cloned.*/foo/bar/def cloned.*/foo/bar/def (glob)
1124 --- cloned.*/foo/bar/def * (glob)
1120 --- cloned.*/foo/bar/def * (glob)
1125 +++ cloned.*/foo/bar/def * (glob)
1121 +++ cloned.*/foo/bar/def * (glob)
1126 @@ -*,0 +1* @@ (glob)
1122 @@ -*,0 +1* @@ (glob)
1127 +changed
1123 +changed
1128 diff -Nru cloned.*/main cloned.*/main (glob)
1124 diff -Nru cloned.*/main cloned.*/main (glob)
1129 --- cloned.*/main * (glob)
1125 --- cloned.*/main * (glob)
1130 +++ cloned.*/main * (glob)
1126 +++ cloned.*/main * (glob)
1131 @@ -1* +1* @@ (glob)
1127 @@ -1* +1* @@ (glob)
1132 -main
1128 -main
1133 +foo
1129 +foo
1134 diff -Nru cloned.*/sub1/.hgsubstate cloned.*/sub1/.hgsubstate (glob)
1130 diff -Nru cloned.*/sub1/.hgsubstate cloned.*/sub1/.hgsubstate (glob)
1135 --- cloned.*/sub1/.hgsubstate * (glob)
1131 --- cloned.*/sub1/.hgsubstate * (glob)
1136 +++ cloned.*/sub1/.hgsubstate * (glob)
1132 +++ cloned.*/sub1/.hgsubstate * (glob)
1137 @@ -1* +1* @@ (glob)
1133 @@ -1* +1* @@ (glob)
1138 -c57a0840e3badd667ef3c3ef65471609acb2ba3c sub2
1134 -c57a0840e3badd667ef3c3ef65471609acb2ba3c sub2
1139 +c77908c81ccea3794a896c79e98b0e004aee2e9e sub2
1135 +c77908c81ccea3794a896c79e98b0e004aee2e9e sub2
1140 diff -Nru cloned.*/sub1/sub2/folder/test.txt cloned.*/sub1/sub2/folder/test.txt (glob)
1136 diff -Nru cloned.*/sub1/sub2/folder/test.txt cloned.*/sub1/sub2/folder/test.txt (glob)
1141 --- cloned.*/sub1/sub2/folder/test.txt * (glob)
1137 --- cloned.*/sub1/sub2/folder/test.txt * (glob)
1142 +++ cloned.*/sub1/sub2/folder/test.txt * (glob)
1138 +++ cloned.*/sub1/sub2/folder/test.txt * (glob)
1143 @@ -*,0 +1* @@ (glob)
1139 @@ -*,0 +1* @@ (glob)
1144 +subfolder
1140 +subfolder
1145 diff -Nru cloned.*/sub1/sub2/sub2 cloned.*/sub1/sub2/sub2 (glob)
1141 diff -Nru cloned.*/sub1/sub2/sub2 cloned.*/sub1/sub2/sub2 (glob)
1146 --- cloned.*/sub1/sub2/sub2 * (glob)
1142 --- cloned.*/sub1/sub2/sub2 * (glob)
1147 +++ cloned.*/sub1/sub2/sub2 * (glob)
1143 +++ cloned.*/sub1/sub2/sub2 * (glob)
1148 @@ -1* +1* @@ (glob)
1144 @@ -1* +1* @@ (glob)
1149 -sub2
1145 -sub2
1150 +modified
1146 +modified
1151 diff -Nru cloned.*/sub3/a.txt cloned.*/sub3/a.txt (glob)
1147 diff -Nru cloned.*/sub3/a.txt cloned.*/sub3/a.txt (glob)
1152 --- cloned.*/sub3/a.txt * (glob)
1148 --- cloned.*/sub3/a.txt * (glob)
1153 +++ cloned.*/sub3/a.txt * (glob)
1149 +++ cloned.*/sub3/a.txt * (glob)
1154 @@ -*,0 +1* @@ (glob)
1150 @@ -*,0 +1* @@ (glob)
1155 +xyz
1151 +xyz
1156 [1]
1152 [1]
1157
1153
1158 $ echo mod > sub1/sub2/sub2
1154 $ echo mod > sub1/sub2/sub2
1159 $ hg --config extensions.extdiff= pdiff -S
1155 $ hg --config extensions.extdiff= pdiff -S
1160 \r (no-eol) (esc)
1156 \r (no-eol) (esc)
1161 archiving (sub1) [ <=> ] 0\r (no-eol) (esc)
1157 archiving (sub1) [ <=> ] 0\r (no-eol) (esc)
1162 \r (no-eol) (esc)
1158 \r (no-eol) (esc)
1163 \r (no-eol) (esc)
1159 \r (no-eol) (esc)
1164 archiving (sub1/sub2) [ ] 0/1\r (no-eol) (esc)
1160 archiving (sub1/sub2) [ ] 0/1\r (no-eol) (esc)
1165 archiving (sub1/sub2) [==============================>] 1/1\r (no-eol) (esc)
1161 archiving (sub1/sub2) [==============================>] 1/1\r (no-eol) (esc)
1166 \r (no-eol) (esc)
1162 \r (no-eol) (esc)
1167 --- */cloned.*/sub1/sub2/sub2 * (glob)
1163 --- */cloned.*/sub1/sub2/sub2 * (glob)
1168 +++ */cloned/sub1/sub2/sub2 * (glob)
1164 +++ */cloned/sub1/sub2/sub2 * (glob)
1169 @@ -1* +1* @@ (glob)
1165 @@ -1* +1* @@ (glob)
1170 -modified
1166 -modified
1171 +mod
1167 +mod
1172 [1]
1168 [1]
1173
1169
1174 $ cd ..
1170 $ cd ..
@@ -1,150 +1,146 b''
1 TODO: fix rhg bugs that make this test fail when status is enabled
2 $ unset RHG_STATUS
3
4
5 $ hg init repo
1 $ hg init repo
6 $ cd repo
2 $ cd repo
7 $ hg init subrepo
3 $ hg init subrepo
8 $ echo a > subrepo/a
4 $ echo a > subrepo/a
9 $ hg -R subrepo ci -Am adda
5 $ hg -R subrepo ci -Am adda
10 adding a
6 adding a
11 $ echo 'subrepo = subrepo' > .hgsub
7 $ echo 'subrepo = subrepo' > .hgsub
12 $ hg ci -Am addsubrepo
8 $ hg ci -Am addsubrepo
13 adding .hgsub
9 adding .hgsub
14 $ echo b > subrepo/b
10 $ echo b > subrepo/b
15 $ hg -R subrepo ci -Am addb
11 $ hg -R subrepo ci -Am addb
16 adding b
12 adding b
17 $ hg ci -m updatedsub
13 $ hg ci -m updatedsub
18
14
19 ignore blanklines in .hgsubstate
15 ignore blanklines in .hgsubstate
20
16
21 >>> open('.hgsubstate', 'wb').write(b'\n\n \t \n \n') and None
17 >>> open('.hgsubstate', 'wb').write(b'\n\n \t \n \n') and None
22 $ hg st --subrepos
18 $ hg st --subrepos
23 M .hgsubstate
19 M .hgsubstate
24 $ hg revert -qC .hgsubstate
20 $ hg revert -qC .hgsubstate
25
21
26 abort more gracefully on .hgsubstate parsing error
22 abort more gracefully on .hgsubstate parsing error
27
23
28 $ cp .hgsubstate .hgsubstate.old
24 $ cp .hgsubstate .hgsubstate.old
29 >>> open('.hgsubstate', 'wb').write(b'\ninvalid') and None
25 >>> open('.hgsubstate', 'wb').write(b'\ninvalid') and None
30 $ hg st --subrepos --cwd $TESTTMP -R $TESTTMP/repo
26 $ hg st --subrepos --cwd $TESTTMP -R $TESTTMP/repo
31 abort: invalid subrepository revision specifier in 'repo/.hgsubstate' line 2
27 abort: invalid subrepository revision specifier in 'repo/.hgsubstate' line 2
32 [255]
28 [255]
33 $ mv .hgsubstate.old .hgsubstate
29 $ mv .hgsubstate.old .hgsubstate
34
30
35 delete .hgsub and revert it
31 delete .hgsub and revert it
36
32
37 $ rm .hgsub
33 $ rm .hgsub
38 $ hg revert .hgsub
34 $ hg revert .hgsub
39 warning: subrepo spec file '.hgsub' not found
35 warning: subrepo spec file '.hgsub' not found
40 warning: subrepo spec file '.hgsub' not found
36 warning: subrepo spec file '.hgsub' not found
41
37
42 delete .hgsubstate and revert it
38 delete .hgsubstate and revert it
43
39
44 $ rm .hgsubstate
40 $ rm .hgsubstate
45 $ hg revert .hgsubstate
41 $ hg revert .hgsubstate
46
42
47 delete .hgsub and update
43 delete .hgsub and update
48
44
49 $ rm .hgsub
45 $ rm .hgsub
50 $ hg up 0 --cwd $TESTTMP -R $TESTTMP/repo
46 $ hg up 0 --cwd $TESTTMP -R $TESTTMP/repo
51 warning: subrepo spec file 'repo/.hgsub' not found
47 warning: subrepo spec file 'repo/.hgsub' not found
52 warning: subrepo spec file 'repo/.hgsub' not found
48 warning: subrepo spec file 'repo/.hgsub' not found
53 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
49 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
54 $ hg st
50 $ hg st
55 warning: subrepo spec file '.hgsub' not found
51 warning: subrepo spec file '.hgsub' not found
56 ! .hgsub
52 ! .hgsub
57 $ ls -A subrepo
53 $ ls -A subrepo
58 .hg
54 .hg
59 a
55 a
60
56
61 delete .hgsubstate and update
57 delete .hgsubstate and update
62
58
63 $ hg up -C
59 $ hg up -C
64 warning: subrepo spec file '.hgsub' not found
60 warning: subrepo spec file '.hgsub' not found
65 warning: subrepo spec file '.hgsub' not found
61 warning: subrepo spec file '.hgsub' not found
66 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
62 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
67 $ rm .hgsubstate
63 $ rm .hgsubstate
68 $ hg up 0
64 $ hg up 0
69 other [destination] changed .hgsubstate which local [working copy] deleted
65 other [destination] changed .hgsubstate which local [working copy] deleted
70 use (c)hanged version or leave (d)eleted? c
66 use (c)hanged version or leave (d)eleted? c
71 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
67 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
72 $ hg st
68 $ hg st
73 $ ls -A subrepo
69 $ ls -A subrepo
74 .hg
70 .hg
75 a
71 a
76
72
77 Enable obsolete
73 Enable obsolete
78
74
79 $ cat >> $HGRCPATH << EOF
75 $ cat >> $HGRCPATH << EOF
80 > [ui]
76 > [ui]
81 > logtemplate= {rev}:{node|short} {desc|firstline}
77 > logtemplate= {rev}:{node|short} {desc|firstline}
82 > [phases]
78 > [phases]
83 > publish=False
79 > publish=False
84 > [experimental]
80 > [experimental]
85 > evolution.createmarkers=True
81 > evolution.createmarkers=True
86 > EOF
82 > EOF
87
83
88 check that we can update parent repo with missing (amended) subrepo revision
84 check that we can update parent repo with missing (amended) subrepo revision
89
85
90 $ hg up --repository subrepo -r tip
86 $ hg up --repository subrepo -r tip
91 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
87 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
92 $ hg ci -m "updated subrepo to tip"
88 $ hg ci -m "updated subrepo to tip"
93 created new head
89 created new head
94 $ cd subrepo
90 $ cd subrepo
95 $ hg update -r tip
91 $ hg update -r tip
96 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
92 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
97 $ echo foo > a
93 $ echo foo > a
98 $ hg commit --amend -m "addb (amended)"
94 $ hg commit --amend -m "addb (amended)"
99 $ cd ..
95 $ cd ..
100 $ hg update --clean .
96 $ hg update --clean .
101 revision 102a90ea7b4a in subrepository "subrepo" is hidden
97 revision 102a90ea7b4a in subrepository "subrepo" is hidden
102 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
98 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
103
99
104 check that --hidden is propagated to the subrepo
100 check that --hidden is propagated to the subrepo
105
101
106 $ hg -R subrepo up tip
102 $ hg -R subrepo up tip
107 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
103 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
108 $ hg ci -m 'commit with amended subrepo'
104 $ hg ci -m 'commit with amended subrepo'
109 $ echo bar > subrepo/a
105 $ echo bar > subrepo/a
110 $ hg -R subrepo ci --amend -m "amend a (again)"
106 $ hg -R subrepo ci --amend -m "amend a (again)"
111 $ hg --hidden cat subrepo/a
107 $ hg --hidden cat subrepo/a
112 foo
108 foo
113
109
114 verify will warn if locked-in subrepo revisions are hidden or missing
110 verify will warn if locked-in subrepo revisions are hidden or missing
115
111
116 $ hg ci -m "amended subrepo (again)"
112 $ hg ci -m "amended subrepo (again)"
117 $ hg --config extensions.strip= --hidden strip -R subrepo -qr 'tip' --config devel.strip-obsmarkers=no
113 $ hg --config extensions.strip= --hidden strip -R subrepo -qr 'tip' --config devel.strip-obsmarkers=no
118 $ hg verify
114 $ hg verify
119 checking changesets
115 checking changesets
120 checking manifests
116 checking manifests
121 crosschecking files in changesets and manifests
117 crosschecking files in changesets and manifests
122 checking files
118 checking files
123 checked 5 changesets with 5 changes to 2 files
119 checked 5 changesets with 5 changes to 2 files
124 checking subrepo links
120 checking subrepo links
125 subrepo 'subrepo' is hidden in revision a66de08943b6
121 subrepo 'subrepo' is hidden in revision a66de08943b6
126 subrepo 'subrepo' is hidden in revision 674d05939c1e
122 subrepo 'subrepo' is hidden in revision 674d05939c1e
127 subrepo 'subrepo' not found in revision a7d05d9055a4
123 subrepo 'subrepo' not found in revision a7d05d9055a4
128
124
129 verifying shouldn't init a new subrepo if the reference doesn't exist
125 verifying shouldn't init a new subrepo if the reference doesn't exist
130
126
131 $ mv subrepo b
127 $ mv subrepo b
132 $ hg verify
128 $ hg verify
133 checking changesets
129 checking changesets
134 checking manifests
130 checking manifests
135 crosschecking files in changesets and manifests
131 crosschecking files in changesets and manifests
136 checking files
132 checking files
137 checked 5 changesets with 5 changes to 2 files
133 checked 5 changesets with 5 changes to 2 files
138 checking subrepo links
134 checking subrepo links
139 0: repository $TESTTMP/repo/subrepo not found
135 0: repository $TESTTMP/repo/subrepo not found
140 1: repository $TESTTMP/repo/subrepo not found
136 1: repository $TESTTMP/repo/subrepo not found
141 3: repository $TESTTMP/repo/subrepo not found
137 3: repository $TESTTMP/repo/subrepo not found
142 4: repository $TESTTMP/repo/subrepo not found
138 4: repository $TESTTMP/repo/subrepo not found
143 $ ls -A
139 $ ls -A
144 .hg
140 .hg
145 .hgsub
141 .hgsub
146 .hgsubstate
142 .hgsubstate
147 b
143 b
148 $ mv b subrepo
144 $ mv b subrepo
149
145
150 $ cd ..
146 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now