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