Show More
@@ -1,92 +1,91 b'' | |||||
|
1 | use crate::errors::HgError; | |||
1 | use crate::operations::{find_root, FindRootError}; |
|
2 | use crate::operations::{find_root, FindRootError}; | |
2 | use crate::requirements; |
|
3 | use crate::requirements; | |
3 | use memmap::{Mmap, MmapOptions}; |
|
4 | use memmap::{Mmap, MmapOptions}; | |
4 | use std::path::{Path, PathBuf}; |
|
5 | use std::path::{Path, PathBuf}; | |
5 |
|
6 | |||
6 | /// A repository on disk |
|
7 | /// A repository on disk | |
7 | pub struct Repo { |
|
8 | pub struct Repo { | |
8 | working_directory: PathBuf, |
|
9 | working_directory: PathBuf, | |
9 | dot_hg: PathBuf, |
|
10 | dot_hg: PathBuf, | |
10 | store: PathBuf, |
|
11 | store: PathBuf, | |
11 | } |
|
12 | } | |
12 |
|
13 | |||
13 | /// Filesystem access abstraction for the contents of a given "base" diretory |
|
14 | /// Filesystem access abstraction for the contents of a given "base" diretory | |
14 | #[derive(Clone, Copy)] |
|
15 | #[derive(Clone, Copy)] | |
15 | pub(crate) struct Vfs<'a> { |
|
16 | pub(crate) struct Vfs<'a> { | |
16 | base: &'a Path, |
|
17 | base: &'a Path, | |
17 | } |
|
18 | } | |
18 |
|
19 | |||
19 | impl Repo { |
|
20 | impl Repo { | |
20 | /// Returns `None` if the given path doesnβt look like a repository |
|
21 | /// Returns `None` if the given path doesnβt look like a repository | |
21 | /// (doesnβt contain a `.hg` sub-directory). |
|
22 | /// (doesnβt contain a `.hg` sub-directory). | |
22 | pub fn for_path(root: impl Into<PathBuf>) -> Self { |
|
23 | pub fn for_path(root: impl Into<PathBuf>) -> Self { | |
23 | let working_directory = root.into(); |
|
24 | let working_directory = root.into(); | |
24 | let dot_hg = working_directory.join(".hg"); |
|
25 | let dot_hg = working_directory.join(".hg"); | |
25 | Self { |
|
26 | Self { | |
26 | store: dot_hg.join("store"), |
|
27 | store: dot_hg.join("store"), | |
27 | dot_hg, |
|
28 | dot_hg, | |
28 | working_directory, |
|
29 | working_directory, | |
29 | } |
|
30 | } | |
30 | } |
|
31 | } | |
31 |
|
32 | |||
32 | pub fn find() -> Result<Self, FindRootError> { |
|
33 | pub fn find() -> Result<Self, FindRootError> { | |
33 | find_root().map(Self::for_path) |
|
34 | find_root().map(Self::for_path) | |
34 | } |
|
35 | } | |
35 |
|
36 | |||
36 | pub fn check_requirements( |
|
37 | pub fn check_requirements(&self) -> Result<(), HgError> { | |
37 | &self, |
|
|||
38 | ) -> Result<(), requirements::RequirementsError> { |
|
|||
39 | requirements::check(self) |
|
38 | requirements::check(self) | |
40 | } |
|
39 | } | |
41 |
|
40 | |||
42 | pub fn working_directory_path(&self) -> &Path { |
|
41 | pub fn working_directory_path(&self) -> &Path { | |
43 | &self.working_directory |
|
42 | &self.working_directory | |
44 | } |
|
43 | } | |
45 |
|
44 | |||
46 | /// For accessing repository files (in `.hg`), except for the store |
|
45 | /// For accessing repository files (in `.hg`), except for the store | |
47 | /// (`.hg/store`). |
|
46 | /// (`.hg/store`). | |
48 | pub(crate) fn hg_vfs(&self) -> Vfs<'_> { |
|
47 | pub(crate) fn hg_vfs(&self) -> Vfs<'_> { | |
49 | Vfs { base: &self.dot_hg } |
|
48 | Vfs { base: &self.dot_hg } | |
50 | } |
|
49 | } | |
51 |
|
50 | |||
52 | /// For accessing repository store files (in `.hg/store`) |
|
51 | /// For accessing repository store files (in `.hg/store`) | |
53 | pub(crate) fn store_vfs(&self) -> Vfs<'_> { |
|
52 | pub(crate) fn store_vfs(&self) -> Vfs<'_> { | |
54 | Vfs { base: &self.store } |
|
53 | Vfs { base: &self.store } | |
55 | } |
|
54 | } | |
56 |
|
55 | |||
57 | /// For accessing the working copy |
|
56 | /// For accessing the working copy | |
58 |
|
57 | |||
59 | // The undescore prefix silences the "never used" warning. Remove before |
|
58 | // The undescore prefix silences the "never used" warning. Remove before | |
60 | // using. |
|
59 | // using. | |
61 | pub(crate) fn _working_directory_vfs(&self) -> Vfs<'_> { |
|
60 | pub(crate) fn _working_directory_vfs(&self) -> Vfs<'_> { | |
62 | Vfs { |
|
61 | Vfs { | |
63 | base: &self.working_directory, |
|
62 | base: &self.working_directory, | |
64 | } |
|
63 | } | |
65 | } |
|
64 | } | |
66 | } |
|
65 | } | |
67 |
|
66 | |||
68 | impl Vfs<'_> { |
|
67 | impl Vfs<'_> { | |
69 | pub(crate) fn read( |
|
68 | pub(crate) fn read( | |
70 | &self, |
|
69 | &self, | |
71 | relative_path: impl AsRef<Path>, |
|
70 | relative_path: impl AsRef<Path>, | |
72 | ) -> std::io::Result<Vec<u8>> { |
|
71 | ) -> std::io::Result<Vec<u8>> { | |
73 | std::fs::read(self.base.join(relative_path)) |
|
72 | std::fs::read(self.base.join(relative_path)) | |
74 | } |
|
73 | } | |
75 |
|
74 | |||
76 | pub(crate) fn open( |
|
75 | pub(crate) fn open( | |
77 | &self, |
|
76 | &self, | |
78 | relative_path: impl AsRef<Path>, |
|
77 | relative_path: impl AsRef<Path>, | |
79 | ) -> std::io::Result<std::fs::File> { |
|
78 | ) -> std::io::Result<std::fs::File> { | |
80 | std::fs::File::open(self.base.join(relative_path)) |
|
79 | std::fs::File::open(self.base.join(relative_path)) | |
81 | } |
|
80 | } | |
82 |
|
81 | |||
83 | pub(crate) fn mmap_open( |
|
82 | pub(crate) fn mmap_open( | |
84 | &self, |
|
83 | &self, | |
85 | relative_path: impl AsRef<Path>, |
|
84 | relative_path: impl AsRef<Path>, | |
86 | ) -> std::io::Result<Mmap> { |
|
85 | ) -> std::io::Result<Mmap> { | |
87 | let file = self.open(relative_path)?; |
|
86 | let file = self.open(relative_path)?; | |
88 | // TODO: what are the safety requirements here? |
|
87 | // TODO: what are the safety requirements here? | |
89 | let mmap = unsafe { MmapOptions::new().map(&file) }?; |
|
88 | let mmap = unsafe { MmapOptions::new().map(&file) }?; | |
90 | Ok(mmap) |
|
89 | Ok(mmap) | |
91 | } |
|
90 | } | |
92 | } |
|
91 | } |
@@ -1,76 +1,70 b'' | |||||
|
1 | use crate::errors::{HgError, HgResultExt, IoResultExt}; | |||
1 | use crate::repo::Repo; |
|
2 | use crate::repo::Repo; | |
2 | use std::io; |
|
|||
3 |
|
3 | |||
4 | #[derive(Debug)] |
|
4 | fn parse(bytes: &[u8]) -> Result<Vec<String>, HgError> { | |
5 | pub enum RequirementsError { |
|
|||
6 | // TODO: include a path? |
|
|||
7 | Io(io::Error), |
|
|||
8 | /// The `requires` file is corrupted |
|
|||
9 | Corrupted, |
|
|||
10 | /// The repository requires a feature that we don't support |
|
|||
11 | Unsupported { |
|
|||
12 | feature: String, |
|
|||
13 | }, |
|
|||
14 | } |
|
|||
15 |
|
||||
16 | fn parse(bytes: &[u8]) -> Result<Vec<String>, ()> { |
|
|||
17 | // The Python code reading this file uses `str.splitlines` |
|
5 | // The Python code reading this file uses `str.splitlines` | |
18 | // which looks for a number of line separators (even including a couple of |
|
6 | // which looks for a number of line separators (even including a couple of | |
19 | // non-ASCII ones), but Python code writing it always uses `\n`. |
|
7 | // non-ASCII ones), but Python code writing it always uses `\n`. | |
20 | let lines = bytes.split(|&byte| byte == b'\n'); |
|
8 | let lines = bytes.split(|&byte| byte == b'\n'); | |
21 |
|
9 | |||
22 | lines |
|
10 | lines | |
23 | .filter(|line| !line.is_empty()) |
|
11 | .filter(|line| !line.is_empty()) | |
24 | .map(|line| { |
|
12 | .map(|line| { | |
25 | // Python uses Unicode `str.isalnum` but feature names are all |
|
13 | // Python uses Unicode `str.isalnum` but feature names are all | |
26 | // ASCII |
|
14 | // ASCII | |
27 | if line[0].is_ascii_alphanumeric() && line.is_ascii() { |
|
15 | if line[0].is_ascii_alphanumeric() && line.is_ascii() { | |
28 | Ok(String::from_utf8(line.into()).unwrap()) |
|
16 | Ok(String::from_utf8(line.into()).unwrap()) | |
29 | } else { |
|
17 | } else { | |
30 | Err(()) |
|
18 | Err(HgError::corrupted("parse error in 'requires' file")) | |
31 | } |
|
19 | } | |
32 | }) |
|
20 | }) | |
33 | .collect() |
|
21 | .collect() | |
34 | } |
|
22 | } | |
35 |
|
23 | |||
36 |
pub fn load(repo: &Repo) -> Result<Vec<String>, |
|
24 | pub fn load(repo: &Repo) -> Result<Vec<String>, HgError> { | |
37 | match repo.hg_vfs().read("requires") { |
|
25 | if let Some(bytes) = repo | |
38 | Ok(bytes) => parse(&bytes).map_err(|()| RequirementsError::Corrupted), |
|
26 | .hg_vfs() | |
39 |
|
27 | .read("requires") | ||
|
28 | .for_file("requires".as_ref()) | |||
|
29 | .io_not_found_as_none()? | |||
|
30 | { | |||
|
31 | parse(&bytes) | |||
|
32 | } else { | |||
40 | // Treat a missing file the same as an empty file. |
|
33 | // Treat a missing file the same as an empty file. | |
41 | // From `mercurial/localrepo.py`: |
|
34 | // From `mercurial/localrepo.py`: | |
42 | // > requires file contains a newline-delimited list of |
|
35 | // > requires file contains a newline-delimited list of | |
43 | // > features/capabilities the opener (us) must have in order to use |
|
36 | // > features/capabilities the opener (us) must have in order to use | |
44 | // > the repository. This file was introduced in Mercurial 0.9.2, |
|
37 | // > the repository. This file was introduced in Mercurial 0.9.2, | |
45 | // > which means very old repositories may not have one. We assume |
|
38 | // > which means very old repositories may not have one. We assume | |
46 | // > a missing file translates to no requirements. |
|
39 | // > a missing file translates to no requirements. | |
47 | Err(error) if error.kind() == std::io::ErrorKind::NotFound => { |
|
|||
48 |
|
|
40 | Ok(Vec::new()) | |
49 |
|
|
41 | } | |
50 |
|
||||
51 | Err(error) => Err(RequirementsError::Io(error))?, |
|
|||
52 | } |
|
|||
53 | } |
|
42 | } | |
54 |
|
43 | |||
55 |
pub fn check(repo: &Repo) -> Result<(), |
|
44 | pub fn check(repo: &Repo) -> Result<(), HgError> { | |
56 | for feature in load(repo)? { |
|
45 | for feature in load(repo)? { | |
57 | if !SUPPORTED.contains(&&*feature) { |
|
46 | if !SUPPORTED.contains(&&*feature) { | |
58 | return Err(RequirementsError::Unsupported { feature }); |
|
47 | // TODO: collect and all unknown features and include them in the | |
|
48 | // error message? | |||
|
49 | return Err(HgError::UnsupportedFeature(format!( | |||
|
50 | "repository requires feature unknown to this Mercurial: {}", | |||
|
51 | feature | |||
|
52 | ))); | |||
59 | } |
|
53 | } | |
60 | } |
|
54 | } | |
61 | Ok(()) |
|
55 | Ok(()) | |
62 | } |
|
56 | } | |
63 |
|
57 | |||
64 | // TODO: set this to actually-supported features |
|
58 | // TODO: set this to actually-supported features | |
65 | const SUPPORTED: &[&str] = &[ |
|
59 | const SUPPORTED: &[&str] = &[ | |
66 | "dotencode", |
|
60 | "dotencode", | |
67 | "fncache", |
|
61 | "fncache", | |
68 | "generaldelta", |
|
62 | "generaldelta", | |
69 | "revlogv1", |
|
63 | "revlogv1", | |
70 | "sparserevlog", |
|
64 | "sparserevlog", | |
71 | "store", |
|
65 | "store", | |
72 | // As of this writing everything rhg does is read-only. |
|
66 | // As of this writing everything rhg does is read-only. | |
73 | // When it starts writing to the repository, itβll need to either keep the |
|
67 | // When it starts writing to the repository, itβll need to either keep the | |
74 | // persistent nodemap up to date or remove this entry: |
|
68 | // persistent nodemap up to date or remove this entry: | |
75 | "persistent-nodemap", |
|
69 | "persistent-nodemap", | |
76 | ]; |
|
70 | ]; |
@@ -1,160 +1,146 b'' | |||||
1 | use crate::exitcode; |
|
1 | use crate::exitcode; | |
2 | use crate::ui::utf8_to_local; |
|
2 | use crate::ui::utf8_to_local; | |
3 | use crate::ui::UiError; |
|
3 | use crate::ui::UiError; | |
4 | use format_bytes::format_bytes; |
|
4 | use format_bytes::format_bytes; | |
5 | use hg::errors::HgError; |
|
5 | use hg::errors::HgError; | |
6 | use hg::operations::FindRootError; |
|
6 | use hg::operations::FindRootError; | |
7 | use hg::requirements::RequirementsError; |
|
|||
8 | use hg::revlog::revlog::RevlogError; |
|
7 | use hg::revlog::revlog::RevlogError; | |
9 | use hg::utils::files::get_bytes_from_path; |
|
8 | use hg::utils::files::get_bytes_from_path; | |
10 | use std::convert::From; |
|
9 | use std::convert::From; | |
11 | use std::path::PathBuf; |
|
10 | use std::path::PathBuf; | |
12 |
|
11 | |||
13 | /// The kind of command error |
|
12 | /// The kind of command error | |
14 | #[derive(Debug, derive_more::From)] |
|
13 | #[derive(Debug, derive_more::From)] | |
15 | pub enum CommandError { |
|
14 | pub enum CommandError { | |
16 | /// The root of the repository cannot be found |
|
15 | /// The root of the repository cannot be found | |
17 | RootNotFound(PathBuf), |
|
16 | RootNotFound(PathBuf), | |
18 | /// The current directory cannot be found |
|
17 | /// The current directory cannot be found | |
19 | CurrentDirNotFound(std::io::Error), |
|
18 | CurrentDirNotFound(std::io::Error), | |
20 | /// `.hg/requires` |
|
|||
21 | #[from] |
|
|||
22 | RequirementsError(RequirementsError), |
|
|||
23 | /// The standard output stream cannot be written to |
|
19 | /// The standard output stream cannot be written to | |
24 | StdoutError, |
|
20 | StdoutError, | |
25 | /// The standard error stream cannot be written to |
|
21 | /// The standard error stream cannot be written to | |
26 | StderrError, |
|
22 | StderrError, | |
27 | /// The command aborted |
|
23 | /// The command aborted | |
28 | Abort(Option<Vec<u8>>), |
|
24 | Abort(Option<Vec<u8>>), | |
29 | /// A mercurial capability as not been implemented. |
|
25 | /// A mercurial capability as not been implemented. | |
30 | Unimplemented, |
|
26 | Unimplemented, | |
31 | /// Common cases |
|
27 | /// Common cases | |
32 | #[from] |
|
28 | #[from] | |
33 | Other(HgError), |
|
29 | Other(HgError), | |
34 | } |
|
30 | } | |
35 |
|
31 | |||
36 | impl CommandError { |
|
32 | impl CommandError { | |
37 | pub fn get_exit_code(&self) -> exitcode::ExitCode { |
|
33 | pub fn get_exit_code(&self) -> exitcode::ExitCode { | |
38 | match self { |
|
34 | match self { | |
39 | CommandError::RootNotFound(_) => exitcode::ABORT, |
|
35 | CommandError::RootNotFound(_) => exitcode::ABORT, | |
40 | CommandError::CurrentDirNotFound(_) => exitcode::ABORT, |
|
36 | CommandError::CurrentDirNotFound(_) => exitcode::ABORT, | |
41 | CommandError::RequirementsError( |
|
|||
42 | RequirementsError::Unsupported { .. }, |
|
|||
43 | ) => exitcode::UNIMPLEMENTED_COMMAND, |
|
|||
44 | CommandError::RequirementsError(_) => exitcode::ABORT, |
|
|||
45 | CommandError::StdoutError => exitcode::ABORT, |
|
37 | CommandError::StdoutError => exitcode::ABORT, | |
46 | CommandError::StderrError => exitcode::ABORT, |
|
38 | CommandError::StderrError => exitcode::ABORT, | |
47 | CommandError::Abort(_) => exitcode::ABORT, |
|
39 | CommandError::Abort(_) => exitcode::ABORT, | |
48 | CommandError::Unimplemented => exitcode::UNIMPLEMENTED_COMMAND, |
|
40 | CommandError::Unimplemented => exitcode::UNIMPLEMENTED_COMMAND, | |
49 | CommandError::Other(HgError::UnsupportedFeature(_)) => { |
|
41 | CommandError::Other(HgError::UnsupportedFeature(_)) => { | |
50 | exitcode::UNIMPLEMENTED_COMMAND |
|
42 | exitcode::UNIMPLEMENTED_COMMAND | |
51 | } |
|
43 | } | |
52 | CommandError::Other(_) => exitcode::ABORT, |
|
44 | CommandError::Other(_) => exitcode::ABORT, | |
53 | } |
|
45 | } | |
54 | } |
|
46 | } | |
55 |
|
47 | |||
56 | /// Return the message corresponding to the error if any |
|
48 | /// Return the message corresponding to the error if any | |
57 | pub fn get_error_message_bytes(&self) -> Option<Vec<u8>> { |
|
49 | pub fn get_error_message_bytes(&self) -> Option<Vec<u8>> { | |
58 | match self { |
|
50 | match self { | |
59 | CommandError::RootNotFound(path) => { |
|
51 | CommandError::RootNotFound(path) => { | |
60 | let bytes = get_bytes_from_path(path); |
|
52 | let bytes = get_bytes_from_path(path); | |
61 | Some(format_bytes!( |
|
53 | Some(format_bytes!( | |
62 | b"abort: no repository found in '{}' (.hg not found)!\n", |
|
54 | b"abort: no repository found in '{}' (.hg not found)!\n", | |
63 | bytes.as_slice() |
|
55 | bytes.as_slice() | |
64 | )) |
|
56 | )) | |
65 | } |
|
57 | } | |
66 | CommandError::CurrentDirNotFound(e) => Some(format_bytes!( |
|
58 | CommandError::CurrentDirNotFound(e) => Some(format_bytes!( | |
67 | b"abort: error getting current working directory: {}\n", |
|
59 | b"abort: error getting current working directory: {}\n", | |
68 | e.to_string().as_bytes(), |
|
60 | e.to_string().as_bytes(), | |
69 | )), |
|
61 | )), | |
70 | CommandError::RequirementsError(RequirementsError::Corrupted) => { |
|
|||
71 | Some( |
|
|||
72 | "abort: .hg/requires is corrupted\n".as_bytes().to_owned(), |
|
|||
73 | ) |
|
|||
74 | } |
|
|||
75 | CommandError::Abort(message) => message.to_owned(), |
|
62 | CommandError::Abort(message) => message.to_owned(), | |
76 |
|
63 | |||
77 |
CommandError:: |
|
64 | CommandError::StdoutError | |
78 | | CommandError::StdoutError |
|
|||
79 | | CommandError::StderrError |
|
65 | | CommandError::StderrError | |
80 | | CommandError::Unimplemented |
|
66 | | CommandError::Unimplemented | |
81 | | CommandError::Other(HgError::UnsupportedFeature(_)) => None, |
|
67 | | CommandError::Other(HgError::UnsupportedFeature(_)) => None, | |
82 |
|
68 | |||
83 | CommandError::Other(e) => { |
|
69 | CommandError::Other(e) => { | |
84 | Some(format_bytes!(b"{}\n", e.to_string().as_bytes())) |
|
70 | Some(format_bytes!(b"{}\n", e.to_string().as_bytes())) | |
85 | } |
|
71 | } | |
86 | } |
|
72 | } | |
87 | } |
|
73 | } | |
88 |
|
74 | |||
89 | /// Exist the process with the corresponding exit code. |
|
75 | /// Exist the process with the corresponding exit code. | |
90 | pub fn exit(&self) { |
|
76 | pub fn exit(&self) { | |
91 | std::process::exit(self.get_exit_code()) |
|
77 | std::process::exit(self.get_exit_code()) | |
92 | } |
|
78 | } | |
93 | } |
|
79 | } | |
94 |
|
80 | |||
95 | impl From<UiError> for CommandError { |
|
81 | impl From<UiError> for CommandError { | |
96 | fn from(error: UiError) -> Self { |
|
82 | fn from(error: UiError) -> Self { | |
97 | match error { |
|
83 | match error { | |
98 | UiError::StdoutError(_) => CommandError::StdoutError, |
|
84 | UiError::StdoutError(_) => CommandError::StdoutError, | |
99 | UiError::StderrError(_) => CommandError::StderrError, |
|
85 | UiError::StderrError(_) => CommandError::StderrError, | |
100 | } |
|
86 | } | |
101 | } |
|
87 | } | |
102 | } |
|
88 | } | |
103 |
|
89 | |||
104 | impl From<FindRootError> for CommandError { |
|
90 | impl From<FindRootError> for CommandError { | |
105 | fn from(err: FindRootError) -> Self { |
|
91 | fn from(err: FindRootError) -> Self { | |
106 | match err { |
|
92 | match err { | |
107 | FindRootError::RootNotFound(path) => { |
|
93 | FindRootError::RootNotFound(path) => { | |
108 | CommandError::RootNotFound(path) |
|
94 | CommandError::RootNotFound(path) | |
109 | } |
|
95 | } | |
110 | FindRootError::GetCurrentDirError(e) => { |
|
96 | FindRootError::GetCurrentDirError(e) => { | |
111 | CommandError::CurrentDirNotFound(e) |
|
97 | CommandError::CurrentDirNotFound(e) | |
112 | } |
|
98 | } | |
113 | } |
|
99 | } | |
114 | } |
|
100 | } | |
115 | } |
|
101 | } | |
116 |
|
102 | |||
117 | impl From<(RevlogError, &str)> for CommandError { |
|
103 | impl From<(RevlogError, &str)> for CommandError { | |
118 | fn from((err, rev): (RevlogError, &str)) -> CommandError { |
|
104 | fn from((err, rev): (RevlogError, &str)) -> CommandError { | |
119 | match err { |
|
105 | match err { | |
120 | RevlogError::IoError(err) => CommandError::Abort(Some( |
|
106 | RevlogError::IoError(err) => CommandError::Abort(Some( | |
121 | utf8_to_local(&format!("abort: {}\n", err)).into(), |
|
107 | utf8_to_local(&format!("abort: {}\n", err)).into(), | |
122 | )), |
|
108 | )), | |
123 | RevlogError::InvalidRevision => CommandError::Abort(Some( |
|
109 | RevlogError::InvalidRevision => CommandError::Abort(Some( | |
124 | utf8_to_local(&format!( |
|
110 | utf8_to_local(&format!( | |
125 | "abort: invalid revision identifier {}\n", |
|
111 | "abort: invalid revision identifier {}\n", | |
126 | rev |
|
112 | rev | |
127 | )) |
|
113 | )) | |
128 | .into(), |
|
114 | .into(), | |
129 | )), |
|
115 | )), | |
130 | RevlogError::AmbiguousPrefix => CommandError::Abort(Some( |
|
116 | RevlogError::AmbiguousPrefix => CommandError::Abort(Some( | |
131 | utf8_to_local(&format!( |
|
117 | utf8_to_local(&format!( | |
132 | "abort: ambiguous revision identifier {}\n", |
|
118 | "abort: ambiguous revision identifier {}\n", | |
133 | rev |
|
119 | rev | |
134 | )) |
|
120 | )) | |
135 | .into(), |
|
121 | .into(), | |
136 | )), |
|
122 | )), | |
137 | RevlogError::UnsuportedVersion(version) => { |
|
123 | RevlogError::UnsuportedVersion(version) => { | |
138 | CommandError::Abort(Some( |
|
124 | CommandError::Abort(Some( | |
139 | utf8_to_local(&format!( |
|
125 | utf8_to_local(&format!( | |
140 | "abort: unsupported revlog version {}\n", |
|
126 | "abort: unsupported revlog version {}\n", | |
141 | version |
|
127 | version | |
142 | )) |
|
128 | )) | |
143 | .into(), |
|
129 | .into(), | |
144 | )) |
|
130 | )) | |
145 | } |
|
131 | } | |
146 | RevlogError::Corrupted => { |
|
132 | RevlogError::Corrupted => { | |
147 | CommandError::Abort(Some("abort: corrupted revlog\n".into())) |
|
133 | CommandError::Abort(Some("abort: corrupted revlog\n".into())) | |
148 | } |
|
134 | } | |
149 | RevlogError::UnknowDataFormat(format) => { |
|
135 | RevlogError::UnknowDataFormat(format) => { | |
150 | CommandError::Abort(Some( |
|
136 | CommandError::Abort(Some( | |
151 | utf8_to_local(&format!( |
|
137 | utf8_to_local(&format!( | |
152 | "abort: unknow revlog dataformat {:?}\n", |
|
138 | "abort: unknow revlog dataformat {:?}\n", | |
153 | format |
|
139 | format | |
154 | )) |
|
140 | )) | |
155 | .into(), |
|
141 | .into(), | |
156 | )) |
|
142 | )) | |
157 | } |
|
143 | } | |
158 | } |
|
144 | } | |
159 | } |
|
145 | } | |
160 | } |
|
146 | } |
@@ -1,204 +1,204 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 <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: error getting current working directory: $ENOENT$ |
|
41 | abort: error getting current working directory: $ENOENT$ | |
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 | dotencode |
|
156 | dotencode | |
157 | fncache |
|
157 | fncache | |
158 | generaldelta |
|
158 | generaldelta | |
159 | revlogv1 |
|
159 | revlogv1 | |
160 | sparserevlog |
|
160 | sparserevlog | |
161 | store |
|
161 | store | |
162 | indoor-pool |
|
162 | indoor-pool | |
163 |
|
163 | |||
164 | $ echo -e '\xFF' >> .hg/requires |
|
164 | $ echo -e '\xFF' >> .hg/requires | |
165 | $ rhg debugrequirements |
|
165 | $ rhg debugrequirements | |
166 | abort: .hg/requires is corrupted |
|
166 | corrupted repository: parse error in 'requires' file | |
167 | [255] |
|
167 | [255] | |
168 |
|
168 | |||
169 | Persistent nodemap |
|
169 | Persistent nodemap | |
170 | $ cd $TESTTMP |
|
170 | $ cd $TESTTMP | |
171 | $ rm -rf repository |
|
171 | $ rm -rf repository | |
172 | $ hg init repository |
|
172 | $ hg init repository | |
173 | $ cd repository |
|
173 | $ cd repository | |
174 | $ rhg debugrequirements | grep nodemap |
|
174 | $ rhg debugrequirements | grep nodemap | |
175 | [1] |
|
175 | [1] | |
176 | $ hg debugbuilddag .+5000 --overwritten-file --config "storage.revlog.nodemap.mode=warn" |
|
176 | $ hg debugbuilddag .+5000 --overwritten-file --config "storage.revlog.nodemap.mode=warn" | |
177 | $ hg id -r tip |
|
177 | $ hg id -r tip | |
178 | c3ae8dec9fad tip |
|
178 | c3ae8dec9fad tip | |
179 | $ ls .hg/store/00changelog* |
|
179 | $ ls .hg/store/00changelog* | |
180 | .hg/store/00changelog.d |
|
180 | .hg/store/00changelog.d | |
181 | .hg/store/00changelog.i |
|
181 | .hg/store/00changelog.i | |
182 | $ rhg files -r c3ae8dec9fad |
|
182 | $ rhg files -r c3ae8dec9fad | |
183 | of |
|
183 | of | |
184 |
|
184 | |||
185 | $ cd $TESTTMP |
|
185 | $ cd $TESTTMP | |
186 | $ rm -rf repository |
|
186 | $ rm -rf repository | |
187 | $ hg --config format.use-persistent-nodemap=True init repository |
|
187 | $ hg --config format.use-persistent-nodemap=True init repository | |
188 | $ cd repository |
|
188 | $ cd repository | |
189 | $ rhg debugrequirements | grep nodemap |
|
189 | $ rhg debugrequirements | grep nodemap | |
190 | persistent-nodemap |
|
190 | persistent-nodemap | |
191 | $ hg debugbuilddag .+5000 --overwritten-file --config "storage.revlog.nodemap.mode=warn" |
|
191 | $ hg debugbuilddag .+5000 --overwritten-file --config "storage.revlog.nodemap.mode=warn" | |
192 | $ hg id -r tip |
|
192 | $ hg id -r tip | |
193 | c3ae8dec9fad tip |
|
193 | c3ae8dec9fad tip | |
194 | $ ls .hg/store/00changelog* |
|
194 | $ ls .hg/store/00changelog* | |
195 | .hg/store/00changelog-*.nd (glob) |
|
195 | .hg/store/00changelog-*.nd (glob) | |
196 | .hg/store/00changelog.d |
|
196 | .hg/store/00changelog.d | |
197 | .hg/store/00changelog.i |
|
197 | .hg/store/00changelog.i | |
198 | .hg/store/00changelog.n |
|
198 | .hg/store/00changelog.n | |
199 |
|
199 | |||
200 | Specifying revisions by changeset ID |
|
200 | Specifying revisions by changeset ID | |
201 | $ rhg files -r c3ae8dec9fad |
|
201 | $ rhg files -r c3ae8dec9fad | |
202 | of |
|
202 | of | |
203 | $ rhg cat -r c3ae8dec9fad of |
|
203 | $ rhg cat -r c3ae8dec9fad of | |
204 | r5000 |
|
204 | r5000 |
General Comments 0
You need to be logged in to leave comments.
Login now