Show More
@@ -1,176 +1,139 b'' | |||
|
1 | 1 | // list_tracked_files.rs |
|
2 | 2 | // |
|
3 | 3 | // Copyright 2020 Antoine Cezar <antoine.cezar@octobus.net> |
|
4 | 4 | // |
|
5 | 5 | // This software may be used and distributed according to the terms of the |
|
6 | 6 | // GNU General Public License version 2 or any later version. |
|
7 | 7 | |
|
8 | 8 | use std::convert::From; |
|
9 | 9 | use std::path::{Path, PathBuf}; |
|
10 | 10 | |
|
11 | 11 | use crate::revlog::changelog::Changelog; |
|
12 |
use crate::revlog::manifest:: |
|
|
12 | use crate::revlog::manifest::Manifest; | |
|
13 | 13 | use crate::revlog::path_encode::path_encode; |
|
14 | 14 | use crate::revlog::revlog::Revlog; |
|
15 | 15 | use crate::revlog::revlog::RevlogError; |
|
16 | 16 | use crate::revlog::Node; |
|
17 | 17 | use crate::revlog::NodePrefix; |
|
18 | 18 | use crate::revlog::Revision; |
|
19 | 19 | use crate::utils::files::get_path_from_bytes; |
|
20 | 20 | use crate::utils::hg_path::{HgPath, HgPathBuf}; |
|
21 | 21 | |
|
22 | 22 | const METADATA_DELIMITER: [u8; 2] = [b'\x01', b'\n']; |
|
23 | 23 | |
|
24 | 24 | /// Kind of error encountered by `CatRev` |
|
25 | 25 | #[derive(Debug)] |
|
26 | 26 | pub enum CatRevErrorKind { |
|
27 | 27 | /// Error when reading a `revlog` file. |
|
28 | 28 | IoError(std::io::Error), |
|
29 | 29 | /// The revision has not been found. |
|
30 | 30 | InvalidRevision, |
|
31 | 31 | /// Found more than one revision whose ID match the requested prefix |
|
32 | 32 | AmbiguousPrefix, |
|
33 | 33 | /// A `revlog` file is corrupted. |
|
34 | 34 | CorruptedRevlog, |
|
35 | 35 | /// The `revlog` format version is not supported. |
|
36 | 36 | UnsuportedRevlogVersion(u16), |
|
37 | 37 | /// The `revlog` data format is not supported. |
|
38 | 38 | UnknowRevlogDataFormat(u8), |
|
39 | 39 | } |
|
40 | 40 | |
|
41 | 41 | /// A `CatRev` error |
|
42 | 42 | #[derive(Debug)] |
|
43 | 43 | pub struct CatRevError { |
|
44 | 44 | /// Kind of error encountered by `CatRev` |
|
45 | 45 | pub kind: CatRevErrorKind, |
|
46 | 46 | } |
|
47 | 47 | |
|
48 | 48 | impl From<CatRevErrorKind> for CatRevError { |
|
49 | 49 | fn from(kind: CatRevErrorKind) -> Self { |
|
50 | 50 | CatRevError { kind } |
|
51 | 51 | } |
|
52 | 52 | } |
|
53 | 53 | |
|
54 | 54 | impl From<RevlogError> for CatRevError { |
|
55 | 55 | fn from(err: RevlogError) -> Self { |
|
56 | 56 | match err { |
|
57 | 57 | RevlogError::IoError(err) => CatRevErrorKind::IoError(err), |
|
58 | 58 | RevlogError::UnsuportedVersion(version) => { |
|
59 | 59 | CatRevErrorKind::UnsuportedRevlogVersion(version) |
|
60 | 60 | } |
|
61 | 61 | RevlogError::InvalidRevision => CatRevErrorKind::InvalidRevision, |
|
62 | 62 | RevlogError::AmbiguousPrefix => CatRevErrorKind::AmbiguousPrefix, |
|
63 | 63 | RevlogError::Corrupted => CatRevErrorKind::CorruptedRevlog, |
|
64 | 64 | RevlogError::UnknowDataFormat(format) => { |
|
65 | 65 | CatRevErrorKind::UnknowRevlogDataFormat(format) |
|
66 | 66 | } |
|
67 | 67 | } |
|
68 | 68 | .into() |
|
69 | 69 | } |
|
70 | 70 | } |
|
71 | 71 | |
|
72 | 72 | /// List files under Mercurial control at a given revision. |
|
73 | pub struct CatRev<'a> { | |
|
74 | root: &'a Path, | |
|
75 |
|
|
|
76 | rev: &'a str, | |
|
77 | /// The files to output. | |
|
78 | files: &'a [HgPathBuf], | |
|
79 | /// The changelog file | |
|
80 | changelog: Changelog, | |
|
81 | /// The manifest file | |
|
82 | manifest: Manifest, | |
|
83 | /// The manifest entry corresponding to the revision. | |
|
84 | /// | |
|
85 | /// Used to hold the owner of the returned references. | |
|
86 | manifest_entry: Option<ManifestEntry>, | |
|
87 | } | |
|
73 | /// | |
|
74 | /// * `root`: Repository root | |
|
75 | /// * `rev`: The revision to cat the files from. | |
|
76 | /// * `files`: The files to output. | |
|
77 | pub fn cat( | |
|
78 | root: &Path, | |
|
79 | rev: &str, | |
|
80 | files: &[HgPathBuf], | |
|
81 | ) -> Result<Vec<u8>, CatRevError> { | |
|
82 | let changelog = Changelog::open(&root)?; | |
|
83 | let manifest = Manifest::open(&root)?; | |
|
84 | ||
|
85 | let changelog_entry = match rev.parse::<Revision>() { | |
|
86 | Ok(rev) => changelog.get_rev(rev)?, | |
|
87 | _ => { | |
|
88 | let changelog_node = NodePrefix::from_hex(&rev) | |
|
89 | .map_err(|_| CatRevErrorKind::InvalidRevision)?; | |
|
90 | changelog.get_node(changelog_node.borrow())? | |
|
91 | } | |
|
92 | }; | |
|
93 | let manifest_node = Node::from_hex(&changelog_entry.manifest_node()?) | |
|
94 | .map_err(|_| CatRevErrorKind::CorruptedRevlog)?; | |
|
95 | ||
|
96 | let manifest_entry = manifest.get_node((&manifest_node).into())?; | |
|
97 | let mut bytes = vec![]; | |
|
88 | 98 | |
|
89 | impl<'a> CatRev<'a> { | |
|
90 | pub fn new( | |
|
91 | root: &'a Path, | |
|
92 | rev: &'a str, | |
|
93 | files: &'a [HgPathBuf], | |
|
94 | ) -> Result<Self, CatRevError> { | |
|
95 | let changelog = Changelog::open(&root)?; | |
|
96 | let manifest = Manifest::open(&root)?; | |
|
97 | let manifest_entry = None; | |
|
99 | for (manifest_file, node_bytes) in manifest_entry.files_with_nodes() { | |
|
100 | for cat_file in files.iter() { | |
|
101 | if cat_file.as_bytes() == manifest_file.as_bytes() { | |
|
102 | let index_path = store_path(root, manifest_file, b".i"); | |
|
103 | let data_path = store_path(root, manifest_file, b".d"); | |
|
98 | 104 | |
|
99 | Ok(Self { | |
|
100 | root, | |
|
101 | rev, | |
|
102 | files, | |
|
103 | changelog, | |
|
104 | manifest, | |
|
105 | manifest_entry, | |
|
106 | }) | |
|
105 | let file_log = Revlog::open(&index_path, Some(&data_path))?; | |
|
106 | let file_node = Node::from_hex(node_bytes) | |
|
107 | .map_err(|_| CatRevErrorKind::CorruptedRevlog)?; | |
|
108 | let file_rev = file_log.get_node_rev((&file_node).into())?; | |
|
109 | let data = file_log.get_rev_data(file_rev)?; | |
|
110 | if data.starts_with(&METADATA_DELIMITER) { | |
|
111 | let end_delimiter_position = data | |
|
112 | [METADATA_DELIMITER.len()..] | |
|
113 | .windows(METADATA_DELIMITER.len()) | |
|
114 | .position(|bytes| bytes == METADATA_DELIMITER); | |
|
115 | if let Some(position) = end_delimiter_position { | |
|
116 | let offset = METADATA_DELIMITER.len() * 2; | |
|
117 | bytes.extend(data[position + offset..].iter()); | |
|
118 | } | |
|
119 | } else { | |
|
120 | bytes.extend(data); | |
|
121 | } | |
|
122 | } | |
|
123 | } | |
|
107 | 124 | } |
|
108 | 125 | |
|
109 | pub fn run(&mut self) -> Result<Vec<u8>, CatRevError> { | |
|
110 | let changelog_entry = match self.rev.parse::<Revision>() { | |
|
111 | Ok(rev) => self.changelog.get_rev(rev)?, | |
|
112 | _ => { | |
|
113 | let changelog_node = NodePrefix::from_hex(&self.rev) | |
|
114 | .map_err(|_| CatRevErrorKind::InvalidRevision)?; | |
|
115 | self.changelog.get_node(changelog_node.borrow())? | |
|
116 | } | |
|
117 | }; | |
|
118 | let manifest_node = Node::from_hex(&changelog_entry.manifest_node()?) | |
|
119 | .map_err(|_| CatRevErrorKind::CorruptedRevlog)?; | |
|
120 | ||
|
121 | self.manifest_entry = | |
|
122 | Some(self.manifest.get_node((&manifest_node).into())?); | |
|
123 | if let Some(ref manifest_entry) = self.manifest_entry { | |
|
124 | let mut bytes = vec![]; | |
|
125 | ||
|
126 | for (manifest_file, node_bytes) in | |
|
127 | manifest_entry.files_with_nodes() | |
|
128 | { | |
|
129 | for cat_file in self.files.iter() { | |
|
130 | if cat_file.as_bytes() == manifest_file.as_bytes() { | |
|
131 | let index_path = | |
|
132 | store_path(self.root, manifest_file, b".i"); | |
|
133 | let data_path = | |
|
134 | store_path(self.root, manifest_file, b".d"); | |
|
135 | ||
|
136 | let file_log = | |
|
137 | Revlog::open(&index_path, Some(&data_path))?; | |
|
138 | let file_node = Node::from_hex(node_bytes) | |
|
139 | .map_err(|_| CatRevErrorKind::CorruptedRevlog)?; | |
|
140 | let file_rev = | |
|
141 | file_log.get_node_rev((&file_node).into())?; | |
|
142 | let data = file_log.get_rev_data(file_rev)?; | |
|
143 | if data.starts_with(&METADATA_DELIMITER) { | |
|
144 | let end_delimiter_position = data | |
|
145 | [METADATA_DELIMITER.len()..] | |
|
146 | .windows(METADATA_DELIMITER.len()) | |
|
147 | .position(|bytes| bytes == METADATA_DELIMITER); | |
|
148 | if let Some(position) = end_delimiter_position { | |
|
149 | let offset = METADATA_DELIMITER.len() * 2; | |
|
150 | bytes.extend(data[position + offset..].iter()); | |
|
151 | } | |
|
152 | } else { | |
|
153 | bytes.extend(data); | |
|
154 | } | |
|
155 | } | |
|
156 | } | |
|
157 | } | |
|
158 | ||
|
159 | Ok(bytes) | |
|
160 | } else { | |
|
161 | unreachable!("manifest_entry should have been stored"); | |
|
162 | } | |
|
163 | } | |
|
126 | Ok(bytes) | |
|
164 | 127 | } |
|
165 | 128 | |
|
166 | 129 | fn store_path(root: &Path, hg_path: &HgPath, suffix: &[u8]) -> PathBuf { |
|
167 | 130 | let encoded_bytes = |
|
168 | 131 | path_encode(&[b"data/", hg_path.as_bytes(), suffix].concat()); |
|
169 | 132 | [ |
|
170 | 133 | root, |
|
171 | 134 | &Path::new(".hg/store/"), |
|
172 | 135 | get_path_from_bytes(&encoded_bytes), |
|
173 | 136 | ] |
|
174 | 137 | .iter() |
|
175 | 138 | .collect() |
|
176 | 139 | } |
@@ -1,120 +1,103 b'' | |||
|
1 | 1 | // debugdata.rs |
|
2 | 2 | // |
|
3 | 3 | // Copyright 2020 Antoine Cezar <antoine.cezar@octobus.net> |
|
4 | 4 | // |
|
5 | 5 | // This software may be used and distributed according to the terms of the |
|
6 | 6 | // GNU General Public License version 2 or any later version. |
|
7 | 7 | |
|
8 | use super::find_root; | |
|
8 | use std::path::Path; | |
|
9 | ||
|
9 | 10 | use crate::revlog::revlog::{Revlog, RevlogError}; |
|
10 | 11 | use crate::revlog::NodePrefix; |
|
11 | 12 | use crate::revlog::Revision; |
|
12 | 13 | |
|
13 | 14 | /// Kind of data to debug |
|
14 | 15 | #[derive(Debug, Copy, Clone)] |
|
15 | 16 | pub enum DebugDataKind { |
|
16 | 17 | Changelog, |
|
17 | 18 | Manifest, |
|
18 | 19 | } |
|
19 | 20 | |
|
20 | 21 | /// Kind of error encountered by DebugData |
|
21 | 22 | #[derive(Debug)] |
|
22 | 23 | pub enum DebugDataErrorKind { |
|
23 | FindRootError(find_root::FindRootError), | |
|
24 | 24 | /// Error when reading a `revlog` file. |
|
25 | 25 | IoError(std::io::Error), |
|
26 | 26 | /// The revision has not been found. |
|
27 | 27 | InvalidRevision, |
|
28 | 28 | /// Found more than one revision whose ID match the requested prefix |
|
29 | 29 | AmbiguousPrefix, |
|
30 | 30 | /// A `revlog` file is corrupted. |
|
31 | 31 | CorruptedRevlog, |
|
32 | 32 | /// The `revlog` format version is not supported. |
|
33 | 33 | UnsuportedRevlogVersion(u16), |
|
34 | 34 | /// The `revlog` data format is not supported. |
|
35 | 35 | UnknowRevlogDataFormat(u8), |
|
36 | 36 | } |
|
37 | 37 | |
|
38 | 38 | /// A DebugData error |
|
39 | 39 | #[derive(Debug)] |
|
40 | 40 | pub struct DebugDataError { |
|
41 | 41 | /// Kind of error encountered by DebugData |
|
42 | 42 | pub kind: DebugDataErrorKind, |
|
43 | 43 | } |
|
44 | 44 | |
|
45 | 45 | impl From<DebugDataErrorKind> for DebugDataError { |
|
46 | 46 | fn from(kind: DebugDataErrorKind) -> Self { |
|
47 | 47 | DebugDataError { kind } |
|
48 | 48 | } |
|
49 | 49 | } |
|
50 | 50 | |
|
51 | impl From<find_root::FindRootError> for DebugDataError { | |
|
52 | fn from(err: find_root::FindRootError) -> Self { | |
|
53 | let kind = DebugDataErrorKind::FindRootError(err); | |
|
54 | DebugDataError { kind } | |
|
55 | } | |
|
56 | } | |
|
57 | ||
|
58 | 51 | impl From<std::io::Error> for DebugDataError { |
|
59 | 52 | fn from(err: std::io::Error) -> Self { |
|
60 | 53 | let kind = DebugDataErrorKind::IoError(err); |
|
61 | 54 | DebugDataError { kind } |
|
62 | 55 | } |
|
63 | 56 | } |
|
64 | 57 | |
|
65 | 58 | impl From<RevlogError> for DebugDataError { |
|
66 | 59 | fn from(err: RevlogError) -> Self { |
|
67 | 60 | match err { |
|
68 | 61 | RevlogError::IoError(err) => DebugDataErrorKind::IoError(err), |
|
69 | 62 | RevlogError::UnsuportedVersion(version) => { |
|
70 | 63 | DebugDataErrorKind::UnsuportedRevlogVersion(version) |
|
71 | 64 | } |
|
72 | 65 | RevlogError::InvalidRevision => { |
|
73 | 66 | DebugDataErrorKind::InvalidRevision |
|
74 | 67 | } |
|
75 | 68 | RevlogError::AmbiguousPrefix => { |
|
76 | 69 | DebugDataErrorKind::AmbiguousPrefix |
|
77 | 70 | } |
|
78 | 71 | RevlogError::Corrupted => DebugDataErrorKind::CorruptedRevlog, |
|
79 | 72 | RevlogError::UnknowDataFormat(format) => { |
|
80 | 73 | DebugDataErrorKind::UnknowRevlogDataFormat(format) |
|
81 | 74 | } |
|
82 | 75 | } |
|
83 | 76 | .into() |
|
84 | 77 | } |
|
85 | 78 | } |
|
86 | 79 | |
|
87 | 80 | /// Dump the contents data of a revision. |
|
88 | pub struct DebugData<'a> { | |
|
89 | /// Revision or hash of the revision. | |
|
90 |
rev: & |
|
|
91 | /// Kind of data to debug. | |
|
81 | pub fn debug_data( | |
|
82 | root: &Path, | |
|
83 | rev: &str, | |
|
92 | 84 | kind: DebugDataKind, |
|
93 | } | |
|
94 | ||
|
95 | impl<'a> DebugData<'a> { | |
|
96 | pub fn new(rev: &'a str, kind: DebugDataKind) -> Self { | |
|
97 | DebugData { rev, kind } | |
|
98 | } | |
|
85 | ) -> Result<Vec<u8>, DebugDataError> { | |
|
86 | let index_file = match kind { | |
|
87 | DebugDataKind::Changelog => root.join(".hg/store/00changelog.i"), | |
|
88 | DebugDataKind::Manifest => root.join(".hg/store/00manifest.i"), | |
|
89 | }; | |
|
90 | let revlog = Revlog::open(&index_file, None)?; | |
|
99 | 91 | |
|
100 | pub fn run(&mut self) -> Result<Vec<u8>, DebugDataError> { | |
|
101 | let root = find_root::FindRoot::new().run()?; | |
|
102 | let index_file = match self.kind { | |
|
103 | DebugDataKind::Changelog => root.join(".hg/store/00changelog.i"), | |
|
104 | DebugDataKind::Manifest => root.join(".hg/store/00manifest.i"), | |
|
105 | }; | |
|
106 | let revlog = Revlog::open(&index_file, None)?; | |
|
92 | let data = match rev.parse::<Revision>() { | |
|
93 | Ok(rev) => revlog.get_rev_data(rev)?, | |
|
94 | _ => { | |
|
95 | let node = NodePrefix::from_hex(&rev) | |
|
96 | .map_err(|_| DebugDataErrorKind::InvalidRevision)?; | |
|
97 | let rev = revlog.get_node_rev(node.borrow())?; | |
|
98 | revlog.get_rev_data(rev)? | |
|
99 | } | |
|
100 | }; | |
|
107 | 101 | |
|
108 | let data = match self.rev.parse::<Revision>() { | |
|
109 | Ok(rev) => revlog.get_rev_data(rev)?, | |
|
110 | _ => { | |
|
111 | let node = NodePrefix::from_hex(&self.rev) | |
|
112 | .map_err(|_| DebugDataErrorKind::InvalidRevision)?; | |
|
113 | let rev = revlog.get_node_rev(node.borrow())?; | |
|
114 | revlog.get_rev_data(rev)? | |
|
115 | } | |
|
116 | }; | |
|
117 | ||
|
118 | Ok(data) | |
|
119 | } | |
|
102 | Ok(data) | |
|
120 | 103 | } |
@@ -1,119 +1,100 b'' | |||
|
1 | 1 | use std::fmt; |
|
2 | 2 | use std::path::{Path, PathBuf}; |
|
3 | 3 | |
|
4 | 4 | /// Kind of error encoutered by FindRoot |
|
5 | 5 | #[derive(Debug)] |
|
6 | 6 | pub enum FindRootErrorKind { |
|
7 | 7 | /// Root of the repository has not been found |
|
8 | 8 | /// Contains the current directory used by FindRoot |
|
9 | 9 | RootNotFound(PathBuf), |
|
10 | 10 | /// The current directory does not exists or permissions are insufficient |
|
11 | 11 | /// to get access to it |
|
12 | 12 | GetCurrentDirError(std::io::Error), |
|
13 | 13 | } |
|
14 | 14 | |
|
15 | 15 | /// A FindRoot error |
|
16 | 16 | #[derive(Debug)] |
|
17 | 17 | pub struct FindRootError { |
|
18 | 18 | /// Kind of error encoutered by FindRoot |
|
19 | 19 | pub kind: FindRootErrorKind, |
|
20 | 20 | } |
|
21 | 21 | |
|
22 | 22 | impl std::error::Error for FindRootError {} |
|
23 | 23 | |
|
24 | 24 | impl fmt::Display for FindRootError { |
|
25 | 25 | fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { |
|
26 | 26 | unimplemented!() |
|
27 | 27 | } |
|
28 | 28 | } |
|
29 | 29 | |
|
30 | 30 | /// Find the root of the repository |
|
31 | /// by searching for a .hg directory in the current directory and its | |
|
31 | /// by searching for a .hg directory in the process’ current directory and its | |
|
32 | 32 | /// ancestors |
|
33 | pub struct FindRoot<'a> { | |
|
34 | current_dir: Option<&'a Path>, | |
|
33 | pub fn find_root() -> Result<PathBuf, FindRootError> { | |
|
34 | let current_dir = std::env::current_dir().map_err(|e| FindRootError { | |
|
35 | kind: FindRootErrorKind::GetCurrentDirError(e), | |
|
36 | })?; | |
|
37 | Ok(find_root_from_path(¤t_dir)?.into()) | |
|
35 | 38 | } |
|
36 | 39 | |
|
37 | impl<'a> FindRoot<'a> { | |
|
38 | pub fn new() -> Self { | |
|
39 | Self { current_dir: None } | |
|
40 | /// Find the root of the repository | |
|
41 | /// by searching for a .hg directory in the given directory and its ancestors | |
|
42 | pub fn find_root_from_path(start: &Path) -> Result<&Path, FindRootError> { | |
|
43 | if start.join(".hg").exists() { | |
|
44 | return Ok(start); | |
|
40 | 45 | } |
|
41 | ||
|
42 | pub fn new_from_path(current_dir: &'a Path) -> Self { | |
|
43 | Self { | |
|
44 | current_dir: Some(current_dir), | |
|
46 | for ancestor in start.ancestors() { | |
|
47 | if ancestor.join(".hg").exists() { | |
|
48 | return Ok(ancestor); | |
|
45 | 49 | } |
|
46 | 50 | } |
|
47 | ||
|
48 | pub fn run(&self) -> Result<PathBuf, FindRootError> { | |
|
49 | let current_dir = match self.current_dir { | |
|
50 | None => std::env::current_dir().or_else(|e| { | |
|
51 | Err(FindRootError { | |
|
52 | kind: FindRootErrorKind::GetCurrentDirError(e), | |
|
53 | }) | |
|
54 | })?, | |
|
55 | Some(path) => path.into(), | |
|
56 | }; | |
|
57 | ||
|
58 | if current_dir.join(".hg").exists() { | |
|
59 | return Ok(current_dir); | |
|
60 | } | |
|
61 | let ancestors = current_dir.ancestors(); | |
|
62 | for parent in ancestors { | |
|
63 | if parent.join(".hg").exists() { | |
|
64 | return Ok(parent.into()); | |
|
65 | } | |
|
66 | } | |
|
67 | Err(FindRootError { | |
|
68 | kind: FindRootErrorKind::RootNotFound(current_dir.to_path_buf()), | |
|
69 | }) | |
|
70 | } | |
|
51 | Err(FindRootError { | |
|
52 | kind: FindRootErrorKind::RootNotFound(start.into()), | |
|
53 | }) | |
|
71 | 54 | } |
|
72 | 55 | |
|
73 | 56 | #[cfg(test)] |
|
74 | 57 | mod tests { |
|
75 | 58 | use super::*; |
|
76 | 59 | use std::fs; |
|
77 | 60 | use tempfile; |
|
78 | 61 | |
|
79 | 62 | #[test] |
|
80 | 63 | fn dot_hg_not_found() { |
|
81 | 64 | let tmp_dir = tempfile::tempdir().unwrap(); |
|
82 | 65 | let path = tmp_dir.path(); |
|
83 | 66 | |
|
84 |
let err = |
|
|
67 | let err = find_root_from_path(&path).unwrap_err(); | |
|
85 | 68 | |
|
86 | 69 | // TODO do something better |
|
87 | 70 | assert!(match err { |
|
88 | 71 | FindRootError { kind } => match kind { |
|
89 | 72 | FindRootErrorKind::RootNotFound(p) => p == path.to_path_buf(), |
|
90 | 73 | _ => false, |
|
91 | 74 | }, |
|
92 | 75 | }) |
|
93 | 76 | } |
|
94 | 77 | |
|
95 | 78 | #[test] |
|
96 | 79 | fn dot_hg_in_current_path() { |
|
97 | 80 | let tmp_dir = tempfile::tempdir().unwrap(); |
|
98 | 81 | let root = tmp_dir.path(); |
|
99 | 82 | fs::create_dir_all(root.join(".hg")).unwrap(); |
|
100 | 83 | |
|
101 |
let result = |
|
|
84 | let result = find_root_from_path(&root).unwrap(); | |
|
102 | 85 | |
|
103 | 86 | assert_eq!(result, root) |
|
104 | 87 | } |
|
105 | 88 | |
|
106 | 89 | #[test] |
|
107 | 90 | fn dot_hg_in_parent() { |
|
108 | 91 | let tmp_dir = tempfile::tempdir().unwrap(); |
|
109 | 92 | let root = tmp_dir.path(); |
|
110 | 93 | fs::create_dir_all(root.join(".hg")).unwrap(); |
|
111 | 94 | |
|
112 | let result = | |
|
113 | FindRoot::new_from_path(&root.join("some/nested/directory")) | |
|
114 | .run() | |
|
115 | .unwrap(); | |
|
95 | let directory = root.join("some/nested/directory"); | |
|
96 | let result = find_root_from_path(&directory).unwrap(); | |
|
116 | 97 | |
|
117 | 98 | assert_eq!(result, root) |
|
118 | 99 | } |
|
119 | 100 | } /* tests */ |
@@ -1,194 +1,167 b'' | |||
|
1 | 1 | // list_tracked_files.rs |
|
2 | 2 | // |
|
3 | 3 | // Copyright 2020 Antoine Cezar <antoine.cezar@octobus.net> |
|
4 | 4 | // |
|
5 | 5 | // This software may be used and distributed according to the terms of the |
|
6 | 6 | // GNU General Public License version 2 or any later version. |
|
7 | 7 | |
|
8 | 8 | use crate::dirstate::parsers::parse_dirstate; |
|
9 | 9 | use crate::revlog::changelog::Changelog; |
|
10 | 10 | use crate::revlog::manifest::{Manifest, ManifestEntry}; |
|
11 | 11 | use crate::revlog::node::{Node, NodePrefix}; |
|
12 | 12 | use crate::revlog::revlog::RevlogError; |
|
13 | 13 | use crate::revlog::Revision; |
|
14 | 14 | use crate::utils::hg_path::HgPath; |
|
15 | 15 | use crate::{DirstateParseError, EntryState}; |
|
16 | 16 | use rayon::prelude::*; |
|
17 | 17 | use std::convert::From; |
|
18 | 18 | use std::fs; |
|
19 | 19 | use std::path::Path; |
|
20 | 20 | |
|
21 | 21 | /// Kind of error encountered by `ListDirstateTrackedFiles` |
|
22 | 22 | #[derive(Debug)] |
|
23 | 23 | pub enum ListDirstateTrackedFilesErrorKind { |
|
24 | 24 | /// Error when reading the `dirstate` file |
|
25 | 25 | IoError(std::io::Error), |
|
26 | 26 | /// Error when parsing the `dirstate` file |
|
27 | 27 | ParseError(DirstateParseError), |
|
28 | 28 | } |
|
29 | 29 | |
|
30 | 30 | /// A `ListDirstateTrackedFiles` error |
|
31 | 31 | #[derive(Debug)] |
|
32 | 32 | pub struct ListDirstateTrackedFilesError { |
|
33 | 33 | /// Kind of error encountered by `ListDirstateTrackedFiles` |
|
34 | 34 | pub kind: ListDirstateTrackedFilesErrorKind, |
|
35 | 35 | } |
|
36 | 36 | |
|
37 | 37 | impl From<ListDirstateTrackedFilesErrorKind> |
|
38 | 38 | for ListDirstateTrackedFilesError |
|
39 | 39 | { |
|
40 | 40 | fn from(kind: ListDirstateTrackedFilesErrorKind) -> Self { |
|
41 | 41 | ListDirstateTrackedFilesError { kind } |
|
42 | 42 | } |
|
43 | 43 | } |
|
44 | 44 | |
|
45 | 45 | impl From<std::io::Error> for ListDirstateTrackedFilesError { |
|
46 | 46 | fn from(err: std::io::Error) -> Self { |
|
47 | 47 | let kind = ListDirstateTrackedFilesErrorKind::IoError(err); |
|
48 | 48 | ListDirstateTrackedFilesError { kind } |
|
49 | 49 | } |
|
50 | 50 | } |
|
51 | 51 | |
|
52 | 52 | /// List files under Mercurial control in the working directory |
|
53 | 53 | /// by reading the dirstate |
|
54 |
pub struct |
|
|
54 | pub struct Dirstate { | |
|
55 | 55 | /// The `dirstate` content. |
|
56 | 56 | content: Vec<u8>, |
|
57 | 57 | } |
|
58 | 58 | |
|
59 |
impl |
|
|
59 | impl Dirstate { | |
|
60 | 60 | pub fn new(root: &Path) -> Result<Self, ListDirstateTrackedFilesError> { |
|
61 | 61 | let dirstate = root.join(".hg/dirstate"); |
|
62 | 62 | let content = fs::read(&dirstate)?; |
|
63 | 63 | Ok(Self { content }) |
|
64 | 64 | } |
|
65 | 65 | |
|
66 |
pub fn |
|
|
67 |
& |
|
|
66 | pub fn tracked_files( | |
|
67 | &self, | |
|
68 | 68 | ) -> Result<Vec<&HgPath>, ListDirstateTrackedFilesError> { |
|
69 | 69 | let (_, entries, _) = parse_dirstate(&self.content) |
|
70 | 70 | .map_err(ListDirstateTrackedFilesErrorKind::ParseError)?; |
|
71 | 71 | let mut files: Vec<&HgPath> = entries |
|
72 | 72 | .into_iter() |
|
73 | 73 | .filter_map(|(path, entry)| match entry.state { |
|
74 | 74 | EntryState::Removed => None, |
|
75 | 75 | _ => Some(path), |
|
76 | 76 | }) |
|
77 | 77 | .collect(); |
|
78 | 78 | files.par_sort_unstable(); |
|
79 | 79 | Ok(files) |
|
80 | 80 | } |
|
81 | 81 | } |
|
82 | 82 | |
|
83 | 83 | /// Kind of error encountered by `ListRevTrackedFiles` |
|
84 | 84 | #[derive(Debug)] |
|
85 | 85 | pub enum ListRevTrackedFilesErrorKind { |
|
86 | 86 | /// Error when reading a `revlog` file. |
|
87 | 87 | IoError(std::io::Error), |
|
88 | 88 | /// The revision has not been found. |
|
89 | 89 | InvalidRevision, |
|
90 | 90 | /// Found more than one revision whose ID match the requested prefix |
|
91 | 91 | AmbiguousPrefix, |
|
92 | 92 | /// A `revlog` file is corrupted. |
|
93 | 93 | CorruptedRevlog, |
|
94 | 94 | /// The `revlog` format version is not supported. |
|
95 | 95 | UnsuportedRevlogVersion(u16), |
|
96 | 96 | /// The `revlog` data format is not supported. |
|
97 | 97 | UnknowRevlogDataFormat(u8), |
|
98 | 98 | } |
|
99 | 99 | |
|
100 | 100 | /// A `ListRevTrackedFiles` error |
|
101 | 101 | #[derive(Debug)] |
|
102 | 102 | pub struct ListRevTrackedFilesError { |
|
103 | 103 | /// Kind of error encountered by `ListRevTrackedFiles` |
|
104 | 104 | pub kind: ListRevTrackedFilesErrorKind, |
|
105 | 105 | } |
|
106 | 106 | |
|
107 | 107 | impl From<ListRevTrackedFilesErrorKind> for ListRevTrackedFilesError { |
|
108 | 108 | fn from(kind: ListRevTrackedFilesErrorKind) -> Self { |
|
109 | 109 | ListRevTrackedFilesError { kind } |
|
110 | 110 | } |
|
111 | 111 | } |
|
112 | 112 | |
|
113 | 113 | impl From<RevlogError> for ListRevTrackedFilesError { |
|
114 | 114 | fn from(err: RevlogError) -> Self { |
|
115 | 115 | match err { |
|
116 | 116 | RevlogError::IoError(err) => { |
|
117 | 117 | ListRevTrackedFilesErrorKind::IoError(err) |
|
118 | 118 | } |
|
119 | 119 | RevlogError::UnsuportedVersion(version) => { |
|
120 | 120 | ListRevTrackedFilesErrorKind::UnsuportedRevlogVersion(version) |
|
121 | 121 | } |
|
122 | 122 | RevlogError::InvalidRevision => { |
|
123 | 123 | ListRevTrackedFilesErrorKind::InvalidRevision |
|
124 | 124 | } |
|
125 | 125 | RevlogError::AmbiguousPrefix => { |
|
126 | 126 | ListRevTrackedFilesErrorKind::AmbiguousPrefix |
|
127 | 127 | } |
|
128 | 128 | RevlogError::Corrupted => { |
|
129 | 129 | ListRevTrackedFilesErrorKind::CorruptedRevlog |
|
130 | 130 | } |
|
131 | 131 | RevlogError::UnknowDataFormat(format) => { |
|
132 | 132 | ListRevTrackedFilesErrorKind::UnknowRevlogDataFormat(format) |
|
133 | 133 | } |
|
134 | 134 | } |
|
135 | 135 | .into() |
|
136 | 136 | } |
|
137 | 137 | } |
|
138 | 138 | |
|
139 | 139 | /// List files under Mercurial control at a given revision. |
|
140 |
pub |
|
|
141 | /// The revision to list the files from. | |
|
142 |
rev: & |
|
|
143 | /// The changelog file | |
|
144 |
changelog |
|
|
145 | /// The manifest file | |
|
146 | manifest: Manifest, | |
|
147 | /// The manifest entry corresponding to the revision. | |
|
148 | /// | |
|
149 | /// Used to hold the owner of the returned references. | |
|
150 | manifest_entry: Option<ManifestEntry>, | |
|
140 | pub fn list_rev_tracked_files( | |
|
141 | root: &Path, | |
|
142 | rev: &str, | |
|
143 | ) -> Result<FilesForRev, ListRevTrackedFilesError> { | |
|
144 | let changelog = Changelog::open(root)?; | |
|
145 | let manifest = Manifest::open(root)?; | |
|
146 | ||
|
147 | let changelog_entry = match rev.parse::<Revision>() { | |
|
148 | Ok(rev) => changelog.get_rev(rev)?, | |
|
149 | _ => { | |
|
150 | let changelog_node = NodePrefix::from_hex(&rev) | |
|
151 | .or(Err(ListRevTrackedFilesErrorKind::InvalidRevision))?; | |
|
152 | changelog.get_node(changelog_node.borrow())? | |
|
153 | } | |
|
154 | }; | |
|
155 | let manifest_node = Node::from_hex(&changelog_entry.manifest_node()?) | |
|
156 | .or(Err(ListRevTrackedFilesErrorKind::CorruptedRevlog))?; | |
|
157 | let manifest_entry = manifest.get_node((&manifest_node).into())?; | |
|
158 | Ok(FilesForRev(manifest_entry)) | |
|
151 | 159 | } |
|
152 | 160 | |
|
153 | impl<'a> ListRevTrackedFiles<'a> { | |
|
154 | pub fn new( | |
|
155 | root: &Path, | |
|
156 | rev: &'a str, | |
|
157 | ) -> Result<Self, ListRevTrackedFilesError> { | |
|
158 | let changelog = Changelog::open(root)?; | |
|
159 | let manifest = Manifest::open(root)?; | |
|
160 | ||
|
161 | Ok(Self { | |
|
162 | rev, | |
|
163 | changelog, | |
|
164 | manifest, | |
|
165 | manifest_entry: None, | |
|
166 | }) | |
|
167 | } | |
|
161 | pub struct FilesForRev(ManifestEntry); | |
|
168 | 162 | |
|
169 | pub fn run( | |
|
170 | &mut self, | |
|
171 | ) -> Result<impl Iterator<Item = &HgPath>, ListRevTrackedFilesError> { | |
|
172 | let changelog_entry = match self.rev.parse::<Revision>() { | |
|
173 | Ok(rev) => self.changelog.get_rev(rev)?, | |
|
174 | _ => { | |
|
175 | let changelog_node = NodePrefix::from_hex(&self.rev) | |
|
176 | .or(Err(ListRevTrackedFilesErrorKind::InvalidRevision))?; | |
|
177 | self.changelog.get_node(changelog_node.borrow())? | |
|
178 | } | |
|
179 | }; | |
|
180 | let manifest_node = Node::from_hex(&changelog_entry.manifest_node()?) | |
|
181 | .or(Err(ListRevTrackedFilesErrorKind::CorruptedRevlog))?; | |
|
182 | ||
|
183 | self.manifest_entry = | |
|
184 | Some(self.manifest.get_node((&manifest_node).into())?); | |
|
185 | ||
|
186 | if let Some(ref manifest_entry) = self.manifest_entry { | |
|
187 | Ok(manifest_entry.files()) | |
|
188 | } else { | |
|
189 | panic!( | |
|
190 | "manifest entry should have been stored in self.manifest_node to ensure its lifetime since references are returned from it" | |
|
191 | ) | |
|
192 | } | |
|
163 | impl FilesForRev { | |
|
164 | pub fn iter(&self) -> impl Iterator<Item = &HgPath> { | |
|
165 | self.0.files() | |
|
193 | 166 | } |
|
194 | 167 | } |
@@ -1,28 +1,23 b'' | |||
|
1 | 1 | //! A distinction is made between operations and commands. |
|
2 | 2 | //! An operation is what can be done whereas a command is what is exposed by |
|
3 | 3 | //! the cli. A single command can use several operations to achieve its goal. |
|
4 | 4 | |
|
5 | 5 | mod cat; |
|
6 | 6 | mod debugdata; |
|
7 | 7 | mod dirstate_status; |
|
8 | 8 | mod find_root; |
|
9 | 9 | mod list_tracked_files; |
|
10 |
pub use cat::{ |
|
|
10 | pub use cat::{cat, CatRevError, CatRevErrorKind}; | |
|
11 | 11 | pub use debugdata::{ |
|
12 |
|
|
|
12 | debug_data, DebugDataError, DebugDataErrorKind, DebugDataKind, | |
|
13 | 13 | }; |
|
14 | pub use find_root::{FindRoot, FindRootError, FindRootErrorKind}; | |
|
15 | pub use list_tracked_files::{ | |
|
16 | ListDirstateTrackedFiles, ListDirstateTrackedFilesError, | |
|
17 | ListDirstateTrackedFilesErrorKind, | |
|
14 | pub use find_root::{ | |
|
15 | find_root, find_root_from_path, FindRootError, FindRootErrorKind, | |
|
18 | 16 | }; |
|
19 | 17 | pub use list_tracked_files::{ |
|
20 |
|
|
|
18 | list_rev_tracked_files, FilesForRev, ListRevTrackedFilesError, | |
|
21 | 19 | ListRevTrackedFilesErrorKind, |
|
22 | 20 | }; |
|
23 | ||
|
24 | // TODO add an `Operation` trait when GAT have landed (rust #44265): | |
|
25 | // there is no way to currently define a trait which can both return | |
|
26 | // references to `self` and to passed data, which is what we would need. | |
|
27 | // Generic Associated Types may fix this and allow us to have a unified | |
|
28 | // interface. | |
|
21 | pub use list_tracked_files::{ | |
|
22 | Dirstate, ListDirstateTrackedFilesError, ListDirstateTrackedFilesErrorKind, | |
|
23 | }; |
@@ -1,108 +1,106 b'' | |||
|
1 | 1 | use crate::commands::Command; |
|
2 | 2 | use crate::error::{CommandError, CommandErrorKind}; |
|
3 | 3 | use crate::ui::utf8_to_local; |
|
4 | 4 | use crate::ui::Ui; |
|
5 |
use hg::operations:: |
|
|
6 |
use hg::operations::{ |
|
|
5 | use hg::operations::find_root; | |
|
6 | use hg::operations::{cat, CatRevError, CatRevErrorKind}; | |
|
7 | 7 | use hg::requirements; |
|
8 | 8 | use hg::utils::hg_path::HgPathBuf; |
|
9 | 9 | use micro_timer::timed; |
|
10 | 10 | use std::convert::TryFrom; |
|
11 | 11 | |
|
12 | 12 | pub const HELP_TEXT: &str = " |
|
13 | 13 | Output the current or given revision of files |
|
14 | 14 | "; |
|
15 | 15 | |
|
16 | 16 | pub struct CatCommand<'a> { |
|
17 | 17 | rev: Option<&'a str>, |
|
18 | 18 | files: Vec<&'a str>, |
|
19 | 19 | } |
|
20 | 20 | |
|
21 | 21 | impl<'a> CatCommand<'a> { |
|
22 | 22 | pub fn new(rev: Option<&'a str>, files: Vec<&'a str>) -> Self { |
|
23 | 23 | Self { rev, files } |
|
24 | 24 | } |
|
25 | 25 | |
|
26 | 26 | fn display(&self, ui: &Ui, data: &[u8]) -> Result<(), CommandError> { |
|
27 | 27 | ui.write_stdout(data)?; |
|
28 | 28 | Ok(()) |
|
29 | 29 | } |
|
30 | 30 | } |
|
31 | 31 | |
|
32 | 32 | impl<'a> Command for CatCommand<'a> { |
|
33 | 33 | #[timed] |
|
34 | 34 | fn run(&self, ui: &Ui) -> Result<(), CommandError> { |
|
35 |
let root = |
|
|
35 | let root = find_root()?; | |
|
36 | 36 | requirements::check(&root)?; |
|
37 | 37 | let cwd = std::env::current_dir() |
|
38 | 38 | .or_else(|e| Err(CommandErrorKind::CurrentDirNotFound(e)))?; |
|
39 | 39 | |
|
40 | 40 | let mut files = vec![]; |
|
41 | 41 | for file in self.files.iter() { |
|
42 | 42 | let normalized = cwd.join(&file); |
|
43 | 43 | let stripped = normalized |
|
44 | 44 | .strip_prefix(&root) |
|
45 | 45 | .or(Err(CommandErrorKind::Abort(None)))?; |
|
46 | 46 | let hg_file = HgPathBuf::try_from(stripped.to_path_buf()) |
|
47 | 47 | .or(Err(CommandErrorKind::Abort(None)))?; |
|
48 | 48 | files.push(hg_file); |
|
49 | 49 | } |
|
50 | 50 | |
|
51 | 51 | match self.rev { |
|
52 | 52 | Some(rev) => { |
|
53 |
let |
|
|
53 | let data = cat(&root, rev, &files) | |
|
54 | 54 | .map_err(|e| map_rev_error(rev, e))?; |
|
55 | let data = | |
|
56 | operation.run().map_err(|e| map_rev_error(rev, e))?; | |
|
57 | 55 | self.display(ui, &data) |
|
58 | 56 | } |
|
59 | 57 | None => Err(CommandErrorKind::Unimplemented.into()), |
|
60 | 58 | } |
|
61 | 59 | } |
|
62 | 60 | } |
|
63 | 61 | |
|
64 | 62 | /// Convert `CatRevErrorKind` to `CommandError` |
|
65 | 63 | fn map_rev_error(rev: &str, err: CatRevError) -> CommandError { |
|
66 | 64 | CommandError { |
|
67 | 65 | kind: match err.kind { |
|
68 | 66 | CatRevErrorKind::IoError(err) => CommandErrorKind::Abort(Some( |
|
69 | 67 | utf8_to_local(&format!("abort: {}\n", err)).into(), |
|
70 | 68 | )), |
|
71 | 69 | CatRevErrorKind::InvalidRevision => CommandErrorKind::Abort(Some( |
|
72 | 70 | utf8_to_local(&format!( |
|
73 | 71 | "abort: invalid revision identifier {}\n", |
|
74 | 72 | rev |
|
75 | 73 | )) |
|
76 | 74 | .into(), |
|
77 | 75 | )), |
|
78 | 76 | CatRevErrorKind::AmbiguousPrefix => CommandErrorKind::Abort(Some( |
|
79 | 77 | utf8_to_local(&format!( |
|
80 | 78 | "abort: ambiguous revision identifier {}\n", |
|
81 | 79 | rev |
|
82 | 80 | )) |
|
83 | 81 | .into(), |
|
84 | 82 | )), |
|
85 | 83 | CatRevErrorKind::UnsuportedRevlogVersion(version) => { |
|
86 | 84 | CommandErrorKind::Abort(Some( |
|
87 | 85 | utf8_to_local(&format!( |
|
88 | 86 | "abort: unsupported revlog version {}\n", |
|
89 | 87 | version |
|
90 | 88 | )) |
|
91 | 89 | .into(), |
|
92 | 90 | )) |
|
93 | 91 | } |
|
94 | 92 | CatRevErrorKind::CorruptedRevlog => CommandErrorKind::Abort(Some( |
|
95 | 93 | "abort: corrupted revlog\n".into(), |
|
96 | 94 | )), |
|
97 | 95 | CatRevErrorKind::UnknowRevlogDataFormat(format) => { |
|
98 | 96 | CommandErrorKind::Abort(Some( |
|
99 | 97 | utf8_to_local(&format!( |
|
100 | 98 | "abort: unknow revlog dataformat {:?}\n", |
|
101 | 99 | format |
|
102 | 100 | )) |
|
103 | 101 | .into(), |
|
104 | 102 | )) |
|
105 | 103 | } |
|
106 | 104 | }, |
|
107 | 105 | } |
|
108 | 106 | } |
@@ -1,91 +1,91 b'' | |||
|
1 | 1 | use crate::commands::Command; |
|
2 | 2 | use crate::error::{CommandError, CommandErrorKind}; |
|
3 | 3 | use crate::ui::utf8_to_local; |
|
4 | 4 | use crate::ui::Ui; |
|
5 | use hg::operations::find_root; | |
|
5 | 6 | use hg::operations::{ |
|
6 |
|
|
|
7 | debug_data, DebugDataError, DebugDataErrorKind, DebugDataKind, | |
|
7 | 8 | }; |
|
8 | 9 | use micro_timer::timed; |
|
9 | 10 | |
|
10 | 11 | pub const HELP_TEXT: &str = " |
|
11 | 12 | Dump the contents of a data file revision |
|
12 | 13 | "; |
|
13 | 14 | |
|
14 | 15 | pub struct DebugDataCommand<'a> { |
|
15 | 16 | rev: &'a str, |
|
16 | 17 | kind: DebugDataKind, |
|
17 | 18 | } |
|
18 | 19 | |
|
19 | 20 | impl<'a> DebugDataCommand<'a> { |
|
20 | 21 | pub fn new(rev: &'a str, kind: DebugDataKind) -> Self { |
|
21 | 22 | DebugDataCommand { rev, kind } |
|
22 | 23 | } |
|
23 | 24 | } |
|
24 | 25 | |
|
25 | 26 | impl<'a> Command for DebugDataCommand<'a> { |
|
26 | 27 | #[timed] |
|
27 | 28 | fn run(&self, ui: &Ui) -> Result<(), CommandError> { |
|
28 | let mut operation = DebugData::new(self.rev, self.kind); | |
|
29 | let data = | |
|
30 |
|
|
|
29 | let root = find_root()?; | |
|
30 | let data = debug_data(&root, self.rev, self.kind) | |
|
31 | .map_err(|e| to_command_error(self.rev, e))?; | |
|
31 | 32 | |
|
32 | 33 | let mut stdout = ui.stdout_buffer(); |
|
33 | 34 | stdout.write_all(&data)?; |
|
34 | 35 | stdout.flush()?; |
|
35 | 36 | |
|
36 | 37 | Ok(()) |
|
37 | 38 | } |
|
38 | 39 | } |
|
39 | 40 | |
|
40 | 41 | /// Convert operation errors to command errors |
|
41 | 42 | fn to_command_error(rev: &str, err: DebugDataError) -> CommandError { |
|
42 | 43 | match err.kind { |
|
43 | DebugDataErrorKind::FindRootError(err) => CommandError::from(err), | |
|
44 | 44 | DebugDataErrorKind::IoError(err) => CommandError { |
|
45 | 45 | kind: CommandErrorKind::Abort(Some( |
|
46 | 46 | utf8_to_local(&format!("abort: {}\n", err)).into(), |
|
47 | 47 | )), |
|
48 | 48 | }, |
|
49 | 49 | DebugDataErrorKind::InvalidRevision => CommandError { |
|
50 | 50 | kind: CommandErrorKind::Abort(Some( |
|
51 | 51 | utf8_to_local(&format!( |
|
52 | 52 | "abort: invalid revision identifier{}\n", |
|
53 | 53 | rev |
|
54 | 54 | )) |
|
55 | 55 | .into(), |
|
56 | 56 | )), |
|
57 | 57 | }, |
|
58 | 58 | DebugDataErrorKind::AmbiguousPrefix => CommandError { |
|
59 | 59 | kind: CommandErrorKind::Abort(Some( |
|
60 | 60 | utf8_to_local(&format!( |
|
61 | 61 | "abort: ambiguous revision identifier{}\n", |
|
62 | 62 | rev |
|
63 | 63 | )) |
|
64 | 64 | .into(), |
|
65 | 65 | )), |
|
66 | 66 | }, |
|
67 | 67 | DebugDataErrorKind::UnsuportedRevlogVersion(version) => CommandError { |
|
68 | 68 | kind: CommandErrorKind::Abort(Some( |
|
69 | 69 | utf8_to_local(&format!( |
|
70 | 70 | "abort: unsupported revlog version {}\n", |
|
71 | 71 | version |
|
72 | 72 | )) |
|
73 | 73 | .into(), |
|
74 | 74 | )), |
|
75 | 75 | }, |
|
76 | 76 | DebugDataErrorKind::CorruptedRevlog => CommandError { |
|
77 | 77 | kind: CommandErrorKind::Abort(Some( |
|
78 | 78 | "abort: corrupted revlog\n".into(), |
|
79 | 79 | )), |
|
80 | 80 | }, |
|
81 | 81 | DebugDataErrorKind::UnknowRevlogDataFormat(format) => CommandError { |
|
82 | 82 | kind: CommandErrorKind::Abort(Some( |
|
83 | 83 | utf8_to_local(&format!( |
|
84 | 84 | "abort: unknow revlog dataformat {:?}\n", |
|
85 | 85 | format |
|
86 | 86 | )) |
|
87 | 87 | .into(), |
|
88 | 88 | )), |
|
89 | 89 | }, |
|
90 | 90 | } |
|
91 | 91 | } |
@@ -1,30 +1,30 b'' | |||
|
1 | 1 | use crate::commands::Command; |
|
2 | 2 | use crate::error::CommandError; |
|
3 | 3 | use crate::ui::Ui; |
|
4 |
use hg::operations:: |
|
|
4 | use hg::operations::find_root; | |
|
5 | 5 | use hg::requirements; |
|
6 | 6 | |
|
7 | 7 | pub const HELP_TEXT: &str = " |
|
8 | 8 | Print the current repo requirements. |
|
9 | 9 | "; |
|
10 | 10 | |
|
11 | 11 | pub struct DebugRequirementsCommand {} |
|
12 | 12 | |
|
13 | 13 | impl DebugRequirementsCommand { |
|
14 | 14 | pub fn new() -> Self { |
|
15 | 15 | DebugRequirementsCommand {} |
|
16 | 16 | } |
|
17 | 17 | } |
|
18 | 18 | |
|
19 | 19 | impl Command for DebugRequirementsCommand { |
|
20 | 20 | fn run(&self, ui: &Ui) -> Result<(), CommandError> { |
|
21 |
let root = |
|
|
21 | let root = find_root()?; | |
|
22 | 22 | let mut output = String::new(); |
|
23 | 23 | for req in requirements::load(&root)? { |
|
24 | 24 | output.push_str(&req); |
|
25 | 25 | output.push('\n'); |
|
26 | 26 | } |
|
27 | 27 | ui.write_stdout(output.as_bytes())?; |
|
28 | 28 | Ok(()) |
|
29 | 29 | } |
|
30 | 30 | } |
@@ -1,147 +1,144 b'' | |||
|
1 | 1 | use crate::commands::Command; |
|
2 | 2 | use crate::error::{CommandError, CommandErrorKind}; |
|
3 | 3 | use crate::ui::utf8_to_local; |
|
4 | 4 | use crate::ui::Ui; |
|
5 |
use hg::operations:: |
|
|
5 | use hg::operations::find_root; | |
|
6 | 6 | use hg::operations::{ |
|
7 |
|
|
|
8 |
List |
|
|
7 | list_rev_tracked_files, ListRevTrackedFilesError, | |
|
8 | ListRevTrackedFilesErrorKind, | |
|
9 | 9 | }; |
|
10 | 10 | use hg::operations::{ |
|
11 | ListRevTrackedFiles, ListRevTrackedFilesError, | |
|
12 | ListRevTrackedFilesErrorKind, | |
|
11 | Dirstate, ListDirstateTrackedFilesError, ListDirstateTrackedFilesErrorKind, | |
|
13 | 12 | }; |
|
14 | 13 | use hg::requirements; |
|
15 | 14 | use hg::utils::files::{get_bytes_from_path, relativize_path}; |
|
16 | 15 | use hg::utils::hg_path::{HgPath, HgPathBuf}; |
|
17 | 16 | use std::path::Path; |
|
18 | 17 | |
|
19 | 18 | pub const HELP_TEXT: &str = " |
|
20 | 19 | List tracked files. |
|
21 | 20 | |
|
22 | 21 | Returns 0 on success. |
|
23 | 22 | "; |
|
24 | 23 | |
|
25 | 24 | pub struct FilesCommand<'a> { |
|
26 | 25 | rev: Option<&'a str>, |
|
27 | 26 | } |
|
28 | 27 | |
|
29 | 28 | impl<'a> FilesCommand<'a> { |
|
30 | 29 | pub fn new(rev: Option<&'a str>) -> Self { |
|
31 | 30 | FilesCommand { rev } |
|
32 | 31 | } |
|
33 | 32 | |
|
34 | 33 | fn display_files( |
|
35 | 34 | &self, |
|
36 | 35 | ui: &Ui, |
|
37 | 36 | root: &Path, |
|
38 | 37 | files: impl IntoIterator<Item = &'a HgPath>, |
|
39 | 38 | ) -> Result<(), CommandError> { |
|
40 | 39 | let cwd = std::env::current_dir() |
|
41 | 40 | .or_else(|e| Err(CommandErrorKind::CurrentDirNotFound(e)))?; |
|
42 | 41 | let rooted_cwd = cwd |
|
43 | 42 | .strip_prefix(root) |
|
44 | 43 | .expect("cwd was already checked within the repository"); |
|
45 | 44 | let rooted_cwd = HgPathBuf::from(get_bytes_from_path(rooted_cwd)); |
|
46 | 45 | |
|
47 | 46 | let mut stdout = ui.stdout_buffer(); |
|
48 | 47 | |
|
49 | 48 | for file in files { |
|
50 | 49 | stdout.write_all(relativize_path(file, &rooted_cwd).as_ref())?; |
|
51 | 50 | stdout.write_all(b"\n")?; |
|
52 | 51 | } |
|
53 | 52 | stdout.flush()?; |
|
54 | 53 | Ok(()) |
|
55 | 54 | } |
|
56 | 55 | } |
|
57 | 56 | |
|
58 | 57 | impl<'a> Command for FilesCommand<'a> { |
|
59 | 58 | fn run(&self, ui: &Ui) -> Result<(), CommandError> { |
|
60 |
let root = |
|
|
59 | let root = find_root()?; | |
|
61 | 60 | requirements::check(&root)?; |
|
62 | 61 | if let Some(rev) = self.rev { |
|
63 |
let |
|
|
62 | let files = list_rev_tracked_files(&root, rev) | |
|
64 | 63 | .map_err(|e| map_rev_error(rev, e))?; |
|
65 | let files = operation.run().map_err(|e| map_rev_error(rev, e))?; | |
|
66 | self.display_files(ui, &root, files) | |
|
64 | self.display_files(ui, &root, files.iter()) | |
|
67 | 65 | } else { |
|
68 | let mut operation = ListDirstateTrackedFiles::new(&root) | |
|
69 |
|
|
|
70 | let files = operation.run().map_err(map_dirstate_error)?; | |
|
66 | let distate = Dirstate::new(&root).map_err(map_dirstate_error)?; | |
|
67 | let files = distate.tracked_files().map_err(map_dirstate_error)?; | |
|
71 | 68 | self.display_files(ui, &root, files) |
|
72 | 69 | } |
|
73 | 70 | } |
|
74 | 71 | } |
|
75 | 72 | |
|
76 | 73 | /// Convert `ListRevTrackedFilesErrorKind` to `CommandError` |
|
77 | 74 | fn map_rev_error(rev: &str, err: ListRevTrackedFilesError) -> CommandError { |
|
78 | 75 | CommandError { |
|
79 | 76 | kind: match err.kind { |
|
80 | 77 | ListRevTrackedFilesErrorKind::IoError(err) => { |
|
81 | 78 | CommandErrorKind::Abort(Some( |
|
82 | 79 | utf8_to_local(&format!("abort: {}\n", err)).into(), |
|
83 | 80 | )) |
|
84 | 81 | } |
|
85 | 82 | ListRevTrackedFilesErrorKind::InvalidRevision => { |
|
86 | 83 | CommandErrorKind::Abort(Some( |
|
87 | 84 | utf8_to_local(&format!( |
|
88 | 85 | "abort: invalid revision identifier {}\n", |
|
89 | 86 | rev |
|
90 | 87 | )) |
|
91 | 88 | .into(), |
|
92 | 89 | )) |
|
93 | 90 | } |
|
94 | 91 | ListRevTrackedFilesErrorKind::AmbiguousPrefix => { |
|
95 | 92 | CommandErrorKind::Abort(Some( |
|
96 | 93 | utf8_to_local(&format!( |
|
97 | 94 | "abort: ambiguous revision identifier {}\n", |
|
98 | 95 | rev |
|
99 | 96 | )) |
|
100 | 97 | .into(), |
|
101 | 98 | )) |
|
102 | 99 | } |
|
103 | 100 | ListRevTrackedFilesErrorKind::UnsuportedRevlogVersion(version) => { |
|
104 | 101 | CommandErrorKind::Abort(Some( |
|
105 | 102 | utf8_to_local(&format!( |
|
106 | 103 | "abort: unsupported revlog version {}\n", |
|
107 | 104 | version |
|
108 | 105 | )) |
|
109 | 106 | .into(), |
|
110 | 107 | )) |
|
111 | 108 | } |
|
112 | 109 | ListRevTrackedFilesErrorKind::CorruptedRevlog => { |
|
113 | 110 | CommandErrorKind::Abort(Some( |
|
114 | 111 | "abort: corrupted revlog\n".into(), |
|
115 | 112 | )) |
|
116 | 113 | } |
|
117 | 114 | ListRevTrackedFilesErrorKind::UnknowRevlogDataFormat(format) => { |
|
118 | 115 | CommandErrorKind::Abort(Some( |
|
119 | 116 | utf8_to_local(&format!( |
|
120 | 117 | "abort: unknow revlog dataformat {:?}\n", |
|
121 | 118 | format |
|
122 | 119 | )) |
|
123 | 120 | .into(), |
|
124 | 121 | )) |
|
125 | 122 | } |
|
126 | 123 | }, |
|
127 | 124 | } |
|
128 | 125 | } |
|
129 | 126 | |
|
130 | 127 | /// Convert `ListDirstateTrackedFilesError` to `CommandError` |
|
131 | 128 | fn map_dirstate_error(err: ListDirstateTrackedFilesError) -> CommandError { |
|
132 | 129 | CommandError { |
|
133 | 130 | kind: match err.kind { |
|
134 | 131 | ListDirstateTrackedFilesErrorKind::IoError(err) => { |
|
135 | 132 | CommandErrorKind::Abort(Some( |
|
136 | 133 | utf8_to_local(&format!("abort: {}\n", err)).into(), |
|
137 | 134 | )) |
|
138 | 135 | } |
|
139 | 136 | ListDirstateTrackedFilesErrorKind::ParseError(_) => { |
|
140 | 137 | CommandErrorKind::Abort(Some( |
|
141 | 138 | // TODO find a better error message |
|
142 | 139 | b"abort: parse error\n".to_vec(), |
|
143 | 140 | )) |
|
144 | 141 | } |
|
145 | 142 | }, |
|
146 | 143 | } |
|
147 | 144 | } |
@@ -1,32 +1,32 b'' | |||
|
1 | 1 | use crate::commands::Command; |
|
2 | 2 | use crate::error::CommandError; |
|
3 | 3 | use crate::ui::Ui; |
|
4 | 4 | use format_bytes::format_bytes; |
|
5 |
use hg::operations:: |
|
|
5 | use hg::operations::find_root; | |
|
6 | 6 | use hg::utils::files::get_bytes_from_path; |
|
7 | 7 | |
|
8 | 8 | pub const HELP_TEXT: &str = " |
|
9 | 9 | Print the root directory of the current repository. |
|
10 | 10 | |
|
11 | 11 | Returns 0 on success. |
|
12 | 12 | "; |
|
13 | 13 | |
|
14 | 14 | pub struct RootCommand {} |
|
15 | 15 | |
|
16 | 16 | impl RootCommand { |
|
17 | 17 | pub fn new() -> Self { |
|
18 | 18 | RootCommand {} |
|
19 | 19 | } |
|
20 | 20 | } |
|
21 | 21 | |
|
22 | 22 | impl Command for RootCommand { |
|
23 | 23 | fn run(&self, ui: &Ui) -> Result<(), CommandError> { |
|
24 |
let path_buf = |
|
|
24 | let path_buf = find_root()?; | |
|
25 | 25 | |
|
26 | 26 | let bytes = get_bytes_from_path(path_buf); |
|
27 | 27 | |
|
28 | 28 | ui.write_stdout(&format_bytes!(b"{}\n", bytes.as_slice()))?; |
|
29 | 29 | |
|
30 | 30 | Ok(()) |
|
31 | 31 | } |
|
32 | 32 | } |
General Comments 0
You need to be logged in to leave comments.
Login now