Show More
@@ -1,51 +1,73 b'' | |||
|
1 | 1 | use crate::error::CommandError; |
|
2 | 2 | use crate::ui::Ui; |
|
3 | use clap::Arg; | |
|
3 | 4 | use clap::ArgMatches; |
|
4 | 5 | use hg::config::Config; |
|
5 | 6 | use hg::operations::cat; |
|
6 | 7 | use hg::repo::Repo; |
|
7 | 8 | use hg::utils::hg_path::HgPathBuf; |
|
8 | 9 | use micro_timer::timed; |
|
9 | 10 | use std::convert::TryFrom; |
|
10 | 11 | |
|
11 | 12 | pub const HELP_TEXT: &str = " |
|
12 | 13 | Output the current or given revision of files |
|
13 | 14 | "; |
|
14 | 15 | |
|
16 | pub fn args() -> clap::App<'static, 'static> { | |
|
17 | clap::SubCommand::with_name("cat") | |
|
18 | .arg( | |
|
19 | Arg::with_name("rev") | |
|
20 | .help("search the repository as it is in REV") | |
|
21 | .short("-r") | |
|
22 | .long("--revision") | |
|
23 | .value_name("REV") | |
|
24 | .takes_value(true), | |
|
25 | ) | |
|
26 | .arg( | |
|
27 | clap::Arg::with_name("files") | |
|
28 | .required(true) | |
|
29 | .multiple(true) | |
|
30 | .empty_values(false) | |
|
31 | .value_name("FILE") | |
|
32 | .help("Activity to start: activity@category"), | |
|
33 | ) | |
|
34 | .about(HELP_TEXT) | |
|
35 | } | |
|
36 | ||
|
15 | 37 | #[timed] |
|
16 | 38 | pub fn run( |
|
17 | 39 | ui: &Ui, |
|
18 | 40 | config: &Config, |
|
19 | 41 | args: &ArgMatches, |
|
20 | 42 | ) -> Result<(), CommandError> { |
|
21 | 43 | let rev = args.value_of("rev"); |
|
22 | 44 | let file_args = match args.values_of("files") { |
|
23 | 45 | Some(files) => files.collect(), |
|
24 | 46 | None => vec![], |
|
25 | 47 | }; |
|
26 | 48 | |
|
27 | 49 | let repo = Repo::find(config)?; |
|
28 | 50 | let cwd = hg::utils::current_dir()?; |
|
29 | 51 | |
|
30 | 52 | let mut files = vec![]; |
|
31 | 53 | for file in file_args.iter() { |
|
32 | 54 | // TODO: actually normalize `..` path segments etc? |
|
33 | 55 | let normalized = cwd.join(&file); |
|
34 | 56 | let stripped = normalized |
|
35 | 57 | .strip_prefix(&repo.working_directory_path()) |
|
36 | 58 | // TODO: error message for path arguments outside of the repo |
|
37 | 59 | .map_err(|_| CommandError::abort(""))?; |
|
38 | 60 | let hg_file = HgPathBuf::try_from(stripped.to_path_buf()) |
|
39 | 61 | .map_err(|e| CommandError::abort(e.to_string()))?; |
|
40 | 62 | files.push(hg_file); |
|
41 | 63 | } |
|
42 | 64 | |
|
43 | 65 | match rev { |
|
44 | 66 | Some(rev) => { |
|
45 | 67 | let data = cat(&repo, rev, &files).map_err(|e| (e, rev))?; |
|
46 | 68 | ui.write_stdout(&data)?; |
|
47 | 69 | Ok(()) |
|
48 | 70 | } |
|
49 | 71 | None => Err(CommandError::Unimplemented.into()), |
|
50 | 72 | } |
|
51 | 73 | } |
@@ -1,42 +1,72 b'' | |||
|
1 | 1 | use crate::error::CommandError; |
|
2 | 2 | use crate::ui::Ui; |
|
3 | use clap::Arg; | |
|
4 | use clap::ArgGroup; | |
|
3 | 5 | use clap::ArgMatches; |
|
4 | 6 | use hg::config::Config; |
|
5 | 7 | use hg::operations::{debug_data, DebugDataKind}; |
|
6 | 8 | use hg::repo::Repo; |
|
7 | 9 | use micro_timer::timed; |
|
8 | 10 | |
|
9 | 11 | pub const HELP_TEXT: &str = " |
|
10 | 12 | Dump the contents of a data file revision |
|
11 | 13 | "; |
|
12 | 14 | |
|
15 | pub fn args() -> clap::App<'static, 'static> { | |
|
16 | clap::SubCommand::with_name("debugdata") | |
|
17 | .arg( | |
|
18 | Arg::with_name("changelog") | |
|
19 | .help("open changelog") | |
|
20 | .short("-c") | |
|
21 | .long("--changelog"), | |
|
22 | ) | |
|
23 | .arg( | |
|
24 | Arg::with_name("manifest") | |
|
25 | .help("open manifest") | |
|
26 | .short("-m") | |
|
27 | .long("--manifest"), | |
|
28 | ) | |
|
29 | .group( | |
|
30 | ArgGroup::with_name("") | |
|
31 | .args(&["changelog", "manifest"]) | |
|
32 | .required(true), | |
|
33 | ) | |
|
34 | .arg( | |
|
35 | Arg::with_name("rev") | |
|
36 | .help("revision") | |
|
37 | .required(true) | |
|
38 | .value_name("REV"), | |
|
39 | ) | |
|
40 | .about(HELP_TEXT) | |
|
41 | } | |
|
42 | ||
|
13 | 43 | #[timed] |
|
14 | 44 | pub fn run( |
|
15 | 45 | ui: &Ui, |
|
16 | 46 | config: &Config, |
|
17 | 47 | args: &ArgMatches, |
|
18 | 48 | ) -> Result<(), CommandError> { |
|
19 | 49 | let rev = args |
|
20 | 50 | .value_of("rev") |
|
21 | 51 | .expect("rev should be a required argument"); |
|
22 | 52 | let kind = |
|
23 | 53 | match (args.is_present("changelog"), args.is_present("manifest")) { |
|
24 | 54 | (true, false) => DebugDataKind::Changelog, |
|
25 | 55 | (false, true) => DebugDataKind::Manifest, |
|
26 | 56 | (true, true) => { |
|
27 | 57 | unreachable!("Should not happen since options are exclusive") |
|
28 | 58 | } |
|
29 | 59 | (false, false) => { |
|
30 | 60 | unreachable!("Should not happen since options are required") |
|
31 | 61 | } |
|
32 | 62 | }; |
|
33 | 63 | |
|
34 | 64 | let repo = Repo::find(config)?; |
|
35 | 65 | let data = debug_data(&repo, rev, kind).map_err(|e| (e, rev))?; |
|
36 | 66 | |
|
37 | 67 | let mut stdout = ui.stdout_buffer(); |
|
38 | 68 | stdout.write_all(&data)?; |
|
39 | 69 | stdout.flush()?; |
|
40 | 70 | |
|
41 | 71 | Ok(()) |
|
42 | 72 | } |
@@ -1,26 +1,30 b'' | |||
|
1 | 1 | use crate::error::CommandError; |
|
2 | 2 | use crate::ui::Ui; |
|
3 | 3 | use clap::ArgMatches; |
|
4 | 4 | use hg::config::Config; |
|
5 | 5 | use hg::repo::Repo; |
|
6 | 6 | |
|
7 | 7 | pub const HELP_TEXT: &str = " |
|
8 | 8 | Print the current repo requirements. |
|
9 | 9 | "; |
|
10 | 10 | |
|
11 | pub fn args() -> clap::App<'static, 'static> { | |
|
12 | clap::SubCommand::with_name("debugrequirements").about(HELP_TEXT) | |
|
13 | } | |
|
14 | ||
|
11 | 15 | pub fn run( |
|
12 | 16 | ui: &Ui, |
|
13 | 17 | config: &Config, |
|
14 | 18 | _args: &ArgMatches, |
|
15 | 19 | ) -> Result<(), CommandError> { |
|
16 | 20 | let repo = Repo::find(config)?; |
|
17 | 21 | let mut output = String::new(); |
|
18 | 22 | let mut requirements: Vec<_> = repo.requirements().iter().collect(); |
|
19 | 23 | requirements.sort(); |
|
20 | 24 | for req in requirements { |
|
21 | 25 | output.push_str(req); |
|
22 | 26 | output.push('\n'); |
|
23 | 27 | } |
|
24 | 28 | ui.write_stdout(output.as_bytes())?; |
|
25 | 29 | Ok(()) |
|
26 | 30 | } |
@@ -1,55 +1,69 b'' | |||
|
1 | 1 | use crate::error::CommandError; |
|
2 | 2 | use crate::ui::Ui; |
|
3 | use clap::Arg; | |
|
3 | 4 | use clap::ArgMatches; |
|
4 | 5 | use hg::config::Config; |
|
5 | 6 | use hg::operations::list_rev_tracked_files; |
|
6 | 7 | use hg::operations::Dirstate; |
|
7 | 8 | use hg::repo::Repo; |
|
8 | 9 | use hg::utils::files::{get_bytes_from_path, relativize_path}; |
|
9 | 10 | use hg::utils::hg_path::{HgPath, HgPathBuf}; |
|
10 | 11 | |
|
11 | 12 | pub const HELP_TEXT: &str = " |
|
12 | 13 | List tracked files. |
|
13 | 14 | |
|
14 | 15 | Returns 0 on success. |
|
15 | 16 | "; |
|
16 | 17 | |
|
18 | pub fn args() -> clap::App<'static, 'static> { | |
|
19 | clap::SubCommand::with_name("files") | |
|
20 | .arg( | |
|
21 | Arg::with_name("rev") | |
|
22 | .help("search the repository as it is in REV") | |
|
23 | .short("-r") | |
|
24 | .long("--revision") | |
|
25 | .value_name("REV") | |
|
26 | .takes_value(true), | |
|
27 | ) | |
|
28 | .about(HELP_TEXT) | |
|
29 | } | |
|
30 | ||
|
17 | 31 | pub fn run( |
|
18 | 32 | ui: &Ui, |
|
19 | 33 | config: &Config, |
|
20 | 34 | args: &ArgMatches, |
|
21 | 35 | ) -> Result<(), CommandError> { |
|
22 | 36 | let rev = args.value_of("rev"); |
|
23 | 37 | |
|
24 | 38 | let repo = Repo::find(config)?; |
|
25 | 39 | if let Some(rev) = rev { |
|
26 | 40 | let files = |
|
27 | 41 | list_rev_tracked_files(&repo, rev).map_err(|e| (e, rev))?; |
|
28 | 42 | display_files(ui, &repo, files.iter()) |
|
29 | 43 | } else { |
|
30 | 44 | let distate = Dirstate::new(&repo)?; |
|
31 | 45 | let files = distate.tracked_files()?; |
|
32 | 46 | display_files(ui, &repo, files) |
|
33 | 47 | } |
|
34 | 48 | } |
|
35 | 49 | |
|
36 | 50 | fn display_files<'a>( |
|
37 | 51 | ui: &Ui, |
|
38 | 52 | repo: &Repo, |
|
39 | 53 | files: impl IntoIterator<Item = &'a HgPath>, |
|
40 | 54 | ) -> Result<(), CommandError> { |
|
41 | 55 | let cwd = hg::utils::current_dir()?; |
|
42 | 56 | let rooted_cwd = cwd |
|
43 | 57 | .strip_prefix(repo.working_directory_path()) |
|
44 | 58 | .expect("cwd was already checked within the repository"); |
|
45 | 59 | let rooted_cwd = HgPathBuf::from(get_bytes_from_path(rooted_cwd)); |
|
46 | 60 | |
|
47 | 61 | let mut stdout = ui.stdout_buffer(); |
|
48 | 62 | |
|
49 | 63 | for file in files { |
|
50 | 64 | stdout.write_all(relativize_path(file, &rooted_cwd).as_ref())?; |
|
51 | 65 | stdout.write_all(b"\n")?; |
|
52 | 66 | } |
|
53 | 67 | stdout.flush()?; |
|
54 | 68 | Ok(()) |
|
55 | 69 | } |
@@ -1,24 +1,28 b'' | |||
|
1 | 1 | use crate::error::CommandError; |
|
2 | 2 | use crate::ui::Ui; |
|
3 | 3 | use clap::ArgMatches; |
|
4 | 4 | use format_bytes::format_bytes; |
|
5 | 5 | use hg::config::Config; |
|
6 | 6 | use hg::repo::Repo; |
|
7 | 7 | use hg::utils::files::get_bytes_from_path; |
|
8 | 8 | |
|
9 | 9 | pub const HELP_TEXT: &str = " |
|
10 | 10 | Print the root directory of the current repository. |
|
11 | 11 | |
|
12 | 12 | Returns 0 on success. |
|
13 | 13 | "; |
|
14 | 14 | |
|
15 | pub fn args() -> clap::App<'static, 'static> { | |
|
16 | clap::SubCommand::with_name("root").about(HELP_TEXT) | |
|
17 | } | |
|
18 | ||
|
15 | 19 | pub fn run( |
|
16 | 20 | ui: &Ui, |
|
17 | 21 | config: &Config, |
|
18 | 22 | _args: &ArgMatches, |
|
19 | 23 | ) -> Result<(), CommandError> { |
|
20 | 24 | let repo = Repo::find(config)?; |
|
21 | 25 | let bytes = get_bytes_from_path(repo.working_directory_path()); |
|
22 | 26 | ui.write_stdout(&format_bytes!(b"{}\n", bytes.as_slice()))?; |
|
23 | 27 | Ok(()) |
|
24 | 28 | } |
@@ -1,137 +1,73 b'' | |||
|
1 | 1 | extern crate log; |
|
2 | 2 | use clap::App; |
|
3 | 3 | use clap::AppSettings; |
|
4 | use clap::Arg; | |
|
5 | use clap::ArgGroup; | |
|
6 | 4 | use clap::ArgMatches; |
|
7 | use clap::SubCommand; | |
|
8 | 5 | use format_bytes::format_bytes; |
|
9 | 6 | |
|
10 | 7 | mod commands; |
|
11 | 8 | mod error; |
|
12 | 9 | mod exitcode; |
|
13 | 10 | mod ui; |
|
14 | 11 | use error::CommandError; |
|
15 | 12 | |
|
16 | 13 | fn main() { |
|
17 | 14 | env_logger::init(); |
|
18 | 15 | let app = App::new("rhg") |
|
19 | 16 | .setting(AppSettings::AllowInvalidUtf8) |
|
20 | 17 | .setting(AppSettings::SubcommandRequired) |
|
21 | 18 | .setting(AppSettings::VersionlessSubcommands) |
|
22 | 19 | .version("0.0.1") |
|
23 | .subcommand( | |
|
24 | SubCommand::with_name("root").about(commands::root::HELP_TEXT), | |
|
25 | ) | |
|
26 | .subcommand( | |
|
27 | SubCommand::with_name("files") | |
|
28 | .arg( | |
|
29 | Arg::with_name("rev") | |
|
30 | .help("search the repository as it is in REV") | |
|
31 | .short("-r") | |
|
32 | .long("--revision") | |
|
33 | .value_name("REV") | |
|
34 | .takes_value(true), | |
|
35 | ) | |
|
36 | .about(commands::files::HELP_TEXT), | |
|
37 | ) | |
|
38 | .subcommand( | |
|
39 | SubCommand::with_name("cat") | |
|
40 | .arg( | |
|
41 | Arg::with_name("rev") | |
|
42 | .help("search the repository as it is in REV") | |
|
43 | .short("-r") | |
|
44 | .long("--revision") | |
|
45 | .value_name("REV") | |
|
46 | .takes_value(true), | |
|
47 | ) | |
|
48 | .arg( | |
|
49 | clap::Arg::with_name("files") | |
|
50 | .required(true) | |
|
51 | .multiple(true) | |
|
52 | .empty_values(false) | |
|
53 | .value_name("FILE") | |
|
54 | .help("Activity to start: activity@category"), | |
|
55 | ) | |
|
56 | .about(commands::cat::HELP_TEXT), | |
|
57 | ) | |
|
58 | .subcommand( | |
|
59 | SubCommand::with_name("debugdata") | |
|
60 | .about(commands::debugdata::HELP_TEXT) | |
|
61 | .arg( | |
|
62 | Arg::with_name("changelog") | |
|
63 | .help("open changelog") | |
|
64 | .short("-c") | |
|
65 | .long("--changelog"), | |
|
66 | ) | |
|
67 | .arg( | |
|
68 | Arg::with_name("manifest") | |
|
69 | .help("open manifest") | |
|
70 | .short("-m") | |
|
71 | .long("--manifest"), | |
|
72 | ) | |
|
73 | .group( | |
|
74 | ArgGroup::with_name("") | |
|
75 | .args(&["changelog", "manifest"]) | |
|
76 | .required(true), | |
|
77 | ) | |
|
78 | .arg( | |
|
79 | Arg::with_name("rev") | |
|
80 | .help("revision") | |
|
81 | .required(true) | |
|
82 | .value_name("REV"), | |
|
83 | ), | |
|
84 | ) | |
|
85 | .subcommand( | |
|
86 | SubCommand::with_name("debugrequirements") | |
|
87 | .about(commands::debugrequirements::HELP_TEXT), | |
|
88 | ); | |
|
20 | .subcommand(commands::root::args()) | |
|
21 | .subcommand(commands::files::args()) | |
|
22 | .subcommand(commands::cat::args()) | |
|
23 | .subcommand(commands::debugdata::args()) | |
|
24 | .subcommand(commands::debugrequirements::args()); | |
|
89 | 25 | |
|
90 | 26 | let matches = app.clone().get_matches_safe().unwrap_or_else(|err| { |
|
91 | 27 | let _ = ui::Ui::new().writeln_stderr_str(&err.message); |
|
92 | 28 | std::process::exit(exitcode::UNIMPLEMENTED) |
|
93 | 29 | }); |
|
94 | 30 | |
|
95 | 31 | let ui = ui::Ui::new(); |
|
96 | 32 | |
|
97 | 33 | let command_result = match_subcommand(matches, &ui); |
|
98 | 34 | |
|
99 | 35 | let exit_code = match command_result { |
|
100 | 36 | Ok(_) => exitcode::OK, |
|
101 | 37 | |
|
102 | 38 | // Exit with a specific code and no error message to let a potential |
|
103 | 39 | // wrapper script fallback to Python-based Mercurial. |
|
104 | 40 | Err(CommandError::Unimplemented) => exitcode::UNIMPLEMENTED, |
|
105 | 41 | |
|
106 | 42 | Err(CommandError::Abort { message }) => { |
|
107 | 43 | if !message.is_empty() { |
|
108 | 44 | // Ignore errors when writing to stderr, we’re already exiting |
|
109 | 45 | // with failure code so there’s not much more we can do. |
|
110 | 46 | let _ = |
|
111 | 47 | ui.write_stderr(&format_bytes!(b"abort: {}\n", message)); |
|
112 | 48 | } |
|
113 | 49 | exitcode::ABORT |
|
114 | 50 | } |
|
115 | 51 | }; |
|
116 | 52 | std::process::exit(exit_code) |
|
117 | 53 | } |
|
118 | 54 | |
|
119 | 55 | fn match_subcommand( |
|
120 | 56 | matches: ArgMatches, |
|
121 | 57 | ui: &ui::Ui, |
|
122 | 58 | ) -> Result<(), CommandError> { |
|
123 | 59 | let config = hg::config::Config::load()?; |
|
124 | 60 | |
|
125 | 61 | match matches.subcommand() { |
|
126 | 62 | ("root", Some(matches)) => commands::root::run(ui, &config, matches), |
|
127 | 63 | ("files", Some(matches)) => commands::files::run(ui, &config, matches), |
|
128 | 64 | ("cat", Some(matches)) => commands::cat::run(ui, &config, matches), |
|
129 | 65 | ("debugdata", Some(matches)) => { |
|
130 | 66 | commands::debugdata::run(ui, &config, matches) |
|
131 | 67 | } |
|
132 | 68 | ("debugrequirements", Some(matches)) => { |
|
133 | 69 | commands::debugrequirements::run(ui, &config, matches) |
|
134 | 70 | } |
|
135 | 71 | _ => unreachable!(), // Because of AppSettings::SubcommandRequired, |
|
136 | 72 | } |
|
137 | 73 | } |
General Comments 0
You need to be logged in to leave comments.
Login now