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