##// END OF EJS Templates
rhg: Add support for -R and --repository command-line arguments...
Simon Sapin -
r47231:1a00a578 default draft
parent child Browse files
Show More
@@ -1,6 +1,7 b''
1 use crate::config::{Config, ConfigError, ConfigParseError};
1 use crate::config::{Config, ConfigError, ConfigParseError};
2 use crate::errors::{HgError, IoResultExt};
2 use crate::errors::{HgError, IoResultExt};
3 use crate::requirements;
3 use crate::requirements;
4 use crate::utils::current_dir;
4 use crate::utils::files::get_path_from_bytes;
5 use crate::utils::files::get_path_from_bytes;
5 use memmap::{Mmap, MmapOptions};
6 use memmap::{Mmap, MmapOptions};
6 use std::collections::HashSet;
7 use std::collections::HashSet;
@@ -18,7 +19,7 b' pub struct Repo {'
18 #[derive(Debug, derive_more::From)]
19 #[derive(Debug, derive_more::From)]
19 pub enum RepoError {
20 pub enum RepoError {
20 NotFound {
21 NotFound {
21 current_directory: PathBuf,
22 at: PathBuf,
22 },
23 },
23 #[from]
24 #[from]
24 ConfigParseError(ConfigParseError),
25 ConfigParseError(ConfigParseError),
@@ -44,15 +45,36 b" pub(crate) struct Vfs<'a> {"
44 impl Repo {
45 impl Repo {
45 /// Search the current directory and its ancestores for a repository:
46 /// Search the current directory and its ancestores for a repository:
46 /// a working directory that contains a `.hg` sub-directory.
47 /// a working directory that contains a `.hg` sub-directory.
47 pub fn find(config: &Config) -> Result<Self, RepoError> {
48 ///
48 let current_directory = crate::utils::current_dir()?;
49 /// `explicit_path` is for `--repository` command-line arguments.
49 // ancestors() is inclusive: it first yields `current_directory` as-is.
50 pub fn find(
50 for ancestor in current_directory.ancestors() {
51 config: &Config,
51 if ancestor.join(".hg").is_dir() {
52 explicit_path: Option<&Path>,
52 return Ok(Self::new_at_path(ancestor.to_owned(), config)?);
53 ) -> Result<Self, RepoError> {
54 if let Some(root) = explicit_path {
55 // Having an absolute path isn’t necessary here but can help code
56 // elsewhere
57 let root = current_dir()?.join(root);
58 if root.join(".hg").is_dir() {
59 Self::new_at_path(root, config)
60 } else {
61 Err(RepoError::NotFound {
62 at: root.to_owned(),
63 })
53 }
64 }
65 } else {
66 let current_directory = crate::utils::current_dir()?;
67 // ancestors() is inclusive: it first yields `current_directory`
68 // as-is.
69 for ancestor in current_directory.ancestors() {
70 if ancestor.join(".hg").is_dir() {
71 return Self::new_at_path(ancestor.to_owned(), config);
72 }
73 }
74 Err(RepoError::NotFound {
75 at: current_directory,
76 })
54 }
77 }
55 Err(RepoError::NotFound { current_directory })
56 }
78 }
57
79
58 /// To be called after checking that `.hg` is a sub-directory
80 /// To be called after checking that `.hg` is a sub-directory
@@ -8,6 +8,7 b' use hg::repo::Repo;'
8 use hg::utils::hg_path::HgPathBuf;
8 use hg::utils::hg_path::HgPathBuf;
9 use micro_timer::timed;
9 use micro_timer::timed;
10 use std::convert::TryFrom;
10 use std::convert::TryFrom;
11 use std::path::Path;
11
12
12 pub const HELP_TEXT: &str = "
13 pub const HELP_TEXT: &str = "
13 Output the current or given revision of files
14 Output the current or given revision of files
@@ -38,6 +39,7 b" pub fn args() -> clap::App<'static, 'sta"
38 pub fn run(
39 pub fn run(
39 ui: &Ui,
40 ui: &Ui,
40 config: &Config,
41 config: &Config,
42 repo_path: Option<&Path>,
41 args: &ArgMatches,
43 args: &ArgMatches,
42 ) -> Result<(), CommandError> {
44 ) -> Result<(), CommandError> {
43 let rev = args.value_of("rev");
45 let rev = args.value_of("rev");
@@ -46,7 +48,7 b' pub fn run('
46 None => vec![],
48 None => vec![],
47 };
49 };
48
50
49 let repo = Repo::find(config)?;
51 let repo = Repo::find(config, repo_path)?;
50 let cwd = hg::utils::current_dir()?;
52 let cwd = hg::utils::current_dir()?;
51
53
52 let mut files = vec![];
54 let mut files = vec![];
@@ -7,6 +7,7 b' use hg::config::Config;'
7 use hg::operations::{debug_data, DebugDataKind};
7 use hg::operations::{debug_data, DebugDataKind};
8 use hg::repo::Repo;
8 use hg::repo::Repo;
9 use micro_timer::timed;
9 use micro_timer::timed;
10 use std::path::Path;
10
11
11 pub const HELP_TEXT: &str = "
12 pub const HELP_TEXT: &str = "
12 Dump the contents of a data file revision
13 Dump the contents of a data file revision
@@ -44,6 +45,7 b" pub fn args() -> clap::App<'static, 'sta"
44 pub fn run(
45 pub fn run(
45 ui: &Ui,
46 ui: &Ui,
46 config: &Config,
47 config: &Config,
48 repo_path: Option<&Path>,
47 args: &ArgMatches,
49 args: &ArgMatches,
48 ) -> Result<(), CommandError> {
50 ) -> Result<(), CommandError> {
49 let rev = args
51 let rev = args
@@ -61,7 +63,7 b' pub fn run('
61 }
63 }
62 };
64 };
63
65
64 let repo = Repo::find(config)?;
66 let repo = Repo::find(config, repo_path)?;
65 let data = debug_data(&repo, rev, kind).map_err(|e| (e, rev))?;
67 let data = debug_data(&repo, rev, kind).map_err(|e| (e, rev))?;
66
68
67 let mut stdout = ui.stdout_buffer();
69 let mut stdout = ui.stdout_buffer();
@@ -3,6 +3,7 b' use crate::ui::Ui;'
3 use clap::ArgMatches;
3 use clap::ArgMatches;
4 use hg::config::Config;
4 use hg::config::Config;
5 use hg::repo::Repo;
5 use hg::repo::Repo;
6 use std::path::Path;
6
7
7 pub const HELP_TEXT: &str = "
8 pub const HELP_TEXT: &str = "
8 Print the current repo requirements.
9 Print the current repo requirements.
@@ -15,9 +16,10 b" pub fn args() -> clap::App<'static, 'sta"
15 pub fn run(
16 pub fn run(
16 ui: &Ui,
17 ui: &Ui,
17 config: &Config,
18 config: &Config,
19 repo_path: Option<&Path>,
18 _args: &ArgMatches,
20 _args: &ArgMatches,
19 ) -> Result<(), CommandError> {
21 ) -> Result<(), CommandError> {
20 let repo = Repo::find(config)?;
22 let repo = Repo::find(config, repo_path)?;
21 let mut output = String::new();
23 let mut output = String::new();
22 let mut requirements: Vec<_> = repo.requirements().iter().collect();
24 let mut requirements: Vec<_> = repo.requirements().iter().collect();
23 requirements.sort();
25 requirements.sort();
@@ -8,6 +8,7 b' use hg::operations::Dirstate;'
8 use hg::repo::Repo;
8 use hg::repo::Repo;
9 use hg::utils::files::{get_bytes_from_path, relativize_path};
9 use hg::utils::files::{get_bytes_from_path, relativize_path};
10 use hg::utils::hg_path::{HgPath, HgPathBuf};
10 use hg::utils::hg_path::{HgPath, HgPathBuf};
11 use std::path::Path;
11
12
12 pub const HELP_TEXT: &str = "
13 pub const HELP_TEXT: &str = "
13 List tracked files.
14 List tracked files.
@@ -31,11 +32,12 b" pub fn args() -> clap::App<'static, 'sta"
31 pub fn run(
32 pub fn run(
32 ui: &Ui,
33 ui: &Ui,
33 config: &Config,
34 config: &Config,
35 repo_path: Option<&Path>,
34 args: &ArgMatches,
36 args: &ArgMatches,
35 ) -> Result<(), CommandError> {
37 ) -> Result<(), CommandError> {
36 let rev = args.value_of("rev");
38 let rev = args.value_of("rev");
37
39
38 let repo = Repo::find(config)?;
40 let repo = Repo::find(config, repo_path)?;
39 if let Some(rev) = rev {
41 if let Some(rev) = rev {
40 let files =
42 let files =
41 list_rev_tracked_files(&repo, rev).map_err(|e| (e, rev))?;
43 list_rev_tracked_files(&repo, rev).map_err(|e| (e, rev))?;
@@ -52,16 +54,15 b" fn display_files<'a>("
52 repo: &Repo,
54 repo: &Repo,
53 files: impl IntoIterator<Item = &'a HgPath>,
55 files: impl IntoIterator<Item = &'a HgPath>,
54 ) -> Result<(), CommandError> {
56 ) -> Result<(), CommandError> {
55 let cwd = hg::utils::current_dir()?;
57 let cwd = HgPathBuf::from(get_bytes_from_path(hg::utils::current_dir()?));
56 let rooted_cwd = cwd
58 let working_directory =
57 .strip_prefix(repo.working_directory_path())
59 HgPathBuf::from(get_bytes_from_path(repo.working_directory_path()));
58 .expect("cwd was already checked within the repository");
59 let rooted_cwd = HgPathBuf::from(get_bytes_from_path(rooted_cwd));
60
60
61 let mut stdout = ui.stdout_buffer();
61 let mut stdout = ui.stdout_buffer();
62
62
63 for file in files {
63 for file in files {
64 stdout.write_all(relativize_path(file, &rooted_cwd).as_ref())?;
64 let file = working_directory.join(file);
65 stdout.write_all(relativize_path(&file, &cwd).as_ref())?;
65 stdout.write_all(b"\n")?;
66 stdout.write_all(b"\n")?;
66 }
67 }
67 stdout.flush()?;
68 stdout.flush()?;
@@ -5,6 +5,7 b' use format_bytes::format_bytes;'
5 use hg::config::Config;
5 use hg::config::Config;
6 use hg::repo::Repo;
6 use hg::repo::Repo;
7 use hg::utils::files::get_bytes_from_path;
7 use hg::utils::files::get_bytes_from_path;
8 use std::path::Path;
8
9
9 pub const HELP_TEXT: &str = "
10 pub const HELP_TEXT: &str = "
10 Print the root directory of the current repository.
11 Print the root directory of the current repository.
@@ -19,9 +20,10 b" pub fn args() -> clap::App<'static, 'sta"
19 pub fn run(
20 pub fn run(
20 ui: &Ui,
21 ui: &Ui,
21 config: &Config,
22 config: &Config,
23 repo_path: Option<&Path>,
22 _args: &ArgMatches,
24 _args: &ArgMatches,
23 ) -> Result<(), CommandError> {
25 ) -> Result<(), CommandError> {
24 let repo = Repo::find(config)?;
26 let repo = Repo::find(config, repo_path)?;
25 let bytes = get_bytes_from_path(repo.working_directory_path());
27 let bytes = get_bytes_from_path(repo.working_directory_path());
26 ui.write_stdout(&format_bytes!(b"{}\n", bytes.as_slice()))?;
28 ui.write_stdout(&format_bytes!(b"{}\n", bytes.as_slice()))?;
27 Ok(())
29 Ok(())
@@ -54,10 +54,10 b' impl From<UiError> for CommandError {'
54 impl From<RepoError> for CommandError {
54 impl From<RepoError> for CommandError {
55 fn from(error: RepoError) -> Self {
55 fn from(error: RepoError) -> Self {
56 match error {
56 match error {
57 RepoError::NotFound { current_directory } => CommandError::Abort {
57 RepoError::NotFound { at } => CommandError::Abort {
58 message: format_bytes!(
58 message: format_bytes!(
59 b"no repository found in '{}' (.hg not found)!",
59 b"no repository found in '{}' (.hg not found)!",
60 get_bytes_from_path(current_directory)
60 get_bytes_from_path(at)
61 ),
61 ),
62 },
62 },
63 RepoError::ConfigParseError(error) => error.into(),
63 RepoError::ConfigParseError(error) => error.into(),
@@ -1,14 +1,27 b''
1 extern crate log;
1 extern crate log;
2 use clap::App;
2 use clap::App;
3 use clap::AppSettings;
3 use clap::AppSettings;
4 use clap::Arg;
4 use clap::ArgMatches;
5 use clap::ArgMatches;
5 use format_bytes::format_bytes;
6 use format_bytes::format_bytes;
7 use std::path::Path;
6
8
7 mod error;
9 mod error;
8 mod exitcode;
10 mod exitcode;
9 mod ui;
11 mod ui;
10 use error::CommandError;
12 use error::CommandError;
11
13
14 fn add_global_args<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b> {
15 app.arg(
16 Arg::with_name("repository")
17 .help("repository root directory")
18 .short("-R")
19 .long("--repository")
20 .value_name("REPO")
21 .takes_value(true),
22 )
23 }
24
12 fn main() {
25 fn main() {
13 env_logger::init();
26 env_logger::init();
14 let app = App::new("rhg")
27 let app = App::new("rhg")
@@ -16,6 +29,7 b' fn main() {'
16 .setting(AppSettings::SubcommandRequired)
29 .setting(AppSettings::SubcommandRequired)
17 .setting(AppSettings::VersionlessSubcommands)
30 .setting(AppSettings::VersionlessSubcommands)
18 .version("0.0.1");
31 .version("0.0.1");
32 let app = add_global_args(app);
19 let app = add_subcommand_args(app);
33 let app = add_subcommand_args(app);
20
34
21 let ui = ui::Ui::new();
35 let ui = ui::Ui::new();
@@ -24,15 +38,22 b' fn main() {'
24 let _ = ui.writeln_stderr_str(&err.message);
38 let _ = ui.writeln_stderr_str(&err.message);
25 std::process::exit(exitcode::UNIMPLEMENTED)
39 std::process::exit(exitcode::UNIMPLEMENTED)
26 });
40 });
41
27 let (subcommand_name, subcommand_matches) = matches.subcommand();
42 let (subcommand_name, subcommand_matches) = matches.subcommand();
28 let run = subcommand_run_fn(subcommand_name)
43 let run = subcommand_run_fn(subcommand_name)
29 .expect("unknown subcommand name from clap despite AppSettings::SubcommandRequired");
44 .expect("unknown subcommand name from clap despite AppSettings::SubcommandRequired");
30 let args = subcommand_matches
45 let args = subcommand_matches
31 .expect("no subcommand arguments from clap despite AppSettings::SubcommandRequired");
46 .expect("no subcommand arguments from clap despite AppSettings::SubcommandRequired");
32
47
48 // Global arguments can be in either based on e.g. `hg -R ./foo log` v.s.
49 // `hg log -R ./foo`
50 let global_arg =
51 |name| args.value_of_os(name).or_else(|| matches.value_of_os(name));
52
53 let repo_path = global_arg("repository").map(Path::new);
33 let result = (|| -> Result<(), CommandError> {
54 let result = (|| -> Result<(), CommandError> {
34 let config = hg::config::Config::load()?;
55 let config = hg::config::Config::load()?;
35 run(&ui, &config, args)
56 run(&ui, &config, repo_path, args)
36 })();
57 })();
37
58
38 let exit_code = match result {
59 let exit_code = match result {
@@ -66,13 +87,14 b' macro_rules! subcommands {'
66 fn add_subcommand_args<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b> {
87 fn add_subcommand_args<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b> {
67 app
88 app
68 $(
89 $(
69 .subcommand(commands::$command::args())
90 .subcommand(add_global_args(commands::$command::args()))
70 )+
91 )+
71 }
92 }
72
93
73 fn subcommand_run_fn(name: &str) -> Option<fn(
94 fn subcommand_run_fn(name: &str) -> Option<fn(
74 &ui::Ui,
95 &ui::Ui,
75 &hg::config::Config,
96 &hg::config::Config,
97 Option<&Path>,
76 &ArgMatches,
98 &ArgMatches,
77 ) -> Result<(), CommandError>> {
99 ) -> Result<(), CommandError>> {
78 match name {
100 match name {
@@ -15,7 +15,7 b' Unimplemented command'
15 error: Found argument 'unimplemented-command' which wasn't expected, or isn't valid in this context
15 error: Found argument 'unimplemented-command' which wasn't expected, or isn't valid in this context
16
16
17 USAGE:
17 USAGE:
18 rhg <SUBCOMMAND>
18 rhg [OPTIONS] <SUBCOMMAND>
19
19
20 For more information try --help
20 For more information try --help
21 [252]
21 [252]
@@ -204,35 +204,30 b' Crate a shared repository'
204
204
205 $ cd $TESTTMP
205 $ cd $TESTTMP
206 $ hg init repo1
206 $ hg init repo1
207 $ cd repo1
207 $ echo a > repo1/a
208 $ echo a > a
208 $ hg -R repo1 commit -A -m'init'
209 $ hg commit -A -m'init'
210 adding a
209 adding a
211
210
212 $ cd ..
213 $ hg share repo1 repo2
211 $ hg share repo1 repo2
214 updating working directory
212 updating working directory
215 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
213 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
216
214
217 And check that basic rhg commands work with sharing
215 And check that basic rhg commands work with sharing
218
216
219 $ cd repo2
217 $ rhg files -R repo2
220 $ rhg files
218 repo2/a
221 a
219 $ rhg -R repo2 cat -r 0 repo2/a
222 $ rhg cat -r 0 a
223 a
220 a
224
221
225 Same with relative sharing
222 Same with relative sharing
226
223
227 $ cd ..
228 $ hg share repo2 repo3 --relative
224 $ hg share repo2 repo3 --relative
229 updating working directory
225 updating working directory
230 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
226 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
231
227
232 $ cd repo3
228 $ rhg files -R repo3
233 $ rhg files
229 repo3/a
234 a
230 $ rhg -R repo3 cat -r 0 repo3/a
235 $ rhg cat -r 0 a
236 a
231 a
237
232
238 Same with share-safe
233 Same with share-safe
General Comments 0
You need to be logged in to leave comments. Login now