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