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 |
|
|
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 | /// | |
|
49 | /// `explicit_path` is for `--repository` command-line arguments. | |||
|
50 | pub fn find( | |||
|
51 | config: &Config, | |||
|
52 | explicit_path: Option<&Path>, | |||
|
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 | }) | |||
|
64 | } | |||
|
65 | } else { | |||
48 | let current_directory = crate::utils::current_dir()?; |
|
66 | let current_directory = crate::utils::current_dir()?; | |
49 |
// ancestors() is inclusive: it first yields `current_directory` |
|
67 | // ancestors() is inclusive: it first yields `current_directory` | |
|
68 | // as-is. | |||
50 | for ancestor in current_directory.ancestors() { |
|
69 | for ancestor in current_directory.ancestors() { | |
51 | if ancestor.join(".hg").is_dir() { |
|
70 | if ancestor.join(".hg").is_dir() { | |
52 |
return |
|
71 | return Self::new_at_path(ancestor.to_owned(), config); | |
53 | } |
|
72 | } | |
54 | } |
|
73 | } | |
55 |
Err(RepoError::NotFound { |
|
74 | Err(RepoError::NotFound { | |
|
75 | at: current_directory, | |||
|
76 | }) | |||
|
77 | } | |||
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 |
|
|
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 { |
|
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( |
|
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 |
$ |
|
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