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