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