##// END OF EJS Templates
rhg: Remove `rhg.fallback-executable=hg` default configuration...
Simon Sapin -
r47482:bde90e9b default
parent child Browse files
Show More
@@ -1,443 +1,455 b''
1 extern crate log;
1 extern crate log;
2 use crate::ui::Ui;
2 use crate::ui::Ui;
3 use clap::App;
3 use clap::App;
4 use clap::AppSettings;
4 use clap::AppSettings;
5 use clap::Arg;
5 use clap::Arg;
6 use clap::ArgMatches;
6 use clap::ArgMatches;
7 use format_bytes::{format_bytes, join};
7 use format_bytes::{format_bytes, join};
8 use hg::config::Config;
8 use hg::config::Config;
9 use hg::repo::{Repo, RepoError};
9 use hg::repo::{Repo, RepoError};
10 use hg::utils::files::{get_bytes_from_os_str, get_path_from_bytes};
10 use hg::utils::files::{get_bytes_from_os_str, get_path_from_bytes};
11 use hg::utils::SliceExt;
11 use hg::utils::SliceExt;
12 use std::ffi::OsString;
12 use std::ffi::OsString;
13 use std::path::PathBuf;
13 use std::path::PathBuf;
14 use std::process::Command;
14 use std::process::Command;
15
15
16 mod blackbox;
16 mod blackbox;
17 mod error;
17 mod error;
18 mod exitcode;
18 mod exitcode;
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 let run = subcommand_run_fn(subcommand_name)
71 let run = subcommand_run_fn(subcommand_name)
72 .expect("unknown subcommand name from clap despite AppSettings::SubcommandRequired");
72 .expect("unknown subcommand name from clap despite AppSettings::SubcommandRequired");
73 let subcommand_args = subcommand_matches
73 let subcommand_args = subcommand_matches
74 .expect("no subcommand arguments from clap despite AppSettings::SubcommandRequired");
74 .expect("no subcommand arguments from clap despite AppSettings::SubcommandRequired");
75
75
76 let invocation = CliInvocation {
76 let invocation = CliInvocation {
77 ui,
77 ui,
78 subcommand_args,
78 subcommand_args,
79 config,
79 config,
80 repo,
80 repo,
81 };
81 };
82 let blackbox = blackbox::Blackbox::new(&invocation, process_start_time)?;
82 let blackbox = blackbox::Blackbox::new(&invocation, process_start_time)?;
83 blackbox.log_command_start();
83 blackbox.log_command_start();
84 let result = run(&invocation);
84 let result = run(&invocation);
85 blackbox.log_command_end(exit_code(&result));
85 blackbox.log_command_end(exit_code(&result));
86 result
86 result
87 }
87 }
88
88
89 fn main() {
89 fn main() {
90 // Run this first, before we find out if the blackbox extension is even
90 // Run this first, before we find out if the blackbox extension is even
91 // enabled, in order to include everything in-between in the duration
91 // enabled, in order to include everything in-between in the duration
92 // measurements. Reading config files can be slow if they’re on NFS.
92 // measurements. Reading config files can be slow if they’re on NFS.
93 let process_start_time = blackbox::ProcessStartTime::now();
93 let process_start_time = blackbox::ProcessStartTime::now();
94
94
95 env_logger::init();
95 env_logger::init();
96 let ui = ui::Ui::new();
96 let ui = ui::Ui::new();
97
97
98 let early_args = EarlyArgs::parse(std::env::args_os());
98 let early_args = EarlyArgs::parse(std::env::args_os());
99
99
100 let initial_current_dir = early_args.cwd.map(|cwd| {
100 let initial_current_dir = early_args.cwd.map(|cwd| {
101 let cwd = get_path_from_bytes(&cwd);
101 let cwd = get_path_from_bytes(&cwd);
102 std::env::current_dir()
102 std::env::current_dir()
103 .and_then(|initial| {
103 .and_then(|initial| {
104 std::env::set_current_dir(cwd)?;
104 std::env::set_current_dir(cwd)?;
105 Ok(initial)
105 Ok(initial)
106 })
106 })
107 .unwrap_or_else(|error| {
107 .unwrap_or_else(|error| {
108 exit(
108 exit(
109 &None,
109 &None,
110 &ui,
110 &ui,
111 OnUnsupported::Abort,
111 OnUnsupported::Abort,
112 Err(CommandError::abort(format!(
112 Err(CommandError::abort(format!(
113 "abort: {}: '{}'",
113 "abort: {}: '{}'",
114 error,
114 error,
115 cwd.display()
115 cwd.display()
116 ))),
116 ))),
117 )
117 )
118 })
118 })
119 });
119 });
120
120
121 let non_repo_config =
121 let non_repo_config =
122 Config::load(early_args.config).unwrap_or_else(|error| {
122 Config::load(early_args.config).unwrap_or_else(|error| {
123 // Normally this is decided based on config, but we don’t have that
123 // Normally this is decided based on config, but we don’t have that
124 // available. As of this writing config loading never returns an
124 // available. As of this writing config loading never returns an
125 // "unsupported" error but that is not enforced by the type system.
125 // "unsupported" error but that is not enforced by the type system.
126 let on_unsupported = OnUnsupported::Abort;
126 let on_unsupported = OnUnsupported::Abort;
127
127
128 exit(&initial_current_dir, &ui, on_unsupported, Err(error.into()))
128 exit(&initial_current_dir, &ui, on_unsupported, Err(error.into()))
129 });
129 });
130
130
131 if let Some(repo_path_bytes) = &early_args.repo {
131 if let Some(repo_path_bytes) = &early_args.repo {
132 lazy_static::lazy_static! {
132 lazy_static::lazy_static! {
133 static ref SCHEME_RE: regex::bytes::Regex =
133 static ref SCHEME_RE: regex::bytes::Regex =
134 // Same as `_matchscheme` in `mercurial/util.py`
134 // Same as `_matchscheme` in `mercurial/util.py`
135 regex::bytes::Regex::new("^[a-zA-Z0-9+.\\-]+:").unwrap();
135 regex::bytes::Regex::new("^[a-zA-Z0-9+.\\-]+:").unwrap();
136 }
136 }
137 if SCHEME_RE.is_match(&repo_path_bytes) {
137 if SCHEME_RE.is_match(&repo_path_bytes) {
138 exit(
138 exit(
139 &initial_current_dir,
139 &initial_current_dir,
140 &ui,
140 &ui,
141 OnUnsupported::from_config(&non_repo_config),
141 OnUnsupported::from_config(&ui, &non_repo_config),
142 Err(CommandError::UnsupportedFeature {
142 Err(CommandError::UnsupportedFeature {
143 message: format_bytes!(
143 message: format_bytes!(
144 b"URL-like --repository {}",
144 b"URL-like --repository {}",
145 repo_path_bytes
145 repo_path_bytes
146 ),
146 ),
147 }),
147 }),
148 )
148 )
149 }
149 }
150 }
150 }
151 let repo_path = early_args.repo.as_deref().map(get_path_from_bytes);
151 let repo_path = early_args.repo.as_deref().map(get_path_from_bytes);
152 let repo_result = match Repo::find(&non_repo_config, repo_path) {
152 let repo_result = match Repo::find(&non_repo_config, repo_path) {
153 Ok(repo) => Ok(repo),
153 Ok(repo) => Ok(repo),
154 Err(RepoError::NotFound { at }) if repo_path.is_none() => {
154 Err(RepoError::NotFound { at }) if repo_path.is_none() => {
155 // Not finding a repo is not fatal yet, if `-R` was not given
155 // Not finding a repo is not fatal yet, if `-R` was not given
156 Err(NoRepoInCwdError { cwd: at })
156 Err(NoRepoInCwdError { cwd: at })
157 }
157 }
158 Err(error) => exit(
158 Err(error) => exit(
159 &initial_current_dir,
159 &initial_current_dir,
160 &ui,
160 &ui,
161 OnUnsupported::from_config(&non_repo_config),
161 OnUnsupported::from_config(&ui, &non_repo_config),
162 Err(error.into()),
162 Err(error.into()),
163 ),
163 ),
164 };
164 };
165
165
166 let config = if let Ok(repo) = &repo_result {
166 let config = if let Ok(repo) = &repo_result {
167 repo.config()
167 repo.config()
168 } else {
168 } else {
169 &non_repo_config
169 &non_repo_config
170 };
170 };
171 let on_unsupported = OnUnsupported::from_config(&ui, config);
171
172
172 let result = main_with_result(
173 let result = main_with_result(
173 &process_start_time,
174 &process_start_time,
174 &ui,
175 &ui,
175 repo_result.as_ref(),
176 repo_result.as_ref(),
176 config,
177 config,
177 );
178 );
178 exit(
179 exit(&initial_current_dir, &ui, on_unsupported, result)
179 &initial_current_dir,
180 &ui,
181 OnUnsupported::from_config(config),
182 result,
183 )
184 }
180 }
185
181
186 fn exit_code(result: &Result<(), CommandError>) -> i32 {
182 fn exit_code(result: &Result<(), CommandError>) -> i32 {
187 match result {
183 match result {
188 Ok(()) => exitcode::OK,
184 Ok(()) => exitcode::OK,
189 Err(CommandError::Abort { .. }) => exitcode::ABORT,
185 Err(CommandError::Abort { .. }) => exitcode::ABORT,
190 Err(CommandError::Unsuccessful) => exitcode::UNSUCCESSFUL,
186 Err(CommandError::Unsuccessful) => exitcode::UNSUCCESSFUL,
191
187
192 // Exit with a specific code and no error message to let a potential
188 // Exit with a specific code and no error message to let a potential
193 // wrapper script fallback to Python-based Mercurial.
189 // wrapper script fallback to Python-based Mercurial.
194 Err(CommandError::UnsupportedFeature { .. }) => {
190 Err(CommandError::UnsupportedFeature { .. }) => {
195 exitcode::UNIMPLEMENTED
191 exitcode::UNIMPLEMENTED
196 }
192 }
197 }
193 }
198 }
194 }
199
195
200 fn exit(
196 fn exit(
201 initial_current_dir: &Option<PathBuf>,
197 initial_current_dir: &Option<PathBuf>,
202 ui: &Ui,
198 ui: &Ui,
203 mut on_unsupported: OnUnsupported,
199 mut on_unsupported: OnUnsupported,
204 result: Result<(), CommandError>,
200 result: Result<(), CommandError>,
205 ) -> ! {
201 ) -> ! {
206 if let (
202 if let (
207 OnUnsupported::Fallback { executable },
203 OnUnsupported::Fallback { executable },
208 Err(CommandError::UnsupportedFeature { .. }),
204 Err(CommandError::UnsupportedFeature { .. }),
209 ) = (&on_unsupported, &result)
205 ) = (&on_unsupported, &result)
210 {
206 {
211 let mut args = std::env::args_os();
207 let mut args = std::env::args_os();
212 let executable_path = get_path_from_bytes(&executable);
208 let executable_path = get_path_from_bytes(&executable);
213 let this_executable = args.next().expect("exepcted argv[0] to exist");
209 let this_executable = args.next().expect("exepcted argv[0] to exist");
214 if executable_path == &PathBuf::from(this_executable) {
210 if executable_path == &PathBuf::from(this_executable) {
215 // Avoid spawning infinitely many processes until resource
211 // Avoid spawning infinitely many processes until resource
216 // exhaustion.
212 // exhaustion.
217 let _ = ui.write_stderr(&format_bytes!(
213 let _ = ui.write_stderr(&format_bytes!(
218 b"Blocking recursive fallback. The 'rhg.fallback-executable = {}' config \
214 b"Blocking recursive fallback. The 'rhg.fallback-executable = {}' config \
219 points to `rhg` itself.\n",
215 points to `rhg` itself.\n",
220 executable
216 executable
221 ));
217 ));
222 on_unsupported = OnUnsupported::Abort
218 on_unsupported = OnUnsupported::Abort
223 } else {
219 } else {
224 // `args` is now `argv[1..]` since we’ve already consumed `argv[0]`
220 // `args` is now `argv[1..]` since we’ve already consumed `argv[0]`
225 let mut command = Command::new(executable_path);
221 let mut command = Command::new(executable_path);
226 command.args(args);
222 command.args(args);
227 if let Some(initial) = initial_current_dir {
223 if let Some(initial) = initial_current_dir {
228 command.current_dir(initial);
224 command.current_dir(initial);
229 }
225 }
230 let result = command.status();
226 let result = command.status();
231 match result {
227 match result {
232 Ok(status) => std::process::exit(
228 Ok(status) => std::process::exit(
233 status.code().unwrap_or(exitcode::ABORT),
229 status.code().unwrap_or(exitcode::ABORT),
234 ),
230 ),
235 Err(error) => {
231 Err(error) => {
236 let _ = ui.write_stderr(&format_bytes!(
232 let _ = ui.write_stderr(&format_bytes!(
237 b"tried to fall back to a '{}' sub-process but got error {}\n",
233 b"tried to fall back to a '{}' sub-process but got error {}\n",
238 executable, format_bytes::Utf8(error)
234 executable, format_bytes::Utf8(error)
239 ));
235 ));
240 on_unsupported = OnUnsupported::Abort
236 on_unsupported = OnUnsupported::Abort
241 }
237 }
242 }
238 }
243 }
239 }
244 }
240 }
241 exit_no_fallback(ui, on_unsupported, result)
242 }
243
244 fn exit_no_fallback(
245 ui: &Ui,
246 on_unsupported: OnUnsupported,
247 result: Result<(), CommandError>,
248 ) -> ! {
245 match &result {
249 match &result {
246 Ok(_) => {}
250 Ok(_) => {}
247 Err(CommandError::Unsuccessful) => {}
251 Err(CommandError::Unsuccessful) => {}
248 Err(CommandError::Abort { message }) => {
252 Err(CommandError::Abort { message }) => {
249 if !message.is_empty() {
253 if !message.is_empty() {
250 // Ignore errors when writing to stderr, we’re already exiting
254 // Ignore errors when writing to stderr, we’re already exiting
251 // with failure code so there’s not much more we can do.
255 // with failure code so there’s not much more we can do.
252 let _ = ui.write_stderr(&format_bytes!(b"{}\n", message));
256 let _ = ui.write_stderr(&format_bytes!(b"{}\n", message));
253 }
257 }
254 }
258 }
255 Err(CommandError::UnsupportedFeature { message }) => {
259 Err(CommandError::UnsupportedFeature { message }) => {
256 match on_unsupported {
260 match on_unsupported {
257 OnUnsupported::Abort => {
261 OnUnsupported::Abort => {
258 let _ = ui.write_stderr(&format_bytes!(
262 let _ = ui.write_stderr(&format_bytes!(
259 b"unsupported feature: {}\n",
263 b"unsupported feature: {}\n",
260 message
264 message
261 ));
265 ));
262 }
266 }
263 OnUnsupported::AbortSilent => {}
267 OnUnsupported::AbortSilent => {}
264 OnUnsupported::Fallback { .. } => unreachable!(),
268 OnUnsupported::Fallback { .. } => unreachable!(),
265 }
269 }
266 }
270 }
267 }
271 }
268 std::process::exit(exit_code(&result))
272 std::process::exit(exit_code(&result))
269 }
273 }
270
274
271 macro_rules! subcommands {
275 macro_rules! subcommands {
272 ($( $command: ident )+) => {
276 ($( $command: ident )+) => {
273 mod commands {
277 mod commands {
274 $(
278 $(
275 pub mod $command;
279 pub mod $command;
276 )+
280 )+
277 }
281 }
278
282
279 fn add_subcommand_args<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b> {
283 fn add_subcommand_args<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b> {
280 app
284 app
281 $(
285 $(
282 .subcommand(commands::$command::args())
286 .subcommand(commands::$command::args())
283 )+
287 )+
284 }
288 }
285
289
286 pub type RunFn = fn(&CliInvocation) -> Result<(), CommandError>;
290 pub type RunFn = fn(&CliInvocation) -> Result<(), CommandError>;
287
291
288 fn subcommand_run_fn(name: &str) -> Option<RunFn> {
292 fn subcommand_run_fn(name: &str) -> Option<RunFn> {
289 match name {
293 match name {
290 $(
294 $(
291 stringify!($command) => Some(commands::$command::run),
295 stringify!($command) => Some(commands::$command::run),
292 )+
296 )+
293 _ => None,
297 _ => None,
294 }
298 }
295 }
299 }
296 };
300 };
297 }
301 }
298
302
299 subcommands! {
303 subcommands! {
300 cat
304 cat
301 debugdata
305 debugdata
302 debugrequirements
306 debugrequirements
303 files
307 files
304 root
308 root
305 config
309 config
306 }
310 }
307 pub struct CliInvocation<'a> {
311 pub struct CliInvocation<'a> {
308 ui: &'a Ui,
312 ui: &'a Ui,
309 subcommand_args: &'a ArgMatches<'a>,
313 subcommand_args: &'a ArgMatches<'a>,
310 config: &'a Config,
314 config: &'a Config,
311 /// References inside `Result` is a bit peculiar but allow
315 /// References inside `Result` is a bit peculiar but allow
312 /// `invocation.repo?` to work out with `&CliInvocation` since this
316 /// `invocation.repo?` to work out with `&CliInvocation` since this
313 /// `Result` type is `Copy`.
317 /// `Result` type is `Copy`.
314 repo: Result<&'a Repo, &'a NoRepoInCwdError>,
318 repo: Result<&'a Repo, &'a NoRepoInCwdError>,
315 }
319 }
316
320
317 struct NoRepoInCwdError {
321 struct NoRepoInCwdError {
318 cwd: PathBuf,
322 cwd: PathBuf,
319 }
323 }
320
324
321 /// CLI arguments to be parsed "early" in order to be able to read
325 /// CLI arguments to be parsed "early" in order to be able to read
322 /// configuration before using Clap. Ideally we would also use Clap for this,
326 /// configuration before using Clap. Ideally we would also use Clap for this,
323 /// see <https://github.com/clap-rs/clap/discussions/2366>.
327 /// see <https://github.com/clap-rs/clap/discussions/2366>.
324 ///
328 ///
325 /// These arguments are still declared when we do use Clap later, so that Clap
329 /// These arguments are still declared when we do use Clap later, so that Clap
326 /// does not return an error for their presence.
330 /// does not return an error for their presence.
327 struct EarlyArgs {
331 struct EarlyArgs {
328 /// Values of all `--config` arguments. (Possibly none)
332 /// Values of all `--config` arguments. (Possibly none)
329 config: Vec<Vec<u8>>,
333 config: Vec<Vec<u8>>,
330 /// Value of the `-R` or `--repository` argument, if any.
334 /// Value of the `-R` or `--repository` argument, if any.
331 repo: Option<Vec<u8>>,
335 repo: Option<Vec<u8>>,
332 /// Value of the `--cwd` argument, if any.
336 /// Value of the `--cwd` argument, if any.
333 cwd: Option<Vec<u8>>,
337 cwd: Option<Vec<u8>>,
334 }
338 }
335
339
336 impl EarlyArgs {
340 impl EarlyArgs {
337 fn parse(args: impl IntoIterator<Item = OsString>) -> Self {
341 fn parse(args: impl IntoIterator<Item = OsString>) -> Self {
338 let mut args = args.into_iter().map(get_bytes_from_os_str);
342 let mut args = args.into_iter().map(get_bytes_from_os_str);
339 let mut config = Vec::new();
343 let mut config = Vec::new();
340 let mut repo = None;
344 let mut repo = None;
341 let mut cwd = None;
345 let mut cwd = None;
342 // Use `while let` instead of `for` so that we can also call
346 // Use `while let` instead of `for` so that we can also call
343 // `args.next()` inside the loop.
347 // `args.next()` inside the loop.
344 while let Some(arg) = args.next() {
348 while let Some(arg) = args.next() {
345 if arg == b"--config" {
349 if arg == b"--config" {
346 if let Some(value) = args.next() {
350 if let Some(value) = args.next() {
347 config.push(value)
351 config.push(value)
348 }
352 }
349 } else if let Some(value) = arg.drop_prefix(b"--config=") {
353 } else if let Some(value) = arg.drop_prefix(b"--config=") {
350 config.push(value.to_owned())
354 config.push(value.to_owned())
351 }
355 }
352
356
353 if arg == b"--cwd" {
357 if arg == b"--cwd" {
354 if let Some(value) = args.next() {
358 if let Some(value) = args.next() {
355 cwd = Some(value)
359 cwd = Some(value)
356 }
360 }
357 } else if let Some(value) = arg.drop_prefix(b"--cwd=") {
361 } else if let Some(value) = arg.drop_prefix(b"--cwd=") {
358 cwd = Some(value.to_owned())
362 cwd = Some(value.to_owned())
359 }
363 }
360
364
361 if arg == b"--repository" || arg == b"-R" {
365 if arg == b"--repository" || arg == b"-R" {
362 if let Some(value) = args.next() {
366 if let Some(value) = args.next() {
363 repo = Some(value)
367 repo = Some(value)
364 }
368 }
365 } else if let Some(value) = arg.drop_prefix(b"--repository=") {
369 } else if let Some(value) = arg.drop_prefix(b"--repository=") {
366 repo = Some(value.to_owned())
370 repo = Some(value.to_owned())
367 } else if let Some(value) = arg.drop_prefix(b"-R") {
371 } else if let Some(value) = arg.drop_prefix(b"-R") {
368 repo = Some(value.to_owned())
372 repo = Some(value.to_owned())
369 }
373 }
370 }
374 }
371 Self { config, repo, cwd }
375 Self { config, repo, cwd }
372 }
376 }
373 }
377 }
374
378
375 /// What to do when encountering some unsupported feature.
379 /// What to do when encountering some unsupported feature.
376 ///
380 ///
377 /// See `HgError::UnsupportedFeature` and `CommandError::UnsupportedFeature`.
381 /// See `HgError::UnsupportedFeature` and `CommandError::UnsupportedFeature`.
378 enum OnUnsupported {
382 enum OnUnsupported {
379 /// Print an error message describing what feature is not supported,
383 /// Print an error message describing what feature is not supported,
380 /// and exit with code 252.
384 /// and exit with code 252.
381 Abort,
385 Abort,
382 /// Silently exit with code 252.
386 /// Silently exit with code 252.
383 AbortSilent,
387 AbortSilent,
384 /// Try running a Python implementation
388 /// Try running a Python implementation
385 Fallback { executable: Vec<u8> },
389 Fallback { executable: Vec<u8> },
386 }
390 }
387
391
388 impl OnUnsupported {
392 impl OnUnsupported {
389 const DEFAULT: Self = OnUnsupported::Abort;
393 const DEFAULT: Self = OnUnsupported::Abort;
390 const DEFAULT_FALLBACK_EXECUTABLE: &'static [u8] = b"hg";
391
394
392 fn from_config(config: &Config) -> Self {
395 fn from_config(ui: &Ui, config: &Config) -> Self {
393 match config
396 match config
394 .get(b"rhg", b"on-unsupported")
397 .get(b"rhg", b"on-unsupported")
395 .map(|value| value.to_ascii_lowercase())
398 .map(|value| value.to_ascii_lowercase())
396 .as_deref()
399 .as_deref()
397 {
400 {
398 Some(b"abort") => OnUnsupported::Abort,
401 Some(b"abort") => OnUnsupported::Abort,
399 Some(b"abort-silent") => OnUnsupported::AbortSilent,
402 Some(b"abort-silent") => OnUnsupported::AbortSilent,
400 Some(b"fallback") => OnUnsupported::Fallback {
403 Some(b"fallback") => OnUnsupported::Fallback {
401 executable: config
404 executable: config
402 .get(b"rhg", b"fallback-executable")
405 .get(b"rhg", b"fallback-executable")
403 .unwrap_or(Self::DEFAULT_FALLBACK_EXECUTABLE)
406 .unwrap_or_else(|| {
407 exit_no_fallback(
408 ui,
409 Self::Abort,
410 Err(CommandError::abort(
411 "abort: 'rhg.on-unsupported=fallback' without \
412 'rhg.fallback-executable' set."
413 )),
414 )
415 })
404 .to_owned(),
416 .to_owned(),
405 },
417 },
406 None => Self::DEFAULT,
418 None => Self::DEFAULT,
407 Some(_) => {
419 Some(_) => {
408 // TODO: warn about unknown config value
420 // TODO: warn about unknown config value
409 Self::DEFAULT
421 Self::DEFAULT
410 }
422 }
411 }
423 }
412 }
424 }
413 }
425 }
414
426
415 const SUPPORTED_EXTENSIONS: &[&[u8]] = &[b"blackbox", b"share"];
427 const SUPPORTED_EXTENSIONS: &[&[u8]] = &[b"blackbox", b"share"];
416
428
417 fn check_extensions(config: &Config) -> Result<(), CommandError> {
429 fn check_extensions(config: &Config) -> Result<(), CommandError> {
418 let enabled = config.get_section_keys(b"extensions");
430 let enabled = config.get_section_keys(b"extensions");
419
431
420 let mut unsupported = enabled;
432 let mut unsupported = enabled;
421 for supported in SUPPORTED_EXTENSIONS {
433 for supported in SUPPORTED_EXTENSIONS {
422 unsupported.remove(supported);
434 unsupported.remove(supported);
423 }
435 }
424
436
425 if let Some(ignored_list) =
437 if let Some(ignored_list) =
426 config.get_simple_list(b"rhg", b"ignored-extensions")
438 config.get_simple_list(b"rhg", b"ignored-extensions")
427 {
439 {
428 for ignored in ignored_list {
440 for ignored in ignored_list {
429 unsupported.remove(ignored);
441 unsupported.remove(ignored);
430 }
442 }
431 }
443 }
432
444
433 if unsupported.is_empty() {
445 if unsupported.is_empty() {
434 Ok(())
446 Ok(())
435 } else {
447 } else {
436 Err(CommandError::UnsupportedFeature {
448 Err(CommandError::UnsupportedFeature {
437 message: format_bytes!(
449 message: format_bytes!(
438 b"extensions: {} (consider adding them to 'rhg.ignored-extensions' config)",
450 b"extensions: {} (consider adding them to 'rhg.ignored-extensions' config)",
439 join(unsupported, b", ")
451 join(unsupported, b", ")
440 ),
452 ),
441 })
453 })
442 }
454 }
443 }
455 }
@@ -1,299 +1,307 b''
1 #require rhg
1 #require rhg
2
2
3 $ NO_FALLBACK="env RHG_ON_UNSUPPORTED=abort"
3 $ NO_FALLBACK="env RHG_ON_UNSUPPORTED=abort"
4
4
5 Unimplemented command
5 Unimplemented command
6 $ $NO_FALLBACK rhg unimplemented-command
6 $ $NO_FALLBACK rhg unimplemented-command
7 unsupported feature: error: Found argument 'unimplemented-command' which wasn't expected, or isn't valid in this context
7 unsupported feature: error: Found argument 'unimplemented-command' which wasn't expected, or isn't valid in this context
8
8
9 USAGE:
9 USAGE:
10 rhg [OPTIONS] <SUBCOMMAND>
10 rhg [OPTIONS] <SUBCOMMAND>
11
11
12 For more information try --help
12 For more information try --help
13
13
14 [252]
14 [252]
15 $ rhg unimplemented-command --config rhg.on-unsupported=abort-silent
15 $ rhg unimplemented-command --config rhg.on-unsupported=abort-silent
16 [252]
16 [252]
17
17
18 Finding root
18 Finding root
19 $ $NO_FALLBACK rhg root
19 $ $NO_FALLBACK rhg root
20 abort: no repository found in '$TESTTMP' (.hg not found)!
20 abort: no repository found in '$TESTTMP' (.hg not found)!
21 [255]
21 [255]
22
22
23 $ hg init repository
23 $ hg init repository
24 $ cd repository
24 $ cd repository
25 $ $NO_FALLBACK rhg root
25 $ $NO_FALLBACK rhg root
26 $TESTTMP/repository
26 $TESTTMP/repository
27
27
28 Reading and setting configuration
28 Reading and setting configuration
29 $ echo "[ui]" >> $HGRCPATH
29 $ echo "[ui]" >> $HGRCPATH
30 $ echo "username = user1" >> $HGRCPATH
30 $ echo "username = user1" >> $HGRCPATH
31 $ $NO_FALLBACK rhg config ui.username
31 $ $NO_FALLBACK rhg config ui.username
32 user1
32 user1
33 $ echo "[ui]" >> .hg/hgrc
33 $ echo "[ui]" >> .hg/hgrc
34 $ echo "username = user2" >> .hg/hgrc
34 $ echo "username = user2" >> .hg/hgrc
35 $ $NO_FALLBACK rhg config ui.username
35 $ $NO_FALLBACK rhg config ui.username
36 user2
36 user2
37 $ $NO_FALLBACK rhg --config ui.username=user3 config ui.username
37 $ $NO_FALLBACK rhg --config ui.username=user3 config ui.username
38 user3
38 user3
39
39
40 Unwritable file descriptor
40 Unwritable file descriptor
41 $ $NO_FALLBACK rhg root > /dev/full
41 $ $NO_FALLBACK rhg root > /dev/full
42 abort: No space left on device (os error 28)
42 abort: No space left on device (os error 28)
43 [255]
43 [255]
44
44
45 Deleted repository
45 Deleted repository
46 $ rm -rf `pwd`
46 $ rm -rf `pwd`
47 $ $NO_FALLBACK rhg root
47 $ $NO_FALLBACK rhg root
48 abort: error getting current working directory: $ENOENT$
48 abort: error getting current working directory: $ENOENT$
49 [255]
49 [255]
50
50
51 Listing tracked files
51 Listing tracked files
52 $ cd $TESTTMP
52 $ cd $TESTTMP
53 $ hg init repository
53 $ hg init repository
54 $ cd repository
54 $ cd repository
55 $ for i in 1 2 3; do
55 $ for i in 1 2 3; do
56 > echo $i >> file$i
56 > echo $i >> file$i
57 > hg add file$i
57 > hg add file$i
58 > done
58 > done
59 > hg commit -m "commit $i" -q
59 > hg commit -m "commit $i" -q
60
60
61 Listing tracked files from root
61 Listing tracked files from root
62 $ $NO_FALLBACK rhg files
62 $ $NO_FALLBACK rhg files
63 file1
63 file1
64 file2
64 file2
65 file3
65 file3
66
66
67 Listing tracked files from subdirectory
67 Listing tracked files from subdirectory
68 $ mkdir -p path/to/directory
68 $ mkdir -p path/to/directory
69 $ cd path/to/directory
69 $ cd path/to/directory
70 $ $NO_FALLBACK rhg files
70 $ $NO_FALLBACK rhg files
71 ../../../file1
71 ../../../file1
72 ../../../file2
72 ../../../file2
73 ../../../file3
73 ../../../file3
74
74
75 Listing tracked files through broken pipe
75 Listing tracked files through broken pipe
76 $ $NO_FALLBACK rhg files | head -n 1
76 $ $NO_FALLBACK rhg files | head -n 1
77 ../../../file1
77 ../../../file1
78
78
79 Debuging data in inline index
79 Debuging data in inline index
80 $ cd $TESTTMP
80 $ cd $TESTTMP
81 $ rm -rf repository
81 $ rm -rf repository
82 $ hg init repository
82 $ hg init repository
83 $ cd repository
83 $ cd repository
84 $ for i in 1 2 3 4 5 6; do
84 $ for i in 1 2 3 4 5 6; do
85 > echo $i >> file-$i
85 > echo $i >> file-$i
86 > hg add file-$i
86 > hg add file-$i
87 > hg commit -m "Commit $i" -q
87 > hg commit -m "Commit $i" -q
88 > done
88 > done
89 $ $NO_FALLBACK rhg debugdata -c 2
89 $ $NO_FALLBACK rhg debugdata -c 2
90 8d0267cb034247ebfa5ee58ce59e22e57a492297
90 8d0267cb034247ebfa5ee58ce59e22e57a492297
91 test
91 test
92 0 0
92 0 0
93 file-3
93 file-3
94
94
95 Commit 3 (no-eol)
95 Commit 3 (no-eol)
96 $ $NO_FALLBACK rhg debugdata -m 2
96 $ $NO_FALLBACK rhg debugdata -m 2
97 file-1\x00b8e02f6433738021a065f94175c7cd23db5f05be (esc)
97 file-1\x00b8e02f6433738021a065f94175c7cd23db5f05be (esc)
98 file-2\x005d9299349fc01ddd25d0070d149b124d8f10411e (esc)
98 file-2\x005d9299349fc01ddd25d0070d149b124d8f10411e (esc)
99 file-3\x002661d26c649684b482d10f91960cc3db683c38b4 (esc)
99 file-3\x002661d26c649684b482d10f91960cc3db683c38b4 (esc)
100
100
101 Debuging with full node id
101 Debuging with full node id
102 $ $NO_FALLBACK rhg debugdata -c `hg log -r 0 -T '{node}'`
102 $ $NO_FALLBACK rhg debugdata -c `hg log -r 0 -T '{node}'`
103 d1d1c679d3053e8926061b6f45ca52009f011e3f
103 d1d1c679d3053e8926061b6f45ca52009f011e3f
104 test
104 test
105 0 0
105 0 0
106 file-1
106 file-1
107
107
108 Commit 1 (no-eol)
108 Commit 1 (no-eol)
109
109
110 Specifying revisions by changeset ID
110 Specifying revisions by changeset ID
111 $ hg log -T '{node}\n'
111 $ hg log -T '{node}\n'
112 c6ad58c44207b6ff8a4fbbca7045a5edaa7e908b
112 c6ad58c44207b6ff8a4fbbca7045a5edaa7e908b
113 d654274993d0149eecc3cc03214f598320211900
113 d654274993d0149eecc3cc03214f598320211900
114 f646af7e96481d3a5470b695cf30ad8e3ab6c575
114 f646af7e96481d3a5470b695cf30ad8e3ab6c575
115 cf8b83f14ead62b374b6e91a0e9303b85dfd9ed7
115 cf8b83f14ead62b374b6e91a0e9303b85dfd9ed7
116 91c6f6e73e39318534dc415ea4e8a09c99cd74d6
116 91c6f6e73e39318534dc415ea4e8a09c99cd74d6
117 6ae9681c6d30389694d8701faf24b583cf3ccafe
117 6ae9681c6d30389694d8701faf24b583cf3ccafe
118 $ $NO_FALLBACK rhg files -r cf8b83
118 $ $NO_FALLBACK rhg files -r cf8b83
119 file-1
119 file-1
120 file-2
120 file-2
121 file-3
121 file-3
122 $ $NO_FALLBACK rhg cat -r cf8b83 file-2
122 $ $NO_FALLBACK rhg cat -r cf8b83 file-2
123 2
123 2
124 $ $NO_FALLBACK rhg cat -r c file-2
124 $ $NO_FALLBACK rhg cat -r c file-2
125 abort: ambiguous revision identifier: c
125 abort: ambiguous revision identifier: c
126 [255]
126 [255]
127 $ $NO_FALLBACK rhg cat -r d file-2
127 $ $NO_FALLBACK rhg cat -r d file-2
128 2
128 2
129
129
130 Cat files
130 Cat files
131 $ cd $TESTTMP
131 $ cd $TESTTMP
132 $ rm -rf repository
132 $ rm -rf repository
133 $ hg init repository
133 $ hg init repository
134 $ cd repository
134 $ cd repository
135 $ echo "original content" > original
135 $ echo "original content" > original
136 $ hg add original
136 $ hg add original
137 $ hg commit -m "add original" original
137 $ hg commit -m "add original" original
138 $ $NO_FALLBACK rhg cat -r 0 original
138 $ $NO_FALLBACK rhg cat -r 0 original
139 original content
139 original content
140 Cat copied file should not display copy metadata
140 Cat copied file should not display copy metadata
141 $ hg copy original copy_of_original
141 $ hg copy original copy_of_original
142 $ hg commit -m "add copy of original"
142 $ hg commit -m "add copy of original"
143 $ $NO_FALLBACK rhg cat -r 1 copy_of_original
143 $ $NO_FALLBACK rhg cat -r 1 copy_of_original
144 original content
144 original content
145
145
146 Fallback to Python
146 Fallback to Python
147 $ $NO_FALLBACK rhg cat original
147 $ $NO_FALLBACK rhg cat original
148 unsupported feature: `rhg cat` without `--rev` / `-r`
148 unsupported feature: `rhg cat` without `--rev` / `-r`
149 [252]
149 [252]
150 $ rhg cat original
150 $ rhg cat original
151 original content
151 original content
152
152
153 $ FALLBACK_EXE="$RHG_FALLBACK_EXECUTABLE"
154 $ unset RHG_FALLBACK_EXECUTABLE
155 $ rhg cat original
156 abort: 'rhg.on-unsupported=fallback' without 'rhg.fallback-executable' set.
157 [255]
158 $ RHG_FALLBACK_EXECUTABLE="$FALLBACK_EXE"
159 $ export RHG_FALLBACK_EXECUTABLE
160
153 $ rhg cat original --config rhg.fallback-executable=false
161 $ rhg cat original --config rhg.fallback-executable=false
154 [1]
162 [1]
155
163
156 $ rhg cat original --config rhg.fallback-executable=hg-non-existent
164 $ rhg cat original --config rhg.fallback-executable=hg-non-existent
157 tried to fall back to a 'hg-non-existent' sub-process but got error $ENOENT$
165 tried to fall back to a 'hg-non-existent' sub-process but got error $ENOENT$
158 unsupported feature: `rhg cat` without `--rev` / `-r`
166 unsupported feature: `rhg cat` without `--rev` / `-r`
159 [252]
167 [252]
160
168
161 $ rhg cat original --config rhg.fallback-executable=rhg
169 $ rhg cat original --config rhg.fallback-executable=rhg
162 Blocking recursive fallback. The 'rhg.fallback-executable = rhg' config points to `rhg` itself.
170 Blocking recursive fallback. The 'rhg.fallback-executable = rhg' config points to `rhg` itself.
163 unsupported feature: `rhg cat` without `--rev` / `-r`
171 unsupported feature: `rhg cat` without `--rev` / `-r`
164 [252]
172 [252]
165
173
166 Requirements
174 Requirements
167 $ $NO_FALLBACK rhg debugrequirements
175 $ $NO_FALLBACK rhg debugrequirements
168 dotencode
176 dotencode
169 fncache
177 fncache
170 generaldelta
178 generaldelta
171 revlogv1
179 revlogv1
172 sparserevlog
180 sparserevlog
173 store
181 store
174
182
175 $ echo indoor-pool >> .hg/requires
183 $ echo indoor-pool >> .hg/requires
176 $ $NO_FALLBACK rhg files
184 $ $NO_FALLBACK rhg files
177 unsupported feature: repository requires feature unknown to this Mercurial: indoor-pool
185 unsupported feature: repository requires feature unknown to this Mercurial: indoor-pool
178 [252]
186 [252]
179
187
180 $ $NO_FALLBACK rhg cat -r 1 copy_of_original
188 $ $NO_FALLBACK rhg cat -r 1 copy_of_original
181 unsupported feature: repository requires feature unknown to this Mercurial: indoor-pool
189 unsupported feature: repository requires feature unknown to this Mercurial: indoor-pool
182 [252]
190 [252]
183
191
184 $ $NO_FALLBACK rhg debugrequirements
192 $ $NO_FALLBACK rhg debugrequirements
185 unsupported feature: repository requires feature unknown to this Mercurial: indoor-pool
193 unsupported feature: repository requires feature unknown to this Mercurial: indoor-pool
186 [252]
194 [252]
187
195
188 $ echo -e '\xFF' >> .hg/requires
196 $ echo -e '\xFF' >> .hg/requires
189 $ $NO_FALLBACK rhg debugrequirements
197 $ $NO_FALLBACK rhg debugrequirements
190 abort: parse error in 'requires' file
198 abort: parse error in 'requires' file
191 [255]
199 [255]
192
200
193 Persistent nodemap
201 Persistent nodemap
194 $ cd $TESTTMP
202 $ cd $TESTTMP
195 $ rm -rf repository
203 $ rm -rf repository
196 $ hg init repository
204 $ hg init repository
197 $ cd repository
205 $ cd repository
198 $ $NO_FALLBACK rhg debugrequirements | grep nodemap
206 $ $NO_FALLBACK rhg debugrequirements | grep nodemap
199 [1]
207 [1]
200 $ hg debugbuilddag .+5000 --overwritten-file --config "storage.revlog.nodemap.mode=warn"
208 $ hg debugbuilddag .+5000 --overwritten-file --config "storage.revlog.nodemap.mode=warn"
201 $ hg id -r tip
209 $ hg id -r tip
202 c3ae8dec9fad tip
210 c3ae8dec9fad tip
203 $ ls .hg/store/00changelog*
211 $ ls .hg/store/00changelog*
204 .hg/store/00changelog.d
212 .hg/store/00changelog.d
205 .hg/store/00changelog.i
213 .hg/store/00changelog.i
206 $ $NO_FALLBACK rhg files -r c3ae8dec9fad
214 $ $NO_FALLBACK rhg files -r c3ae8dec9fad
207 of
215 of
208
216
209 $ cd $TESTTMP
217 $ cd $TESTTMP
210 $ rm -rf repository
218 $ rm -rf repository
211 $ hg --config format.use-persistent-nodemap=True init repository
219 $ hg --config format.use-persistent-nodemap=True init repository
212 $ cd repository
220 $ cd repository
213 $ $NO_FALLBACK rhg debugrequirements | grep nodemap
221 $ $NO_FALLBACK rhg debugrequirements | grep nodemap
214 persistent-nodemap
222 persistent-nodemap
215 $ hg debugbuilddag .+5000 --overwritten-file --config "storage.revlog.nodemap.mode=warn"
223 $ hg debugbuilddag .+5000 --overwritten-file --config "storage.revlog.nodemap.mode=warn"
216 $ hg id -r tip
224 $ hg id -r tip
217 c3ae8dec9fad tip
225 c3ae8dec9fad tip
218 $ ls .hg/store/00changelog*
226 $ ls .hg/store/00changelog*
219 .hg/store/00changelog-*.nd (glob)
227 .hg/store/00changelog-*.nd (glob)
220 .hg/store/00changelog.d
228 .hg/store/00changelog.d
221 .hg/store/00changelog.i
229 .hg/store/00changelog.i
222 .hg/store/00changelog.n
230 .hg/store/00changelog.n
223
231
224 Specifying revisions by changeset ID
232 Specifying revisions by changeset ID
225 $ $NO_FALLBACK rhg files -r c3ae8dec9fad
233 $ $NO_FALLBACK rhg files -r c3ae8dec9fad
226 of
234 of
227 $ $NO_FALLBACK rhg cat -r c3ae8dec9fad of
235 $ $NO_FALLBACK rhg cat -r c3ae8dec9fad of
228 r5000
236 r5000
229
237
230 Crate a shared repository
238 Crate a shared repository
231
239
232 $ echo "[extensions]" >> $HGRCPATH
240 $ echo "[extensions]" >> $HGRCPATH
233 $ echo "share = " >> $HGRCPATH
241 $ echo "share = " >> $HGRCPATH
234
242
235 $ cd $TESTTMP
243 $ cd $TESTTMP
236 $ hg init repo1
244 $ hg init repo1
237 $ echo a > repo1/a
245 $ echo a > repo1/a
238 $ hg -R repo1 commit -A -m'init'
246 $ hg -R repo1 commit -A -m'init'
239 adding a
247 adding a
240
248
241 $ hg share repo1 repo2
249 $ hg share repo1 repo2
242 updating working directory
250 updating working directory
243 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
251 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
244
252
245 And check that basic rhg commands work with sharing
253 And check that basic rhg commands work with sharing
246
254
247 $ $NO_FALLBACK rhg files -R repo2
255 $ $NO_FALLBACK rhg files -R repo2
248 repo2/a
256 repo2/a
249 $ $NO_FALLBACK rhg -R repo2 cat -r 0 repo2/a
257 $ $NO_FALLBACK rhg -R repo2 cat -r 0 repo2/a
250 a
258 a
251
259
252 Same with relative sharing
260 Same with relative sharing
253
261
254 $ hg share repo2 repo3 --relative
262 $ hg share repo2 repo3 --relative
255 updating working directory
263 updating working directory
256 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
264 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
257
265
258 $ $NO_FALLBACK rhg files -R repo3
266 $ $NO_FALLBACK rhg files -R repo3
259 repo3/a
267 repo3/a
260 $ $NO_FALLBACK rhg -R repo3 cat -r 0 repo3/a
268 $ $NO_FALLBACK rhg -R repo3 cat -r 0 repo3/a
261 a
269 a
262
270
263 Same with share-safe
271 Same with share-safe
264
272
265 $ echo "[format]" >> $HGRCPATH
273 $ echo "[format]" >> $HGRCPATH
266 $ echo "use-share-safe = True" >> $HGRCPATH
274 $ echo "use-share-safe = True" >> $HGRCPATH
267
275
268 $ cd $TESTTMP
276 $ cd $TESTTMP
269 $ hg init repo4
277 $ hg init repo4
270 $ cd repo4
278 $ cd repo4
271 $ echo a > a
279 $ echo a > a
272 $ hg commit -A -m'init'
280 $ hg commit -A -m'init'
273 adding a
281 adding a
274
282
275 $ cd ..
283 $ cd ..
276 $ hg share repo4 repo5
284 $ hg share repo4 repo5
277 updating working directory
285 updating working directory
278 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
286 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
279
287
280 And check that basic rhg commands work with sharing
288 And check that basic rhg commands work with sharing
281
289
282 $ cd repo5
290 $ cd repo5
283 $ $NO_FALLBACK rhg files
291 $ $NO_FALLBACK rhg files
284 a
292 a
285 $ $NO_FALLBACK rhg cat -r 0 a
293 $ $NO_FALLBACK rhg cat -r 0 a
286 a
294 a
287
295
288 The blackbox extension is supported
296 The blackbox extension is supported
289
297
290 $ echo "[extensions]" >> $HGRCPATH
298 $ echo "[extensions]" >> $HGRCPATH
291 $ echo "blackbox =" >> $HGRCPATH
299 $ echo "blackbox =" >> $HGRCPATH
292 $ echo "[blackbox]" >> $HGRCPATH
300 $ echo "[blackbox]" >> $HGRCPATH
293 $ echo "maxsize = 1" >> $HGRCPATH
301 $ echo "maxsize = 1" >> $HGRCPATH
294 $ $NO_FALLBACK rhg files > /dev/null
302 $ $NO_FALLBACK rhg files > /dev/null
295 $ cat .hg/blackbox.log
303 $ cat .hg/blackbox.log
296 ????/??/?? ??:??:??.??? * @d3873e73d99ef67873dac33fbcc66268d5d2b6f4 (*)> (rust) files exited 0 after 0.??? seconds (glob)
304 ????/??/?? ??:??:??.??? * @d3873e73d99ef67873dac33fbcc66268d5d2b6f4 (*)> (rust) files exited 0 after 0.??? seconds (glob)
297 $ cat .hg/blackbox.log.1
305 $ cat .hg/blackbox.log.1
298 ????/??/?? ??:??:??.??? * @d3873e73d99ef67873dac33fbcc66268d5d2b6f4 (*)> (rust) files (glob)
306 ????/??/?? ??:??:??.??? * @d3873e73d99ef67873dac33fbcc66268d5d2b6f4 (*)> (rust) files (glob)
299
307
General Comments 0
You need to be logged in to leave comments. Login now