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