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