##// 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,216 +1,238 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;
7 use std::path::{Path, PathBuf};
8 use std::path::{Path, PathBuf};
8
9
9 /// A repository on disk
10 /// A repository on disk
10 pub struct Repo {
11 pub struct Repo {
11 working_directory: PathBuf,
12 working_directory: PathBuf,
12 dot_hg: PathBuf,
13 dot_hg: PathBuf,
13 store: PathBuf,
14 store: PathBuf,
14 requirements: HashSet<String>,
15 requirements: HashSet<String>,
15 config: Config,
16 config: Config,
16 }
17 }
17
18
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),
25 #[from]
26 #[from]
26 Other(HgError),
27 Other(HgError),
27 }
28 }
28
29
29 impl From<ConfigError> for RepoError {
30 impl From<ConfigError> for RepoError {
30 fn from(error: ConfigError) -> Self {
31 fn from(error: ConfigError) -> Self {
31 match error {
32 match error {
32 ConfigError::Parse(error) => error.into(),
33 ConfigError::Parse(error) => error.into(),
33 ConfigError::Other(error) => error.into(),
34 ConfigError::Other(error) => error.into(),
34 }
35 }
35 }
36 }
36 }
37 }
37
38
38 /// Filesystem access abstraction for the contents of a given "base" diretory
39 /// Filesystem access abstraction for the contents of a given "base" diretory
39 #[derive(Clone, Copy)]
40 #[derive(Clone, Copy)]
40 pub(crate) struct Vfs<'a> {
41 pub(crate) struct Vfs<'a> {
41 base: &'a Path,
42 base: &'a Path,
42 }
43 }
43
44
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
59 fn new_at_path(
81 fn new_at_path(
60 working_directory: PathBuf,
82 working_directory: PathBuf,
61 config: &Config,
83 config: &Config,
62 ) -> Result<Self, RepoError> {
84 ) -> Result<Self, RepoError> {
63 let dot_hg = working_directory.join(".hg");
85 let dot_hg = working_directory.join(".hg");
64
86
65 let mut repo_config_files = Vec::new();
87 let mut repo_config_files = Vec::new();
66 repo_config_files.push(dot_hg.join("hgrc"));
88 repo_config_files.push(dot_hg.join("hgrc"));
67 repo_config_files.push(dot_hg.join("hgrc-not-shared"));
89 repo_config_files.push(dot_hg.join("hgrc-not-shared"));
68
90
69 let hg_vfs = Vfs { base: &dot_hg };
91 let hg_vfs = Vfs { base: &dot_hg };
70 let mut reqs = requirements::load_if_exists(hg_vfs)?;
92 let mut reqs = requirements::load_if_exists(hg_vfs)?;
71 let relative =
93 let relative =
72 reqs.contains(requirements::RELATIVE_SHARED_REQUIREMENT);
94 reqs.contains(requirements::RELATIVE_SHARED_REQUIREMENT);
73 let shared =
95 let shared =
74 reqs.contains(requirements::SHARED_REQUIREMENT) || relative;
96 reqs.contains(requirements::SHARED_REQUIREMENT) || relative;
75
97
76 // From `mercurial/localrepo.py`:
98 // From `mercurial/localrepo.py`:
77 //
99 //
78 // if .hg/requires contains the sharesafe requirement, it means
100 // if .hg/requires contains the sharesafe requirement, it means
79 // there exists a `.hg/store/requires` too and we should read it
101 // there exists a `.hg/store/requires` too and we should read it
80 // NOTE: presence of SHARESAFE_REQUIREMENT imply that store requirement
102 // NOTE: presence of SHARESAFE_REQUIREMENT imply that store requirement
81 // is present. We never write SHARESAFE_REQUIREMENT for a repo if store
103 // is present. We never write SHARESAFE_REQUIREMENT for a repo if store
82 // is not present, refer checkrequirementscompat() for that
104 // is not present, refer checkrequirementscompat() for that
83 //
105 //
84 // However, if SHARESAFE_REQUIREMENT is not present, it means that the
106 // However, if SHARESAFE_REQUIREMENT is not present, it means that the
85 // repository was shared the old way. We check the share source
107 // repository was shared the old way. We check the share source
86 // .hg/requires for SHARESAFE_REQUIREMENT to detect whether the
108 // .hg/requires for SHARESAFE_REQUIREMENT to detect whether the
87 // current repository needs to be reshared
109 // current repository needs to be reshared
88 let share_safe = reqs.contains(requirements::SHARESAFE_REQUIREMENT);
110 let share_safe = reqs.contains(requirements::SHARESAFE_REQUIREMENT);
89
111
90 let store_path;
112 let store_path;
91 if !shared {
113 if !shared {
92 store_path = dot_hg.join("store");
114 store_path = dot_hg.join("store");
93 if share_safe {
115 if share_safe {
94 reqs.extend(requirements::load(Vfs { base: &store_path })?);
116 reqs.extend(requirements::load(Vfs { base: &store_path })?);
95 }
117 }
96 } else {
118 } else {
97 let bytes = hg_vfs.read("sharedpath")?;
119 let bytes = hg_vfs.read("sharedpath")?;
98 let mut shared_path = get_path_from_bytes(&bytes).to_owned();
120 let mut shared_path = get_path_from_bytes(&bytes).to_owned();
99 if relative {
121 if relative {
100 shared_path = dot_hg.join(shared_path)
122 shared_path = dot_hg.join(shared_path)
101 }
123 }
102 if !shared_path.is_dir() {
124 if !shared_path.is_dir() {
103 return Err(HgError::corrupted(format!(
125 return Err(HgError::corrupted(format!(
104 ".hg/sharedpath points to nonexistent directory {}",
126 ".hg/sharedpath points to nonexistent directory {}",
105 shared_path.display()
127 shared_path.display()
106 ))
128 ))
107 .into());
129 .into());
108 }
130 }
109
131
110 store_path = shared_path.join("store");
132 store_path = shared_path.join("store");
111
133
112 let source_is_share_safe =
134 let source_is_share_safe =
113 requirements::load(Vfs { base: &shared_path })?
135 requirements::load(Vfs { base: &shared_path })?
114 .contains(requirements::SHARESAFE_REQUIREMENT);
136 .contains(requirements::SHARESAFE_REQUIREMENT);
115
137
116 if share_safe && !source_is_share_safe {
138 if share_safe && !source_is_share_safe {
117 return Err(match config
139 return Err(match config
118 .get(b"safe-mismatch", b"source-not-safe")
140 .get(b"safe-mismatch", b"source-not-safe")
119 {
141 {
120 Some(b"abort") | None => HgError::abort(
142 Some(b"abort") | None => HgError::abort(
121 "share source does not support share-safe requirement",
143 "share source does not support share-safe requirement",
122 ),
144 ),
123 _ => HgError::unsupported("share-safe downgrade"),
145 _ => HgError::unsupported("share-safe downgrade"),
124 }
146 }
125 .into());
147 .into());
126 } else if source_is_share_safe && !share_safe {
148 } else if source_is_share_safe && !share_safe {
127 return Err(
149 return Err(
128 match config.get(b"safe-mismatch", b"source-safe") {
150 match config.get(b"safe-mismatch", b"source-safe") {
129 Some(b"abort") | None => HgError::abort(
151 Some(b"abort") | None => HgError::abort(
130 "version mismatch: source uses share-safe \
152 "version mismatch: source uses share-safe \
131 functionality while the current share does not",
153 functionality while the current share does not",
132 ),
154 ),
133 _ => HgError::unsupported("share-safe upgrade"),
155 _ => HgError::unsupported("share-safe upgrade"),
134 }
156 }
135 .into(),
157 .into(),
136 );
158 );
137 }
159 }
138
160
139 if share_safe {
161 if share_safe {
140 repo_config_files.insert(0, shared_path.join("hgrc"))
162 repo_config_files.insert(0, shared_path.join("hgrc"))
141 }
163 }
142 }
164 }
143
165
144 let repo_config = config.combine_with_repo(&repo_config_files)?;
166 let repo_config = config.combine_with_repo(&repo_config_files)?;
145
167
146 let repo = Self {
168 let repo = Self {
147 requirements: reqs,
169 requirements: reqs,
148 working_directory,
170 working_directory,
149 store: store_path,
171 store: store_path,
150 dot_hg,
172 dot_hg,
151 config: repo_config,
173 config: repo_config,
152 };
174 };
153
175
154 requirements::check(&repo)?;
176 requirements::check(&repo)?;
155
177
156 Ok(repo)
178 Ok(repo)
157 }
179 }
158
180
159 pub fn working_directory_path(&self) -> &Path {
181 pub fn working_directory_path(&self) -> &Path {
160 &self.working_directory
182 &self.working_directory
161 }
183 }
162
184
163 pub fn requirements(&self) -> &HashSet<String> {
185 pub fn requirements(&self) -> &HashSet<String> {
164 &self.requirements
186 &self.requirements
165 }
187 }
166
188
167 pub fn config(&self) -> &Config {
189 pub fn config(&self) -> &Config {
168 &self.config
190 &self.config
169 }
191 }
170
192
171 /// For accessing repository files (in `.hg`), except for the store
193 /// For accessing repository files (in `.hg`), except for the store
172 /// (`.hg/store`).
194 /// (`.hg/store`).
173 pub(crate) fn hg_vfs(&self) -> Vfs<'_> {
195 pub(crate) fn hg_vfs(&self) -> Vfs<'_> {
174 Vfs { base: &self.dot_hg }
196 Vfs { base: &self.dot_hg }
175 }
197 }
176
198
177 /// For accessing repository store files (in `.hg/store`)
199 /// For accessing repository store files (in `.hg/store`)
178 pub(crate) fn store_vfs(&self) -> Vfs<'_> {
200 pub(crate) fn store_vfs(&self) -> Vfs<'_> {
179 Vfs { base: &self.store }
201 Vfs { base: &self.store }
180 }
202 }
181
203
182 /// For accessing the working copy
204 /// For accessing the working copy
183
205
184 // The undescore prefix silences the "never used" warning. Remove before
206 // The undescore prefix silences the "never used" warning. Remove before
185 // using.
207 // using.
186 pub(crate) fn _working_directory_vfs(&self) -> Vfs<'_> {
208 pub(crate) fn _working_directory_vfs(&self) -> Vfs<'_> {
187 Vfs {
209 Vfs {
188 base: &self.working_directory,
210 base: &self.working_directory,
189 }
211 }
190 }
212 }
191 }
213 }
192
214
193 impl Vfs<'_> {
215 impl Vfs<'_> {
194 pub(crate) fn join(&self, relative_path: impl AsRef<Path>) -> PathBuf {
216 pub(crate) fn join(&self, relative_path: impl AsRef<Path>) -> PathBuf {
195 self.base.join(relative_path)
217 self.base.join(relative_path)
196 }
218 }
197
219
198 pub(crate) fn read(
220 pub(crate) fn read(
199 &self,
221 &self,
200 relative_path: impl AsRef<Path>,
222 relative_path: impl AsRef<Path>,
201 ) -> Result<Vec<u8>, HgError> {
223 ) -> Result<Vec<u8>, HgError> {
202 let path = self.join(relative_path);
224 let path = self.join(relative_path);
203 std::fs::read(&path).for_file(&path)
225 std::fs::read(&path).for_file(&path)
204 }
226 }
205
227
206 pub(crate) fn mmap_open(
228 pub(crate) fn mmap_open(
207 &self,
229 &self,
208 relative_path: impl AsRef<Path>,
230 relative_path: impl AsRef<Path>,
209 ) -> Result<Mmap, HgError> {
231 ) -> Result<Mmap, HgError> {
210 let path = self.base.join(relative_path);
232 let path = self.base.join(relative_path);
211 let file = std::fs::File::open(&path).for_file(&path)?;
233 let file = std::fs::File::open(&path).for_file(&path)?;
212 // TODO: what are the safety requirements here?
234 // TODO: what are the safety requirements here?
213 let mmap = unsafe { MmapOptions::new().map(&file) }.for_file(&path)?;
235 let mmap = unsafe { MmapOptions::new().map(&file) }.for_file(&path)?;
214 Ok(mmap)
236 Ok(mmap)
215 }
237 }
216 }
238 }
@@ -1,73 +1,75 b''
1 use crate::error::CommandError;
1 use crate::error::CommandError;
2 use crate::ui::Ui;
2 use crate::ui::Ui;
3 use clap::Arg;
3 use clap::Arg;
4 use clap::ArgMatches;
4 use clap::ArgMatches;
5 use hg::config::Config;
5 use hg::config::Config;
6 use hg::operations::cat;
6 use hg::operations::cat;
7 use hg::repo::Repo;
7 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
14 ";
15 ";
15
16
16 pub fn args() -> clap::App<'static, 'static> {
17 pub fn args() -> clap::App<'static, 'static> {
17 clap::SubCommand::with_name("cat")
18 clap::SubCommand::with_name("cat")
18 .arg(
19 .arg(
19 Arg::with_name("rev")
20 Arg::with_name("rev")
20 .help("search the repository as it is in REV")
21 .help("search the repository as it is in REV")
21 .short("-r")
22 .short("-r")
22 .long("--revision")
23 .long("--revision")
23 .value_name("REV")
24 .value_name("REV")
24 .takes_value(true),
25 .takes_value(true),
25 )
26 )
26 .arg(
27 .arg(
27 clap::Arg::with_name("files")
28 clap::Arg::with_name("files")
28 .required(true)
29 .required(true)
29 .multiple(true)
30 .multiple(true)
30 .empty_values(false)
31 .empty_values(false)
31 .value_name("FILE")
32 .value_name("FILE")
32 .help("Activity to start: activity@category"),
33 .help("Activity to start: activity@category"),
33 )
34 )
34 .about(HELP_TEXT)
35 .about(HELP_TEXT)
35 }
36 }
36
37
37 #[timed]
38 #[timed]
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");
44 let file_args = match args.values_of("files") {
46 let file_args = match args.values_of("files") {
45 Some(files) => files.collect(),
47 Some(files) => files.collect(),
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![];
53 for file in file_args.iter() {
55 for file in file_args.iter() {
54 // TODO: actually normalize `..` path segments etc?
56 // TODO: actually normalize `..` path segments etc?
55 let normalized = cwd.join(&file);
57 let normalized = cwd.join(&file);
56 let stripped = normalized
58 let stripped = normalized
57 .strip_prefix(&repo.working_directory_path())
59 .strip_prefix(&repo.working_directory_path())
58 // TODO: error message for path arguments outside of the repo
60 // TODO: error message for path arguments outside of the repo
59 .map_err(|_| CommandError::abort(""))?;
61 .map_err(|_| CommandError::abort(""))?;
60 let hg_file = HgPathBuf::try_from(stripped.to_path_buf())
62 let hg_file = HgPathBuf::try_from(stripped.to_path_buf())
61 .map_err(|e| CommandError::abort(e.to_string()))?;
63 .map_err(|e| CommandError::abort(e.to_string()))?;
62 files.push(hg_file);
64 files.push(hg_file);
63 }
65 }
64
66
65 match rev {
67 match rev {
66 Some(rev) => {
68 Some(rev) => {
67 let data = cat(&repo, rev, &files).map_err(|e| (e, rev))?;
69 let data = cat(&repo, rev, &files).map_err(|e| (e, rev))?;
68 ui.write_stdout(&data)?;
70 ui.write_stdout(&data)?;
69 Ok(())
71 Ok(())
70 }
72 }
71 None => Err(CommandError::Unimplemented.into()),
73 None => Err(CommandError::Unimplemented.into()),
72 }
74 }
73 }
75 }
@@ -1,72 +1,74 b''
1 use crate::error::CommandError;
1 use crate::error::CommandError;
2 use crate::ui::Ui;
2 use crate::ui::Ui;
3 use clap::Arg;
3 use clap::Arg;
4 use clap::ArgGroup;
4 use clap::ArgGroup;
5 use clap::ArgMatches;
5 use clap::ArgMatches;
6 use hg::config::Config;
6 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
13 ";
14 ";
14
15
15 pub fn args() -> clap::App<'static, 'static> {
16 pub fn args() -> clap::App<'static, 'static> {
16 clap::SubCommand::with_name("debugdata")
17 clap::SubCommand::with_name("debugdata")
17 .arg(
18 .arg(
18 Arg::with_name("changelog")
19 Arg::with_name("changelog")
19 .help("open changelog")
20 .help("open changelog")
20 .short("-c")
21 .short("-c")
21 .long("--changelog"),
22 .long("--changelog"),
22 )
23 )
23 .arg(
24 .arg(
24 Arg::with_name("manifest")
25 Arg::with_name("manifest")
25 .help("open manifest")
26 .help("open manifest")
26 .short("-m")
27 .short("-m")
27 .long("--manifest"),
28 .long("--manifest"),
28 )
29 )
29 .group(
30 .group(
30 ArgGroup::with_name("")
31 ArgGroup::with_name("")
31 .args(&["changelog", "manifest"])
32 .args(&["changelog", "manifest"])
32 .required(true),
33 .required(true),
33 )
34 )
34 .arg(
35 .arg(
35 Arg::with_name("rev")
36 Arg::with_name("rev")
36 .help("revision")
37 .help("revision")
37 .required(true)
38 .required(true)
38 .value_name("REV"),
39 .value_name("REV"),
39 )
40 )
40 .about(HELP_TEXT)
41 .about(HELP_TEXT)
41 }
42 }
42
43
43 #[timed]
44 #[timed]
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
50 .value_of("rev")
52 .value_of("rev")
51 .expect("rev should be a required argument");
53 .expect("rev should be a required argument");
52 let kind =
54 let kind =
53 match (args.is_present("changelog"), args.is_present("manifest")) {
55 match (args.is_present("changelog"), args.is_present("manifest")) {
54 (true, false) => DebugDataKind::Changelog,
56 (true, false) => DebugDataKind::Changelog,
55 (false, true) => DebugDataKind::Manifest,
57 (false, true) => DebugDataKind::Manifest,
56 (true, true) => {
58 (true, true) => {
57 unreachable!("Should not happen since options are exclusive")
59 unreachable!("Should not happen since options are exclusive")
58 }
60 }
59 (false, false) => {
61 (false, false) => {
60 unreachable!("Should not happen since options are required")
62 unreachable!("Should not happen since options are required")
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();
68 stdout.write_all(&data)?;
70 stdout.write_all(&data)?;
69 stdout.flush()?;
71 stdout.flush()?;
70
72
71 Ok(())
73 Ok(())
72 }
74 }
@@ -1,30 +1,32 b''
1 use crate::error::CommandError;
1 use crate::error::CommandError;
2 use crate::ui::Ui;
2 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.
9 ";
10 ";
10
11
11 pub fn args() -> clap::App<'static, 'static> {
12 pub fn args() -> clap::App<'static, 'static> {
12 clap::SubCommand::with_name("debugrequirements").about(HELP_TEXT)
13 clap::SubCommand::with_name("debugrequirements").about(HELP_TEXT)
13 }
14 }
14
15
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();
24 for req in requirements {
26 for req in requirements {
25 output.push_str(req);
27 output.push_str(req);
26 output.push('\n');
28 output.push('\n');
27 }
29 }
28 ui.write_stdout(output.as_bytes())?;
30 ui.write_stdout(output.as_bytes())?;
29 Ok(())
31 Ok(())
30 }
32 }
@@ -1,69 +1,70 b''
1 use crate::error::CommandError;
1 use crate::error::CommandError;
2 use crate::ui::Ui;
2 use crate::ui::Ui;
3 use clap::Arg;
3 use clap::Arg;
4 use clap::ArgMatches;
4 use clap::ArgMatches;
5 use hg::config::Config;
5 use hg::config::Config;
6 use hg::operations::list_rev_tracked_files;
6 use hg::operations::list_rev_tracked_files;
7 use hg::operations::Dirstate;
7 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.
14
15
15 Returns 0 on success.
16 Returns 0 on success.
16 ";
17 ";
17
18
18 pub fn args() -> clap::App<'static, 'static> {
19 pub fn args() -> clap::App<'static, 'static> {
19 clap::SubCommand::with_name("files")
20 clap::SubCommand::with_name("files")
20 .arg(
21 .arg(
21 Arg::with_name("rev")
22 Arg::with_name("rev")
22 .help("search the repository as it is in REV")
23 .help("search the repository as it is in REV")
23 .short("-r")
24 .short("-r")
24 .long("--revision")
25 .long("--revision")
25 .value_name("REV")
26 .value_name("REV")
26 .takes_value(true),
27 .takes_value(true),
27 )
28 )
28 .about(HELP_TEXT)
29 .about(HELP_TEXT)
29 }
30 }
30
31
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))?;
42 display_files(ui, &repo, files.iter())
44 display_files(ui, &repo, files.iter())
43 } else {
45 } else {
44 let distate = Dirstate::new(&repo)?;
46 let distate = Dirstate::new(&repo)?;
45 let files = distate.tracked_files()?;
47 let files = distate.tracked_files()?;
46 display_files(ui, &repo, files)
48 display_files(ui, &repo, files)
47 }
49 }
48 }
50 }
49
51
50 fn display_files<'a>(
52 fn display_files<'a>(
51 ui: &Ui,
53 ui: &Ui,
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()?;
68 Ok(())
69 Ok(())
69 }
70 }
@@ -1,28 +1,30 b''
1 use crate::error::CommandError;
1 use crate::error::CommandError;
2 use crate::ui::Ui;
2 use crate::ui::Ui;
3 use clap::ArgMatches;
3 use clap::ArgMatches;
4 use format_bytes::format_bytes;
4 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.
11
12
12 Returns 0 on success.
13 Returns 0 on success.
13 ";
14 ";
14
15
15 pub fn args() -> clap::App<'static, 'static> {
16 pub fn args() -> clap::App<'static, 'static> {
16 clap::SubCommand::with_name("root").about(HELP_TEXT)
17 clap::SubCommand::with_name("root").about(HELP_TEXT)
17 }
18 }
18
19
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(())
28 }
30 }
@@ -1,115 +1,115 b''
1 use crate::ui::utf8_to_local;
1 use crate::ui::utf8_to_local;
2 use crate::ui::UiError;
2 use crate::ui::UiError;
3 use format_bytes::format_bytes;
3 use format_bytes::format_bytes;
4 use hg::config::{ConfigError, ConfigParseError};
4 use hg::config::{ConfigError, ConfigParseError};
5 use hg::errors::HgError;
5 use hg::errors::HgError;
6 use hg::repo::RepoError;
6 use hg::repo::RepoError;
7 use hg::revlog::revlog::RevlogError;
7 use hg::revlog::revlog::RevlogError;
8 use hg::utils::files::get_bytes_from_path;
8 use hg::utils::files::get_bytes_from_path;
9 use std::convert::From;
9 use std::convert::From;
10
10
11 /// The kind of command error
11 /// The kind of command error
12 #[derive(Debug)]
12 #[derive(Debug)]
13 pub enum CommandError {
13 pub enum CommandError {
14 /// Exit with an error message and "standard" failure exit code.
14 /// Exit with an error message and "standard" failure exit code.
15 Abort { message: Vec<u8> },
15 Abort { message: Vec<u8> },
16
16
17 /// A mercurial capability as not been implemented.
17 /// A mercurial capability as not been implemented.
18 ///
18 ///
19 /// There is no error message printed in this case.
19 /// There is no error message printed in this case.
20 /// Instead, we exit with a specic status code and a wrapper script may
20 /// Instead, we exit with a specic status code and a wrapper script may
21 /// fallback to Python-based Mercurial.
21 /// fallback to Python-based Mercurial.
22 Unimplemented,
22 Unimplemented,
23 }
23 }
24
24
25 impl CommandError {
25 impl CommandError {
26 pub fn abort(message: impl AsRef<str>) -> Self {
26 pub fn abort(message: impl AsRef<str>) -> Self {
27 CommandError::Abort {
27 CommandError::Abort {
28 // TODO: bytes-based (instead of Unicode-based) formatting
28 // TODO: bytes-based (instead of Unicode-based) formatting
29 // of error messages to handle non-UTF-8 filenames etc:
29 // of error messages to handle non-UTF-8 filenames etc:
30 // https://www.mercurial-scm.org/wiki/EncodingStrategy#Mixing_output
30 // https://www.mercurial-scm.org/wiki/EncodingStrategy#Mixing_output
31 message: utf8_to_local(message.as_ref()).into(),
31 message: utf8_to_local(message.as_ref()).into(),
32 }
32 }
33 }
33 }
34 }
34 }
35
35
36 impl From<HgError> for CommandError {
36 impl From<HgError> for CommandError {
37 fn from(error: HgError) -> Self {
37 fn from(error: HgError) -> Self {
38 match error {
38 match error {
39 HgError::UnsupportedFeature(_) => CommandError::Unimplemented,
39 HgError::UnsupportedFeature(_) => CommandError::Unimplemented,
40 _ => CommandError::abort(error.to_string()),
40 _ => CommandError::abort(error.to_string()),
41 }
41 }
42 }
42 }
43 }
43 }
44
44
45 impl From<UiError> for CommandError {
45 impl From<UiError> for CommandError {
46 fn from(_error: UiError) -> Self {
46 fn from(_error: UiError) -> Self {
47 // If we already failed writing to stdout or stderr,
47 // If we already failed writing to stdout or stderr,
48 // writing an error message to stderr about it would be likely to fail
48 // writing an error message to stderr about it would be likely to fail
49 // too.
49 // too.
50 CommandError::abort("")
50 CommandError::abort("")
51 }
51 }
52 }
52 }
53
53
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(),
64 RepoError::Other(error) => error.into(),
64 RepoError::Other(error) => error.into(),
65 }
65 }
66 }
66 }
67 }
67 }
68
68
69 impl From<ConfigError> for CommandError {
69 impl From<ConfigError> for CommandError {
70 fn from(error: ConfigError) -> Self {
70 fn from(error: ConfigError) -> Self {
71 match error {
71 match error {
72 ConfigError::Parse(error) => error.into(),
72 ConfigError::Parse(error) => error.into(),
73 ConfigError::Other(error) => error.into(),
73 ConfigError::Other(error) => error.into(),
74 }
74 }
75 }
75 }
76 }
76 }
77
77
78 impl From<ConfigParseError> for CommandError {
78 impl From<ConfigParseError> for CommandError {
79 fn from(error: ConfigParseError) -> Self {
79 fn from(error: ConfigParseError) -> Self {
80 let ConfigParseError {
80 let ConfigParseError {
81 origin,
81 origin,
82 line,
82 line,
83 bytes,
83 bytes,
84 } = error;
84 } = error;
85 let line_message = if let Some(line_number) = line {
85 let line_message = if let Some(line_number) = line {
86 format_bytes!(b" at line {}", line_number.to_string().into_bytes())
86 format_bytes!(b" at line {}", line_number.to_string().into_bytes())
87 } else {
87 } else {
88 Vec::new()
88 Vec::new()
89 };
89 };
90 CommandError::Abort {
90 CommandError::Abort {
91 message: format_bytes!(
91 message: format_bytes!(
92 b"config parse error in {}{}: '{}'",
92 b"config parse error in {}{}: '{}'",
93 origin,
93 origin,
94 line_message,
94 line_message,
95 bytes
95 bytes
96 ),
96 ),
97 }
97 }
98 }
98 }
99 }
99 }
100
100
101 impl From<(RevlogError, &str)> for CommandError {
101 impl From<(RevlogError, &str)> for CommandError {
102 fn from((err, rev): (RevlogError, &str)) -> CommandError {
102 fn from((err, rev): (RevlogError, &str)) -> CommandError {
103 match err {
103 match err {
104 RevlogError::InvalidRevision => CommandError::abort(format!(
104 RevlogError::InvalidRevision => CommandError::abort(format!(
105 "invalid revision identifier {}",
105 "invalid revision identifier {}",
106 rev
106 rev
107 )),
107 )),
108 RevlogError::AmbiguousPrefix => CommandError::abort(format!(
108 RevlogError::AmbiguousPrefix => CommandError::abort(format!(
109 "ambiguous revision identifier {}",
109 "ambiguous revision identifier {}",
110 rev
110 rev
111 )),
111 )),
112 RevlogError::Other(error) => error.into(),
112 RevlogError::Other(error) => error.into(),
113 }
113 }
114 }
114 }
115 }
115 }
@@ -1,94 +1,116 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")
15 .setting(AppSettings::AllowInvalidUtf8)
28 .setting(AppSettings::AllowInvalidUtf8)
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();
22
36
23 let matches = app.clone().get_matches_safe().unwrap_or_else(|err| {
37 let matches = app.clone().get_matches_safe().unwrap_or_else(|err| {
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 {
39 Ok(_) => exitcode::OK,
60 Ok(_) => exitcode::OK,
40
61
41 // Exit with a specific code and no error message to let a potential
62 // Exit with a specific code and no error message to let a potential
42 // wrapper script fallback to Python-based Mercurial.
63 // wrapper script fallback to Python-based Mercurial.
43 Err(CommandError::Unimplemented) => exitcode::UNIMPLEMENTED,
64 Err(CommandError::Unimplemented) => exitcode::UNIMPLEMENTED,
44
65
45 Err(CommandError::Abort { message }) => {
66 Err(CommandError::Abort { message }) => {
46 if !message.is_empty() {
67 if !message.is_empty() {
47 // Ignore errors when writing to stderr, we’re already exiting
68 // Ignore errors when writing to stderr, we’re already exiting
48 // with failure code so there’s not much more we can do.
69 // with failure code so there’s not much more we can do.
49 let _ =
70 let _ =
50 ui.write_stderr(&format_bytes!(b"abort: {}\n", message));
71 ui.write_stderr(&format_bytes!(b"abort: {}\n", message));
51 }
72 }
52 exitcode::ABORT
73 exitcode::ABORT
53 }
74 }
54 };
75 };
55 std::process::exit(exit_code)
76 std::process::exit(exit_code)
56 }
77 }
57
78
58 macro_rules! subcommands {
79 macro_rules! subcommands {
59 ($( $command: ident )+) => {
80 ($( $command: ident )+) => {
60 mod commands {
81 mod commands {
61 $(
82 $(
62 pub mod $command;
83 pub mod $command;
63 )+
84 )+
64 }
85 }
65
86
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 {
79 $(
101 $(
80 stringify!($command) => Some(commands::$command::run),
102 stringify!($command) => Some(commands::$command::run),
81 )+
103 )+
82 _ => None,
104 _ => None,
83 }
105 }
84 }
106 }
85 };
107 };
86 }
108 }
87
109
88 subcommands! {
110 subcommands! {
89 cat
111 cat
90 debugdata
112 debugdata
91 debugrequirements
113 debugrequirements
92 files
114 files
93 root
115 root
94 }
116 }
@@ -1,262 +1,257 b''
1 #require rust
1 #require rust
2
2
3 Define an rhg function that will only run if rhg exists
3 Define an rhg function that will only run if rhg exists
4 $ rhg() {
4 $ rhg() {
5 > if [ -f "$RUNTESTDIR/../rust/target/release/rhg" ]; then
5 > if [ -f "$RUNTESTDIR/../rust/target/release/rhg" ]; then
6 > "$RUNTESTDIR/../rust/target/release/rhg" "$@"
6 > "$RUNTESTDIR/../rust/target/release/rhg" "$@"
7 > else
7 > else
8 > echo "skipped: Cannot find rhg. Try to run cargo build in rust/rhg."
8 > echo "skipped: Cannot find rhg. Try to run cargo build in rust/rhg."
9 > exit 80
9 > exit 80
10 > fi
10 > fi
11 > }
11 > }
12
12
13 Unimplemented command
13 Unimplemented command
14 $ rhg unimplemented-command
14 $ rhg 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]
22
22
23 Finding root
23 Finding root
24 $ rhg root
24 $ rhg root
25 abort: no repository found in '$TESTTMP' (.hg not found)!
25 abort: no repository found in '$TESTTMP' (.hg not found)!
26 [255]
26 [255]
27
27
28 $ hg init repository
28 $ hg init repository
29 $ cd repository
29 $ cd repository
30 $ rhg root
30 $ rhg root
31 $TESTTMP/repository
31 $TESTTMP/repository
32
32
33 Unwritable file descriptor
33 Unwritable file descriptor
34 $ rhg root > /dev/full
34 $ rhg root > /dev/full
35 abort: No space left on device (os error 28)
35 abort: No space left on device (os error 28)
36 [255]
36 [255]
37
37
38 Deleted repository
38 Deleted repository
39 $ rm -rf `pwd`
39 $ rm -rf `pwd`
40 $ rhg root
40 $ rhg root
41 abort: $ENOENT$: current directory
41 abort: $ENOENT$: current directory
42 [255]
42 [255]
43
43
44 Listing tracked files
44 Listing tracked files
45 $ cd $TESTTMP
45 $ cd $TESTTMP
46 $ hg init repository
46 $ hg init repository
47 $ cd repository
47 $ cd repository
48 $ for i in 1 2 3; do
48 $ for i in 1 2 3; do
49 > echo $i >> file$i
49 > echo $i >> file$i
50 > hg add file$i
50 > hg add file$i
51 > done
51 > done
52 > hg commit -m "commit $i" -q
52 > hg commit -m "commit $i" -q
53
53
54 Listing tracked files from root
54 Listing tracked files from root
55 $ rhg files
55 $ rhg files
56 file1
56 file1
57 file2
57 file2
58 file3
58 file3
59
59
60 Listing tracked files from subdirectory
60 Listing tracked files from subdirectory
61 $ mkdir -p path/to/directory
61 $ mkdir -p path/to/directory
62 $ cd path/to/directory
62 $ cd path/to/directory
63 $ rhg files
63 $ rhg files
64 ../../../file1
64 ../../../file1
65 ../../../file2
65 ../../../file2
66 ../../../file3
66 ../../../file3
67
67
68 Listing tracked files through broken pipe
68 Listing tracked files through broken pipe
69 $ rhg files | head -n 1
69 $ rhg files | head -n 1
70 ../../../file1
70 ../../../file1
71
71
72 Debuging data in inline index
72 Debuging data in inline index
73 $ cd $TESTTMP
73 $ cd $TESTTMP
74 $ rm -rf repository
74 $ rm -rf repository
75 $ hg init repository
75 $ hg init repository
76 $ cd repository
76 $ cd repository
77 $ for i in 1 2 3 4 5 6; do
77 $ for i in 1 2 3 4 5 6; do
78 > echo $i >> file-$i
78 > echo $i >> file-$i
79 > hg add file-$i
79 > hg add file-$i
80 > hg commit -m "Commit $i" -q
80 > hg commit -m "Commit $i" -q
81 > done
81 > done
82 $ rhg debugdata -c 2
82 $ rhg debugdata -c 2
83 8d0267cb034247ebfa5ee58ce59e22e57a492297
83 8d0267cb034247ebfa5ee58ce59e22e57a492297
84 test
84 test
85 0 0
85 0 0
86 file-3
86 file-3
87
87
88 Commit 3 (no-eol)
88 Commit 3 (no-eol)
89 $ rhg debugdata -m 2
89 $ rhg debugdata -m 2
90 file-1\x00b8e02f6433738021a065f94175c7cd23db5f05be (esc)
90 file-1\x00b8e02f6433738021a065f94175c7cd23db5f05be (esc)
91 file-2\x005d9299349fc01ddd25d0070d149b124d8f10411e (esc)
91 file-2\x005d9299349fc01ddd25d0070d149b124d8f10411e (esc)
92 file-3\x002661d26c649684b482d10f91960cc3db683c38b4 (esc)
92 file-3\x002661d26c649684b482d10f91960cc3db683c38b4 (esc)
93
93
94 Debuging with full node id
94 Debuging with full node id
95 $ rhg debugdata -c `hg log -r 0 -T '{node}'`
95 $ rhg debugdata -c `hg log -r 0 -T '{node}'`
96 d1d1c679d3053e8926061b6f45ca52009f011e3f
96 d1d1c679d3053e8926061b6f45ca52009f011e3f
97 test
97 test
98 0 0
98 0 0
99 file-1
99 file-1
100
100
101 Commit 1 (no-eol)
101 Commit 1 (no-eol)
102
102
103 Specifying revisions by changeset ID
103 Specifying revisions by changeset ID
104 $ hg log -T '{node}\n'
104 $ hg log -T '{node}\n'
105 c6ad58c44207b6ff8a4fbbca7045a5edaa7e908b
105 c6ad58c44207b6ff8a4fbbca7045a5edaa7e908b
106 d654274993d0149eecc3cc03214f598320211900
106 d654274993d0149eecc3cc03214f598320211900
107 f646af7e96481d3a5470b695cf30ad8e3ab6c575
107 f646af7e96481d3a5470b695cf30ad8e3ab6c575
108 cf8b83f14ead62b374b6e91a0e9303b85dfd9ed7
108 cf8b83f14ead62b374b6e91a0e9303b85dfd9ed7
109 91c6f6e73e39318534dc415ea4e8a09c99cd74d6
109 91c6f6e73e39318534dc415ea4e8a09c99cd74d6
110 6ae9681c6d30389694d8701faf24b583cf3ccafe
110 6ae9681c6d30389694d8701faf24b583cf3ccafe
111 $ rhg files -r cf8b83
111 $ rhg files -r cf8b83
112 file-1
112 file-1
113 file-2
113 file-2
114 file-3
114 file-3
115 $ rhg cat -r cf8b83 file-2
115 $ rhg cat -r cf8b83 file-2
116 2
116 2
117 $ rhg cat -r c file-2
117 $ rhg cat -r c file-2
118 abort: ambiguous revision identifier c
118 abort: ambiguous revision identifier c
119 [255]
119 [255]
120 $ rhg cat -r d file-2
120 $ rhg cat -r d file-2
121 2
121 2
122
122
123 Cat files
123 Cat files
124 $ cd $TESTTMP
124 $ cd $TESTTMP
125 $ rm -rf repository
125 $ rm -rf repository
126 $ hg init repository
126 $ hg init repository
127 $ cd repository
127 $ cd repository
128 $ echo "original content" > original
128 $ echo "original content" > original
129 $ hg add original
129 $ hg add original
130 $ hg commit -m "add original" original
130 $ hg commit -m "add original" original
131 $ rhg cat -r 0 original
131 $ rhg cat -r 0 original
132 original content
132 original content
133 Cat copied file should not display copy metadata
133 Cat copied file should not display copy metadata
134 $ hg copy original copy_of_original
134 $ hg copy original copy_of_original
135 $ hg commit -m "add copy of original"
135 $ hg commit -m "add copy of original"
136 $ rhg cat -r 1 copy_of_original
136 $ rhg cat -r 1 copy_of_original
137 original content
137 original content
138
138
139 Requirements
139 Requirements
140 $ rhg debugrequirements
140 $ rhg debugrequirements
141 dotencode
141 dotencode
142 fncache
142 fncache
143 generaldelta
143 generaldelta
144 revlogv1
144 revlogv1
145 sparserevlog
145 sparserevlog
146 store
146 store
147
147
148 $ echo indoor-pool >> .hg/requires
148 $ echo indoor-pool >> .hg/requires
149 $ rhg files
149 $ rhg files
150 [252]
150 [252]
151
151
152 $ rhg cat -r 1 copy_of_original
152 $ rhg cat -r 1 copy_of_original
153 [252]
153 [252]
154
154
155 $ rhg debugrequirements
155 $ rhg debugrequirements
156 [252]
156 [252]
157
157
158 $ echo -e '\xFF' >> .hg/requires
158 $ echo -e '\xFF' >> .hg/requires
159 $ rhg debugrequirements
159 $ rhg debugrequirements
160 abort: corrupted repository: parse error in 'requires' file
160 abort: corrupted repository: parse error in 'requires' file
161 [255]
161 [255]
162
162
163 Persistent nodemap
163 Persistent nodemap
164 $ cd $TESTTMP
164 $ cd $TESTTMP
165 $ rm -rf repository
165 $ rm -rf repository
166 $ hg init repository
166 $ hg init repository
167 $ cd repository
167 $ cd repository
168 $ rhg debugrequirements | grep nodemap
168 $ rhg debugrequirements | grep nodemap
169 [1]
169 [1]
170 $ hg debugbuilddag .+5000 --overwritten-file --config "storage.revlog.nodemap.mode=warn"
170 $ hg debugbuilddag .+5000 --overwritten-file --config "storage.revlog.nodemap.mode=warn"
171 $ hg id -r tip
171 $ hg id -r tip
172 c3ae8dec9fad tip
172 c3ae8dec9fad tip
173 $ ls .hg/store/00changelog*
173 $ ls .hg/store/00changelog*
174 .hg/store/00changelog.d
174 .hg/store/00changelog.d
175 .hg/store/00changelog.i
175 .hg/store/00changelog.i
176 $ rhg files -r c3ae8dec9fad
176 $ rhg files -r c3ae8dec9fad
177 of
177 of
178
178
179 $ cd $TESTTMP
179 $ cd $TESTTMP
180 $ rm -rf repository
180 $ rm -rf repository
181 $ hg --config format.use-persistent-nodemap=True init repository
181 $ hg --config format.use-persistent-nodemap=True init repository
182 $ cd repository
182 $ cd repository
183 $ rhg debugrequirements | grep nodemap
183 $ rhg debugrequirements | grep nodemap
184 persistent-nodemap
184 persistent-nodemap
185 $ hg debugbuilddag .+5000 --overwritten-file --config "storage.revlog.nodemap.mode=warn"
185 $ hg debugbuilddag .+5000 --overwritten-file --config "storage.revlog.nodemap.mode=warn"
186 $ hg id -r tip
186 $ hg id -r tip
187 c3ae8dec9fad tip
187 c3ae8dec9fad tip
188 $ ls .hg/store/00changelog*
188 $ ls .hg/store/00changelog*
189 .hg/store/00changelog-*.nd (glob)
189 .hg/store/00changelog-*.nd (glob)
190 .hg/store/00changelog.d
190 .hg/store/00changelog.d
191 .hg/store/00changelog.i
191 .hg/store/00changelog.i
192 .hg/store/00changelog.n
192 .hg/store/00changelog.n
193
193
194 Specifying revisions by changeset ID
194 Specifying revisions by changeset ID
195 $ rhg files -r c3ae8dec9fad
195 $ rhg files -r c3ae8dec9fad
196 of
196 of
197 $ rhg cat -r c3ae8dec9fad of
197 $ rhg cat -r c3ae8dec9fad of
198 r5000
198 r5000
199
199
200 Crate a shared repository
200 Crate a shared repository
201
201
202 $ echo "[extensions]" >> $HGRCPATH
202 $ echo "[extensions]" >> $HGRCPATH
203 $ echo "share = " >> $HGRCPATH
203 $ echo "share = " >> $HGRCPATH
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
239
234
240 $ echo "[format]" >> $HGRCPATH
235 $ echo "[format]" >> $HGRCPATH
241 $ echo "use-share-safe = True" >> $HGRCPATH
236 $ echo "use-share-safe = True" >> $HGRCPATH
242
237
243 $ cd $TESTTMP
238 $ cd $TESTTMP
244 $ hg init repo4
239 $ hg init repo4
245 $ cd repo4
240 $ cd repo4
246 $ echo a > a
241 $ echo a > a
247 $ hg commit -A -m'init'
242 $ hg commit -A -m'init'
248 adding a
243 adding a
249
244
250 $ cd ..
245 $ cd ..
251 $ hg share repo4 repo5
246 $ hg share repo4 repo5
252 updating working directory
247 updating working directory
253 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
248 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
254
249
255 And check that basic rhg commands work with sharing
250 And check that basic rhg commands work with sharing
256
251
257 $ cd repo5
252 $ cd repo5
258 $ rhg files
253 $ rhg files
259 a
254 a
260 $ rhg cat -r 0 a
255 $ rhg cat -r 0 a
261 a
256 a
262
257
General Comments 0
You need to be logged in to leave comments. Login now