##// END OF EJS Templates
rhg: only complain about poorly configured fallback when falling back...
Arseniy Alekseyev -
r49176:8960295b default
parent child Browse files
Show More
@@ -1,647 +1,651
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::ffi::OsString;
14 use std::ffi::OsString;
15 use std::path::PathBuf;
15 use std::path::PathBuf;
16 use std::process::Command;
16 use std::process::Command;
17
17
18 mod blackbox;
18 mod blackbox;
19 mod error;
19 mod error;
20 mod ui;
20 mod ui;
21 pub mod utils {
21 pub mod utils {
22 pub mod path_utils;
22 pub mod path_utils;
23 }
23 }
24
24
25 fn main_with_result(
25 fn main_with_result(
26 process_start_time: &blackbox::ProcessStartTime,
26 process_start_time: &blackbox::ProcessStartTime,
27 ui: &ui::Ui,
27 ui: &ui::Ui,
28 repo: Result<&Repo, &NoRepoInCwdError>,
28 repo: Result<&Repo, &NoRepoInCwdError>,
29 config: &Config,
29 config: &Config,
30 ) -> Result<(), CommandError> {
30 ) -> Result<(), CommandError> {
31 check_unsupported(config, ui)?;
31 check_unsupported(config, ui)?;
32
32
33 let app = App::new("rhg")
33 let app = App::new("rhg")
34 .global_setting(AppSettings::AllowInvalidUtf8)
34 .global_setting(AppSettings::AllowInvalidUtf8)
35 .global_setting(AppSettings::DisableVersion)
35 .global_setting(AppSettings::DisableVersion)
36 .setting(AppSettings::SubcommandRequired)
36 .setting(AppSettings::SubcommandRequired)
37 .setting(AppSettings::VersionlessSubcommands)
37 .setting(AppSettings::VersionlessSubcommands)
38 .arg(
38 .arg(
39 Arg::with_name("repository")
39 Arg::with_name("repository")
40 .help("repository root directory")
40 .help("repository root directory")
41 .short("-R")
41 .short("-R")
42 .long("--repository")
42 .long("--repository")
43 .value_name("REPO")
43 .value_name("REPO")
44 .takes_value(true)
44 .takes_value(true)
45 // Both ok: `hg -R ./foo log` or `hg log -R ./foo`
45 // Both ok: `hg -R ./foo log` or `hg log -R ./foo`
46 .global(true),
46 .global(true),
47 )
47 )
48 .arg(
48 .arg(
49 Arg::with_name("config")
49 Arg::with_name("config")
50 .help("set/override config option (use 'section.name=value')")
50 .help("set/override config option (use 'section.name=value')")
51 .long("--config")
51 .long("--config")
52 .value_name("CONFIG")
52 .value_name("CONFIG")
53 .takes_value(true)
53 .takes_value(true)
54 .global(true)
54 .global(true)
55 // Ok: `--config section.key1=val --config section.key2=val2`
55 // Ok: `--config section.key1=val --config section.key2=val2`
56 .multiple(true)
56 .multiple(true)
57 // Not ok: `--config section.key1=val section.key2=val2`
57 // Not ok: `--config section.key1=val section.key2=val2`
58 .number_of_values(1),
58 .number_of_values(1),
59 )
59 )
60 .arg(
60 .arg(
61 Arg::with_name("cwd")
61 Arg::with_name("cwd")
62 .help("change working directory")
62 .help("change working directory")
63 .long("--cwd")
63 .long("--cwd")
64 .value_name("DIR")
64 .value_name("DIR")
65 .takes_value(true)
65 .takes_value(true)
66 .global(true),
66 .global(true),
67 )
67 )
68 .version("0.0.1");
68 .version("0.0.1");
69 let app = add_subcommand_args(app);
69 let app = add_subcommand_args(app);
70
70
71 let matches = app.clone().get_matches_safe()?;
71 let matches = app.clone().get_matches_safe()?;
72
72
73 let (subcommand_name, subcommand_matches) = matches.subcommand();
73 let (subcommand_name, subcommand_matches) = matches.subcommand();
74
74
75 // Mercurial allows users to define "defaults" for commands, fallback
75 // Mercurial allows users to define "defaults" for commands, fallback
76 // if a default is detected for the current command
76 // if a default is detected for the current command
77 let defaults = config.get_str(b"defaults", subcommand_name.as_bytes());
77 let defaults = config.get_str(b"defaults", subcommand_name.as_bytes());
78 if defaults?.is_some() {
78 if defaults?.is_some() {
79 let msg = "`defaults` config set";
79 let msg = "`defaults` config set";
80 return Err(CommandError::unsupported(msg));
80 return Err(CommandError::unsupported(msg));
81 }
81 }
82
82
83 for prefix in ["pre", "post", "fail"].iter() {
83 for prefix in ["pre", "post", "fail"].iter() {
84 // Mercurial allows users to define generic hooks for commands,
84 // Mercurial allows users to define generic hooks for commands,
85 // fallback if any are detected
85 // fallback if any are detected
86 let item = format!("{}-{}", prefix, subcommand_name);
86 let item = format!("{}-{}", prefix, subcommand_name);
87 let hook_for_command = config.get_str(b"hooks", item.as_bytes())?;
87 let hook_for_command = config.get_str(b"hooks", item.as_bytes())?;
88 if hook_for_command.is_some() {
88 if hook_for_command.is_some() {
89 let msg = format!("{}-{} hook defined", prefix, subcommand_name);
89 let msg = format!("{}-{} hook defined", prefix, subcommand_name);
90 return Err(CommandError::unsupported(msg));
90 return Err(CommandError::unsupported(msg));
91 }
91 }
92 }
92 }
93 let run = subcommand_run_fn(subcommand_name)
93 let run = subcommand_run_fn(subcommand_name)
94 .expect("unknown subcommand name from clap despite AppSettings::SubcommandRequired");
94 .expect("unknown subcommand name from clap despite AppSettings::SubcommandRequired");
95 let subcommand_args = subcommand_matches
95 let subcommand_args = subcommand_matches
96 .expect("no subcommand arguments from clap despite AppSettings::SubcommandRequired");
96 .expect("no subcommand arguments from clap despite AppSettings::SubcommandRequired");
97
97
98 let invocation = CliInvocation {
98 let invocation = CliInvocation {
99 ui,
99 ui,
100 subcommand_args,
100 subcommand_args,
101 config,
101 config,
102 repo,
102 repo,
103 };
103 };
104
104
105 if let Ok(repo) = repo {
105 if let Ok(repo) = repo {
106 // We don't support subrepos, fallback if the subrepos file is present
106 // We don't support subrepos, fallback if the subrepos file is present
107 if repo.working_directory_vfs().join(".hgsub").exists() {
107 if repo.working_directory_vfs().join(".hgsub").exists() {
108 let msg = "subrepos (.hgsub is present)";
108 let msg = "subrepos (.hgsub is present)";
109 return Err(CommandError::unsupported(msg));
109 return Err(CommandError::unsupported(msg));
110 }
110 }
111 }
111 }
112
112
113 let blackbox = blackbox::Blackbox::new(&invocation, process_start_time)?;
113 let blackbox = blackbox::Blackbox::new(&invocation, process_start_time)?;
114 blackbox.log_command_start();
114 blackbox.log_command_start();
115 let result = run(&invocation);
115 let result = run(&invocation);
116 blackbox.log_command_end(exit_code(
116 blackbox.log_command_end(exit_code(
117 &result,
117 &result,
118 // TODO: show a warning or combine with original error if `get_bool`
118 // TODO: show a warning or combine with original error if `get_bool`
119 // returns an error
119 // returns an error
120 config
120 config
121 .get_bool(b"ui", b"detailed-exit-code")
121 .get_bool(b"ui", b"detailed-exit-code")
122 .unwrap_or(false),
122 .unwrap_or(false),
123 ));
123 ));
124 result
124 result
125 }
125 }
126
126
127 fn main() {
127 fn main() {
128 // Run this first, before we find out if the blackbox extension is even
128 // Run this first, before we find out if the blackbox extension is even
129 // enabled, in order to include everything in-between in the duration
129 // enabled, in order to include everything in-between in the duration
130 // measurements. Reading config files can be slow if they’re on NFS.
130 // measurements. Reading config files can be slow if they’re on NFS.
131 let process_start_time = blackbox::ProcessStartTime::now();
131 let process_start_time = blackbox::ProcessStartTime::now();
132
132
133 env_logger::init();
133 env_logger::init();
134 let ui = ui::Ui::new();
134 let ui = ui::Ui::new();
135
135
136 let early_args = EarlyArgs::parse(std::env::args_os());
136 let early_args = EarlyArgs::parse(std::env::args_os());
137
137
138 let initial_current_dir = early_args.cwd.map(|cwd| {
138 let initial_current_dir = early_args.cwd.map(|cwd| {
139 let cwd = get_path_from_bytes(&cwd);
139 let cwd = get_path_from_bytes(&cwd);
140 std::env::current_dir()
140 std::env::current_dir()
141 .and_then(|initial| {
141 .and_then(|initial| {
142 std::env::set_current_dir(cwd)?;
142 std::env::set_current_dir(cwd)?;
143 Ok(initial)
143 Ok(initial)
144 })
144 })
145 .unwrap_or_else(|error| {
145 .unwrap_or_else(|error| {
146 exit(
146 exit(
147 &None,
147 &None,
148 &ui,
148 &ui,
149 OnUnsupported::Abort,
149 OnUnsupported::Abort,
150 Err(CommandError::abort(format!(
150 Err(CommandError::abort(format!(
151 "abort: {}: '{}'",
151 "abort: {}: '{}'",
152 error,
152 error,
153 cwd.display()
153 cwd.display()
154 ))),
154 ))),
155 false,
155 false,
156 )
156 )
157 })
157 })
158 });
158 });
159
159
160 let mut non_repo_config =
160 let mut non_repo_config =
161 Config::load_non_repo().unwrap_or_else(|error| {
161 Config::load_non_repo().unwrap_or_else(|error| {
162 // Normally this is decided based on config, but we don’t have that
162 // Normally this is decided based on config, but we don’t have that
163 // available. As of this writing config loading never returns an
163 // available. As of this writing config loading never returns an
164 // "unsupported" error but that is not enforced by the type system.
164 // "unsupported" error but that is not enforced by the type system.
165 let on_unsupported = OnUnsupported::Abort;
165 let on_unsupported = OnUnsupported::Abort;
166
166
167 exit(
167 exit(
168 &initial_current_dir,
168 &initial_current_dir,
169 &ui,
169 &ui,
170 on_unsupported,
170 on_unsupported,
171 Err(error.into()),
171 Err(error.into()),
172 false,
172 false,
173 )
173 )
174 });
174 });
175
175
176 non_repo_config
176 non_repo_config
177 .load_cli_args_config(early_args.config)
177 .load_cli_args_config(early_args.config)
178 .unwrap_or_else(|error| {
178 .unwrap_or_else(|error| {
179 exit(
179 exit(
180 &initial_current_dir,
180 &initial_current_dir,
181 &ui,
181 &ui,
182 OnUnsupported::from_config(&ui, &non_repo_config),
182 OnUnsupported::from_config(&non_repo_config),
183 Err(error.into()),
183 Err(error.into()),
184 non_repo_config
184 non_repo_config
185 .get_bool(b"ui", b"detailed-exit-code")
185 .get_bool(b"ui", b"detailed-exit-code")
186 .unwrap_or(false),
186 .unwrap_or(false),
187 )
187 )
188 });
188 });
189
189
190 if let Some(repo_path_bytes) = &early_args.repo {
190 if let Some(repo_path_bytes) = &early_args.repo {
191 lazy_static::lazy_static! {
191 lazy_static::lazy_static! {
192 static ref SCHEME_RE: regex::bytes::Regex =
192 static ref SCHEME_RE: regex::bytes::Regex =
193 // Same as `_matchscheme` in `mercurial/util.py`
193 // Same as `_matchscheme` in `mercurial/util.py`
194 regex::bytes::Regex::new("^[a-zA-Z0-9+.\\-]+:").unwrap();
194 regex::bytes::Regex::new("^[a-zA-Z0-9+.\\-]+:").unwrap();
195 }
195 }
196 if SCHEME_RE.is_match(&repo_path_bytes) {
196 if SCHEME_RE.is_match(&repo_path_bytes) {
197 exit(
197 exit(
198 &initial_current_dir,
198 &initial_current_dir,
199 &ui,
199 &ui,
200 OnUnsupported::from_config(&ui, &non_repo_config),
200 OnUnsupported::from_config(&non_repo_config),
201 Err(CommandError::UnsupportedFeature {
201 Err(CommandError::UnsupportedFeature {
202 message: format_bytes!(
202 message: format_bytes!(
203 b"URL-like --repository {}",
203 b"URL-like --repository {}",
204 repo_path_bytes
204 repo_path_bytes
205 ),
205 ),
206 }),
206 }),
207 // TODO: show a warning or combine with original error if
207 // TODO: show a warning or combine with original error if
208 // `get_bool` returns an error
208 // `get_bool` returns an error
209 non_repo_config
209 non_repo_config
210 .get_bool(b"ui", b"detailed-exit-code")
210 .get_bool(b"ui", b"detailed-exit-code")
211 .unwrap_or(false),
211 .unwrap_or(false),
212 )
212 )
213 }
213 }
214 }
214 }
215 let repo_arg = early_args.repo.unwrap_or(Vec::new());
215 let repo_arg = early_args.repo.unwrap_or(Vec::new());
216 let repo_path: Option<PathBuf> = {
216 let repo_path: Option<PathBuf> = {
217 if repo_arg.is_empty() {
217 if repo_arg.is_empty() {
218 None
218 None
219 } else {
219 } else {
220 let local_config = {
220 let local_config = {
221 if std::env::var_os("HGRCSKIPREPO").is_none() {
221 if std::env::var_os("HGRCSKIPREPO").is_none() {
222 // TODO: handle errors from find_repo_root
222 // TODO: handle errors from find_repo_root
223 if let Ok(current_dir_path) = Repo::find_repo_root() {
223 if let Ok(current_dir_path) = Repo::find_repo_root() {
224 let config_files = vec![
224 let config_files = vec![
225 ConfigSource::AbsPath(
225 ConfigSource::AbsPath(
226 current_dir_path.join(".hg/hgrc"),
226 current_dir_path.join(".hg/hgrc"),
227 ),
227 ),
228 ConfigSource::AbsPath(
228 ConfigSource::AbsPath(
229 current_dir_path.join(".hg/hgrc-not-shared"),
229 current_dir_path.join(".hg/hgrc-not-shared"),
230 ),
230 ),
231 ];
231 ];
232 // TODO: handle errors from
232 // TODO: handle errors from
233 // `load_from_explicit_sources`
233 // `load_from_explicit_sources`
234 Config::load_from_explicit_sources(config_files).ok()
234 Config::load_from_explicit_sources(config_files).ok()
235 } else {
235 } else {
236 None
236 None
237 }
237 }
238 } else {
238 } else {
239 None
239 None
240 }
240 }
241 };
241 };
242
242
243 let non_repo_config_val = {
243 let non_repo_config_val = {
244 let non_repo_val = non_repo_config.get(b"paths", &repo_arg);
244 let non_repo_val = non_repo_config.get(b"paths", &repo_arg);
245 match &non_repo_val {
245 match &non_repo_val {
246 Some(val) if val.len() > 0 => home::home_dir()
246 Some(val) if val.len() > 0 => home::home_dir()
247 .unwrap_or_else(|| PathBuf::from("~"))
247 .unwrap_or_else(|| PathBuf::from("~"))
248 .join(get_path_from_bytes(val))
248 .join(get_path_from_bytes(val))
249 .canonicalize()
249 .canonicalize()
250 // TODO: handle error and make it similar to python
250 // TODO: handle error and make it similar to python
251 // implementation maybe?
251 // implementation maybe?
252 .ok(),
252 .ok(),
253 _ => None,
253 _ => None,
254 }
254 }
255 };
255 };
256
256
257 let config_val = match &local_config {
257 let config_val = match &local_config {
258 None => non_repo_config_val,
258 None => non_repo_config_val,
259 Some(val) => {
259 Some(val) => {
260 let local_config_val = val.get(b"paths", &repo_arg);
260 let local_config_val = val.get(b"paths", &repo_arg);
261 match &local_config_val {
261 match &local_config_val {
262 Some(val) if val.len() > 0 => {
262 Some(val) if val.len() > 0 => {
263 // presence of a local_config assures that
263 // presence of a local_config assures that
264 // current_dir
264 // current_dir
265 // wont result in an Error
265 // wont result in an Error
266 let canpath = hg::utils::current_dir()
266 let canpath = hg::utils::current_dir()
267 .unwrap()
267 .unwrap()
268 .join(get_path_from_bytes(val))
268 .join(get_path_from_bytes(val))
269 .canonicalize();
269 .canonicalize();
270 canpath.ok().or(non_repo_config_val)
270 canpath.ok().or(non_repo_config_val)
271 }
271 }
272 _ => non_repo_config_val,
272 _ => non_repo_config_val,
273 }
273 }
274 }
274 }
275 };
275 };
276 config_val.or(Some(get_path_from_bytes(&repo_arg).to_path_buf()))
276 config_val.or(Some(get_path_from_bytes(&repo_arg).to_path_buf()))
277 }
277 }
278 };
278 };
279
279
280 let repo_result = match Repo::find(&non_repo_config, repo_path.to_owned())
280 let repo_result = match Repo::find(&non_repo_config, repo_path.to_owned())
281 {
281 {
282 Ok(repo) => Ok(repo),
282 Ok(repo) => Ok(repo),
283 Err(RepoError::NotFound { at }) if repo_path.is_none() => {
283 Err(RepoError::NotFound { at }) if repo_path.is_none() => {
284 // Not finding a repo is not fatal yet, if `-R` was not given
284 // Not finding a repo is not fatal yet, if `-R` was not given
285 Err(NoRepoInCwdError { cwd: at })
285 Err(NoRepoInCwdError { cwd: at })
286 }
286 }
287 Err(error) => exit(
287 Err(error) => exit(
288 &initial_current_dir,
288 &initial_current_dir,
289 &ui,
289 &ui,
290 OnUnsupported::from_config(&ui, &non_repo_config),
290 OnUnsupported::from_config(&non_repo_config),
291 Err(error.into()),
291 Err(error.into()),
292 // TODO: show a warning or combine with original error if
292 // TODO: show a warning or combine with original error if
293 // `get_bool` returns an error
293 // `get_bool` returns an error
294 non_repo_config
294 non_repo_config
295 .get_bool(b"ui", b"detailed-exit-code")
295 .get_bool(b"ui", b"detailed-exit-code")
296 .unwrap_or(false),
296 .unwrap_or(false),
297 ),
297 ),
298 };
298 };
299
299
300 let config = if let Ok(repo) = &repo_result {
300 let config = if let Ok(repo) = &repo_result {
301 repo.config()
301 repo.config()
302 } else {
302 } else {
303 &non_repo_config
303 &non_repo_config
304 };
304 };
305 let on_unsupported = OnUnsupported::from_config(&ui, config);
305 let on_unsupported = OnUnsupported::from_config(config);
306
306
307 let result = main_with_result(
307 let result = main_with_result(
308 &process_start_time,
308 &process_start_time,
309 &ui,
309 &ui,
310 repo_result.as_ref(),
310 repo_result.as_ref(),
311 config,
311 config,
312 );
312 );
313 exit(
313 exit(
314 &initial_current_dir,
314 &initial_current_dir,
315 &ui,
315 &ui,
316 on_unsupported,
316 on_unsupported,
317 result,
317 result,
318 // TODO: show a warning or combine with original error if `get_bool`
318 // TODO: show a warning or combine with original error if `get_bool`
319 // returns an error
319 // returns an error
320 config
320 config
321 .get_bool(b"ui", b"detailed-exit-code")
321 .get_bool(b"ui", b"detailed-exit-code")
322 .unwrap_or(false),
322 .unwrap_or(false),
323 )
323 )
324 }
324 }
325
325
326 fn exit_code(
326 fn exit_code(
327 result: &Result<(), CommandError>,
327 result: &Result<(), CommandError>,
328 use_detailed_exit_code: bool,
328 use_detailed_exit_code: bool,
329 ) -> i32 {
329 ) -> i32 {
330 match result {
330 match result {
331 Ok(()) => exit_codes::OK,
331 Ok(()) => exit_codes::OK,
332 Err(CommandError::Abort {
332 Err(CommandError::Abort {
333 message: _,
333 message: _,
334 detailed_exit_code,
334 detailed_exit_code,
335 }) => {
335 }) => {
336 if use_detailed_exit_code {
336 if use_detailed_exit_code {
337 *detailed_exit_code
337 *detailed_exit_code
338 } else {
338 } else {
339 exit_codes::ABORT
339 exit_codes::ABORT
340 }
340 }
341 }
341 }
342 Err(CommandError::Unsuccessful) => exit_codes::UNSUCCESSFUL,
342 Err(CommandError::Unsuccessful) => exit_codes::UNSUCCESSFUL,
343
343
344 // Exit with a specific code and no error message to let a potential
344 // Exit with a specific code and no error message to let a potential
345 // wrapper script fallback to Python-based Mercurial.
345 // wrapper script fallback to Python-based Mercurial.
346 Err(CommandError::UnsupportedFeature { .. }) => {
346 Err(CommandError::UnsupportedFeature { .. }) => {
347 exit_codes::UNIMPLEMENTED
347 exit_codes::UNIMPLEMENTED
348 }
348 }
349 }
349 }
350 }
350 }
351
351
352 fn exit(
352 fn exit(
353 initial_current_dir: &Option<PathBuf>,
353 initial_current_dir: &Option<PathBuf>,
354 ui: &Ui,
354 ui: &Ui,
355 mut on_unsupported: OnUnsupported,
355 mut on_unsupported: OnUnsupported,
356 result: Result<(), CommandError>,
356 result: Result<(), CommandError>,
357 use_detailed_exit_code: bool,
357 use_detailed_exit_code: bool,
358 ) -> ! {
358 ) -> ! {
359 if let (
359 if let (
360 OnUnsupported::Fallback { executable },
360 OnUnsupported::Fallback { executable },
361 Err(CommandError::UnsupportedFeature { .. }),
361 Err(CommandError::UnsupportedFeature { .. }),
362 ) = (&on_unsupported, &result)
362 ) = (&on_unsupported, &result)
363 {
363 {
364 let mut args = std::env::args_os();
364 let mut args = std::env::args_os();
365 let executable = match executable {
366 None => {
367 exit_no_fallback(
368 ui,
369 OnUnsupported::Abort,
370 Err(CommandError::abort(
371 "abort: 'rhg.on-unsupported=fallback' without \
372 'rhg.fallback-executable' set.",
373 )),
374 false,
375 );
376 }
377 Some(executable) => executable,
378 };
365 let executable_path = get_path_from_bytes(&executable);
379 let executable_path = get_path_from_bytes(&executable);
366 let this_executable = args.next().expect("exepcted argv[0] to exist");
380 let this_executable = args.next().expect("exepcted argv[0] to exist");
367 if executable_path == &PathBuf::from(this_executable) {
381 if executable_path == &PathBuf::from(this_executable) {
368 // Avoid spawning infinitely many processes until resource
382 // Avoid spawning infinitely many processes until resource
369 // exhaustion.
383 // exhaustion.
370 let _ = ui.write_stderr(&format_bytes!(
384 let _ = ui.write_stderr(&format_bytes!(
371 b"Blocking recursive fallback. The 'rhg.fallback-executable = {}' config \
385 b"Blocking recursive fallback. The 'rhg.fallback-executable = {}' config \
372 points to `rhg` itself.\n",
386 points to `rhg` itself.\n",
373 executable
387 executable
374 ));
388 ));
375 on_unsupported = OnUnsupported::Abort
389 on_unsupported = OnUnsupported::Abort
376 } else {
390 } else {
377 // `args` is now `argv[1..]` since we’ve already consumed `argv[0]`
391 // `args` is now `argv[1..]` since we’ve already consumed
392 // `argv[0]`
378 let mut command = Command::new(executable_path);
393 let mut command = Command::new(executable_path);
379 command.args(args);
394 command.args(args);
380 if let Some(initial) = initial_current_dir {
395 if let Some(initial) = initial_current_dir {
381 command.current_dir(initial);
396 command.current_dir(initial);
382 }
397 }
383 let result = command.status();
398 let result = command.status();
384 match result {
399 match result {
385 Ok(status) => std::process::exit(
400 Ok(status) => std::process::exit(
386 status.code().unwrap_or(exit_codes::ABORT),
401 status.code().unwrap_or(exit_codes::ABORT),
387 ),
402 ),
388 Err(error) => {
403 Err(error) => {
389 let _ = ui.write_stderr(&format_bytes!(
404 let _ = ui.write_stderr(&format_bytes!(
390 b"tried to fall back to a '{}' sub-process but got error {}\n",
405 b"tried to fall back to a '{}' sub-process but got error {}\n",
391 executable, format_bytes::Utf8(error)
406 executable, format_bytes::Utf8(error)
392 ));
407 ));
393 on_unsupported = OnUnsupported::Abort
408 on_unsupported = OnUnsupported::Abort
394 }
409 }
395 }
410 }
396 }
411 }
397 }
412 }
398 exit_no_fallback(ui, on_unsupported, result, use_detailed_exit_code)
413 exit_no_fallback(ui, on_unsupported, result, use_detailed_exit_code)
399 }
414 }
400
415
401 fn exit_no_fallback(
416 fn exit_no_fallback(
402 ui: &Ui,
417 ui: &Ui,
403 on_unsupported: OnUnsupported,
418 on_unsupported: OnUnsupported,
404 result: Result<(), CommandError>,
419 result: Result<(), CommandError>,
405 use_detailed_exit_code: bool,
420 use_detailed_exit_code: bool,
406 ) -> ! {
421 ) -> ! {
407 match &result {
422 match &result {
408 Ok(_) => {}
423 Ok(_) => {}
409 Err(CommandError::Unsuccessful) => {}
424 Err(CommandError::Unsuccessful) => {}
410 Err(CommandError::Abort {
425 Err(CommandError::Abort {
411 message,
426 message,
412 detailed_exit_code: _,
427 detailed_exit_code: _,
413 }) => {
428 }) => {
414 if !message.is_empty() {
429 if !message.is_empty() {
415 // Ignore errors when writing to stderr, we’re already exiting
430 // Ignore errors when writing to stderr, we’re already exiting
416 // with failure code so there’s not much more we can do.
431 // with failure code so there’s not much more we can do.
417 let _ = ui.write_stderr(&format_bytes!(b"{}\n", message));
432 let _ = ui.write_stderr(&format_bytes!(b"{}\n", message));
418 }
433 }
419 }
434 }
420 Err(CommandError::UnsupportedFeature { message }) => {
435 Err(CommandError::UnsupportedFeature { message }) => {
421 match on_unsupported {
436 match on_unsupported {
422 OnUnsupported::Abort => {
437 OnUnsupported::Abort => {
423 let _ = ui.write_stderr(&format_bytes!(
438 let _ = ui.write_stderr(&format_bytes!(
424 b"unsupported feature: {}\n",
439 b"unsupported feature: {}\n",
425 message
440 message
426 ));
441 ));
427 }
442 }
428 OnUnsupported::AbortSilent => {}
443 OnUnsupported::AbortSilent => {}
429 OnUnsupported::Fallback { .. } => unreachable!(),
444 OnUnsupported::Fallback { .. } => unreachable!(),
430 }
445 }
431 }
446 }
432 }
447 }
433 std::process::exit(exit_code(&result, use_detailed_exit_code))
448 std::process::exit(exit_code(&result, use_detailed_exit_code))
434 }
449 }
435
450
436 macro_rules! subcommands {
451 macro_rules! subcommands {
437 ($( $command: ident )+) => {
452 ($( $command: ident )+) => {
438 mod commands {
453 mod commands {
439 $(
454 $(
440 pub mod $command;
455 pub mod $command;
441 )+
456 )+
442 }
457 }
443
458
444 fn add_subcommand_args<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b> {
459 fn add_subcommand_args<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b> {
445 app
460 app
446 $(
461 $(
447 .subcommand(commands::$command::args())
462 .subcommand(commands::$command::args())
448 )+
463 )+
449 }
464 }
450
465
451 pub type RunFn = fn(&CliInvocation) -> Result<(), CommandError>;
466 pub type RunFn = fn(&CliInvocation) -> Result<(), CommandError>;
452
467
453 fn subcommand_run_fn(name: &str) -> Option<RunFn> {
468 fn subcommand_run_fn(name: &str) -> Option<RunFn> {
454 match name {
469 match name {
455 $(
470 $(
456 stringify!($command) => Some(commands::$command::run),
471 stringify!($command) => Some(commands::$command::run),
457 )+
472 )+
458 _ => None,
473 _ => None,
459 }
474 }
460 }
475 }
461 };
476 };
462 }
477 }
463
478
464 subcommands! {
479 subcommands! {
465 cat
480 cat
466 debugdata
481 debugdata
467 debugrequirements
482 debugrequirements
468 files
483 files
469 root
484 root
470 config
485 config
471 status
486 status
472 }
487 }
473
488
474 pub struct CliInvocation<'a> {
489 pub struct CliInvocation<'a> {
475 ui: &'a Ui,
490 ui: &'a Ui,
476 subcommand_args: &'a ArgMatches<'a>,
491 subcommand_args: &'a ArgMatches<'a>,
477 config: &'a Config,
492 config: &'a Config,
478 /// References inside `Result` is a bit peculiar but allow
493 /// References inside `Result` is a bit peculiar but allow
479 /// `invocation.repo?` to work out with `&CliInvocation` since this
494 /// `invocation.repo?` to work out with `&CliInvocation` since this
480 /// `Result` type is `Copy`.
495 /// `Result` type is `Copy`.
481 repo: Result<&'a Repo, &'a NoRepoInCwdError>,
496 repo: Result<&'a Repo, &'a NoRepoInCwdError>,
482 }
497 }
483
498
484 struct NoRepoInCwdError {
499 struct NoRepoInCwdError {
485 cwd: PathBuf,
500 cwd: PathBuf,
486 }
501 }
487
502
488 /// CLI arguments to be parsed "early" in order to be able to read
503 /// CLI arguments to be parsed "early" in order to be able to read
489 /// configuration before using Clap. Ideally we would also use Clap for this,
504 /// configuration before using Clap. Ideally we would also use Clap for this,
490 /// see <https://github.com/clap-rs/clap/discussions/2366>.
505 /// see <https://github.com/clap-rs/clap/discussions/2366>.
491 ///
506 ///
492 /// These arguments are still declared when we do use Clap later, so that Clap
507 /// These arguments are still declared when we do use Clap later, so that Clap
493 /// does not return an error for their presence.
508 /// does not return an error for their presence.
494 struct EarlyArgs {
509 struct EarlyArgs {
495 /// Values of all `--config` arguments. (Possibly none)
510 /// Values of all `--config` arguments. (Possibly none)
496 config: Vec<Vec<u8>>,
511 config: Vec<Vec<u8>>,
497 /// Value of the `-R` or `--repository` argument, if any.
512 /// Value of the `-R` or `--repository` argument, if any.
498 repo: Option<Vec<u8>>,
513 repo: Option<Vec<u8>>,
499 /// Value of the `--cwd` argument, if any.
514 /// Value of the `--cwd` argument, if any.
500 cwd: Option<Vec<u8>>,
515 cwd: Option<Vec<u8>>,
501 }
516 }
502
517
503 impl EarlyArgs {
518 impl EarlyArgs {
504 fn parse(args: impl IntoIterator<Item = OsString>) -> Self {
519 fn parse(args: impl IntoIterator<Item = OsString>) -> Self {
505 let mut args = args.into_iter().map(get_bytes_from_os_str);
520 let mut args = args.into_iter().map(get_bytes_from_os_str);
506 let mut config = Vec::new();
521 let mut config = Vec::new();
507 let mut repo = None;
522 let mut repo = None;
508 let mut cwd = None;
523 let mut cwd = None;
509 // Use `while let` instead of `for` so that we can also call
524 // Use `while let` instead of `for` so that we can also call
510 // `args.next()` inside the loop.
525 // `args.next()` inside the loop.
511 while let Some(arg) = args.next() {
526 while let Some(arg) = args.next() {
512 if arg == b"--config" {
527 if arg == b"--config" {
513 if let Some(value) = args.next() {
528 if let Some(value) = args.next() {
514 config.push(value)
529 config.push(value)
515 }
530 }
516 } else if let Some(value) = arg.drop_prefix(b"--config=") {
531 } else if let Some(value) = arg.drop_prefix(b"--config=") {
517 config.push(value.to_owned())
532 config.push(value.to_owned())
518 }
533 }
519
534
520 if arg == b"--cwd" {
535 if arg == b"--cwd" {
521 if let Some(value) = args.next() {
536 if let Some(value) = args.next() {
522 cwd = Some(value)
537 cwd = Some(value)
523 }
538 }
524 } else if let Some(value) = arg.drop_prefix(b"--cwd=") {
539 } else if let Some(value) = arg.drop_prefix(b"--cwd=") {
525 cwd = Some(value.to_owned())
540 cwd = Some(value.to_owned())
526 }
541 }
527
542
528 if arg == b"--repository" || arg == b"-R" {
543 if arg == b"--repository" || arg == b"-R" {
529 if let Some(value) = args.next() {
544 if let Some(value) = args.next() {
530 repo = Some(value)
545 repo = Some(value)
531 }
546 }
532 } else if let Some(value) = arg.drop_prefix(b"--repository=") {
547 } else if let Some(value) = arg.drop_prefix(b"--repository=") {
533 repo = Some(value.to_owned())
548 repo = Some(value.to_owned())
534 } else if let Some(value) = arg.drop_prefix(b"-R") {
549 } else if let Some(value) = arg.drop_prefix(b"-R") {
535 repo = Some(value.to_owned())
550 repo = Some(value.to_owned())
536 }
551 }
537 }
552 }
538 Self { config, repo, cwd }
553 Self { config, repo, cwd }
539 }
554 }
540 }
555 }
541
556
542 /// What to do when encountering some unsupported feature.
557 /// What to do when encountering some unsupported feature.
543 ///
558 ///
544 /// See `HgError::UnsupportedFeature` and `CommandError::UnsupportedFeature`.
559 /// See `HgError::UnsupportedFeature` and `CommandError::UnsupportedFeature`.
545 enum OnUnsupported {
560 enum OnUnsupported {
546 /// Print an error message describing what feature is not supported,
561 /// Print an error message describing what feature is not supported,
547 /// and exit with code 252.
562 /// and exit with code 252.
548 Abort,
563 Abort,
549 /// Silently exit with code 252.
564 /// Silently exit with code 252.
550 AbortSilent,
565 AbortSilent,
551 /// Try running a Python implementation
566 /// Try running a Python implementation
552 Fallback { executable: Vec<u8> },
567 Fallback { executable: Option<Vec<u8>> },
553 }
568 }
554
569
555 impl OnUnsupported {
570 impl OnUnsupported {
556 const DEFAULT: Self = OnUnsupported::Abort;
571 const DEFAULT: Self = OnUnsupported::Abort;
557
572
558 fn from_config(ui: &Ui, config: &Config) -> Self {
573 fn from_config(config: &Config) -> Self {
559 match config
574 match config
560 .get(b"rhg", b"on-unsupported")
575 .get(b"rhg", b"on-unsupported")
561 .map(|value| value.to_ascii_lowercase())
576 .map(|value| value.to_ascii_lowercase())
562 .as_deref()
577 .as_deref()
563 {
578 {
564 Some(b"abort") => OnUnsupported::Abort,
579 Some(b"abort") => OnUnsupported::Abort,
565 Some(b"abort-silent") => OnUnsupported::AbortSilent,
580 Some(b"abort-silent") => OnUnsupported::AbortSilent,
566 Some(b"fallback") => OnUnsupported::Fallback {
581 Some(b"fallback") => OnUnsupported::Fallback {
567 executable: config
582 executable: config
568 .get(b"rhg", b"fallback-executable")
583 .get(b"rhg", b"fallback-executable")
569 .unwrap_or_else(|| {
584 .map(|x| x.to_owned()),
570 exit_no_fallback(
571 ui,
572 Self::Abort,
573 Err(CommandError::abort(
574 "abort: 'rhg.on-unsupported=fallback' without \
575 'rhg.fallback-executable' set."
576 )),
577 false,
578 )
579 })
580 .to_owned(),
581 },
585 },
582 None => Self::DEFAULT,
586 None => Self::DEFAULT,
583 Some(_) => {
587 Some(_) => {
584 // TODO: warn about unknown config value
588 // TODO: warn about unknown config value
585 Self::DEFAULT
589 Self::DEFAULT
586 }
590 }
587 }
591 }
588 }
592 }
589 }
593 }
590
594
591 const SUPPORTED_EXTENSIONS: &[&[u8]] = &[b"blackbox", b"share"];
595 const SUPPORTED_EXTENSIONS: &[&[u8]] = &[b"blackbox", b"share"];
592
596
593 fn check_extensions(config: &Config) -> Result<(), CommandError> {
597 fn check_extensions(config: &Config) -> Result<(), CommandError> {
594 let enabled = config.get_section_keys(b"extensions");
598 let enabled = config.get_section_keys(b"extensions");
595
599
596 let mut unsupported = enabled;
600 let mut unsupported = enabled;
597 for supported in SUPPORTED_EXTENSIONS {
601 for supported in SUPPORTED_EXTENSIONS {
598 unsupported.remove(supported);
602 unsupported.remove(supported);
599 }
603 }
600
604
601 if let Some(ignored_list) = config.get_list(b"rhg", b"ignored-extensions")
605 if let Some(ignored_list) = config.get_list(b"rhg", b"ignored-extensions")
602 {
606 {
603 for ignored in ignored_list {
607 for ignored in ignored_list {
604 unsupported.remove(ignored.as_slice());
608 unsupported.remove(ignored.as_slice());
605 }
609 }
606 }
610 }
607
611
608 if unsupported.is_empty() {
612 if unsupported.is_empty() {
609 Ok(())
613 Ok(())
610 } else {
614 } else {
611 Err(CommandError::UnsupportedFeature {
615 Err(CommandError::UnsupportedFeature {
612 message: format_bytes!(
616 message: format_bytes!(
613 b"extensions: {} (consider adding them to 'rhg.ignored-extensions' config)",
617 b"extensions: {} (consider adding them to 'rhg.ignored-extensions' config)",
614 join(unsupported, b", ")
618 join(unsupported, b", ")
615 ),
619 ),
616 })
620 })
617 }
621 }
618 }
622 }
619
623
620 fn check_unsupported(
624 fn check_unsupported(
621 config: &Config,
625 config: &Config,
622 ui: &ui::Ui,
626 ui: &ui::Ui,
623 ) -> Result<(), CommandError> {
627 ) -> Result<(), CommandError> {
624 check_extensions(config)?;
628 check_extensions(config)?;
625
629
626 if std::env::var_os("HG_PENDING").is_some() {
630 if std::env::var_os("HG_PENDING").is_some() {
627 // TODO: only if the value is `== repo.working_directory`?
631 // TODO: only if the value is `== repo.working_directory`?
628 // What about relative v.s. absolute paths?
632 // What about relative v.s. absolute paths?
629 Err(CommandError::unsupported("$HG_PENDING"))?
633 Err(CommandError::unsupported("$HG_PENDING"))?
630 }
634 }
631
635
632 if config.has_non_empty_section(b"encode") {
636 if config.has_non_empty_section(b"encode") {
633 Err(CommandError::unsupported("[encode] config"))?
637 Err(CommandError::unsupported("[encode] config"))?
634 }
638 }
635
639
636 if config.has_non_empty_section(b"decode") {
640 if config.has_non_empty_section(b"decode") {
637 Err(CommandError::unsupported("[decode] config"))?
641 Err(CommandError::unsupported("[decode] config"))?
638 }
642 }
639
643
640 if let Some(color) = config.get(b"ui", b"color") {
644 if let Some(color) = config.get(b"ui", b"color") {
641 if (color == b"always" || color == b"debug") && !ui.plain() {
645 if (color == b"always" || color == b"debug") && !ui.plain() {
642 Err(CommandError::unsupported("colored output"))?
646 Err(CommandError::unsupported("colored output"))?
643 }
647 }
644 }
648 }
645
649
646 Ok(())
650 Ok(())
647 }
651 }
@@ -1,383 +1,382
1 #require rhg
1 #require rhg
2
2
3 $ NO_FALLBACK="env RHG_ON_UNSUPPORTED=abort"
3 $ NO_FALLBACK="env RHG_ON_UNSUPPORTED=abort"
4
4
5 Unimplemented command
5 Unimplemented command
6 $ $NO_FALLBACK rhg unimplemented-command
6 $ $NO_FALLBACK rhg unimplemented-command
7 unsupported feature: error: Found argument 'unimplemented-command' which wasn't expected, or isn't valid in this context
7 unsupported feature: error: Found argument 'unimplemented-command' which wasn't expected, or isn't valid in this context
8
8
9 USAGE:
9 USAGE:
10 rhg [OPTIONS] <SUBCOMMAND>
10 rhg [OPTIONS] <SUBCOMMAND>
11
11
12 For more information try --help
12 For more information try --help
13
13
14 [252]
14 [252]
15 $ rhg unimplemented-command --config rhg.on-unsupported=abort-silent
15 $ rhg unimplemented-command --config rhg.on-unsupported=abort-silent
16 [252]
16 [252]
17
17
18 Finding root
18 Finding root
19 $ $NO_FALLBACK rhg root
19 $ $NO_FALLBACK rhg root
20 abort: no repository found in '$TESTTMP' (.hg not found)!
20 abort: no repository found in '$TESTTMP' (.hg not found)!
21 [255]
21 [255]
22
22
23 $ hg init repository
23 $ hg init repository
24 $ cd repository
24 $ cd repository
25 $ $NO_FALLBACK rhg root
25 $ $NO_FALLBACK rhg root
26 $TESTTMP/repository
26 $TESTTMP/repository
27
27
28 Reading and setting configuration
28 Reading and setting configuration
29 $ echo "[ui]" >> $HGRCPATH
29 $ echo "[ui]" >> $HGRCPATH
30 $ echo "username = user1" >> $HGRCPATH
30 $ echo "username = user1" >> $HGRCPATH
31 $ $NO_FALLBACK rhg config ui.username
31 $ $NO_FALLBACK rhg config ui.username
32 user1
32 user1
33 $ echo "[ui]" >> .hg/hgrc
33 $ echo "[ui]" >> .hg/hgrc
34 $ echo "username = user2" >> .hg/hgrc
34 $ echo "username = user2" >> .hg/hgrc
35 $ $NO_FALLBACK rhg config ui.username
35 $ $NO_FALLBACK rhg config ui.username
36 user2
36 user2
37 $ $NO_FALLBACK rhg --config ui.username=user3 config ui.username
37 $ $NO_FALLBACK rhg --config ui.username=user3 config ui.username
38 user3
38 user3
39
39
40 Unwritable file descriptor
40 Unwritable file descriptor
41 $ $NO_FALLBACK rhg root > /dev/full
41 $ $NO_FALLBACK rhg root > /dev/full
42 abort: No space left on device (os error 28)
42 abort: No space left on device (os error 28)
43 [255]
43 [255]
44
44
45 Deleted repository
45 Deleted repository
46 $ rm -rf `pwd`
46 $ rm -rf `pwd`
47 $ $NO_FALLBACK rhg root
47 $ $NO_FALLBACK rhg root
48 abort: error getting current working directory: $ENOENT$
48 abort: error getting current working directory: $ENOENT$
49 [255]
49 [255]
50
50
51 Listing tracked files
51 Listing tracked files
52 $ cd $TESTTMP
52 $ cd $TESTTMP
53 $ hg init repository
53 $ hg init repository
54 $ cd repository
54 $ cd repository
55 $ for i in 1 2 3; do
55 $ for i in 1 2 3; do
56 > echo $i >> file$i
56 > echo $i >> file$i
57 > hg add file$i
57 > hg add file$i
58 > done
58 > done
59 > hg commit -m "commit $i" -q
59 > hg commit -m "commit $i" -q
60
60
61 Listing tracked files from root
61 Listing tracked files from root
62 $ $NO_FALLBACK rhg files
62 $ $NO_FALLBACK rhg files
63 file1
63 file1
64 file2
64 file2
65 file3
65 file3
66
66
67 Listing tracked files from subdirectory
67 Listing tracked files from subdirectory
68 $ mkdir -p path/to/directory
68 $ mkdir -p path/to/directory
69 $ cd path/to/directory
69 $ cd path/to/directory
70 $ $NO_FALLBACK rhg files
70 $ $NO_FALLBACK rhg files
71 ../../../file1
71 ../../../file1
72 ../../../file2
72 ../../../file2
73 ../../../file3
73 ../../../file3
74
74
75 Listing tracked files through broken pipe
75 Listing tracked files through broken pipe
76 $ $NO_FALLBACK rhg files | head -n 1
76 $ $NO_FALLBACK rhg files | head -n 1
77 ../../../file1
77 ../../../file1
78
78
79 Debuging data in inline index
79 Debuging data in inline index
80 $ cd $TESTTMP
80 $ cd $TESTTMP
81 $ rm -rf repository
81 $ rm -rf repository
82 $ hg init repository
82 $ hg init repository
83 $ cd repository
83 $ cd repository
84 $ for i in 1 2 3 4 5 6; do
84 $ for i in 1 2 3 4 5 6; do
85 > echo $i >> file-$i
85 > echo $i >> file-$i
86 > hg add file-$i
86 > hg add file-$i
87 > hg commit -m "Commit $i" -q
87 > hg commit -m "Commit $i" -q
88 > done
88 > done
89 $ $NO_FALLBACK rhg debugdata -c 2
89 $ $NO_FALLBACK rhg debugdata -c 2
90 8d0267cb034247ebfa5ee58ce59e22e57a492297
90 8d0267cb034247ebfa5ee58ce59e22e57a492297
91 test
91 test
92 0 0
92 0 0
93 file-3
93 file-3
94
94
95 Commit 3 (no-eol)
95 Commit 3 (no-eol)
96 $ $NO_FALLBACK rhg debugdata -m 2
96 $ $NO_FALLBACK rhg debugdata -m 2
97 file-1\x00b8e02f6433738021a065f94175c7cd23db5f05be (esc)
97 file-1\x00b8e02f6433738021a065f94175c7cd23db5f05be (esc)
98 file-2\x005d9299349fc01ddd25d0070d149b124d8f10411e (esc)
98 file-2\x005d9299349fc01ddd25d0070d149b124d8f10411e (esc)
99 file-3\x002661d26c649684b482d10f91960cc3db683c38b4 (esc)
99 file-3\x002661d26c649684b482d10f91960cc3db683c38b4 (esc)
100
100
101 Debuging with full node id
101 Debuging with full node id
102 $ $NO_FALLBACK rhg debugdata -c `hg log -r 0 -T '{node}'`
102 $ $NO_FALLBACK rhg debugdata -c `hg log -r 0 -T '{node}'`
103 d1d1c679d3053e8926061b6f45ca52009f011e3f
103 d1d1c679d3053e8926061b6f45ca52009f011e3f
104 test
104 test
105 0 0
105 0 0
106 file-1
106 file-1
107
107
108 Commit 1 (no-eol)
108 Commit 1 (no-eol)
109
109
110 Specifying revisions by changeset ID
110 Specifying revisions by changeset ID
111 $ hg log -T '{node}\n'
111 $ hg log -T '{node}\n'
112 c6ad58c44207b6ff8a4fbbca7045a5edaa7e908b
112 c6ad58c44207b6ff8a4fbbca7045a5edaa7e908b
113 d654274993d0149eecc3cc03214f598320211900
113 d654274993d0149eecc3cc03214f598320211900
114 f646af7e96481d3a5470b695cf30ad8e3ab6c575
114 f646af7e96481d3a5470b695cf30ad8e3ab6c575
115 cf8b83f14ead62b374b6e91a0e9303b85dfd9ed7
115 cf8b83f14ead62b374b6e91a0e9303b85dfd9ed7
116 91c6f6e73e39318534dc415ea4e8a09c99cd74d6
116 91c6f6e73e39318534dc415ea4e8a09c99cd74d6
117 6ae9681c6d30389694d8701faf24b583cf3ccafe
117 6ae9681c6d30389694d8701faf24b583cf3ccafe
118 $ $NO_FALLBACK rhg files -r cf8b83
118 $ $NO_FALLBACK rhg files -r cf8b83
119 file-1
119 file-1
120 file-2
120 file-2
121 file-3
121 file-3
122 $ $NO_FALLBACK rhg cat -r cf8b83 file-2
122 $ $NO_FALLBACK rhg cat -r cf8b83 file-2
123 2
123 2
124 $ $NO_FALLBACK rhg cat --rev cf8b83 file-2
124 $ $NO_FALLBACK rhg cat --rev cf8b83 file-2
125 2
125 2
126 $ $NO_FALLBACK rhg cat -r c file-2
126 $ $NO_FALLBACK rhg cat -r c file-2
127 abort: ambiguous revision identifier: c
127 abort: ambiguous revision identifier: c
128 [255]
128 [255]
129 $ $NO_FALLBACK rhg cat -r d file-2
129 $ $NO_FALLBACK rhg cat -r d file-2
130 2
130 2
131 $ $NO_FALLBACK rhg cat -r 0000 file-2
131 $ $NO_FALLBACK rhg cat -r 0000 file-2
132 file-2: no such file in rev 000000000000
132 file-2: no such file in rev 000000000000
133 [1]
133 [1]
134
134
135 Cat files
135 Cat files
136 $ cd $TESTTMP
136 $ cd $TESTTMP
137 $ rm -rf repository
137 $ rm -rf repository
138 $ hg init repository
138 $ hg init repository
139 $ cd repository
139 $ cd repository
140 $ echo "original content" > original
140 $ echo "original content" > original
141 $ hg add original
141 $ hg add original
142 $ hg commit -m "add original" original
142 $ hg commit -m "add original" original
143 Without `--rev`
143 Without `--rev`
144 $ $NO_FALLBACK rhg cat original
144 $ $NO_FALLBACK rhg cat original
145 original content
145 original content
146 With `--rev`
146 With `--rev`
147 $ $NO_FALLBACK rhg cat -r 0 original
147 $ $NO_FALLBACK rhg cat -r 0 original
148 original content
148 original content
149 Cat copied file should not display copy metadata
149 Cat copied file should not display copy metadata
150 $ hg copy original copy_of_original
150 $ hg copy original copy_of_original
151 $ hg commit -m "add copy of original"
151 $ hg commit -m "add copy of original"
152 $ $NO_FALLBACK rhg cat original
152 $ $NO_FALLBACK rhg cat original
153 original content
153 original content
154 $ $NO_FALLBACK rhg cat -r 1 copy_of_original
154 $ $NO_FALLBACK rhg cat -r 1 copy_of_original
155 original content
155 original content
156
156
157
157
158 Fallback to Python
158 Fallback to Python
159 $ $NO_FALLBACK rhg cat original --exclude="*.rs"
159 $ $NO_FALLBACK rhg cat original --exclude="*.rs"
160 unsupported feature: error: Found argument '--exclude' which wasn't expected, or isn't valid in this context
160 unsupported feature: error: Found argument '--exclude' which wasn't expected, or isn't valid in this context
161
161
162 USAGE:
162 USAGE:
163 rhg cat [OPTIONS] <FILE>...
163 rhg cat [OPTIONS] <FILE>...
164
164
165 For more information try --help
165 For more information try --help
166
166
167 [252]
167 [252]
168 $ rhg cat original --exclude="*.rs"
168 $ rhg cat original --exclude="*.rs"
169 original content
169 original content
170
170
171 $ FALLBACK_EXE="$RHG_FALLBACK_EXECUTABLE"
171 $ (unset RHG_FALLBACK_EXECUTABLE; rhg cat original --exclude="*.rs")
172 $ unset RHG_FALLBACK_EXECUTABLE
173 $ rhg cat original --exclude="*.rs"
174 abort: 'rhg.on-unsupported=fallback' without 'rhg.fallback-executable' set.
172 abort: 'rhg.on-unsupported=fallback' without 'rhg.fallback-executable' set.
175 [255]
173 [255]
176 $ RHG_FALLBACK_EXECUTABLE="$FALLBACK_EXE"
174
177 $ export RHG_FALLBACK_EXECUTABLE
175 $ (unset RHG_FALLBACK_EXECUTABLE; rhg cat original)
176 original content
178
177
179 $ rhg cat original --exclude="*.rs" --config rhg.fallback-executable=false
178 $ rhg cat original --exclude="*.rs" --config rhg.fallback-executable=false
180 [1]
179 [1]
181
180
182 $ rhg cat original --exclude="*.rs" --config rhg.fallback-executable=hg-non-existent
181 $ rhg cat original --exclude="*.rs" --config rhg.fallback-executable=hg-non-existent
183 tried to fall back to a 'hg-non-existent' sub-process but got error $ENOENT$
182 tried to fall back to a 'hg-non-existent' sub-process but got error $ENOENT$
184 unsupported feature: error: Found argument '--exclude' which wasn't expected, or isn't valid in this context
183 unsupported feature: error: Found argument '--exclude' which wasn't expected, or isn't valid in this context
185
184
186 USAGE:
185 USAGE:
187 rhg cat [OPTIONS] <FILE>...
186 rhg cat [OPTIONS] <FILE>...
188
187
189 For more information try --help
188 For more information try --help
190
189
191 [252]
190 [252]
192
191
193 $ rhg cat original --exclude="*.rs" --config rhg.fallback-executable=rhg
192 $ rhg cat original --exclude="*.rs" --config rhg.fallback-executable=rhg
194 Blocking recursive fallback. The 'rhg.fallback-executable = rhg' config points to `rhg` itself.
193 Blocking recursive fallback. The 'rhg.fallback-executable = rhg' config points to `rhg` itself.
195 unsupported feature: error: Found argument '--exclude' which wasn't expected, or isn't valid in this context
194 unsupported feature: error: Found argument '--exclude' which wasn't expected, or isn't valid in this context
196
195
197 USAGE:
196 USAGE:
198 rhg cat [OPTIONS] <FILE>...
197 rhg cat [OPTIONS] <FILE>...
199
198
200 For more information try --help
199 For more information try --help
201
200
202 [252]
201 [252]
203
202
204 Fallback with shell path segments
203 Fallback with shell path segments
205 $ $NO_FALLBACK rhg cat .
204 $ $NO_FALLBACK rhg cat .
206 unsupported feature: `..` or `.` path segment
205 unsupported feature: `..` or `.` path segment
207 [252]
206 [252]
208 $ $NO_FALLBACK rhg cat ..
207 $ $NO_FALLBACK rhg cat ..
209 unsupported feature: `..` or `.` path segment
208 unsupported feature: `..` or `.` path segment
210 [252]
209 [252]
211 $ $NO_FALLBACK rhg cat ../..
210 $ $NO_FALLBACK rhg cat ../..
212 unsupported feature: `..` or `.` path segment
211 unsupported feature: `..` or `.` path segment
213 [252]
212 [252]
214
213
215 Fallback with filesets
214 Fallback with filesets
216 $ $NO_FALLBACK rhg cat "set:c or b"
215 $ $NO_FALLBACK rhg cat "set:c or b"
217 unsupported feature: fileset
216 unsupported feature: fileset
218 [252]
217 [252]
219
218
220 Fallback with generic hooks
219 Fallback with generic hooks
221 $ $NO_FALLBACK rhg cat original --config hooks.pre-cat=something
220 $ $NO_FALLBACK rhg cat original --config hooks.pre-cat=something
222 unsupported feature: pre-cat hook defined
221 unsupported feature: pre-cat hook defined
223 [252]
222 [252]
224
223
225 $ $NO_FALLBACK rhg cat original --config hooks.post-cat=something
224 $ $NO_FALLBACK rhg cat original --config hooks.post-cat=something
226 unsupported feature: post-cat hook defined
225 unsupported feature: post-cat hook defined
227 [252]
226 [252]
228
227
229 $ $NO_FALLBACK rhg cat original --config hooks.fail-cat=something
228 $ $NO_FALLBACK rhg cat original --config hooks.fail-cat=something
230 unsupported feature: fail-cat hook defined
229 unsupported feature: fail-cat hook defined
231 [252]
230 [252]
232
231
233 Fallback with [defaults]
232 Fallback with [defaults]
234 $ $NO_FALLBACK rhg cat original --config "defaults.cat=-r null"
233 $ $NO_FALLBACK rhg cat original --config "defaults.cat=-r null"
235 unsupported feature: `defaults` config set
234 unsupported feature: `defaults` config set
236 [252]
235 [252]
237
236
238
237
239 Requirements
238 Requirements
240 $ $NO_FALLBACK rhg debugrequirements
239 $ $NO_FALLBACK rhg debugrequirements
241 dotencode
240 dotencode
242 fncache
241 fncache
243 generaldelta
242 generaldelta
244 persistent-nodemap
243 persistent-nodemap
245 revlog-compression-zstd (zstd !)
244 revlog-compression-zstd (zstd !)
246 revlogv1
245 revlogv1
247 sparserevlog
246 sparserevlog
248 store
247 store
249
248
250 $ echo indoor-pool >> .hg/requires
249 $ echo indoor-pool >> .hg/requires
251 $ $NO_FALLBACK rhg files
250 $ $NO_FALLBACK rhg files
252 unsupported feature: repository requires feature unknown to this Mercurial: indoor-pool
251 unsupported feature: repository requires feature unknown to this Mercurial: indoor-pool
253 [252]
252 [252]
254
253
255 $ $NO_FALLBACK rhg cat -r 1 copy_of_original
254 $ $NO_FALLBACK rhg cat -r 1 copy_of_original
256 unsupported feature: repository requires feature unknown to this Mercurial: indoor-pool
255 unsupported feature: repository requires feature unknown to this Mercurial: indoor-pool
257 [252]
256 [252]
258
257
259 $ $NO_FALLBACK rhg debugrequirements
258 $ $NO_FALLBACK rhg debugrequirements
260 unsupported feature: repository requires feature unknown to this Mercurial: indoor-pool
259 unsupported feature: repository requires feature unknown to this Mercurial: indoor-pool
261 [252]
260 [252]
262
261
263 $ echo -e '\xFF' >> .hg/requires
262 $ echo -e '\xFF' >> .hg/requires
264 $ $NO_FALLBACK rhg debugrequirements
263 $ $NO_FALLBACK rhg debugrequirements
265 abort: parse error in 'requires' file
264 abort: parse error in 'requires' file
266 [255]
265 [255]
267
266
268 Persistent nodemap
267 Persistent nodemap
269 $ cd $TESTTMP
268 $ cd $TESTTMP
270 $ rm -rf repository
269 $ rm -rf repository
271 $ hg --config format.use-persistent-nodemap=no init repository
270 $ hg --config format.use-persistent-nodemap=no init repository
272 $ cd repository
271 $ cd repository
273 $ $NO_FALLBACK rhg debugrequirements | grep nodemap
272 $ $NO_FALLBACK rhg debugrequirements | grep nodemap
274 [1]
273 [1]
275 $ hg debugbuilddag .+5000 --overwritten-file --config "storage.revlog.nodemap.mode=warn"
274 $ hg debugbuilddag .+5000 --overwritten-file --config "storage.revlog.nodemap.mode=warn"
276 $ hg id -r tip
275 $ hg id -r tip
277 c3ae8dec9fad tip
276 c3ae8dec9fad tip
278 $ ls .hg/store/00changelog*
277 $ ls .hg/store/00changelog*
279 .hg/store/00changelog.d
278 .hg/store/00changelog.d
280 .hg/store/00changelog.i
279 .hg/store/00changelog.i
281 $ $NO_FALLBACK rhg files -r c3ae8dec9fad
280 $ $NO_FALLBACK rhg files -r c3ae8dec9fad
282 of
281 of
283
282
284 $ cd $TESTTMP
283 $ cd $TESTTMP
285 $ rm -rf repository
284 $ rm -rf repository
286 $ hg --config format.use-persistent-nodemap=True init repository
285 $ hg --config format.use-persistent-nodemap=True init repository
287 $ cd repository
286 $ cd repository
288 $ $NO_FALLBACK rhg debugrequirements | grep nodemap
287 $ $NO_FALLBACK rhg debugrequirements | grep nodemap
289 persistent-nodemap
288 persistent-nodemap
290 $ hg debugbuilddag .+5000 --overwritten-file --config "storage.revlog.nodemap.mode=warn"
289 $ hg debugbuilddag .+5000 --overwritten-file --config "storage.revlog.nodemap.mode=warn"
291 $ hg id -r tip
290 $ hg id -r tip
292 c3ae8dec9fad tip
291 c3ae8dec9fad tip
293 $ ls .hg/store/00changelog*
292 $ ls .hg/store/00changelog*
294 .hg/store/00changelog-*.nd (glob)
293 .hg/store/00changelog-*.nd (glob)
295 .hg/store/00changelog.d
294 .hg/store/00changelog.d
296 .hg/store/00changelog.i
295 .hg/store/00changelog.i
297 .hg/store/00changelog.n
296 .hg/store/00changelog.n
298
297
299 Specifying revisions by changeset ID
298 Specifying revisions by changeset ID
300 $ $NO_FALLBACK rhg files -r c3ae8dec9fad
299 $ $NO_FALLBACK rhg files -r c3ae8dec9fad
301 of
300 of
302 $ $NO_FALLBACK rhg cat -r c3ae8dec9fad of
301 $ $NO_FALLBACK rhg cat -r c3ae8dec9fad of
303 r5000
302 r5000
304
303
305 Crate a shared repository
304 Crate a shared repository
306
305
307 $ echo "[extensions]" >> $HGRCPATH
306 $ echo "[extensions]" >> $HGRCPATH
308 $ echo "share = " >> $HGRCPATH
307 $ echo "share = " >> $HGRCPATH
309
308
310 $ cd $TESTTMP
309 $ cd $TESTTMP
311 $ hg init repo1
310 $ hg init repo1
312 $ echo a > repo1/a
311 $ echo a > repo1/a
313 $ hg -R repo1 commit -A -m'init'
312 $ hg -R repo1 commit -A -m'init'
314 adding a
313 adding a
315
314
316 $ hg share repo1 repo2
315 $ hg share repo1 repo2
317 updating working directory
316 updating working directory
318 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
317 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
319
318
320 And check that basic rhg commands work with sharing
319 And check that basic rhg commands work with sharing
321
320
322 $ $NO_FALLBACK rhg files -R repo2
321 $ $NO_FALLBACK rhg files -R repo2
323 repo2/a
322 repo2/a
324 $ $NO_FALLBACK rhg -R repo2 cat -r 0 repo2/a
323 $ $NO_FALLBACK rhg -R repo2 cat -r 0 repo2/a
325 a
324 a
326
325
327 Same with relative sharing
326 Same with relative sharing
328
327
329 $ hg share repo2 repo3 --relative
328 $ hg share repo2 repo3 --relative
330 updating working directory
329 updating working directory
331 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
330 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
332
331
333 $ $NO_FALLBACK rhg files -R repo3
332 $ $NO_FALLBACK rhg files -R repo3
334 repo3/a
333 repo3/a
335 $ $NO_FALLBACK rhg -R repo3 cat -r 0 repo3/a
334 $ $NO_FALLBACK rhg -R repo3 cat -r 0 repo3/a
336 a
335 a
337
336
338 Same with share-safe
337 Same with share-safe
339
338
340 $ echo "[format]" >> $HGRCPATH
339 $ echo "[format]" >> $HGRCPATH
341 $ echo "use-share-safe = True" >> $HGRCPATH
340 $ echo "use-share-safe = True" >> $HGRCPATH
342
341
343 $ cd $TESTTMP
342 $ cd $TESTTMP
344 $ hg init repo4
343 $ hg init repo4
345 $ cd repo4
344 $ cd repo4
346 $ echo a > a
345 $ echo a > a
347 $ hg commit -A -m'init'
346 $ hg commit -A -m'init'
348 adding a
347 adding a
349
348
350 $ cd ..
349 $ cd ..
351 $ hg share repo4 repo5
350 $ hg share repo4 repo5
352 updating working directory
351 updating working directory
353 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
352 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
354
353
355 And check that basic rhg commands work with sharing
354 And check that basic rhg commands work with sharing
356
355
357 $ cd repo5
356 $ cd repo5
358 $ $NO_FALLBACK rhg files
357 $ $NO_FALLBACK rhg files
359 a
358 a
360 $ $NO_FALLBACK rhg cat -r 0 a
359 $ $NO_FALLBACK rhg cat -r 0 a
361 a
360 a
362
361
363 The blackbox extension is supported
362 The blackbox extension is supported
364
363
365 $ echo "[extensions]" >> $HGRCPATH
364 $ echo "[extensions]" >> $HGRCPATH
366 $ echo "blackbox =" >> $HGRCPATH
365 $ echo "blackbox =" >> $HGRCPATH
367 $ echo "[blackbox]" >> $HGRCPATH
366 $ echo "[blackbox]" >> $HGRCPATH
368 $ echo "maxsize = 1" >> $HGRCPATH
367 $ echo "maxsize = 1" >> $HGRCPATH
369 $ $NO_FALLBACK rhg files > /dev/null
368 $ $NO_FALLBACK rhg files > /dev/null
370 $ cat .hg/blackbox.log
369 $ cat .hg/blackbox.log
371 ????/??/?? ??:??:??.??? * @d3873e73d99ef67873dac33fbcc66268d5d2b6f4 (*)> (rust) files exited 0 after 0.??? seconds (glob)
370 ????/??/?? ??:??:??.??? * @d3873e73d99ef67873dac33fbcc66268d5d2b6f4 (*)> (rust) files exited 0 after 0.??? seconds (glob)
372 $ cat .hg/blackbox.log.1
371 $ cat .hg/blackbox.log.1
373 ????/??/?? ??:??:??.??? * @d3873e73d99ef67873dac33fbcc66268d5d2b6f4 (*)> (rust) files (glob)
372 ????/??/?? ??:??:??.??? * @d3873e73d99ef67873dac33fbcc66268d5d2b6f4 (*)> (rust) files (glob)
374
373
375 Subrepos are not supported
374 Subrepos are not supported
376
375
377 $ touch .hgsub
376 $ touch .hgsub
378 $ $NO_FALLBACK rhg files
377 $ $NO_FALLBACK rhg files
379 unsupported feature: subrepos (.hgsub is present)
378 unsupported feature: subrepos (.hgsub is present)
380 [252]
379 [252]
381 $ rhg files
380 $ rhg files
382 a
381 a
383 $ rm .hgsub
382 $ rm .hgsub
General Comments 0
You need to be logged in to leave comments. Login now