##// END OF EJS Templates
rhg: Colored output is not supported...
Simon Sapin -
r49163:d71b9902 default
parent child Browse files
Show More
@@ -1,638 +1,647 b''
1 extern crate log;
1 extern crate log;
2 use crate::error::CommandError;
2 use crate::error::CommandError;
3 use crate::ui::Ui;
3 use crate::ui::Ui;
4 use clap::App;
4 use clap::App;
5 use clap::AppSettings;
5 use clap::AppSettings;
6 use clap::Arg;
6 use clap::Arg;
7 use clap::ArgMatches;
7 use clap::ArgMatches;
8 use format_bytes::{format_bytes, join};
8 use format_bytes::{format_bytes, join};
9 use hg::config::{Config, ConfigSource};
9 use hg::config::{Config, ConfigSource};
10 use hg::exit_codes;
10 use hg::exit_codes;
11 use hg::repo::{Repo, RepoError};
11 use hg::repo::{Repo, RepoError};
12 use hg::utils::files::{get_bytes_from_os_str, get_path_from_bytes};
12 use hg::utils::files::{get_bytes_from_os_str, get_path_from_bytes};
13 use hg::utils::SliceExt;
13 use hg::utils::SliceExt;
14 use std::ffi::OsString;
14 use std::ffi::OsString;
15 use std::path::PathBuf;
15 use std::path::PathBuf;
16 use std::process::Command;
16 use std::process::Command;
17
17
18 mod blackbox;
18 mod blackbox;
19 mod error;
19 mod error;
20 mod ui;
20 mod ui;
21 pub mod utils {
21 pub mod utils {
22 pub mod path_utils;
22 pub mod path_utils;
23 }
23 }
24
24
25 fn main_with_result(
25 fn main_with_result(
26 process_start_time: &blackbox::ProcessStartTime,
26 process_start_time: &blackbox::ProcessStartTime,
27 ui: &ui::Ui,
27 ui: &ui::Ui,
28 repo: Result<&Repo, &NoRepoInCwdError>,
28 repo: Result<&Repo, &NoRepoInCwdError>,
29 config: &Config,
29 config: &Config,
30 ) -> Result<(), CommandError> {
30 ) -> Result<(), CommandError> {
31 check_unsupported(config)?;
31 check_unsupported(config, ui)?;
32
32
33 let app = App::new("rhg")
33 let app = App::new("rhg")
34 .global_setting(AppSettings::AllowInvalidUtf8)
34 .global_setting(AppSettings::AllowInvalidUtf8)
35 .global_setting(AppSettings::DisableVersion)
35 .global_setting(AppSettings::DisableVersion)
36 .setting(AppSettings::SubcommandRequired)
36 .setting(AppSettings::SubcommandRequired)
37 .setting(AppSettings::VersionlessSubcommands)
37 .setting(AppSettings::VersionlessSubcommands)
38 .arg(
38 .arg(
39 Arg::with_name("repository")
39 Arg::with_name("repository")
40 .help("repository root directory")
40 .help("repository root directory")
41 .short("-R")
41 .short("-R")
42 .long("--repository")
42 .long("--repository")
43 .value_name("REPO")
43 .value_name("REPO")
44 .takes_value(true)
44 .takes_value(true)
45 // Both ok: `hg -R ./foo log` or `hg log -R ./foo`
45 // Both ok: `hg -R ./foo log` or `hg log -R ./foo`
46 .global(true),
46 .global(true),
47 )
47 )
48 .arg(
48 .arg(
49 Arg::with_name("config")
49 Arg::with_name("config")
50 .help("set/override config option (use 'section.name=value')")
50 .help("set/override config option (use 'section.name=value')")
51 .long("--config")
51 .long("--config")
52 .value_name("CONFIG")
52 .value_name("CONFIG")
53 .takes_value(true)
53 .takes_value(true)
54 .global(true)
54 .global(true)
55 // Ok: `--config section.key1=val --config section.key2=val2`
55 // Ok: `--config section.key1=val --config section.key2=val2`
56 .multiple(true)
56 .multiple(true)
57 // Not ok: `--config section.key1=val section.key2=val2`
57 // Not ok: `--config section.key1=val section.key2=val2`
58 .number_of_values(1),
58 .number_of_values(1),
59 )
59 )
60 .arg(
60 .arg(
61 Arg::with_name("cwd")
61 Arg::with_name("cwd")
62 .help("change working directory")
62 .help("change working directory")
63 .long("--cwd")
63 .long("--cwd")
64 .value_name("DIR")
64 .value_name("DIR")
65 .takes_value(true)
65 .takes_value(true)
66 .global(true),
66 .global(true),
67 )
67 )
68 .version("0.0.1");
68 .version("0.0.1");
69 let app = add_subcommand_args(app);
69 let app = add_subcommand_args(app);
70
70
71 let matches = app.clone().get_matches_safe()?;
71 let matches = app.clone().get_matches_safe()?;
72
72
73 let (subcommand_name, subcommand_matches) = matches.subcommand();
73 let (subcommand_name, subcommand_matches) = matches.subcommand();
74
74
75 // Mercurial allows users to define "defaults" for commands, fallback
75 // Mercurial allows users to define "defaults" for commands, fallback
76 // if a default is detected for the current command
76 // if a default is detected for the current command
77 let defaults = config.get_str(b"defaults", subcommand_name.as_bytes());
77 let defaults = config.get_str(b"defaults", subcommand_name.as_bytes());
78 if defaults?.is_some() {
78 if defaults?.is_some() {
79 let msg = "`defaults` config set";
79 let msg = "`defaults` config set";
80 return Err(CommandError::unsupported(msg));
80 return Err(CommandError::unsupported(msg));
81 }
81 }
82
82
83 for prefix in ["pre", "post", "fail"].iter() {
83 for prefix in ["pre", "post", "fail"].iter() {
84 // Mercurial allows users to define generic hooks for commands,
84 // Mercurial allows users to define generic hooks for commands,
85 // fallback if any are detected
85 // fallback if any are detected
86 let item = format!("{}-{}", prefix, subcommand_name);
86 let item = format!("{}-{}", prefix, subcommand_name);
87 let hook_for_command = config.get_str(b"hooks", item.as_bytes())?;
87 let hook_for_command = config.get_str(b"hooks", item.as_bytes())?;
88 if hook_for_command.is_some() {
88 if hook_for_command.is_some() {
89 let msg = format!("{}-{} hook defined", prefix, subcommand_name);
89 let msg = format!("{}-{} hook defined", prefix, subcommand_name);
90 return Err(CommandError::unsupported(msg));
90 return Err(CommandError::unsupported(msg));
91 }
91 }
92 }
92 }
93 let run = subcommand_run_fn(subcommand_name)
93 let run = subcommand_run_fn(subcommand_name)
94 .expect("unknown subcommand name from clap despite AppSettings::SubcommandRequired");
94 .expect("unknown subcommand name from clap despite AppSettings::SubcommandRequired");
95 let subcommand_args = subcommand_matches
95 let subcommand_args = subcommand_matches
96 .expect("no subcommand arguments from clap despite AppSettings::SubcommandRequired");
96 .expect("no subcommand arguments from clap despite AppSettings::SubcommandRequired");
97
97
98 let invocation = CliInvocation {
98 let invocation = CliInvocation {
99 ui,
99 ui,
100 subcommand_args,
100 subcommand_args,
101 config,
101 config,
102 repo,
102 repo,
103 };
103 };
104
104
105 if let Ok(repo) = repo {
105 if let Ok(repo) = repo {
106 // We don't support subrepos, fallback if the subrepos file is present
106 // We don't support subrepos, fallback if the subrepos file is present
107 if repo.working_directory_vfs().join(".hgsub").exists() {
107 if repo.working_directory_vfs().join(".hgsub").exists() {
108 let msg = "subrepos (.hgsub is present)";
108 let msg = "subrepos (.hgsub is present)";
109 return Err(CommandError::unsupported(msg));
109 return Err(CommandError::unsupported(msg));
110 }
110 }
111 }
111 }
112
112
113 let blackbox = blackbox::Blackbox::new(&invocation, process_start_time)?;
113 let blackbox = blackbox::Blackbox::new(&invocation, process_start_time)?;
114 blackbox.log_command_start();
114 blackbox.log_command_start();
115 let result = run(&invocation);
115 let result = run(&invocation);
116 blackbox.log_command_end(exit_code(
116 blackbox.log_command_end(exit_code(
117 &result,
117 &result,
118 // TODO: show a warning or combine with original error if `get_bool`
118 // TODO: show a warning or combine with original error if `get_bool`
119 // returns an error
119 // returns an error
120 config
120 config
121 .get_bool(b"ui", b"detailed-exit-code")
121 .get_bool(b"ui", b"detailed-exit-code")
122 .unwrap_or(false),
122 .unwrap_or(false),
123 ));
123 ));
124 result
124 result
125 }
125 }
126
126
127 fn main() {
127 fn main() {
128 // Run this first, before we find out if the blackbox extension is even
128 // Run this first, before we find out if the blackbox extension is even
129 // enabled, in order to include everything in-between in the duration
129 // enabled, in order to include everything in-between in the duration
130 // measurements. Reading config files can be slow if they’re on NFS.
130 // measurements. Reading config files can be slow if they’re on NFS.
131 let process_start_time = blackbox::ProcessStartTime::now();
131 let process_start_time = blackbox::ProcessStartTime::now();
132
132
133 env_logger::init();
133 env_logger::init();
134 let ui = ui::Ui::new();
134 let ui = ui::Ui::new();
135
135
136 let early_args = EarlyArgs::parse(std::env::args_os());
136 let early_args = EarlyArgs::parse(std::env::args_os());
137
137
138 let initial_current_dir = early_args.cwd.map(|cwd| {
138 let initial_current_dir = early_args.cwd.map(|cwd| {
139 let cwd = get_path_from_bytes(&cwd);
139 let cwd = get_path_from_bytes(&cwd);
140 std::env::current_dir()
140 std::env::current_dir()
141 .and_then(|initial| {
141 .and_then(|initial| {
142 std::env::set_current_dir(cwd)?;
142 std::env::set_current_dir(cwd)?;
143 Ok(initial)
143 Ok(initial)
144 })
144 })
145 .unwrap_or_else(|error| {
145 .unwrap_or_else(|error| {
146 exit(
146 exit(
147 &None,
147 &None,
148 &ui,
148 &ui,
149 OnUnsupported::Abort,
149 OnUnsupported::Abort,
150 Err(CommandError::abort(format!(
150 Err(CommandError::abort(format!(
151 "abort: {}: '{}'",
151 "abort: {}: '{}'",
152 error,
152 error,
153 cwd.display()
153 cwd.display()
154 ))),
154 ))),
155 false,
155 false,
156 )
156 )
157 })
157 })
158 });
158 });
159
159
160 let mut non_repo_config =
160 let mut non_repo_config =
161 Config::load_non_repo().unwrap_or_else(|error| {
161 Config::load_non_repo().unwrap_or_else(|error| {
162 // Normally this is decided based on config, but we don’t have that
162 // Normally this is decided based on config, but we don’t have that
163 // available. As of this writing config loading never returns an
163 // available. As of this writing config loading never returns an
164 // "unsupported" error but that is not enforced by the type system.
164 // "unsupported" error but that is not enforced by the type system.
165 let on_unsupported = OnUnsupported::Abort;
165 let on_unsupported = OnUnsupported::Abort;
166
166
167 exit(
167 exit(
168 &initial_current_dir,
168 &initial_current_dir,
169 &ui,
169 &ui,
170 on_unsupported,
170 on_unsupported,
171 Err(error.into()),
171 Err(error.into()),
172 false,
172 false,
173 )
173 )
174 });
174 });
175
175
176 non_repo_config
176 non_repo_config
177 .load_cli_args_config(early_args.config)
177 .load_cli_args_config(early_args.config)
178 .unwrap_or_else(|error| {
178 .unwrap_or_else(|error| {
179 exit(
179 exit(
180 &initial_current_dir,
180 &initial_current_dir,
181 &ui,
181 &ui,
182 OnUnsupported::from_config(&ui, &non_repo_config),
182 OnUnsupported::from_config(&ui, &non_repo_config),
183 Err(error.into()),
183 Err(error.into()),
184 non_repo_config
184 non_repo_config
185 .get_bool(b"ui", b"detailed-exit-code")
185 .get_bool(b"ui", b"detailed-exit-code")
186 .unwrap_or(false),
186 .unwrap_or(false),
187 )
187 )
188 });
188 });
189
189
190 if let Some(repo_path_bytes) = &early_args.repo {
190 if let Some(repo_path_bytes) = &early_args.repo {
191 lazy_static::lazy_static! {
191 lazy_static::lazy_static! {
192 static ref SCHEME_RE: regex::bytes::Regex =
192 static ref SCHEME_RE: regex::bytes::Regex =
193 // Same as `_matchscheme` in `mercurial/util.py`
193 // Same as `_matchscheme` in `mercurial/util.py`
194 regex::bytes::Regex::new("^[a-zA-Z0-9+.\\-]+:").unwrap();
194 regex::bytes::Regex::new("^[a-zA-Z0-9+.\\-]+:").unwrap();
195 }
195 }
196 if SCHEME_RE.is_match(&repo_path_bytes) {
196 if SCHEME_RE.is_match(&repo_path_bytes) {
197 exit(
197 exit(
198 &initial_current_dir,
198 &initial_current_dir,
199 &ui,
199 &ui,
200 OnUnsupported::from_config(&ui, &non_repo_config),
200 OnUnsupported::from_config(&ui, &non_repo_config),
201 Err(CommandError::UnsupportedFeature {
201 Err(CommandError::UnsupportedFeature {
202 message: format_bytes!(
202 message: format_bytes!(
203 b"URL-like --repository {}",
203 b"URL-like --repository {}",
204 repo_path_bytes
204 repo_path_bytes
205 ),
205 ),
206 }),
206 }),
207 // TODO: show a warning or combine with original error if
207 // TODO: show a warning or combine with original error if
208 // `get_bool` returns an error
208 // `get_bool` returns an error
209 non_repo_config
209 non_repo_config
210 .get_bool(b"ui", b"detailed-exit-code")
210 .get_bool(b"ui", b"detailed-exit-code")
211 .unwrap_or(false),
211 .unwrap_or(false),
212 )
212 )
213 }
213 }
214 }
214 }
215 let repo_arg = early_args.repo.unwrap_or(Vec::new());
215 let repo_arg = early_args.repo.unwrap_or(Vec::new());
216 let repo_path: Option<PathBuf> = {
216 let repo_path: Option<PathBuf> = {
217 if repo_arg.is_empty() {
217 if repo_arg.is_empty() {
218 None
218 None
219 } else {
219 } else {
220 let local_config = {
220 let local_config = {
221 if std::env::var_os("HGRCSKIPREPO").is_none() {
221 if std::env::var_os("HGRCSKIPREPO").is_none() {
222 // TODO: handle errors from find_repo_root
222 // TODO: handle errors from find_repo_root
223 if let Ok(current_dir_path) = Repo::find_repo_root() {
223 if let Ok(current_dir_path) = Repo::find_repo_root() {
224 let config_files = vec![
224 let config_files = vec![
225 ConfigSource::AbsPath(
225 ConfigSource::AbsPath(
226 current_dir_path.join(".hg/hgrc"),
226 current_dir_path.join(".hg/hgrc"),
227 ),
227 ),
228 ConfigSource::AbsPath(
228 ConfigSource::AbsPath(
229 current_dir_path.join(".hg/hgrc-not-shared"),
229 current_dir_path.join(".hg/hgrc-not-shared"),
230 ),
230 ),
231 ];
231 ];
232 // TODO: handle errors from
232 // TODO: handle errors from
233 // `load_from_explicit_sources`
233 // `load_from_explicit_sources`
234 Config::load_from_explicit_sources(config_files).ok()
234 Config::load_from_explicit_sources(config_files).ok()
235 } else {
235 } else {
236 None
236 None
237 }
237 }
238 } else {
238 } else {
239 None
239 None
240 }
240 }
241 };
241 };
242
242
243 let non_repo_config_val = {
243 let non_repo_config_val = {
244 let non_repo_val = non_repo_config.get(b"paths", &repo_arg);
244 let non_repo_val = non_repo_config.get(b"paths", &repo_arg);
245 match &non_repo_val {
245 match &non_repo_val {
246 Some(val) if val.len() > 0 => home::home_dir()
246 Some(val) if val.len() > 0 => home::home_dir()
247 .unwrap_or_else(|| PathBuf::from("~"))
247 .unwrap_or_else(|| PathBuf::from("~"))
248 .join(get_path_from_bytes(val))
248 .join(get_path_from_bytes(val))
249 .canonicalize()
249 .canonicalize()
250 // TODO: handle error and make it similar to python
250 // TODO: handle error and make it similar to python
251 // implementation maybe?
251 // implementation maybe?
252 .ok(),
252 .ok(),
253 _ => None,
253 _ => None,
254 }
254 }
255 };
255 };
256
256
257 let config_val = match &local_config {
257 let config_val = match &local_config {
258 None => non_repo_config_val,
258 None => non_repo_config_val,
259 Some(val) => {
259 Some(val) => {
260 let local_config_val = val.get(b"paths", &repo_arg);
260 let local_config_val = val.get(b"paths", &repo_arg);
261 match &local_config_val {
261 match &local_config_val {
262 Some(val) if val.len() > 0 => {
262 Some(val) if val.len() > 0 => {
263 // presence of a local_config assures that
263 // presence of a local_config assures that
264 // current_dir
264 // current_dir
265 // wont result in an Error
265 // wont result in an Error
266 let canpath = hg::utils::current_dir()
266 let canpath = hg::utils::current_dir()
267 .unwrap()
267 .unwrap()
268 .join(get_path_from_bytes(val))
268 .join(get_path_from_bytes(val))
269 .canonicalize();
269 .canonicalize();
270 canpath.ok().or(non_repo_config_val)
270 canpath.ok().or(non_repo_config_val)
271 }
271 }
272 _ => non_repo_config_val,
272 _ => non_repo_config_val,
273 }
273 }
274 }
274 }
275 };
275 };
276 config_val.or(Some(get_path_from_bytes(&repo_arg).to_path_buf()))
276 config_val.or(Some(get_path_from_bytes(&repo_arg).to_path_buf()))
277 }
277 }
278 };
278 };
279
279
280 let repo_result = match Repo::find(&non_repo_config, repo_path.to_owned())
280 let repo_result = match Repo::find(&non_repo_config, repo_path.to_owned())
281 {
281 {
282 Ok(repo) => Ok(repo),
282 Ok(repo) => Ok(repo),
283 Err(RepoError::NotFound { at }) if repo_path.is_none() => {
283 Err(RepoError::NotFound { at }) if repo_path.is_none() => {
284 // Not finding a repo is not fatal yet, if `-R` was not given
284 // Not finding a repo is not fatal yet, if `-R` was not given
285 Err(NoRepoInCwdError { cwd: at })
285 Err(NoRepoInCwdError { cwd: at })
286 }
286 }
287 Err(error) => exit(
287 Err(error) => exit(
288 &initial_current_dir,
288 &initial_current_dir,
289 &ui,
289 &ui,
290 OnUnsupported::from_config(&ui, &non_repo_config),
290 OnUnsupported::from_config(&ui, &non_repo_config),
291 Err(error.into()),
291 Err(error.into()),
292 // TODO: show a warning or combine with original error if
292 // TODO: show a warning or combine with original error if
293 // `get_bool` returns an error
293 // `get_bool` returns an error
294 non_repo_config
294 non_repo_config
295 .get_bool(b"ui", b"detailed-exit-code")
295 .get_bool(b"ui", b"detailed-exit-code")
296 .unwrap_or(false),
296 .unwrap_or(false),
297 ),
297 ),
298 };
298 };
299
299
300 let config = if let Ok(repo) = &repo_result {
300 let config = if let Ok(repo) = &repo_result {
301 repo.config()
301 repo.config()
302 } else {
302 } else {
303 &non_repo_config
303 &non_repo_config
304 };
304 };
305 let on_unsupported = OnUnsupported::from_config(&ui, config);
305 let on_unsupported = OnUnsupported::from_config(&ui, config);
306
306
307 let result = main_with_result(
307 let result = main_with_result(
308 &process_start_time,
308 &process_start_time,
309 &ui,
309 &ui,
310 repo_result.as_ref(),
310 repo_result.as_ref(),
311 config,
311 config,
312 );
312 );
313 exit(
313 exit(
314 &initial_current_dir,
314 &initial_current_dir,
315 &ui,
315 &ui,
316 on_unsupported,
316 on_unsupported,
317 result,
317 result,
318 // TODO: show a warning or combine with original error if `get_bool`
318 // TODO: show a warning or combine with original error if `get_bool`
319 // returns an error
319 // returns an error
320 config
320 config
321 .get_bool(b"ui", b"detailed-exit-code")
321 .get_bool(b"ui", b"detailed-exit-code")
322 .unwrap_or(false),
322 .unwrap_or(false),
323 )
323 )
324 }
324 }
325
325
326 fn exit_code(
326 fn exit_code(
327 result: &Result<(), CommandError>,
327 result: &Result<(), CommandError>,
328 use_detailed_exit_code: bool,
328 use_detailed_exit_code: bool,
329 ) -> i32 {
329 ) -> i32 {
330 match result {
330 match result {
331 Ok(()) => exit_codes::OK,
331 Ok(()) => exit_codes::OK,
332 Err(CommandError::Abort {
332 Err(CommandError::Abort {
333 message: _,
333 message: _,
334 detailed_exit_code,
334 detailed_exit_code,
335 }) => {
335 }) => {
336 if use_detailed_exit_code {
336 if use_detailed_exit_code {
337 *detailed_exit_code
337 *detailed_exit_code
338 } else {
338 } else {
339 exit_codes::ABORT
339 exit_codes::ABORT
340 }
340 }
341 }
341 }
342 Err(CommandError::Unsuccessful) => exit_codes::UNSUCCESSFUL,
342 Err(CommandError::Unsuccessful) => exit_codes::UNSUCCESSFUL,
343
343
344 // Exit with a specific code and no error message to let a potential
344 // Exit with a specific code and no error message to let a potential
345 // wrapper script fallback to Python-based Mercurial.
345 // wrapper script fallback to Python-based Mercurial.
346 Err(CommandError::UnsupportedFeature { .. }) => {
346 Err(CommandError::UnsupportedFeature { .. }) => {
347 exit_codes::UNIMPLEMENTED
347 exit_codes::UNIMPLEMENTED
348 }
348 }
349 }
349 }
350 }
350 }
351
351
352 fn exit(
352 fn exit(
353 initial_current_dir: &Option<PathBuf>,
353 initial_current_dir: &Option<PathBuf>,
354 ui: &Ui,
354 ui: &Ui,
355 mut on_unsupported: OnUnsupported,
355 mut on_unsupported: OnUnsupported,
356 result: Result<(), CommandError>,
356 result: Result<(), CommandError>,
357 use_detailed_exit_code: bool,
357 use_detailed_exit_code: bool,
358 ) -> ! {
358 ) -> ! {
359 if let (
359 if let (
360 OnUnsupported::Fallback { executable },
360 OnUnsupported::Fallback { executable },
361 Err(CommandError::UnsupportedFeature { .. }),
361 Err(CommandError::UnsupportedFeature { .. }),
362 ) = (&on_unsupported, &result)
362 ) = (&on_unsupported, &result)
363 {
363 {
364 let mut args = std::env::args_os();
364 let mut args = std::env::args_os();
365 let executable_path = get_path_from_bytes(&executable);
365 let executable_path = get_path_from_bytes(&executable);
366 let this_executable = args.next().expect("exepcted argv[0] to exist");
366 let this_executable = args.next().expect("exepcted argv[0] to exist");
367 if executable_path == &PathBuf::from(this_executable) {
367 if executable_path == &PathBuf::from(this_executable) {
368 // Avoid spawning infinitely many processes until resource
368 // Avoid spawning infinitely many processes until resource
369 // exhaustion.
369 // exhaustion.
370 let _ = ui.write_stderr(&format_bytes!(
370 let _ = ui.write_stderr(&format_bytes!(
371 b"Blocking recursive fallback. The 'rhg.fallback-executable = {}' config \
371 b"Blocking recursive fallback. The 'rhg.fallback-executable = {}' config \
372 points to `rhg` itself.\n",
372 points to `rhg` itself.\n",
373 executable
373 executable
374 ));
374 ));
375 on_unsupported = OnUnsupported::Abort
375 on_unsupported = OnUnsupported::Abort
376 } else {
376 } else {
377 // `args` is now `argv[1..]` since we’ve already consumed `argv[0]`
377 // `args` is now `argv[1..]` since we’ve already consumed `argv[0]`
378 let mut command = Command::new(executable_path);
378 let mut command = Command::new(executable_path);
379 command.args(args);
379 command.args(args);
380 if let Some(initial) = initial_current_dir {
380 if let Some(initial) = initial_current_dir {
381 command.current_dir(initial);
381 command.current_dir(initial);
382 }
382 }
383 let result = command.status();
383 let result = command.status();
384 match result {
384 match result {
385 Ok(status) => std::process::exit(
385 Ok(status) => std::process::exit(
386 status.code().unwrap_or(exit_codes::ABORT),
386 status.code().unwrap_or(exit_codes::ABORT),
387 ),
387 ),
388 Err(error) => {
388 Err(error) => {
389 let _ = ui.write_stderr(&format_bytes!(
389 let _ = ui.write_stderr(&format_bytes!(
390 b"tried to fall back to a '{}' sub-process but got error {}\n",
390 b"tried to fall back to a '{}' sub-process but got error {}\n",
391 executable, format_bytes::Utf8(error)
391 executable, format_bytes::Utf8(error)
392 ));
392 ));
393 on_unsupported = OnUnsupported::Abort
393 on_unsupported = OnUnsupported::Abort
394 }
394 }
395 }
395 }
396 }
396 }
397 }
397 }
398 exit_no_fallback(ui, on_unsupported, result, use_detailed_exit_code)
398 exit_no_fallback(ui, on_unsupported, result, use_detailed_exit_code)
399 }
399 }
400
400
401 fn exit_no_fallback(
401 fn exit_no_fallback(
402 ui: &Ui,
402 ui: &Ui,
403 on_unsupported: OnUnsupported,
403 on_unsupported: OnUnsupported,
404 result: Result<(), CommandError>,
404 result: Result<(), CommandError>,
405 use_detailed_exit_code: bool,
405 use_detailed_exit_code: bool,
406 ) -> ! {
406 ) -> ! {
407 match &result {
407 match &result {
408 Ok(_) => {}
408 Ok(_) => {}
409 Err(CommandError::Unsuccessful) => {}
409 Err(CommandError::Unsuccessful) => {}
410 Err(CommandError::Abort {
410 Err(CommandError::Abort {
411 message,
411 message,
412 detailed_exit_code: _,
412 detailed_exit_code: _,
413 }) => {
413 }) => {
414 if !message.is_empty() {
414 if !message.is_empty() {
415 // Ignore errors when writing to stderr, we’re already exiting
415 // Ignore errors when writing to stderr, we’re already exiting
416 // with failure code so there’s not much more we can do.
416 // with failure code so there’s not much more we can do.
417 let _ = ui.write_stderr(&format_bytes!(b"{}\n", message));
417 let _ = ui.write_stderr(&format_bytes!(b"{}\n", message));
418 }
418 }
419 }
419 }
420 Err(CommandError::UnsupportedFeature { message }) => {
420 Err(CommandError::UnsupportedFeature { message }) => {
421 match on_unsupported {
421 match on_unsupported {
422 OnUnsupported::Abort => {
422 OnUnsupported::Abort => {
423 let _ = ui.write_stderr(&format_bytes!(
423 let _ = ui.write_stderr(&format_bytes!(
424 b"unsupported feature: {}\n",
424 b"unsupported feature: {}\n",
425 message
425 message
426 ));
426 ));
427 }
427 }
428 OnUnsupported::AbortSilent => {}
428 OnUnsupported::AbortSilent => {}
429 OnUnsupported::Fallback { .. } => unreachable!(),
429 OnUnsupported::Fallback { .. } => unreachable!(),
430 }
430 }
431 }
431 }
432 }
432 }
433 std::process::exit(exit_code(&result, use_detailed_exit_code))
433 std::process::exit(exit_code(&result, use_detailed_exit_code))
434 }
434 }
435
435
436 macro_rules! subcommands {
436 macro_rules! subcommands {
437 ($( $command: ident )+) => {
437 ($( $command: ident )+) => {
438 mod commands {
438 mod commands {
439 $(
439 $(
440 pub mod $command;
440 pub mod $command;
441 )+
441 )+
442 }
442 }
443
443
444 fn add_subcommand_args<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b> {
444 fn add_subcommand_args<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b> {
445 app
445 app
446 $(
446 $(
447 .subcommand(commands::$command::args())
447 .subcommand(commands::$command::args())
448 )+
448 )+
449 }
449 }
450
450
451 pub type RunFn = fn(&CliInvocation) -> Result<(), CommandError>;
451 pub type RunFn = fn(&CliInvocation) -> Result<(), CommandError>;
452
452
453 fn subcommand_run_fn(name: &str) -> Option<RunFn> {
453 fn subcommand_run_fn(name: &str) -> Option<RunFn> {
454 match name {
454 match name {
455 $(
455 $(
456 stringify!($command) => Some(commands::$command::run),
456 stringify!($command) => Some(commands::$command::run),
457 )+
457 )+
458 _ => None,
458 _ => None,
459 }
459 }
460 }
460 }
461 };
461 };
462 }
462 }
463
463
464 subcommands! {
464 subcommands! {
465 cat
465 cat
466 debugdata
466 debugdata
467 debugrequirements
467 debugrequirements
468 files
468 files
469 root
469 root
470 config
470 config
471 status
471 status
472 }
472 }
473
473
474 pub struct CliInvocation<'a> {
474 pub struct CliInvocation<'a> {
475 ui: &'a Ui,
475 ui: &'a Ui,
476 subcommand_args: &'a ArgMatches<'a>,
476 subcommand_args: &'a ArgMatches<'a>,
477 config: &'a Config,
477 config: &'a Config,
478 /// References inside `Result` is a bit peculiar but allow
478 /// References inside `Result` is a bit peculiar but allow
479 /// `invocation.repo?` to work out with `&CliInvocation` since this
479 /// `invocation.repo?` to work out with `&CliInvocation` since this
480 /// `Result` type is `Copy`.
480 /// `Result` type is `Copy`.
481 repo: Result<&'a Repo, &'a NoRepoInCwdError>,
481 repo: Result<&'a Repo, &'a NoRepoInCwdError>,
482 }
482 }
483
483
484 struct NoRepoInCwdError {
484 struct NoRepoInCwdError {
485 cwd: PathBuf,
485 cwd: PathBuf,
486 }
486 }
487
487
488 /// CLI arguments to be parsed "early" in order to be able to read
488 /// CLI arguments to be parsed "early" in order to be able to read
489 /// configuration before using Clap. Ideally we would also use Clap for this,
489 /// configuration before using Clap. Ideally we would also use Clap for this,
490 /// see <https://github.com/clap-rs/clap/discussions/2366>.
490 /// see <https://github.com/clap-rs/clap/discussions/2366>.
491 ///
491 ///
492 /// These arguments are still declared when we do use Clap later, so that Clap
492 /// These arguments are still declared when we do use Clap later, so that Clap
493 /// does not return an error for their presence.
493 /// does not return an error for their presence.
494 struct EarlyArgs {
494 struct EarlyArgs {
495 /// Values of all `--config` arguments. (Possibly none)
495 /// Values of all `--config` arguments. (Possibly none)
496 config: Vec<Vec<u8>>,
496 config: Vec<Vec<u8>>,
497 /// Value of the `-R` or `--repository` argument, if any.
497 /// Value of the `-R` or `--repository` argument, if any.
498 repo: Option<Vec<u8>>,
498 repo: Option<Vec<u8>>,
499 /// Value of the `--cwd` argument, if any.
499 /// Value of the `--cwd` argument, if any.
500 cwd: Option<Vec<u8>>,
500 cwd: Option<Vec<u8>>,
501 }
501 }
502
502
503 impl EarlyArgs {
503 impl EarlyArgs {
504 fn parse(args: impl IntoIterator<Item = OsString>) -> Self {
504 fn parse(args: impl IntoIterator<Item = OsString>) -> Self {
505 let mut args = args.into_iter().map(get_bytes_from_os_str);
505 let mut args = args.into_iter().map(get_bytes_from_os_str);
506 let mut config = Vec::new();
506 let mut config = Vec::new();
507 let mut repo = None;
507 let mut repo = None;
508 let mut cwd = None;
508 let mut cwd = None;
509 // Use `while let` instead of `for` so that we can also call
509 // Use `while let` instead of `for` so that we can also call
510 // `args.next()` inside the loop.
510 // `args.next()` inside the loop.
511 while let Some(arg) = args.next() {
511 while let Some(arg) = args.next() {
512 if arg == b"--config" {
512 if arg == b"--config" {
513 if let Some(value) = args.next() {
513 if let Some(value) = args.next() {
514 config.push(value)
514 config.push(value)
515 }
515 }
516 } else if let Some(value) = arg.drop_prefix(b"--config=") {
516 } else if let Some(value) = arg.drop_prefix(b"--config=") {
517 config.push(value.to_owned())
517 config.push(value.to_owned())
518 }
518 }
519
519
520 if arg == b"--cwd" {
520 if arg == b"--cwd" {
521 if let Some(value) = args.next() {
521 if let Some(value) = args.next() {
522 cwd = Some(value)
522 cwd = Some(value)
523 }
523 }
524 } else if let Some(value) = arg.drop_prefix(b"--cwd=") {
524 } else if let Some(value) = arg.drop_prefix(b"--cwd=") {
525 cwd = Some(value.to_owned())
525 cwd = Some(value.to_owned())
526 }
526 }
527
527
528 if arg == b"--repository" || arg == b"-R" {
528 if arg == b"--repository" || arg == b"-R" {
529 if let Some(value) = args.next() {
529 if let Some(value) = args.next() {
530 repo = Some(value)
530 repo = Some(value)
531 }
531 }
532 } else if let Some(value) = arg.drop_prefix(b"--repository=") {
532 } else if let Some(value) = arg.drop_prefix(b"--repository=") {
533 repo = Some(value.to_owned())
533 repo = Some(value.to_owned())
534 } else if let Some(value) = arg.drop_prefix(b"-R") {
534 } else if let Some(value) = arg.drop_prefix(b"-R") {
535 repo = Some(value.to_owned())
535 repo = Some(value.to_owned())
536 }
536 }
537 }
537 }
538 Self { config, repo, cwd }
538 Self { config, repo, cwd }
539 }
539 }
540 }
540 }
541
541
542 /// What to do when encountering some unsupported feature.
542 /// What to do when encountering some unsupported feature.
543 ///
543 ///
544 /// See `HgError::UnsupportedFeature` and `CommandError::UnsupportedFeature`.
544 /// See `HgError::UnsupportedFeature` and `CommandError::UnsupportedFeature`.
545 enum OnUnsupported {
545 enum OnUnsupported {
546 /// Print an error message describing what feature is not supported,
546 /// Print an error message describing what feature is not supported,
547 /// and exit with code 252.
547 /// and exit with code 252.
548 Abort,
548 Abort,
549 /// Silently exit with code 252.
549 /// Silently exit with code 252.
550 AbortSilent,
550 AbortSilent,
551 /// Try running a Python implementation
551 /// Try running a Python implementation
552 Fallback { executable: Vec<u8> },
552 Fallback { executable: Vec<u8> },
553 }
553 }
554
554
555 impl OnUnsupported {
555 impl OnUnsupported {
556 const DEFAULT: Self = OnUnsupported::Abort;
556 const DEFAULT: Self = OnUnsupported::Abort;
557
557
558 fn from_config(ui: &Ui, config: &Config) -> Self {
558 fn from_config(ui: &Ui, config: &Config) -> Self {
559 match config
559 match config
560 .get(b"rhg", b"on-unsupported")
560 .get(b"rhg", b"on-unsupported")
561 .map(|value| value.to_ascii_lowercase())
561 .map(|value| value.to_ascii_lowercase())
562 .as_deref()
562 .as_deref()
563 {
563 {
564 Some(b"abort") => OnUnsupported::Abort,
564 Some(b"abort") => OnUnsupported::Abort,
565 Some(b"abort-silent") => OnUnsupported::AbortSilent,
565 Some(b"abort-silent") => OnUnsupported::AbortSilent,
566 Some(b"fallback") => OnUnsupported::Fallback {
566 Some(b"fallback") => OnUnsupported::Fallback {
567 executable: config
567 executable: config
568 .get(b"rhg", b"fallback-executable")
568 .get(b"rhg", b"fallback-executable")
569 .unwrap_or_else(|| {
569 .unwrap_or_else(|| {
570 exit_no_fallback(
570 exit_no_fallback(
571 ui,
571 ui,
572 Self::Abort,
572 Self::Abort,
573 Err(CommandError::abort(
573 Err(CommandError::abort(
574 "abort: 'rhg.on-unsupported=fallback' without \
574 "abort: 'rhg.on-unsupported=fallback' without \
575 'rhg.fallback-executable' set."
575 'rhg.fallback-executable' set."
576 )),
576 )),
577 false,
577 false,
578 )
578 )
579 })
579 })
580 .to_owned(),
580 .to_owned(),
581 },
581 },
582 None => Self::DEFAULT,
582 None => Self::DEFAULT,
583 Some(_) => {
583 Some(_) => {
584 // TODO: warn about unknown config value
584 // TODO: warn about unknown config value
585 Self::DEFAULT
585 Self::DEFAULT
586 }
586 }
587 }
587 }
588 }
588 }
589 }
589 }
590
590
591 const SUPPORTED_EXTENSIONS: &[&[u8]] = &[b"blackbox", b"share"];
591 const SUPPORTED_EXTENSIONS: &[&[u8]] = &[b"blackbox", b"share"];
592
592
593 fn check_extensions(config: &Config) -> Result<(), CommandError> {
593 fn check_extensions(config: &Config) -> Result<(), CommandError> {
594 let enabled = config.get_section_keys(b"extensions");
594 let enabled = config.get_section_keys(b"extensions");
595
595
596 let mut unsupported = enabled;
596 let mut unsupported = enabled;
597 for supported in SUPPORTED_EXTENSIONS {
597 for supported in SUPPORTED_EXTENSIONS {
598 unsupported.remove(supported);
598 unsupported.remove(supported);
599 }
599 }
600
600
601 if let Some(ignored_list) = config.get_list(b"rhg", b"ignored-extensions")
601 if let Some(ignored_list) = config.get_list(b"rhg", b"ignored-extensions")
602 {
602 {
603 for ignored in ignored_list {
603 for ignored in ignored_list {
604 unsupported.remove(ignored.as_slice());
604 unsupported.remove(ignored.as_slice());
605 }
605 }
606 }
606 }
607
607
608 if unsupported.is_empty() {
608 if unsupported.is_empty() {
609 Ok(())
609 Ok(())
610 } else {
610 } else {
611 Err(CommandError::UnsupportedFeature {
611 Err(CommandError::UnsupportedFeature {
612 message: format_bytes!(
612 message: format_bytes!(
613 b"extensions: {} (consider adding them to 'rhg.ignored-extensions' config)",
613 b"extensions: {} (consider adding them to 'rhg.ignored-extensions' config)",
614 join(unsupported, b", ")
614 join(unsupported, b", ")
615 ),
615 ),
616 })
616 })
617 }
617 }
618 }
618 }
619
619
620 fn check_unsupported(config: &Config) -> Result<(), CommandError> {
620 fn check_unsupported(
621 config: &Config,
622 ui: &ui::Ui,
623 ) -> Result<(), CommandError> {
621 check_extensions(config)?;
624 check_extensions(config)?;
622
625
623 if std::env::var_os("HG_PENDING").is_some() {
626 if std::env::var_os("HG_PENDING").is_some() {
624 // TODO: only if the value is `== repo.working_directory`?
627 // TODO: only if the value is `== repo.working_directory`?
625 // What about relative v.s. absolute paths?
628 // What about relative v.s. absolute paths?
626 Err(CommandError::unsupported("$HG_PENDING"))?
629 Err(CommandError::unsupported("$HG_PENDING"))?
627 }
630 }
628
631
629 if config.has_non_empty_section(b"encode") {
632 if config.has_non_empty_section(b"encode") {
630 Err(CommandError::unsupported("[encode] config"))?
633 Err(CommandError::unsupported("[encode] config"))?
631 }
634 }
632
635
633 if config.has_non_empty_section(b"decode") {
636 if config.has_non_empty_section(b"decode") {
634 Err(CommandError::unsupported("[decode] config"))?
637 Err(CommandError::unsupported("[decode] config"))?
635 }
638 }
636
639
640 if let Some(color) = config.get(b"ui", b"color") {
641 if (color == b"always" || color == b"debug") && !ui.plain() {
642 Err(CommandError::unsupported("colored output"))?
643 }
644 }
645
637 Ok(())
646 Ok(())
638 }
647 }
@@ -1,411 +1,407 b''
1 TODO: fix rhg bugs that make this test fail when status is enabled
2 $ unset RHG_STATUS
3
4
5 $ cat <<EOF >> $HGRCPATH
1 $ cat <<EOF >> $HGRCPATH
6 > [ui]
2 > [ui]
7 > color = always
3 > color = always
8 > [color]
4 > [color]
9 > mode = ansi
5 > mode = ansi
10 > EOF
6 > EOF
11 Terminfo codes compatibility fix
7 Terminfo codes compatibility fix
12 $ echo "color.none=0" >> $HGRCPATH
8 $ echo "color.none=0" >> $HGRCPATH
13
9
14 $ hg init repo1
10 $ hg init repo1
15 $ cd repo1
11 $ cd repo1
16 $ mkdir a b a/1 b/1 b/2
12 $ mkdir a b a/1 b/1 b/2
17 $ touch in_root a/in_a b/in_b a/1/in_a_1 b/1/in_b_1 b/2/in_b_2
13 $ touch in_root a/in_a b/in_b a/1/in_a_1 b/1/in_b_1 b/2/in_b_2
18
14
19 hg status in repo root:
15 hg status in repo root:
20
16
21 $ hg status
17 $ hg status
22 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4ma/1/in_a_1\x1b[0m (esc)
18 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4ma/1/in_a_1\x1b[0m (esc)
23 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4ma/in_a\x1b[0m (esc)
19 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4ma/in_a\x1b[0m (esc)
24 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4mb/1/in_b_1\x1b[0m (esc)
20 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4mb/1/in_b_1\x1b[0m (esc)
25 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4mb/2/in_b_2\x1b[0m (esc)
21 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4mb/2/in_b_2\x1b[0m (esc)
26 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4mb/in_b\x1b[0m (esc)
22 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4mb/in_b\x1b[0m (esc)
27 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4min_root\x1b[0m (esc)
23 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4min_root\x1b[0m (esc)
28
24
29 $ hg status --color=debug
25 $ hg status --color=debug
30 [status.unknown|? ][status.unknown|a/1/in_a_1]
26 [status.unknown|? ][status.unknown|a/1/in_a_1]
31 [status.unknown|? ][status.unknown|a/in_a]
27 [status.unknown|? ][status.unknown|a/in_a]
32 [status.unknown|? ][status.unknown|b/1/in_b_1]
28 [status.unknown|? ][status.unknown|b/1/in_b_1]
33 [status.unknown|? ][status.unknown|b/2/in_b_2]
29 [status.unknown|? ][status.unknown|b/2/in_b_2]
34 [status.unknown|? ][status.unknown|b/in_b]
30 [status.unknown|? ][status.unknown|b/in_b]
35 [status.unknown|? ][status.unknown|in_root]
31 [status.unknown|? ][status.unknown|in_root]
36 HGPLAIN disables color
32 HGPLAIN disables color
37 $ HGPLAIN=1 hg status --color=debug
33 $ HGPLAIN=1 hg status --color=debug
38 ? a/1/in_a_1 (glob)
34 ? a/1/in_a_1 (glob)
39 ? a/in_a (glob)
35 ? a/in_a (glob)
40 ? b/1/in_b_1 (glob)
36 ? b/1/in_b_1 (glob)
41 ? b/2/in_b_2 (glob)
37 ? b/2/in_b_2 (glob)
42 ? b/in_b (glob)
38 ? b/in_b (glob)
43 ? in_root
39 ? in_root
44 HGPLAINEXCEPT=color does not disable color
40 HGPLAINEXCEPT=color does not disable color
45 $ HGPLAINEXCEPT=color hg status --color=debug
41 $ HGPLAINEXCEPT=color hg status --color=debug
46 [status.unknown|? ][status.unknown|a/1/in_a_1] (glob)
42 [status.unknown|? ][status.unknown|a/1/in_a_1] (glob)
47 [status.unknown|? ][status.unknown|a/in_a] (glob)
43 [status.unknown|? ][status.unknown|a/in_a] (glob)
48 [status.unknown|? ][status.unknown|b/1/in_b_1] (glob)
44 [status.unknown|? ][status.unknown|b/1/in_b_1] (glob)
49 [status.unknown|? ][status.unknown|b/2/in_b_2] (glob)
45 [status.unknown|? ][status.unknown|b/2/in_b_2] (glob)
50 [status.unknown|? ][status.unknown|b/in_b] (glob)
46 [status.unknown|? ][status.unknown|b/in_b] (glob)
51 [status.unknown|? ][status.unknown|in_root]
47 [status.unknown|? ][status.unknown|in_root]
52
48
53 hg status with template
49 hg status with template
54 $ hg status -T "{label('red', path)}\n" --color=debug
50 $ hg status -T "{label('red', path)}\n" --color=debug
55 [red|a/1/in_a_1]
51 [red|a/1/in_a_1]
56 [red|a/in_a]
52 [red|a/in_a]
57 [red|b/1/in_b_1]
53 [red|b/1/in_b_1]
58 [red|b/2/in_b_2]
54 [red|b/2/in_b_2]
59 [red|b/in_b]
55 [red|b/in_b]
60 [red|in_root]
56 [red|in_root]
61
57
62 hg status . in repo root:
58 hg status . in repo root:
63
59
64 $ hg status .
60 $ hg status .
65 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4ma/1/in_a_1\x1b[0m (esc)
61 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4ma/1/in_a_1\x1b[0m (esc)
66 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4ma/in_a\x1b[0m (esc)
62 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4ma/in_a\x1b[0m (esc)
67 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4mb/1/in_b_1\x1b[0m (esc)
63 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4mb/1/in_b_1\x1b[0m (esc)
68 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4mb/2/in_b_2\x1b[0m (esc)
64 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4mb/2/in_b_2\x1b[0m (esc)
69 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4mb/in_b\x1b[0m (esc)
65 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4mb/in_b\x1b[0m (esc)
70 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4min_root\x1b[0m (esc)
66 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4min_root\x1b[0m (esc)
71
67
72 $ hg status --cwd a
68 $ hg status --cwd a
73 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4ma/1/in_a_1\x1b[0m (esc)
69 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4ma/1/in_a_1\x1b[0m (esc)
74 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4ma/in_a\x1b[0m (esc)
70 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4ma/in_a\x1b[0m (esc)
75 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4mb/1/in_b_1\x1b[0m (esc)
71 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4mb/1/in_b_1\x1b[0m (esc)
76 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4mb/2/in_b_2\x1b[0m (esc)
72 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4mb/2/in_b_2\x1b[0m (esc)
77 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4mb/in_b\x1b[0m (esc)
73 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4mb/in_b\x1b[0m (esc)
78 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4min_root\x1b[0m (esc)
74 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4min_root\x1b[0m (esc)
79 $ hg status --cwd a .
75 $ hg status --cwd a .
80 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4m1/in_a_1\x1b[0m (esc)
76 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4m1/in_a_1\x1b[0m (esc)
81 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4min_a\x1b[0m (esc)
77 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4min_a\x1b[0m (esc)
82 $ hg status --cwd a ..
78 $ hg status --cwd a ..
83 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4m1/in_a_1\x1b[0m (esc)
79 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4m1/in_a_1\x1b[0m (esc)
84 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4min_a\x1b[0m (esc)
80 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4min_a\x1b[0m (esc)
85 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4m../b/1/in_b_1\x1b[0m (esc)
81 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4m../b/1/in_b_1\x1b[0m (esc)
86 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4m../b/2/in_b_2\x1b[0m (esc)
82 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4m../b/2/in_b_2\x1b[0m (esc)
87 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4m../b/in_b\x1b[0m (esc)
83 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4m../b/in_b\x1b[0m (esc)
88 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4m../in_root\x1b[0m (esc)
84 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4m../in_root\x1b[0m (esc)
89
85
90 $ hg status --cwd b
86 $ hg status --cwd b
91 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4ma/1/in_a_1\x1b[0m (esc)
87 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4ma/1/in_a_1\x1b[0m (esc)
92 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4ma/in_a\x1b[0m (esc)
88 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4ma/in_a\x1b[0m (esc)
93 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4mb/1/in_b_1\x1b[0m (esc)
89 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4mb/1/in_b_1\x1b[0m (esc)
94 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4mb/2/in_b_2\x1b[0m (esc)
90 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4mb/2/in_b_2\x1b[0m (esc)
95 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4mb/in_b\x1b[0m (esc)
91 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4mb/in_b\x1b[0m (esc)
96 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4min_root\x1b[0m (esc)
92 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4min_root\x1b[0m (esc)
97 $ hg status --cwd b .
93 $ hg status --cwd b .
98 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4m1/in_b_1\x1b[0m (esc)
94 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4m1/in_b_1\x1b[0m (esc)
99 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4m2/in_b_2\x1b[0m (esc)
95 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4m2/in_b_2\x1b[0m (esc)
100 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4min_b\x1b[0m (esc)
96 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4min_b\x1b[0m (esc)
101 $ hg status --cwd b ..
97 $ hg status --cwd b ..
102 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4m../a/1/in_a_1\x1b[0m (esc)
98 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4m../a/1/in_a_1\x1b[0m (esc)
103 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4m../a/in_a\x1b[0m (esc)
99 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4m../a/in_a\x1b[0m (esc)
104 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4m1/in_b_1\x1b[0m (esc)
100 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4m1/in_b_1\x1b[0m (esc)
105 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4m2/in_b_2\x1b[0m (esc)
101 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4m2/in_b_2\x1b[0m (esc)
106 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4min_b\x1b[0m (esc)
102 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4min_b\x1b[0m (esc)
107 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4m../in_root\x1b[0m (esc)
103 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4m../in_root\x1b[0m (esc)
108
104
109 $ hg status --cwd a/1
105 $ hg status --cwd a/1
110 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4ma/1/in_a_1\x1b[0m (esc)
106 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4ma/1/in_a_1\x1b[0m (esc)
111 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4ma/in_a\x1b[0m (esc)
107 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4ma/in_a\x1b[0m (esc)
112 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4mb/1/in_b_1\x1b[0m (esc)
108 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4mb/1/in_b_1\x1b[0m (esc)
113 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4mb/2/in_b_2\x1b[0m (esc)
109 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4mb/2/in_b_2\x1b[0m (esc)
114 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4mb/in_b\x1b[0m (esc)
110 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4mb/in_b\x1b[0m (esc)
115 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4min_root\x1b[0m (esc)
111 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4min_root\x1b[0m (esc)
116 $ hg status --cwd a/1 .
112 $ hg status --cwd a/1 .
117 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4min_a_1\x1b[0m (esc)
113 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4min_a_1\x1b[0m (esc)
118 $ hg status --cwd a/1 ..
114 $ hg status --cwd a/1 ..
119 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4min_a_1\x1b[0m (esc)
115 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4min_a_1\x1b[0m (esc)
120 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4m../in_a\x1b[0m (esc)
116 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4m../in_a\x1b[0m (esc)
121
117
122 $ hg status --cwd b/1
118 $ hg status --cwd b/1
123 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4ma/1/in_a_1\x1b[0m (esc)
119 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4ma/1/in_a_1\x1b[0m (esc)
124 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4ma/in_a\x1b[0m (esc)
120 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4ma/in_a\x1b[0m (esc)
125 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4mb/1/in_b_1\x1b[0m (esc)
121 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4mb/1/in_b_1\x1b[0m (esc)
126 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4mb/2/in_b_2\x1b[0m (esc)
122 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4mb/2/in_b_2\x1b[0m (esc)
127 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4mb/in_b\x1b[0m (esc)
123 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4mb/in_b\x1b[0m (esc)
128 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4min_root\x1b[0m (esc)
124 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4min_root\x1b[0m (esc)
129 $ hg status --cwd b/1 .
125 $ hg status --cwd b/1 .
130 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4min_b_1\x1b[0m (esc)
126 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4min_b_1\x1b[0m (esc)
131 $ hg status --cwd b/1 ..
127 $ hg status --cwd b/1 ..
132 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4min_b_1\x1b[0m (esc)
128 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4min_b_1\x1b[0m (esc)
133 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4m../2/in_b_2\x1b[0m (esc)
129 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4m../2/in_b_2\x1b[0m (esc)
134 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4m../in_b\x1b[0m (esc)
130 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4m../in_b\x1b[0m (esc)
135
131
136 $ hg status --cwd b/2
132 $ hg status --cwd b/2
137 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4ma/1/in_a_1\x1b[0m (esc)
133 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4ma/1/in_a_1\x1b[0m (esc)
138 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4ma/in_a\x1b[0m (esc)
134 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4ma/in_a\x1b[0m (esc)
139 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4mb/1/in_b_1\x1b[0m (esc)
135 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4mb/1/in_b_1\x1b[0m (esc)
140 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4mb/2/in_b_2\x1b[0m (esc)
136 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4mb/2/in_b_2\x1b[0m (esc)
141 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4mb/in_b\x1b[0m (esc)
137 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4mb/in_b\x1b[0m (esc)
142 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4min_root\x1b[0m (esc)
138 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4min_root\x1b[0m (esc)
143 $ hg status --cwd b/2 .
139 $ hg status --cwd b/2 .
144 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4min_b_2\x1b[0m (esc)
140 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4min_b_2\x1b[0m (esc)
145 $ hg status --cwd b/2 ..
141 $ hg status --cwd b/2 ..
146 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4m../1/in_b_1\x1b[0m (esc)
142 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4m../1/in_b_1\x1b[0m (esc)
147 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4min_b_2\x1b[0m (esc)
143 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4min_b_2\x1b[0m (esc)
148 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4m../in_b\x1b[0m (esc)
144 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4m../in_b\x1b[0m (esc)
149
145
150 Make sure --color=never works
146 Make sure --color=never works
151 $ hg status --color=never
147 $ hg status --color=never
152 ? a/1/in_a_1
148 ? a/1/in_a_1
153 ? a/in_a
149 ? a/in_a
154 ? b/1/in_b_1
150 ? b/1/in_b_1
155 ? b/2/in_b_2
151 ? b/2/in_b_2
156 ? b/in_b
152 ? b/in_b
157 ? in_root
153 ? in_root
158
154
159 Make sure ui.formatted=False works
155 Make sure ui.formatted=False works
160 $ hg status --color=auto --config ui.formatted=False
156 $ hg status --color=auto --config ui.formatted=False
161 ? a/1/in_a_1
157 ? a/1/in_a_1
162 ? a/in_a
158 ? a/in_a
163 ? b/1/in_b_1
159 ? b/1/in_b_1
164 ? b/2/in_b_2
160 ? b/2/in_b_2
165 ? b/in_b
161 ? b/in_b
166 ? in_root
162 ? in_root
167
163
168 $ cd ..
164 $ cd ..
169
165
170 $ hg init repo2
166 $ hg init repo2
171 $ cd repo2
167 $ cd repo2
172 $ touch modified removed deleted ignored
168 $ touch modified removed deleted ignored
173 $ echo "^ignored$" > .hgignore
169 $ echo "^ignored$" > .hgignore
174 $ hg ci -A -m 'initial checkin'
170 $ hg ci -A -m 'initial checkin'
175 \x1b[0;32madding .hgignore\x1b[0m (esc)
171 \x1b[0;32madding .hgignore\x1b[0m (esc)
176 \x1b[0;32madding deleted\x1b[0m (esc)
172 \x1b[0;32madding deleted\x1b[0m (esc)
177 \x1b[0;32madding modified\x1b[0m (esc)
173 \x1b[0;32madding modified\x1b[0m (esc)
178 \x1b[0;32madding removed\x1b[0m (esc)
174 \x1b[0;32madding removed\x1b[0m (esc)
179 $ hg log --color=debug
175 $ hg log --color=debug
180 [log.changeset changeset.draft|changeset: 0:389aef86a55e]
176 [log.changeset changeset.draft|changeset: 0:389aef86a55e]
181 [log.tag|tag: tip]
177 [log.tag|tag: tip]
182 [log.user|user: test]
178 [log.user|user: test]
183 [log.date|date: Thu Jan 01 00:00:00 1970 +0000]
179 [log.date|date: Thu Jan 01 00:00:00 1970 +0000]
184 [log.summary|summary: initial checkin]
180 [log.summary|summary: initial checkin]
185
181
186 $ hg log -Tcompact --color=debug
182 $ hg log -Tcompact --color=debug
187 [log.changeset changeset.draft|0][tip] [log.node|389aef86a55e] [log.date|1970-01-01 00:00 +0000] [log.user|test]
183 [log.changeset changeset.draft|0][tip] [log.node|389aef86a55e] [log.date|1970-01-01 00:00 +0000] [log.user|test]
188 [ui.note log.description|initial checkin]
184 [ui.note log.description|initial checkin]
189
185
190 Labels on empty strings should not be displayed, labels on custom
186 Labels on empty strings should not be displayed, labels on custom
191 templates should be.
187 templates should be.
192
188
193 $ hg log --color=debug -T '{label("my.label",author)}\n{label("skipped.label","")}'
189 $ hg log --color=debug -T '{label("my.label",author)}\n{label("skipped.label","")}'
194 [my.label|test]
190 [my.label|test]
195 $ touch modified added unknown ignored
191 $ touch modified added unknown ignored
196 $ hg add added
192 $ hg add added
197 $ hg remove removed
193 $ hg remove removed
198 $ rm deleted
194 $ rm deleted
199
195
200 hg status:
196 hg status:
201
197
202 $ hg status
198 $ hg status
203 \x1b[0;32;1mA \x1b[0m\x1b[0;32;1madded\x1b[0m (esc)
199 \x1b[0;32;1mA \x1b[0m\x1b[0;32;1madded\x1b[0m (esc)
204 \x1b[0;31;1mR \x1b[0m\x1b[0;31;1mremoved\x1b[0m (esc)
200 \x1b[0;31;1mR \x1b[0m\x1b[0;31;1mremoved\x1b[0m (esc)
205 \x1b[0;36;1;4m! \x1b[0m\x1b[0;36;1;4mdeleted\x1b[0m (esc)
201 \x1b[0;36;1;4m! \x1b[0m\x1b[0;36;1;4mdeleted\x1b[0m (esc)
206 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4munknown\x1b[0m (esc)
202 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4munknown\x1b[0m (esc)
207
203
208 hg status modified added removed deleted unknown never-existed ignored:
204 hg status modified added removed deleted unknown never-existed ignored:
209
205
210 $ hg status modified added removed deleted unknown never-existed ignored
206 $ hg status modified added removed deleted unknown never-existed ignored
211 never-existed: * (glob)
207 never-existed: * (glob)
212 \x1b[0;32;1mA \x1b[0m\x1b[0;32;1madded\x1b[0m (esc)
208 \x1b[0;32;1mA \x1b[0m\x1b[0;32;1madded\x1b[0m (esc)
213 \x1b[0;31;1mR \x1b[0m\x1b[0;31;1mremoved\x1b[0m (esc)
209 \x1b[0;31;1mR \x1b[0m\x1b[0;31;1mremoved\x1b[0m (esc)
214 \x1b[0;36;1;4m! \x1b[0m\x1b[0;36;1;4mdeleted\x1b[0m (esc)
210 \x1b[0;36;1;4m! \x1b[0m\x1b[0;36;1;4mdeleted\x1b[0m (esc)
215 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4munknown\x1b[0m (esc)
211 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4munknown\x1b[0m (esc)
216
212
217 $ hg copy modified copied
213 $ hg copy modified copied
218
214
219 hg status -C:
215 hg status -C:
220
216
221 $ hg status -C
217 $ hg status -C
222 \x1b[0;32;1mA \x1b[0m\x1b[0;32;1madded\x1b[0m (esc)
218 \x1b[0;32;1mA \x1b[0m\x1b[0;32;1madded\x1b[0m (esc)
223 \x1b[0;32;1mA \x1b[0m\x1b[0;32;1mcopied\x1b[0m (esc)
219 \x1b[0;32;1mA \x1b[0m\x1b[0;32;1mcopied\x1b[0m (esc)
224 \x1b[0;0m modified\x1b[0m (esc)
220 \x1b[0;0m modified\x1b[0m (esc)
225 \x1b[0;31;1mR \x1b[0m\x1b[0;31;1mremoved\x1b[0m (esc)
221 \x1b[0;31;1mR \x1b[0m\x1b[0;31;1mremoved\x1b[0m (esc)
226 \x1b[0;36;1;4m! \x1b[0m\x1b[0;36;1;4mdeleted\x1b[0m (esc)
222 \x1b[0;36;1;4m! \x1b[0m\x1b[0;36;1;4mdeleted\x1b[0m (esc)
227 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4munknown\x1b[0m (esc)
223 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4munknown\x1b[0m (esc)
228
224
229 hg status -A:
225 hg status -A:
230
226
231 $ hg status -A
227 $ hg status -A
232 \x1b[0;32;1mA \x1b[0m\x1b[0;32;1madded\x1b[0m (esc)
228 \x1b[0;32;1mA \x1b[0m\x1b[0;32;1madded\x1b[0m (esc)
233 \x1b[0;32;1mA \x1b[0m\x1b[0;32;1mcopied\x1b[0m (esc)
229 \x1b[0;32;1mA \x1b[0m\x1b[0;32;1mcopied\x1b[0m (esc)
234 \x1b[0;0m modified\x1b[0m (esc)
230 \x1b[0;0m modified\x1b[0m (esc)
235 \x1b[0;31;1mR \x1b[0m\x1b[0;31;1mremoved\x1b[0m (esc)
231 \x1b[0;31;1mR \x1b[0m\x1b[0;31;1mremoved\x1b[0m (esc)
236 \x1b[0;36;1;4m! \x1b[0m\x1b[0;36;1;4mdeleted\x1b[0m (esc)
232 \x1b[0;36;1;4m! \x1b[0m\x1b[0;36;1;4mdeleted\x1b[0m (esc)
237 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4munknown\x1b[0m (esc)
233 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4munknown\x1b[0m (esc)
238 \x1b[0;30;1mI \x1b[0m\x1b[0;30;1mignored\x1b[0m (esc)
234 \x1b[0;30;1mI \x1b[0m\x1b[0;30;1mignored\x1b[0m (esc)
239 \x1b[0;0mC \x1b[0m\x1b[0;0m.hgignore\x1b[0m (esc)
235 \x1b[0;0mC \x1b[0m\x1b[0;0m.hgignore\x1b[0m (esc)
240 \x1b[0;0mC \x1b[0m\x1b[0;0mmodified\x1b[0m (esc)
236 \x1b[0;0mC \x1b[0m\x1b[0;0mmodified\x1b[0m (esc)
241
237
242
238
243 hg status -A (with terminfo color):
239 hg status -A (with terminfo color):
244
240
245 #if tic
241 #if tic
246
242
247 $ tic -o "$TESTTMP/terminfo" "$TESTDIR/hgterm.ti"
243 $ tic -o "$TESTTMP/terminfo" "$TESTDIR/hgterm.ti"
248 $ ln -s "$TESTTMP/terminfo" "$TESTTMP/terminfo.cdb"
244 $ ln -s "$TESTTMP/terminfo" "$TESTTMP/terminfo.cdb"
249 $ TERM=hgterm TERMINFO="$TESTTMP/terminfo" hg status --config color.mode=terminfo -A
245 $ TERM=hgterm TERMINFO="$TESTTMP/terminfo" hg status --config color.mode=terminfo -A
250 \x1b[30m\x1b[32m\x1b[1mA \x1b[30m\x1b[30m\x1b[32m\x1b[1madded\x1b[30m (esc)
246 \x1b[30m\x1b[32m\x1b[1mA \x1b[30m\x1b[30m\x1b[32m\x1b[1madded\x1b[30m (esc)
251 \x1b[30m\x1b[32m\x1b[1mA \x1b[30m\x1b[30m\x1b[32m\x1b[1mcopied\x1b[30m (esc)
247 \x1b[30m\x1b[32m\x1b[1mA \x1b[30m\x1b[30m\x1b[32m\x1b[1mcopied\x1b[30m (esc)
252 \x1b[30m\x1b[30m modified\x1b[30m (esc)
248 \x1b[30m\x1b[30m modified\x1b[30m (esc)
253 \x1b[30m\x1b[31m\x1b[1mR \x1b[30m\x1b[30m\x1b[31m\x1b[1mremoved\x1b[30m (esc)
249 \x1b[30m\x1b[31m\x1b[1mR \x1b[30m\x1b[30m\x1b[31m\x1b[1mremoved\x1b[30m (esc)
254 \x1b[30m\x1b[36m\x1b[1m\x1b[4m! \x1b[30m\x1b[30m\x1b[36m\x1b[1m\x1b[4mdeleted\x1b[30m (esc)
250 \x1b[30m\x1b[36m\x1b[1m\x1b[4m! \x1b[30m\x1b[30m\x1b[36m\x1b[1m\x1b[4mdeleted\x1b[30m (esc)
255 \x1b[30m\x1b[35m\x1b[1m\x1b[4m? \x1b[30m\x1b[30m\x1b[35m\x1b[1m\x1b[4munknown\x1b[30m (esc)
251 \x1b[30m\x1b[35m\x1b[1m\x1b[4m? \x1b[30m\x1b[30m\x1b[35m\x1b[1m\x1b[4munknown\x1b[30m (esc)
256 \x1b[30m\x1b[30m\x1b[1mI \x1b[30m\x1b[30m\x1b[30m\x1b[1mignored\x1b[30m (esc)
252 \x1b[30m\x1b[30m\x1b[1mI \x1b[30m\x1b[30m\x1b[30m\x1b[1mignored\x1b[30m (esc)
257 \x1b[30m\x1b[30mC \x1b[30m\x1b[30m\x1b[30m.hgignore\x1b[30m (esc)
253 \x1b[30m\x1b[30mC \x1b[30m\x1b[30m\x1b[30m.hgignore\x1b[30m (esc)
258 \x1b[30m\x1b[30mC \x1b[30m\x1b[30m\x1b[30mmodified\x1b[30m (esc)
254 \x1b[30m\x1b[30mC \x1b[30m\x1b[30m\x1b[30mmodified\x1b[30m (esc)
259
255
260 The user can define effects with raw terminfo codes:
256 The user can define effects with raw terminfo codes:
261
257
262 $ cat <<EOF >> $HGRCPATH
258 $ cat <<EOF >> $HGRCPATH
263 > # Completely bogus code for dim
259 > # Completely bogus code for dim
264 > terminfo.dim = \E[88m
260 > terminfo.dim = \E[88m
265 > # We can override what's in the terminfo database, too
261 > # We can override what's in the terminfo database, too
266 > terminfo.bold = \E[2m
262 > terminfo.bold = \E[2m
267 > EOF
263 > EOF
268 $ TERM=hgterm TERMINFO="$TESTTMP/terminfo" hg status --config color.mode=terminfo --config color.status.clean=dim -A
264 $ TERM=hgterm TERMINFO="$TESTTMP/terminfo" hg status --config color.mode=terminfo --config color.status.clean=dim -A
269 \x1b[30m\x1b[32m\x1b[2mA \x1b[30m\x1b[30m\x1b[32m\x1b[2madded\x1b[30m (esc)
265 \x1b[30m\x1b[32m\x1b[2mA \x1b[30m\x1b[30m\x1b[32m\x1b[2madded\x1b[30m (esc)
270 \x1b[30m\x1b[32m\x1b[2mA \x1b[30m\x1b[30m\x1b[32m\x1b[2mcopied\x1b[30m (esc)
266 \x1b[30m\x1b[32m\x1b[2mA \x1b[30m\x1b[30m\x1b[32m\x1b[2mcopied\x1b[30m (esc)
271 \x1b[30m\x1b[30m modified\x1b[30m (esc)
267 \x1b[30m\x1b[30m modified\x1b[30m (esc)
272 \x1b[30m\x1b[31m\x1b[2mR \x1b[30m\x1b[30m\x1b[31m\x1b[2mremoved\x1b[30m (esc)
268 \x1b[30m\x1b[31m\x1b[2mR \x1b[30m\x1b[30m\x1b[31m\x1b[2mremoved\x1b[30m (esc)
273 \x1b[30m\x1b[36m\x1b[2m\x1b[4m! \x1b[30m\x1b[30m\x1b[36m\x1b[2m\x1b[4mdeleted\x1b[30m (esc)
269 \x1b[30m\x1b[36m\x1b[2m\x1b[4m! \x1b[30m\x1b[30m\x1b[36m\x1b[2m\x1b[4mdeleted\x1b[30m (esc)
274 \x1b[30m\x1b[35m\x1b[2m\x1b[4m? \x1b[30m\x1b[30m\x1b[35m\x1b[2m\x1b[4munknown\x1b[30m (esc)
270 \x1b[30m\x1b[35m\x1b[2m\x1b[4m? \x1b[30m\x1b[30m\x1b[35m\x1b[2m\x1b[4munknown\x1b[30m (esc)
275 \x1b[30m\x1b[30m\x1b[2mI \x1b[30m\x1b[30m\x1b[30m\x1b[2mignored\x1b[30m (esc)
271 \x1b[30m\x1b[30m\x1b[2mI \x1b[30m\x1b[30m\x1b[30m\x1b[2mignored\x1b[30m (esc)
276 \x1b[30m\x1b[88mC \x1b[30m\x1b[30m\x1b[88m.hgignore\x1b[30m (esc)
272 \x1b[30m\x1b[88mC \x1b[30m\x1b[30m\x1b[88m.hgignore\x1b[30m (esc)
277 \x1b[30m\x1b[88mC \x1b[30m\x1b[30m\x1b[88mmodified\x1b[30m (esc)
273 \x1b[30m\x1b[88mC \x1b[30m\x1b[30m\x1b[88mmodified\x1b[30m (esc)
278
274
279 #endif
275 #endif
280
276
281
277
282 $ echo "^ignoreddir$" > .hgignore
278 $ echo "^ignoreddir$" > .hgignore
283 $ mkdir ignoreddir
279 $ mkdir ignoreddir
284 $ touch ignoreddir/file
280 $ touch ignoreddir/file
285
281
286 hg status ignoreddir/file:
282 hg status ignoreddir/file:
287
283
288 $ hg status ignoreddir/file
284 $ hg status ignoreddir/file
289
285
290 hg status -i ignoreddir/file:
286 hg status -i ignoreddir/file:
291
287
292 $ hg status -i ignoreddir/file
288 $ hg status -i ignoreddir/file
293 \x1b[0;30;1mI \x1b[0m\x1b[0;30;1mignoreddir/file\x1b[0m (esc)
289 \x1b[0;30;1mI \x1b[0m\x1b[0;30;1mignoreddir/file\x1b[0m (esc)
294 $ cd ..
290 $ cd ..
295
291
296 check 'status -q' and some combinations
292 check 'status -q' and some combinations
297
293
298 $ hg init repo3
294 $ hg init repo3
299 $ cd repo3
295 $ cd repo3
300 $ touch modified removed deleted ignored
296 $ touch modified removed deleted ignored
301 $ echo "^ignored$" > .hgignore
297 $ echo "^ignored$" > .hgignore
302 $ hg commit -A -m 'initial checkin'
298 $ hg commit -A -m 'initial checkin'
303 \x1b[0;32madding .hgignore\x1b[0m (esc)
299 \x1b[0;32madding .hgignore\x1b[0m (esc)
304 \x1b[0;32madding deleted\x1b[0m (esc)
300 \x1b[0;32madding deleted\x1b[0m (esc)
305 \x1b[0;32madding modified\x1b[0m (esc)
301 \x1b[0;32madding modified\x1b[0m (esc)
306 \x1b[0;32madding removed\x1b[0m (esc)
302 \x1b[0;32madding removed\x1b[0m (esc)
307 $ touch added unknown ignored
303 $ touch added unknown ignored
308 $ hg add added
304 $ hg add added
309 $ echo "test" >> modified
305 $ echo "test" >> modified
310 $ hg remove removed
306 $ hg remove removed
311 $ rm deleted
307 $ rm deleted
312 $ hg copy modified copied
308 $ hg copy modified copied
313
309
314 test unknown color
310 test unknown color
315
311
316 $ hg --config color.status.modified=periwinkle status
312 $ hg --config color.status.modified=periwinkle status
317 ignoring unknown color/effect 'periwinkle' (configured in color.status.modified)
313 ignoring unknown color/effect 'periwinkle' (configured in color.status.modified)
318 ignoring unknown color/effect 'periwinkle' (configured in color.status.modified)
314 ignoring unknown color/effect 'periwinkle' (configured in color.status.modified)
319 ignoring unknown color/effect 'periwinkle' (configured in color.status.modified)
315 ignoring unknown color/effect 'periwinkle' (configured in color.status.modified)
320 M modified
316 M modified
321 \x1b[0;32;1mA \x1b[0m\x1b[0;32;1madded\x1b[0m (esc)
317 \x1b[0;32;1mA \x1b[0m\x1b[0;32;1madded\x1b[0m (esc)
322 \x1b[0;32;1mA \x1b[0m\x1b[0;32;1mcopied\x1b[0m (esc)
318 \x1b[0;32;1mA \x1b[0m\x1b[0;32;1mcopied\x1b[0m (esc)
323 \x1b[0;31;1mR \x1b[0m\x1b[0;31;1mremoved\x1b[0m (esc)
319 \x1b[0;31;1mR \x1b[0m\x1b[0;31;1mremoved\x1b[0m (esc)
324 \x1b[0;36;1;4m! \x1b[0m\x1b[0;36;1;4mdeleted\x1b[0m (esc)
320 \x1b[0;36;1;4m! \x1b[0m\x1b[0;36;1;4mdeleted\x1b[0m (esc)
325 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4munknown\x1b[0m (esc)
321 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4munknown\x1b[0m (esc)
326
322
327 Run status with 2 different flags.
323 Run status with 2 different flags.
328 Check if result is the same or different.
324 Check if result is the same or different.
329 If result is not as expected, raise error
325 If result is not as expected, raise error
330
326
331 $ assert() {
327 $ assert() {
332 > hg status $1 > ../a
328 > hg status $1 > ../a
333 > hg status $2 > ../b
329 > hg status $2 > ../b
334 > if diff ../a ../b > /dev/null; then
330 > if diff ../a ../b > /dev/null; then
335 > out=0
331 > out=0
336 > else
332 > else
337 > out=1
333 > out=1
338 > fi
334 > fi
339 > if [ $3 -eq 0 ]; then
335 > if [ $3 -eq 0 ]; then
340 > df="same"
336 > df="same"
341 > else
337 > else
342 > df="different"
338 > df="different"
343 > fi
339 > fi
344 > if [ $out -ne $3 ]; then
340 > if [ $out -ne $3 ]; then
345 > echo "Error on $1 and $2, should be $df."
341 > echo "Error on $1 and $2, should be $df."
346 > fi
342 > fi
347 > }
343 > }
348
344
349 assert flag1 flag2 [0-same | 1-different]
345 assert flag1 flag2 [0-same | 1-different]
350
346
351 $ assert "-q" "-mard" 0
347 $ assert "-q" "-mard" 0
352 $ assert "-A" "-marduicC" 0
348 $ assert "-A" "-marduicC" 0
353 $ assert "-qA" "-mardcC" 0
349 $ assert "-qA" "-mardcC" 0
354 $ assert "-qAui" "-A" 0
350 $ assert "-qAui" "-A" 0
355 $ assert "-qAu" "-marducC" 0
351 $ assert "-qAu" "-marducC" 0
356 $ assert "-qAi" "-mardicC" 0
352 $ assert "-qAi" "-mardicC" 0
357 $ assert "-qu" "-u" 0
353 $ assert "-qu" "-u" 0
358 $ assert "-q" "-u" 1
354 $ assert "-q" "-u" 1
359 $ assert "-m" "-a" 1
355 $ assert "-m" "-a" 1
360 $ assert "-r" "-d" 1
356 $ assert "-r" "-d" 1
361 $ cd ..
357 $ cd ..
362
358
363 test 'resolve -l'
359 test 'resolve -l'
364
360
365 $ hg init repo4
361 $ hg init repo4
366 $ cd repo4
362 $ cd repo4
367 $ echo "file a" > a
363 $ echo "file a" > a
368 $ echo "file b" > b
364 $ echo "file b" > b
369 $ hg add a b
365 $ hg add a b
370 $ hg commit -m "initial"
366 $ hg commit -m "initial"
371 $ echo "file a change 1" > a
367 $ echo "file a change 1" > a
372 $ echo "file b change 1" > b
368 $ echo "file b change 1" > b
373 $ hg commit -m "head 1"
369 $ hg commit -m "head 1"
374 $ hg update 0
370 $ hg update 0
375 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
371 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
376 $ echo "file a change 2" > a
372 $ echo "file a change 2" > a
377 $ echo "file b change 2" > b
373 $ echo "file b change 2" > b
378 $ hg commit -m "head 2"
374 $ hg commit -m "head 2"
379 created new head
375 created new head
380 $ hg merge
376 $ hg merge
381 merging a
377 merging a
382 merging b
378 merging b
383 warning: conflicts while merging a! (edit, then use 'hg resolve --mark')
379 warning: conflicts while merging a! (edit, then use 'hg resolve --mark')
384 warning: conflicts while merging b! (edit, then use 'hg resolve --mark')
380 warning: conflicts while merging b! (edit, then use 'hg resolve --mark')
385 0 files updated, 0 files merged, 0 files removed, 2 files unresolved
381 0 files updated, 0 files merged, 0 files removed, 2 files unresolved
386 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
382 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
387 [1]
383 [1]
388 $ hg resolve -m b
384 $ hg resolve -m b
389
385
390 hg resolve with one unresolved, one resolved:
386 hg resolve with one unresolved, one resolved:
391
387
392 $ hg resolve -l
388 $ hg resolve -l
393 \x1b[0;31;1mU \x1b[0m\x1b[0;31;1ma\x1b[0m (esc)
389 \x1b[0;31;1mU \x1b[0m\x1b[0;31;1ma\x1b[0m (esc)
394 \x1b[0;32;1mR \x1b[0m\x1b[0;32;1mb\x1b[0m (esc)
390 \x1b[0;32;1mR \x1b[0m\x1b[0;32;1mb\x1b[0m (esc)
395
391
396 color coding of error message with current availability of curses
392 color coding of error message with current availability of curses
397
393
398 $ hg unknowncommand > /dev/null
394 $ hg unknowncommand > /dev/null
399 hg: unknown command 'unknowncommand'
395 hg: unknown command 'unknowncommand'
400 (use 'hg help' for a list of commands)
396 (use 'hg help' for a list of commands)
401 [10]
397 [10]
402
398
403 color coding of error message without curses
399 color coding of error message without curses
404
400
405 $ echo 'raise ImportError' > curses.py
401 $ echo 'raise ImportError' > curses.py
406 $ PYTHONPATH=`pwd`:$PYTHONPATH hg unknowncommand > /dev/null
402 $ PYTHONPATH=`pwd`:$PYTHONPATH hg unknowncommand > /dev/null
407 hg: unknown command 'unknowncommand'
403 hg: unknown command 'unknowncommand'
408 (use 'hg help' for a list of commands)
404 (use 'hg help' for a list of commands)
409 [10]
405 [10]
410
406
411 $ cd ..
407 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now