rhg: Add support for -R and --repository command-line arguments
Differential Revision: https://phab.mercurial-scm.org/D9970
--- a/rust/hg-core/src/repo.rs Mon Feb 08 21:28:52 2021 +0100
+++ b/rust/hg-core/src/repo.rs Mon Feb 08 21:37:30 2021 +0100
@@ -1,6 +1,7 @@
use crate::config::{Config, ConfigError, ConfigParseError};
use crate::errors::{HgError, IoResultExt};
use crate::requirements;
+use crate::utils::current_dir;
use crate::utils::files::get_path_from_bytes;
use memmap::{Mmap, MmapOptions};
use std::collections::HashSet;
@@ -18,7 +19,7 @@
#[derive(Debug, derive_more::From)]
pub enum RepoError {
NotFound {
- current_directory: PathBuf,
+ at: PathBuf,
},
#[from]
ConfigParseError(ConfigParseError),
@@ -44,15 +45,36 @@
impl Repo {
/// Search the current directory and its ancestores for a repository:
/// a working directory that contains a `.hg` sub-directory.
- pub fn find(config: &Config) -> Result<Self, RepoError> {
- let current_directory = crate::utils::current_dir()?;
- // ancestors() is inclusive: it first yields `current_directory` as-is.
- for ancestor in current_directory.ancestors() {
- if ancestor.join(".hg").is_dir() {
- return Ok(Self::new_at_path(ancestor.to_owned(), config)?);
+ ///
+ /// `explicit_path` is for `--repository` command-line arguments.
+ pub fn find(
+ config: &Config,
+ explicit_path: Option<&Path>,
+ ) -> Result<Self, RepoError> {
+ if let Some(root) = explicit_path {
+ // Having an absolute path isn’t necessary here but can help code
+ // elsewhere
+ let root = current_dir()?.join(root);
+ if root.join(".hg").is_dir() {
+ Self::new_at_path(root, config)
+ } else {
+ Err(RepoError::NotFound {
+ at: root.to_owned(),
+ })
}
+ } else {
+ let current_directory = crate::utils::current_dir()?;
+ // ancestors() is inclusive: it first yields `current_directory`
+ // as-is.
+ for ancestor in current_directory.ancestors() {
+ if ancestor.join(".hg").is_dir() {
+ return Self::new_at_path(ancestor.to_owned(), config);
+ }
+ }
+ Err(RepoError::NotFound {
+ at: current_directory,
+ })
}
- Err(RepoError::NotFound { current_directory })
}
/// To be called after checking that `.hg` is a sub-directory
--- a/rust/rhg/src/commands/cat.rs Mon Feb 08 21:28:52 2021 +0100
+++ b/rust/rhg/src/commands/cat.rs Mon Feb 08 21:37:30 2021 +0100
@@ -8,6 +8,7 @@
use hg::utils::hg_path::HgPathBuf;
use micro_timer::timed;
use std::convert::TryFrom;
+use std::path::Path;
pub const HELP_TEXT: &str = "
Output the current or given revision of files
@@ -38,6 +39,7 @@
pub fn run(
ui: &Ui,
config: &Config,
+ repo_path: Option<&Path>,
args: &ArgMatches,
) -> Result<(), CommandError> {
let rev = args.value_of("rev");
@@ -46,7 +48,7 @@
None => vec![],
};
- let repo = Repo::find(config)?;
+ let repo = Repo::find(config, repo_path)?;
let cwd = hg::utils::current_dir()?;
let mut files = vec![];
--- a/rust/rhg/src/commands/debugdata.rs Mon Feb 08 21:28:52 2021 +0100
+++ b/rust/rhg/src/commands/debugdata.rs Mon Feb 08 21:37:30 2021 +0100
@@ -7,6 +7,7 @@
use hg::operations::{debug_data, DebugDataKind};
use hg::repo::Repo;
use micro_timer::timed;
+use std::path::Path;
pub const HELP_TEXT: &str = "
Dump the contents of a data file revision
@@ -44,6 +45,7 @@
pub fn run(
ui: &Ui,
config: &Config,
+ repo_path: Option<&Path>,
args: &ArgMatches,
) -> Result<(), CommandError> {
let rev = args
@@ -61,7 +63,7 @@
}
};
- let repo = Repo::find(config)?;
+ let repo = Repo::find(config, repo_path)?;
let data = debug_data(&repo, rev, kind).map_err(|e| (e, rev))?;
let mut stdout = ui.stdout_buffer();
--- a/rust/rhg/src/commands/debugrequirements.rs Mon Feb 08 21:28:52 2021 +0100
+++ b/rust/rhg/src/commands/debugrequirements.rs Mon Feb 08 21:37:30 2021 +0100
@@ -3,6 +3,7 @@
use clap::ArgMatches;
use hg::config::Config;
use hg::repo::Repo;
+use std::path::Path;
pub const HELP_TEXT: &str = "
Print the current repo requirements.
@@ -15,9 +16,10 @@
pub fn run(
ui: &Ui,
config: &Config,
+ repo_path: Option<&Path>,
_args: &ArgMatches,
) -> Result<(), CommandError> {
- let repo = Repo::find(config)?;
+ let repo = Repo::find(config, repo_path)?;
let mut output = String::new();
let mut requirements: Vec<_> = repo.requirements().iter().collect();
requirements.sort();
--- a/rust/rhg/src/commands/files.rs Mon Feb 08 21:28:52 2021 +0100
+++ b/rust/rhg/src/commands/files.rs Mon Feb 08 21:37:30 2021 +0100
@@ -8,6 +8,7 @@
use hg::repo::Repo;
use hg::utils::files::{get_bytes_from_path, relativize_path};
use hg::utils::hg_path::{HgPath, HgPathBuf};
+use std::path::Path;
pub const HELP_TEXT: &str = "
List tracked files.
@@ -31,11 +32,12 @@
pub fn run(
ui: &Ui,
config: &Config,
+ repo_path: Option<&Path>,
args: &ArgMatches,
) -> Result<(), CommandError> {
let rev = args.value_of("rev");
- let repo = Repo::find(config)?;
+ let repo = Repo::find(config, repo_path)?;
if let Some(rev) = rev {
let files =
list_rev_tracked_files(&repo, rev).map_err(|e| (e, rev))?;
@@ -52,16 +54,15 @@
repo: &Repo,
files: impl IntoIterator<Item = &'a HgPath>,
) -> Result<(), CommandError> {
- let cwd = hg::utils::current_dir()?;
- let rooted_cwd = cwd
- .strip_prefix(repo.working_directory_path())
- .expect("cwd was already checked within the repository");
- let rooted_cwd = HgPathBuf::from(get_bytes_from_path(rooted_cwd));
+ let cwd = HgPathBuf::from(get_bytes_from_path(hg::utils::current_dir()?));
+ let working_directory =
+ HgPathBuf::from(get_bytes_from_path(repo.working_directory_path()));
let mut stdout = ui.stdout_buffer();
for file in files {
- stdout.write_all(relativize_path(file, &rooted_cwd).as_ref())?;
+ let file = working_directory.join(file);
+ stdout.write_all(relativize_path(&file, &cwd).as_ref())?;
stdout.write_all(b"\n")?;
}
stdout.flush()?;
--- a/rust/rhg/src/commands/root.rs Mon Feb 08 21:28:52 2021 +0100
+++ b/rust/rhg/src/commands/root.rs Mon Feb 08 21:37:30 2021 +0100
@@ -5,6 +5,7 @@
use hg::config::Config;
use hg::repo::Repo;
use hg::utils::files::get_bytes_from_path;
+use std::path::Path;
pub const HELP_TEXT: &str = "
Print the root directory of the current repository.
@@ -19,9 +20,10 @@
pub fn run(
ui: &Ui,
config: &Config,
+ repo_path: Option<&Path>,
_args: &ArgMatches,
) -> Result<(), CommandError> {
- let repo = Repo::find(config)?;
+ let repo = Repo::find(config, repo_path)?;
let bytes = get_bytes_from_path(repo.working_directory_path());
ui.write_stdout(&format_bytes!(b"{}\n", bytes.as_slice()))?;
Ok(())
--- a/rust/rhg/src/error.rs Mon Feb 08 21:28:52 2021 +0100
+++ b/rust/rhg/src/error.rs Mon Feb 08 21:37:30 2021 +0100
@@ -54,10 +54,10 @@
impl From<RepoError> for CommandError {
fn from(error: RepoError) -> Self {
match error {
- RepoError::NotFound { current_directory } => CommandError::Abort {
+ RepoError::NotFound { at } => CommandError::Abort {
message: format_bytes!(
b"no repository found in '{}' (.hg not found)!",
- get_bytes_from_path(current_directory)
+ get_bytes_from_path(at)
),
},
RepoError::ConfigParseError(error) => error.into(),
--- a/rust/rhg/src/main.rs Mon Feb 08 21:28:52 2021 +0100
+++ b/rust/rhg/src/main.rs Mon Feb 08 21:37:30 2021 +0100
@@ -1,14 +1,27 @@
extern crate log;
use clap::App;
use clap::AppSettings;
+use clap::Arg;
use clap::ArgMatches;
use format_bytes::format_bytes;
+use std::path::Path;
mod error;
mod exitcode;
mod ui;
use error::CommandError;
+fn add_global_args<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b> {
+ app.arg(
+ Arg::with_name("repository")
+ .help("repository root directory")
+ .short("-R")
+ .long("--repository")
+ .value_name("REPO")
+ .takes_value(true),
+ )
+}
+
fn main() {
env_logger::init();
let app = App::new("rhg")
@@ -16,6 +29,7 @@
.setting(AppSettings::SubcommandRequired)
.setting(AppSettings::VersionlessSubcommands)
.version("0.0.1");
+ let app = add_global_args(app);
let app = add_subcommand_args(app);
let ui = ui::Ui::new();
@@ -24,15 +38,22 @@
let _ = ui.writeln_stderr_str(&err.message);
std::process::exit(exitcode::UNIMPLEMENTED)
});
+
let (subcommand_name, subcommand_matches) = matches.subcommand();
let run = subcommand_run_fn(subcommand_name)
.expect("unknown subcommand name from clap despite AppSettings::SubcommandRequired");
let args = subcommand_matches
.expect("no subcommand arguments from clap despite AppSettings::SubcommandRequired");
+ // Global arguments can be in either based on e.g. `hg -R ./foo log` v.s.
+ // `hg log -R ./foo`
+ let global_arg =
+ |name| args.value_of_os(name).or_else(|| matches.value_of_os(name));
+
+ let repo_path = global_arg("repository").map(Path::new);
let result = (|| -> Result<(), CommandError> {
let config = hg::config::Config::load()?;
- run(&ui, &config, args)
+ run(&ui, &config, repo_path, args)
})();
let exit_code = match result {
@@ -66,13 +87,14 @@
fn add_subcommand_args<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b> {
app
$(
- .subcommand(commands::$command::args())
+ .subcommand(add_global_args(commands::$command::args()))
)+
}
fn subcommand_run_fn(name: &str) -> Option<fn(
&ui::Ui,
&hg::config::Config,
+ Option<&Path>,
&ArgMatches,
) -> Result<(), CommandError>> {
match name {
--- a/tests/test-rhg.t Mon Feb 08 21:28:52 2021 +0100
+++ b/tests/test-rhg.t Mon Feb 08 21:37:30 2021 +0100
@@ -15,7 +15,7 @@
error: Found argument 'unimplemented-command' which wasn't expected, or isn't valid in this context
USAGE:
- rhg <SUBCOMMAND>
+ rhg [OPTIONS] <SUBCOMMAND>
For more information try --help
[252]
@@ -204,35 +204,30 @@
$ cd $TESTTMP
$ hg init repo1
- $ cd repo1
- $ echo a > a
- $ hg commit -A -m'init'
+ $ echo a > repo1/a
+ $ hg -R repo1 commit -A -m'init'
adding a
- $ cd ..
$ hg share repo1 repo2
updating working directory
1 files updated, 0 files merged, 0 files removed, 0 files unresolved
And check that basic rhg commands work with sharing
- $ cd repo2
- $ rhg files
- a
- $ rhg cat -r 0 a
+ $ rhg files -R repo2
+ repo2/a
+ $ rhg -R repo2 cat -r 0 repo2/a
a
Same with relative sharing
- $ cd ..
$ hg share repo2 repo3 --relative
updating working directory
1 files updated, 0 files merged, 0 files removed, 0 files unresolved
- $ cd repo3
- $ rhg files
- a
- $ rhg cat -r 0 a
+ $ rhg files -R repo3
+ repo3/a
+ $ rhg -R repo3 cat -r 0 repo3/a
a
Same with share-safe