##// END OF EJS Templates
rhg: allow specifying a changeset ID prefix...
Simon Sapin -
r46646:8d616409 default
parent child Browse files
Show More
@@ -1,169 +1,172 b''
1 // list_tracked_files.rs
1 // list_tracked_files.rs
2 //
2 //
3 // Copyright 2020 Antoine Cezar <antoine.cezar@octobus.net>
3 // Copyright 2020 Antoine Cezar <antoine.cezar@octobus.net>
4 //
4 //
5 // This software may be used and distributed according to the terms of the
5 // This software may be used and distributed according to the terms of the
6 // GNU General Public License version 2 or any later version.
6 // GNU General Public License version 2 or any later version.
7
7
8 use std::convert::From;
8 use std::convert::From;
9 use std::path::{Path, PathBuf};
9 use std::path::{Path, PathBuf};
10
10
11 use crate::revlog::changelog::Changelog;
11 use crate::revlog::changelog::Changelog;
12 use crate::revlog::manifest::{Manifest, ManifestEntry};
12 use crate::revlog::manifest::{Manifest, ManifestEntry};
13 use crate::revlog::path_encode::path_encode;
13 use crate::revlog::path_encode::path_encode;
14 use crate::revlog::revlog::Revlog;
14 use crate::revlog::revlog::Revlog;
15 use crate::revlog::revlog::RevlogError;
15 use crate::revlog::revlog::RevlogError;
16 use crate::revlog::Revision;
16 use crate::revlog::Revision;
17 use crate::utils::files::get_path_from_bytes;
17 use crate::utils::files::get_path_from_bytes;
18 use crate::utils::hg_path::{HgPath, HgPathBuf};
18 use crate::utils::hg_path::{HgPath, HgPathBuf};
19
19
20 const METADATA_DELIMITER: [u8; 2] = [b'\x01', b'\n'];
20 const METADATA_DELIMITER: [u8; 2] = [b'\x01', b'\n'];
21
21
22 /// Kind of error encountered by `CatRev`
22 /// Kind of error encountered by `CatRev`
23 #[derive(Debug)]
23 #[derive(Debug)]
24 pub enum CatRevErrorKind {
24 pub enum CatRevErrorKind {
25 /// Error when reading a `revlog` file.
25 /// Error when reading a `revlog` file.
26 IoError(std::io::Error),
26 IoError(std::io::Error),
27 /// The revision has not been found.
27 /// The revision has not been found.
28 InvalidRevision,
28 InvalidRevision,
29 /// Found more than one revision whose ID match the requested prefix
30 AmbiguousPrefix,
29 /// A `revlog` file is corrupted.
31 /// A `revlog` file is corrupted.
30 CorruptedRevlog,
32 CorruptedRevlog,
31 /// The `revlog` format version is not supported.
33 /// The `revlog` format version is not supported.
32 UnsuportedRevlogVersion(u16),
34 UnsuportedRevlogVersion(u16),
33 /// The `revlog` data format is not supported.
35 /// The `revlog` data format is not supported.
34 UnknowRevlogDataFormat(u8),
36 UnknowRevlogDataFormat(u8),
35 }
37 }
36
38
37 /// A `CatRev` error
39 /// A `CatRev` error
38 #[derive(Debug)]
40 #[derive(Debug)]
39 pub struct CatRevError {
41 pub struct CatRevError {
40 /// Kind of error encountered by `CatRev`
42 /// Kind of error encountered by `CatRev`
41 pub kind: CatRevErrorKind,
43 pub kind: CatRevErrorKind,
42 }
44 }
43
45
44 impl From<CatRevErrorKind> for CatRevError {
46 impl From<CatRevErrorKind> for CatRevError {
45 fn from(kind: CatRevErrorKind) -> Self {
47 fn from(kind: CatRevErrorKind) -> Self {
46 CatRevError { kind }
48 CatRevError { kind }
47 }
49 }
48 }
50 }
49
51
50 impl From<RevlogError> for CatRevError {
52 impl From<RevlogError> for CatRevError {
51 fn from(err: RevlogError) -> Self {
53 fn from(err: RevlogError) -> Self {
52 match err {
54 match err {
53 RevlogError::IoError(err) => CatRevErrorKind::IoError(err),
55 RevlogError::IoError(err) => CatRevErrorKind::IoError(err),
54 RevlogError::UnsuportedVersion(version) => {
56 RevlogError::UnsuportedVersion(version) => {
55 CatRevErrorKind::UnsuportedRevlogVersion(version)
57 CatRevErrorKind::UnsuportedRevlogVersion(version)
56 }
58 }
57 RevlogError::InvalidRevision => CatRevErrorKind::InvalidRevision,
59 RevlogError::InvalidRevision => CatRevErrorKind::InvalidRevision,
60 RevlogError::AmbiguousPrefix => CatRevErrorKind::AmbiguousPrefix,
58 RevlogError::Corrupted => CatRevErrorKind::CorruptedRevlog,
61 RevlogError::Corrupted => CatRevErrorKind::CorruptedRevlog,
59 RevlogError::UnknowDataFormat(format) => {
62 RevlogError::UnknowDataFormat(format) => {
60 CatRevErrorKind::UnknowRevlogDataFormat(format)
63 CatRevErrorKind::UnknowRevlogDataFormat(format)
61 }
64 }
62 }
65 }
63 .into()
66 .into()
64 }
67 }
65 }
68 }
66
69
67 /// List files under Mercurial control at a given revision.
70 /// List files under Mercurial control at a given revision.
68 pub struct CatRev<'a> {
71 pub struct CatRev<'a> {
69 root: &'a PathBuf,
72 root: &'a PathBuf,
70 /// The revision to cat the files from.
73 /// The revision to cat the files from.
71 rev: &'a str,
74 rev: &'a str,
72 /// The files to output.
75 /// The files to output.
73 files: &'a [HgPathBuf],
76 files: &'a [HgPathBuf],
74 /// The changelog file
77 /// The changelog file
75 changelog: Changelog,
78 changelog: Changelog,
76 /// The manifest file
79 /// The manifest file
77 manifest: Manifest,
80 manifest: Manifest,
78 /// The manifest entry corresponding to the revision.
81 /// The manifest entry corresponding to the revision.
79 ///
82 ///
80 /// Used to hold the owner of the returned references.
83 /// Used to hold the owner of the returned references.
81 manifest_entry: Option<ManifestEntry>,
84 manifest_entry: Option<ManifestEntry>,
82 }
85 }
83
86
84 impl<'a> CatRev<'a> {
87 impl<'a> CatRev<'a> {
85 pub fn new(
88 pub fn new(
86 root: &'a PathBuf,
89 root: &'a PathBuf,
87 rev: &'a str,
90 rev: &'a str,
88 files: &'a [HgPathBuf],
91 files: &'a [HgPathBuf],
89 ) -> Result<Self, CatRevError> {
92 ) -> Result<Self, CatRevError> {
90 let changelog = Changelog::open(&root)?;
93 let changelog = Changelog::open(&root)?;
91 let manifest = Manifest::open(&root)?;
94 let manifest = Manifest::open(&root)?;
92 let manifest_entry = None;
95 let manifest_entry = None;
93
96
94 Ok(Self {
97 Ok(Self {
95 root,
98 root,
96 rev,
99 rev,
97 files,
100 files,
98 changelog,
101 changelog,
99 manifest,
102 manifest,
100 manifest_entry,
103 manifest_entry,
101 })
104 })
102 }
105 }
103
106
104 pub fn run(&mut self) -> Result<Vec<u8>, CatRevError> {
107 pub fn run(&mut self) -> Result<Vec<u8>, CatRevError> {
105 let changelog_entry = match self.rev.parse::<Revision>() {
108 let changelog_entry = match self.rev.parse::<Revision>() {
106 Ok(rev) => self.changelog.get_rev(rev)?,
109 Ok(rev) => self.changelog.get_rev(rev)?,
107 _ => {
110 _ => {
108 let changelog_node = hex::decode(&self.rev)
111 let changelog_node = hex::decode(&self.rev)
109 .map_err(|_| CatRevErrorKind::InvalidRevision)?;
112 .map_err(|_| CatRevErrorKind::InvalidRevision)?;
110 self.changelog.get_node(&changelog_node)?
113 self.changelog.get_node(&changelog_node)?
111 }
114 }
112 };
115 };
113 let manifest_node = hex::decode(&changelog_entry.manifest_node()?)
116 let manifest_node = hex::decode(&changelog_entry.manifest_node()?)
114 .map_err(|_| CatRevErrorKind::CorruptedRevlog)?;
117 .map_err(|_| CatRevErrorKind::CorruptedRevlog)?;
115
118
116 self.manifest_entry = Some(self.manifest.get_node(&manifest_node)?);
119 self.manifest_entry = Some(self.manifest.get_node(&manifest_node)?);
117 if let Some(ref manifest_entry) = self.manifest_entry {
120 if let Some(ref manifest_entry) = self.manifest_entry {
118 let mut bytes = vec![];
121 let mut bytes = vec![];
119
122
120 for (manifest_file, node_bytes) in
123 for (manifest_file, node_bytes) in
121 manifest_entry.files_with_nodes()
124 manifest_entry.files_with_nodes()
122 {
125 {
123 for cat_file in self.files.iter() {
126 for cat_file in self.files.iter() {
124 if cat_file.as_bytes() == manifest_file.as_bytes() {
127 if cat_file.as_bytes() == manifest_file.as_bytes() {
125 let index_path =
128 let index_path =
126 store_path(self.root, manifest_file, b".i");
129 store_path(self.root, manifest_file, b".i");
127 let data_path =
130 let data_path =
128 store_path(self.root, manifest_file, b".d");
131 store_path(self.root, manifest_file, b".d");
129
132
130 let file_log =
133 let file_log =
131 Revlog::open(&index_path, Some(&data_path))?;
134 Revlog::open(&index_path, Some(&data_path))?;
132 let file_node = hex::decode(&node_bytes)
135 let file_node = hex::decode(&node_bytes)
133 .map_err(|_| CatRevErrorKind::CorruptedRevlog)?;
136 .map_err(|_| CatRevErrorKind::CorruptedRevlog)?;
134 let file_rev = file_log.get_node_rev(&file_node)?;
137 let file_rev = file_log.get_node_rev(&file_node)?;
135 let data = file_log.get_rev_data(file_rev)?;
138 let data = file_log.get_rev_data(file_rev)?;
136 if data.starts_with(&METADATA_DELIMITER) {
139 if data.starts_with(&METADATA_DELIMITER) {
137 let end_delimiter_position = data
140 let end_delimiter_position = data
138 [METADATA_DELIMITER.len()..]
141 [METADATA_DELIMITER.len()..]
139 .windows(METADATA_DELIMITER.len())
142 .windows(METADATA_DELIMITER.len())
140 .position(|bytes| bytes == METADATA_DELIMITER);
143 .position(|bytes| bytes == METADATA_DELIMITER);
141 if let Some(position) = end_delimiter_position {
144 if let Some(position) = end_delimiter_position {
142 let offset = METADATA_DELIMITER.len() * 2;
145 let offset = METADATA_DELIMITER.len() * 2;
143 bytes.extend(data[position + offset..].iter());
146 bytes.extend(data[position + offset..].iter());
144 }
147 }
145 } else {
148 } else {
146 bytes.extend(data);
149 bytes.extend(data);
147 }
150 }
148 }
151 }
149 }
152 }
150 }
153 }
151
154
152 Ok(bytes)
155 Ok(bytes)
153 } else {
156 } else {
154 unreachable!("manifest_entry should have been stored");
157 unreachable!("manifest_entry should have been stored");
155 }
158 }
156 }
159 }
157 }
160 }
158
161
159 fn store_path(root: &Path, hg_path: &HgPath, suffix: &[u8]) -> PathBuf {
162 fn store_path(root: &Path, hg_path: &HgPath, suffix: &[u8]) -> PathBuf {
160 let encoded_bytes =
163 let encoded_bytes =
161 path_encode(&[b"data/", hg_path.as_bytes(), suffix].concat());
164 path_encode(&[b"data/", hg_path.as_bytes(), suffix].concat());
162 [
165 [
163 root,
166 root,
164 &Path::new(".hg/store/"),
167 &Path::new(".hg/store/"),
165 get_path_from_bytes(&encoded_bytes),
168 get_path_from_bytes(&encoded_bytes),
166 ]
169 ]
167 .iter()
170 .iter()
168 .collect()
171 .collect()
169 }
172 }
@@ -1,114 +1,119 b''
1 // debugdata.rs
1 // debugdata.rs
2 //
2 //
3 // Copyright 2020 Antoine Cezar <antoine.cezar@octobus.net>
3 // Copyright 2020 Antoine Cezar <antoine.cezar@octobus.net>
4 //
4 //
5 // This software may be used and distributed according to the terms of the
5 // This software may be used and distributed according to the terms of the
6 // GNU General Public License version 2 or any later version.
6 // GNU General Public License version 2 or any later version.
7
7
8 use super::find_root;
8 use super::find_root;
9 use crate::revlog::revlog::{Revlog, RevlogError};
9 use crate::revlog::revlog::{Revlog, RevlogError};
10 use crate::revlog::Revision;
10 use crate::revlog::Revision;
11
11
12 /// Kind of data to debug
12 /// Kind of data to debug
13 #[derive(Debug, Copy, Clone)]
13 #[derive(Debug, Copy, Clone)]
14 pub enum DebugDataKind {
14 pub enum DebugDataKind {
15 Changelog,
15 Changelog,
16 Manifest,
16 Manifest,
17 }
17 }
18
18
19 /// Kind of error encountered by DebugData
19 /// Kind of error encountered by DebugData
20 #[derive(Debug)]
20 #[derive(Debug)]
21 pub enum DebugDataErrorKind {
21 pub enum DebugDataErrorKind {
22 FindRootError(find_root::FindRootError),
22 FindRootError(find_root::FindRootError),
23 /// Error when reading a `revlog` file.
23 /// Error when reading a `revlog` file.
24 IoError(std::io::Error),
24 IoError(std::io::Error),
25 /// The revision has not been found.
25 /// The revision has not been found.
26 InvalidRevision,
26 InvalidRevision,
27 /// Found more than one revision whose ID match the requested prefix
28 AmbiguousPrefix,
27 /// A `revlog` file is corrupted.
29 /// A `revlog` file is corrupted.
28 CorruptedRevlog,
30 CorruptedRevlog,
29 /// The `revlog` format version is not supported.
31 /// The `revlog` format version is not supported.
30 UnsuportedRevlogVersion(u16),
32 UnsuportedRevlogVersion(u16),
31 /// The `revlog` data format is not supported.
33 /// The `revlog` data format is not supported.
32 UnknowRevlogDataFormat(u8),
34 UnknowRevlogDataFormat(u8),
33 }
35 }
34
36
35 /// A DebugData error
37 /// A DebugData error
36 #[derive(Debug)]
38 #[derive(Debug)]
37 pub struct DebugDataError {
39 pub struct DebugDataError {
38 /// Kind of error encountered by DebugData
40 /// Kind of error encountered by DebugData
39 pub kind: DebugDataErrorKind,
41 pub kind: DebugDataErrorKind,
40 }
42 }
41
43
42 impl From<DebugDataErrorKind> for DebugDataError {
44 impl From<DebugDataErrorKind> for DebugDataError {
43 fn from(kind: DebugDataErrorKind) -> Self {
45 fn from(kind: DebugDataErrorKind) -> Self {
44 DebugDataError { kind }
46 DebugDataError { kind }
45 }
47 }
46 }
48 }
47
49
48 impl From<find_root::FindRootError> for DebugDataError {
50 impl From<find_root::FindRootError> for DebugDataError {
49 fn from(err: find_root::FindRootError) -> Self {
51 fn from(err: find_root::FindRootError) -> Self {
50 let kind = DebugDataErrorKind::FindRootError(err);
52 let kind = DebugDataErrorKind::FindRootError(err);
51 DebugDataError { kind }
53 DebugDataError { kind }
52 }
54 }
53 }
55 }
54
56
55 impl From<std::io::Error> for DebugDataError {
57 impl From<std::io::Error> for DebugDataError {
56 fn from(err: std::io::Error) -> Self {
58 fn from(err: std::io::Error) -> Self {
57 let kind = DebugDataErrorKind::IoError(err);
59 let kind = DebugDataErrorKind::IoError(err);
58 DebugDataError { kind }
60 DebugDataError { kind }
59 }
61 }
60 }
62 }
61
63
62 impl From<RevlogError> for DebugDataError {
64 impl From<RevlogError> for DebugDataError {
63 fn from(err: RevlogError) -> Self {
65 fn from(err: RevlogError) -> Self {
64 match err {
66 match err {
65 RevlogError::IoError(err) => DebugDataErrorKind::IoError(err),
67 RevlogError::IoError(err) => DebugDataErrorKind::IoError(err),
66 RevlogError::UnsuportedVersion(version) => {
68 RevlogError::UnsuportedVersion(version) => {
67 DebugDataErrorKind::UnsuportedRevlogVersion(version)
69 DebugDataErrorKind::UnsuportedRevlogVersion(version)
68 }
70 }
69 RevlogError::InvalidRevision => {
71 RevlogError::InvalidRevision => {
70 DebugDataErrorKind::InvalidRevision
72 DebugDataErrorKind::InvalidRevision
71 }
73 }
74 RevlogError::AmbiguousPrefix => {
75 DebugDataErrorKind::AmbiguousPrefix
76 }
72 RevlogError::Corrupted => DebugDataErrorKind::CorruptedRevlog,
77 RevlogError::Corrupted => DebugDataErrorKind::CorruptedRevlog,
73 RevlogError::UnknowDataFormat(format) => {
78 RevlogError::UnknowDataFormat(format) => {
74 DebugDataErrorKind::UnknowRevlogDataFormat(format)
79 DebugDataErrorKind::UnknowRevlogDataFormat(format)
75 }
80 }
76 }
81 }
77 .into()
82 .into()
78 }
83 }
79 }
84 }
80
85
81 /// Dump the contents data of a revision.
86 /// Dump the contents data of a revision.
82 pub struct DebugData<'a> {
87 pub struct DebugData<'a> {
83 /// Revision or hash of the revision.
88 /// Revision or hash of the revision.
84 rev: &'a str,
89 rev: &'a str,
85 /// Kind of data to debug.
90 /// Kind of data to debug.
86 kind: DebugDataKind,
91 kind: DebugDataKind,
87 }
92 }
88
93
89 impl<'a> DebugData<'a> {
94 impl<'a> DebugData<'a> {
90 pub fn new(rev: &'a str, kind: DebugDataKind) -> Self {
95 pub fn new(rev: &'a str, kind: DebugDataKind) -> Self {
91 DebugData { rev, kind }
96 DebugData { rev, kind }
92 }
97 }
93
98
94 pub fn run(&mut self) -> Result<Vec<u8>, DebugDataError> {
99 pub fn run(&mut self) -> Result<Vec<u8>, DebugDataError> {
95 let root = find_root::FindRoot::new().run()?;
100 let root = find_root::FindRoot::new().run()?;
96 let index_file = match self.kind {
101 let index_file = match self.kind {
97 DebugDataKind::Changelog => root.join(".hg/store/00changelog.i"),
102 DebugDataKind::Changelog => root.join(".hg/store/00changelog.i"),
98 DebugDataKind::Manifest => root.join(".hg/store/00manifest.i"),
103 DebugDataKind::Manifest => root.join(".hg/store/00manifest.i"),
99 };
104 };
100 let revlog = Revlog::open(&index_file, None)?;
105 let revlog = Revlog::open(&index_file, None)?;
101
106
102 let data = match self.rev.parse::<Revision>() {
107 let data = match self.rev.parse::<Revision>() {
103 Ok(rev) => revlog.get_rev_data(rev)?,
108 Ok(rev) => revlog.get_rev_data(rev)?,
104 _ => {
109 _ => {
105 let node = hex::decode(&self.rev)
110 let node = hex::decode(&self.rev)
106 .map_err(|_| DebugDataErrorKind::InvalidRevision)?;
111 .map_err(|_| DebugDataErrorKind::InvalidRevision)?;
107 let rev = revlog.get_node_rev(&node)?;
112 let rev = revlog.get_node_rev(&node)?;
108 revlog.get_rev_data(rev)?
113 revlog.get_rev_data(rev)?
109 }
114 }
110 };
115 };
111
116
112 Ok(data)
117 Ok(data)
113 }
118 }
114 }
119 }
@@ -1,187 +1,192 b''
1 // list_tracked_files.rs
1 // list_tracked_files.rs
2 //
2 //
3 // Copyright 2020 Antoine Cezar <antoine.cezar@octobus.net>
3 // Copyright 2020 Antoine Cezar <antoine.cezar@octobus.net>
4 //
4 //
5 // This software may be used and distributed according to the terms of the
5 // This software may be used and distributed according to the terms of the
6 // GNU General Public License version 2 or any later version.
6 // GNU General Public License version 2 or any later version.
7
7
8 use crate::dirstate::parsers::parse_dirstate;
8 use crate::dirstate::parsers::parse_dirstate;
9 use crate::revlog::changelog::Changelog;
9 use crate::revlog::changelog::Changelog;
10 use crate::revlog::manifest::{Manifest, ManifestEntry};
10 use crate::revlog::manifest::{Manifest, ManifestEntry};
11 use crate::revlog::revlog::RevlogError;
11 use crate::revlog::revlog::RevlogError;
12 use crate::revlog::Revision;
12 use crate::revlog::Revision;
13 use crate::utils::hg_path::HgPath;
13 use crate::utils::hg_path::HgPath;
14 use crate::{DirstateParseError, EntryState};
14 use crate::{DirstateParseError, EntryState};
15 use rayon::prelude::*;
15 use rayon::prelude::*;
16 use std::convert::From;
16 use std::convert::From;
17 use std::fs;
17 use std::fs;
18 use std::path::PathBuf;
18 use std::path::PathBuf;
19
19
20 /// Kind of error encountered by `ListDirstateTrackedFiles`
20 /// Kind of error encountered by `ListDirstateTrackedFiles`
21 #[derive(Debug)]
21 #[derive(Debug)]
22 pub enum ListDirstateTrackedFilesErrorKind {
22 pub enum ListDirstateTrackedFilesErrorKind {
23 /// Error when reading the `dirstate` file
23 /// Error when reading the `dirstate` file
24 IoError(std::io::Error),
24 IoError(std::io::Error),
25 /// Error when parsing the `dirstate` file
25 /// Error when parsing the `dirstate` file
26 ParseError(DirstateParseError),
26 ParseError(DirstateParseError),
27 }
27 }
28
28
29 /// A `ListDirstateTrackedFiles` error
29 /// A `ListDirstateTrackedFiles` error
30 #[derive(Debug)]
30 #[derive(Debug)]
31 pub struct ListDirstateTrackedFilesError {
31 pub struct ListDirstateTrackedFilesError {
32 /// Kind of error encountered by `ListDirstateTrackedFiles`
32 /// Kind of error encountered by `ListDirstateTrackedFiles`
33 pub kind: ListDirstateTrackedFilesErrorKind,
33 pub kind: ListDirstateTrackedFilesErrorKind,
34 }
34 }
35
35
36 impl From<ListDirstateTrackedFilesErrorKind>
36 impl From<ListDirstateTrackedFilesErrorKind>
37 for ListDirstateTrackedFilesError
37 for ListDirstateTrackedFilesError
38 {
38 {
39 fn from(kind: ListDirstateTrackedFilesErrorKind) -> Self {
39 fn from(kind: ListDirstateTrackedFilesErrorKind) -> Self {
40 ListDirstateTrackedFilesError { kind }
40 ListDirstateTrackedFilesError { kind }
41 }
41 }
42 }
42 }
43
43
44 impl From<std::io::Error> for ListDirstateTrackedFilesError {
44 impl From<std::io::Error> for ListDirstateTrackedFilesError {
45 fn from(err: std::io::Error) -> Self {
45 fn from(err: std::io::Error) -> Self {
46 let kind = ListDirstateTrackedFilesErrorKind::IoError(err);
46 let kind = ListDirstateTrackedFilesErrorKind::IoError(err);
47 ListDirstateTrackedFilesError { kind }
47 ListDirstateTrackedFilesError { kind }
48 }
48 }
49 }
49 }
50
50
51 /// List files under Mercurial control in the working directory
51 /// List files under Mercurial control in the working directory
52 /// by reading the dirstate
52 /// by reading the dirstate
53 pub struct ListDirstateTrackedFiles {
53 pub struct ListDirstateTrackedFiles {
54 /// The `dirstate` content.
54 /// The `dirstate` content.
55 content: Vec<u8>,
55 content: Vec<u8>,
56 }
56 }
57
57
58 impl ListDirstateTrackedFiles {
58 impl ListDirstateTrackedFiles {
59 pub fn new(root: &PathBuf) -> Result<Self, ListDirstateTrackedFilesError> {
59 pub fn new(root: &PathBuf) -> Result<Self, ListDirstateTrackedFilesError> {
60 let dirstate = root.join(".hg/dirstate");
60 let dirstate = root.join(".hg/dirstate");
61 let content = fs::read(&dirstate)?;
61 let content = fs::read(&dirstate)?;
62 Ok(Self { content })
62 Ok(Self { content })
63 }
63 }
64
64
65 pub fn run(
65 pub fn run(
66 &mut self,
66 &mut self,
67 ) -> Result<Vec<&HgPath>, ListDirstateTrackedFilesError> {
67 ) -> Result<Vec<&HgPath>, ListDirstateTrackedFilesError> {
68 let (_, entries, _) = parse_dirstate(&self.content)
68 let (_, entries, _) = parse_dirstate(&self.content)
69 .map_err(ListDirstateTrackedFilesErrorKind::ParseError)?;
69 .map_err(ListDirstateTrackedFilesErrorKind::ParseError)?;
70 let mut files: Vec<&HgPath> = entries
70 let mut files: Vec<&HgPath> = entries
71 .into_iter()
71 .into_iter()
72 .filter_map(|(path, entry)| match entry.state {
72 .filter_map(|(path, entry)| match entry.state {
73 EntryState::Removed => None,
73 EntryState::Removed => None,
74 _ => Some(path),
74 _ => Some(path),
75 })
75 })
76 .collect();
76 .collect();
77 files.par_sort_unstable();
77 files.par_sort_unstable();
78 Ok(files)
78 Ok(files)
79 }
79 }
80 }
80 }
81
81
82 /// Kind of error encountered by `ListRevTrackedFiles`
82 /// Kind of error encountered by `ListRevTrackedFiles`
83 #[derive(Debug)]
83 #[derive(Debug)]
84 pub enum ListRevTrackedFilesErrorKind {
84 pub enum ListRevTrackedFilesErrorKind {
85 /// Error when reading a `revlog` file.
85 /// Error when reading a `revlog` file.
86 IoError(std::io::Error),
86 IoError(std::io::Error),
87 /// The revision has not been found.
87 /// The revision has not been found.
88 InvalidRevision,
88 InvalidRevision,
89 /// Found more than one revision whose ID match the requested prefix
90 AmbiguousPrefix,
89 /// A `revlog` file is corrupted.
91 /// A `revlog` file is corrupted.
90 CorruptedRevlog,
92 CorruptedRevlog,
91 /// The `revlog` format version is not supported.
93 /// The `revlog` format version is not supported.
92 UnsuportedRevlogVersion(u16),
94 UnsuportedRevlogVersion(u16),
93 /// The `revlog` data format is not supported.
95 /// The `revlog` data format is not supported.
94 UnknowRevlogDataFormat(u8),
96 UnknowRevlogDataFormat(u8),
95 }
97 }
96
98
97 /// A `ListRevTrackedFiles` error
99 /// A `ListRevTrackedFiles` error
98 #[derive(Debug)]
100 #[derive(Debug)]
99 pub struct ListRevTrackedFilesError {
101 pub struct ListRevTrackedFilesError {
100 /// Kind of error encountered by `ListRevTrackedFiles`
102 /// Kind of error encountered by `ListRevTrackedFiles`
101 pub kind: ListRevTrackedFilesErrorKind,
103 pub kind: ListRevTrackedFilesErrorKind,
102 }
104 }
103
105
104 impl From<ListRevTrackedFilesErrorKind> for ListRevTrackedFilesError {
106 impl From<ListRevTrackedFilesErrorKind> for ListRevTrackedFilesError {
105 fn from(kind: ListRevTrackedFilesErrorKind) -> Self {
107 fn from(kind: ListRevTrackedFilesErrorKind) -> Self {
106 ListRevTrackedFilesError { kind }
108 ListRevTrackedFilesError { kind }
107 }
109 }
108 }
110 }
109
111
110 impl From<RevlogError> for ListRevTrackedFilesError {
112 impl From<RevlogError> for ListRevTrackedFilesError {
111 fn from(err: RevlogError) -> Self {
113 fn from(err: RevlogError) -> Self {
112 match err {
114 match err {
113 RevlogError::IoError(err) => {
115 RevlogError::IoError(err) => {
114 ListRevTrackedFilesErrorKind::IoError(err)
116 ListRevTrackedFilesErrorKind::IoError(err)
115 }
117 }
116 RevlogError::UnsuportedVersion(version) => {
118 RevlogError::UnsuportedVersion(version) => {
117 ListRevTrackedFilesErrorKind::UnsuportedRevlogVersion(version)
119 ListRevTrackedFilesErrorKind::UnsuportedRevlogVersion(version)
118 }
120 }
119 RevlogError::InvalidRevision => {
121 RevlogError::InvalidRevision => {
120 ListRevTrackedFilesErrorKind::InvalidRevision
122 ListRevTrackedFilesErrorKind::InvalidRevision
121 }
123 }
124 RevlogError::AmbiguousPrefix => {
125 ListRevTrackedFilesErrorKind::AmbiguousPrefix
126 }
122 RevlogError::Corrupted => {
127 RevlogError::Corrupted => {
123 ListRevTrackedFilesErrorKind::CorruptedRevlog
128 ListRevTrackedFilesErrorKind::CorruptedRevlog
124 }
129 }
125 RevlogError::UnknowDataFormat(format) => {
130 RevlogError::UnknowDataFormat(format) => {
126 ListRevTrackedFilesErrorKind::UnknowRevlogDataFormat(format)
131 ListRevTrackedFilesErrorKind::UnknowRevlogDataFormat(format)
127 }
132 }
128 }
133 }
129 .into()
134 .into()
130 }
135 }
131 }
136 }
132
137
133 /// List files under Mercurial control at a given revision.
138 /// List files under Mercurial control at a given revision.
134 pub struct ListRevTrackedFiles<'a> {
139 pub struct ListRevTrackedFiles<'a> {
135 /// The revision to list the files from.
140 /// The revision to list the files from.
136 rev: &'a str,
141 rev: &'a str,
137 /// The changelog file
142 /// The changelog file
138 changelog: Changelog,
143 changelog: Changelog,
139 /// The manifest file
144 /// The manifest file
140 manifest: Manifest,
145 manifest: Manifest,
141 /// The manifest entry corresponding to the revision.
146 /// The manifest entry corresponding to the revision.
142 ///
147 ///
143 /// Used to hold the owner of the returned references.
148 /// Used to hold the owner of the returned references.
144 manifest_entry: Option<ManifestEntry>,
149 manifest_entry: Option<ManifestEntry>,
145 }
150 }
146
151
147 impl<'a> ListRevTrackedFiles<'a> {
152 impl<'a> ListRevTrackedFiles<'a> {
148 pub fn new(
153 pub fn new(
149 root: &PathBuf,
154 root: &PathBuf,
150 rev: &'a str,
155 rev: &'a str,
151 ) -> Result<Self, ListRevTrackedFilesError> {
156 ) -> Result<Self, ListRevTrackedFilesError> {
152 let changelog = Changelog::open(&root)?;
157 let changelog = Changelog::open(&root)?;
153 let manifest = Manifest::open(&root)?;
158 let manifest = Manifest::open(&root)?;
154
159
155 Ok(Self {
160 Ok(Self {
156 rev,
161 rev,
157 changelog,
162 changelog,
158 manifest,
163 manifest,
159 manifest_entry: None,
164 manifest_entry: None,
160 })
165 })
161 }
166 }
162
167
163 pub fn run(
168 pub fn run(
164 &mut self,
169 &mut self,
165 ) -> Result<impl Iterator<Item = &HgPath>, ListRevTrackedFilesError> {
170 ) -> Result<impl Iterator<Item = &HgPath>, ListRevTrackedFilesError> {
166 let changelog_entry = match self.rev.parse::<Revision>() {
171 let changelog_entry = match self.rev.parse::<Revision>() {
167 Ok(rev) => self.changelog.get_rev(rev)?,
172 Ok(rev) => self.changelog.get_rev(rev)?,
168 _ => {
173 _ => {
169 let changelog_node = hex::decode(&self.rev)
174 let changelog_node = hex::decode(&self.rev)
170 .or(Err(ListRevTrackedFilesErrorKind::InvalidRevision))?;
175 .or(Err(ListRevTrackedFilesErrorKind::InvalidRevision))?;
171 self.changelog.get_node(&changelog_node)?
176 self.changelog.get_node(&changelog_node)?
172 }
177 }
173 };
178 };
174 let manifest_node = hex::decode(&changelog_entry.manifest_node()?)
179 let manifest_node = hex::decode(&changelog_entry.manifest_node()?)
175 .or(Err(ListRevTrackedFilesErrorKind::CorruptedRevlog))?;
180 .or(Err(ListRevTrackedFilesErrorKind::CorruptedRevlog))?;
176
181
177 self.manifest_entry = Some(self.manifest.get_node(&manifest_node)?);
182 self.manifest_entry = Some(self.manifest.get_node(&manifest_node)?);
178
183
179 if let Some(ref manifest_entry) = self.manifest_entry {
184 if let Some(ref manifest_entry) = self.manifest_entry {
180 Ok(manifest_entry.files())
185 Ok(manifest_entry.files())
181 } else {
186 } else {
182 panic!(
187 panic!(
183 "manifest entry should have been stored in self.manifest_node to ensure its lifetime since references are returned from it"
188 "manifest entry should have been stored in self.manifest_node to ensure its lifetime since references are returned from it"
184 )
189 )
185 }
190 }
186 }
191 }
187 }
192 }
@@ -1,335 +1,344 b''
1 use std::borrow::Cow;
1 use std::borrow::Cow;
2 use std::fs::File;
2 use std::fs::File;
3 use std::io::Read;
3 use std::io::Read;
4 use std::ops::Deref;
4 use std::ops::Deref;
5 use std::path::Path;
5 use std::path::Path;
6
6
7 use byteorder::{BigEndian, ByteOrder};
7 use byteorder::{BigEndian, ByteOrder};
8 use crypto::digest::Digest;
8 use crypto::digest::Digest;
9 use crypto::sha1::Sha1;
9 use crypto::sha1::Sha1;
10 use flate2::read::ZlibDecoder;
10 use flate2::read::ZlibDecoder;
11 use memmap::{Mmap, MmapOptions};
11 use memmap::{Mmap, MmapOptions};
12 use micro_timer::timed;
12 use micro_timer::timed;
13 use zstd;
13 use zstd;
14
14
15 use super::index::Index;
15 use super::index::Index;
16 use super::node::{NODE_BYTES_LENGTH, NULL_NODE_ID};
16 use super::node::{NODE_BYTES_LENGTH, NULL_NODE_ID};
17 use super::patch;
17 use super::patch;
18 use crate::revlog::Revision;
18 use crate::revlog::Revision;
19
19
20 pub enum RevlogError {
20 pub enum RevlogError {
21 IoError(std::io::Error),
21 IoError(std::io::Error),
22 UnsuportedVersion(u16),
22 UnsuportedVersion(u16),
23 InvalidRevision,
23 InvalidRevision,
24 /// Found more than one entry whose ID match the requested prefix
25 AmbiguousPrefix,
24 Corrupted,
26 Corrupted,
25 UnknowDataFormat(u8),
27 UnknowDataFormat(u8),
26 }
28 }
27
29
28 fn mmap_open(path: &Path) -> Result<Mmap, std::io::Error> {
30 fn mmap_open(path: &Path) -> Result<Mmap, std::io::Error> {
29 let file = File::open(path)?;
31 let file = File::open(path)?;
30 let mmap = unsafe { MmapOptions::new().map(&file) }?;
32 let mmap = unsafe { MmapOptions::new().map(&file) }?;
31 Ok(mmap)
33 Ok(mmap)
32 }
34 }
33
35
34 /// Read only implementation of revlog.
36 /// Read only implementation of revlog.
35 pub struct Revlog {
37 pub struct Revlog {
36 /// When index and data are not interleaved: bytes of the revlog index.
38 /// When index and data are not interleaved: bytes of the revlog index.
37 /// When index and data are interleaved: bytes of the revlog index and
39 /// When index and data are interleaved: bytes of the revlog index and
38 /// data.
40 /// data.
39 index: Index,
41 index: Index,
40 /// When index and data are not interleaved: bytes of the revlog data
42 /// When index and data are not interleaved: bytes of the revlog data
41 data_bytes: Option<Box<dyn Deref<Target = [u8]> + Send>>,
43 data_bytes: Option<Box<dyn Deref<Target = [u8]> + Send>>,
42 }
44 }
43
45
44 impl Revlog {
46 impl Revlog {
45 /// Open a revlog index file.
47 /// Open a revlog index file.
46 ///
48 ///
47 /// It will also open the associated data file if index and data are not
49 /// It will also open the associated data file if index and data are not
48 /// interleaved.
50 /// interleaved.
49 #[timed]
51 #[timed]
50 pub fn open(
52 pub fn open(
51 index_path: &Path,
53 index_path: &Path,
52 data_path: Option<&Path>,
54 data_path: Option<&Path>,
53 ) -> Result<Self, RevlogError> {
55 ) -> Result<Self, RevlogError> {
54 let index_mmap =
56 let index_mmap =
55 mmap_open(&index_path).map_err(RevlogError::IoError)?;
57 mmap_open(&index_path).map_err(RevlogError::IoError)?;
56
58
57 let version = get_version(&index_mmap);
59 let version = get_version(&index_mmap);
58 if version != 1 {
60 if version != 1 {
59 return Err(RevlogError::UnsuportedVersion(version));
61 return Err(RevlogError::UnsuportedVersion(version));
60 }
62 }
61
63
62 let index = Index::new(Box::new(index_mmap))?;
64 let index = Index::new(Box::new(index_mmap))?;
63
65
64 let default_data_path = index_path.with_extension("d");
66 let default_data_path = index_path.with_extension("d");
65
67
66 // type annotation required
68 // type annotation required
67 // won't recognize Mmap as Deref<Target = [u8]>
69 // won't recognize Mmap as Deref<Target = [u8]>
68 let data_bytes: Option<Box<dyn Deref<Target = [u8]> + Send>> =
70 let data_bytes: Option<Box<dyn Deref<Target = [u8]> + Send>> =
69 if index.is_inline() {
71 if index.is_inline() {
70 None
72 None
71 } else {
73 } else {
72 let data_path = data_path.unwrap_or(&default_data_path);
74 let data_path = data_path.unwrap_or(&default_data_path);
73 let data_mmap =
75 let data_mmap =
74 mmap_open(data_path).map_err(RevlogError::IoError)?;
76 mmap_open(data_path).map_err(RevlogError::IoError)?;
75 Some(Box::new(data_mmap))
77 Some(Box::new(data_mmap))
76 };
78 };
77
79
78 Ok(Revlog { index, data_bytes })
80 Ok(Revlog { index, data_bytes })
79 }
81 }
80
82
81 /// Return number of entries of the `Revlog`.
83 /// Return number of entries of the `Revlog`.
82 pub fn len(&self) -> usize {
84 pub fn len(&self) -> usize {
83 self.index.len()
85 self.index.len()
84 }
86 }
85
87
86 /// Returns `true` if the `Revlog` has zero `entries`.
88 /// Returns `true` if the `Revlog` has zero `entries`.
87 pub fn is_empty(&self) -> bool {
89 pub fn is_empty(&self) -> bool {
88 self.index.is_empty()
90 self.index.is_empty()
89 }
91 }
90
92
91 /// Return the full data associated to a node.
93 /// Return the full data associated to a node.
92 #[timed]
94 #[timed]
93 pub fn get_node_rev(&self, node: &[u8]) -> Result<Revision, RevlogError> {
95 pub fn get_node_rev(&self, node: &[u8]) -> Result<Revision, RevlogError> {
94 // This is brute force. But it is fast enough for now.
96 // This is brute force. But it is fast enough for now.
95 // Optimization will come later.
97 // Optimization will come later.
98 let mut found_by_prefix = None;
96 for rev in (0..self.len() as Revision).rev() {
99 for rev in (0..self.len() as Revision).rev() {
97 let index_entry =
100 let index_entry =
98 self.index.get_entry(rev).ok_or(RevlogError::Corrupted)?;
101 self.index.get_entry(rev).ok_or(RevlogError::Corrupted)?;
99 if node == index_entry.hash() {
102 if index_entry.hash() == node {
100 return Ok(rev);
103 return Ok(rev);
101 }
104 }
105 if index_entry.hash().starts_with(node) {
106 if found_by_prefix.is_some() {
107 return Err(RevlogError::AmbiguousPrefix);
102 }
108 }
103 Err(RevlogError::InvalidRevision)
109 found_by_prefix = Some(rev)
110 }
111 }
112 found_by_prefix.ok_or(RevlogError::InvalidRevision)
104 }
113 }
105
114
106 /// Return the full data associated to a revision.
115 /// Return the full data associated to a revision.
107 ///
116 ///
108 /// All entries required to build the final data out of deltas will be
117 /// All entries required to build the final data out of deltas will be
109 /// retrieved as needed, and the deltas will be applied to the inital
118 /// retrieved as needed, and the deltas will be applied to the inital
110 /// snapshot to rebuild the final data.
119 /// snapshot to rebuild the final data.
111 #[timed]
120 #[timed]
112 pub fn get_rev_data(&self, rev: Revision) -> Result<Vec<u8>, RevlogError> {
121 pub fn get_rev_data(&self, rev: Revision) -> Result<Vec<u8>, RevlogError> {
113 // Todo return -> Cow
122 // Todo return -> Cow
114 let mut entry = self.get_entry(rev)?;
123 let mut entry = self.get_entry(rev)?;
115 let mut delta_chain = vec![];
124 let mut delta_chain = vec![];
116 while let Some(base_rev) = entry.base_rev {
125 while let Some(base_rev) = entry.base_rev {
117 delta_chain.push(entry);
126 delta_chain.push(entry);
118 entry =
127 entry =
119 self.get_entry(base_rev).or(Err(RevlogError::Corrupted))?;
128 self.get_entry(base_rev).or(Err(RevlogError::Corrupted))?;
120 }
129 }
121
130
122 // TODO do not look twice in the index
131 // TODO do not look twice in the index
123 let index_entry = self
132 let index_entry = self
124 .index
133 .index
125 .get_entry(rev)
134 .get_entry(rev)
126 .ok_or(RevlogError::InvalidRevision)?;
135 .ok_or(RevlogError::InvalidRevision)?;
127
136
128 let data: Vec<u8> = if delta_chain.is_empty() {
137 let data: Vec<u8> = if delta_chain.is_empty() {
129 entry.data()?.into()
138 entry.data()?.into()
130 } else {
139 } else {
131 Revlog::build_data_from_deltas(entry, &delta_chain)?
140 Revlog::build_data_from_deltas(entry, &delta_chain)?
132 };
141 };
133
142
134 if self.check_hash(
143 if self.check_hash(
135 index_entry.p1(),
144 index_entry.p1(),
136 index_entry.p2(),
145 index_entry.p2(),
137 index_entry.hash(),
146 index_entry.hash(),
138 &data,
147 &data,
139 ) {
148 ) {
140 Ok(data)
149 Ok(data)
141 } else {
150 } else {
142 Err(RevlogError::Corrupted)
151 Err(RevlogError::Corrupted)
143 }
152 }
144 }
153 }
145
154
146 /// Check the hash of some given data against the recorded hash.
155 /// Check the hash of some given data against the recorded hash.
147 pub fn check_hash(
156 pub fn check_hash(
148 &self,
157 &self,
149 p1: Revision,
158 p1: Revision,
150 p2: Revision,
159 p2: Revision,
151 expected: &[u8],
160 expected: &[u8],
152 data: &[u8],
161 data: &[u8],
153 ) -> bool {
162 ) -> bool {
154 let e1 = self.index.get_entry(p1);
163 let e1 = self.index.get_entry(p1);
155 let h1 = match e1 {
164 let h1 = match e1 {
156 Some(ref entry) => entry.hash(),
165 Some(ref entry) => entry.hash(),
157 None => &NULL_NODE_ID,
166 None => &NULL_NODE_ID,
158 };
167 };
159 let e2 = self.index.get_entry(p2);
168 let e2 = self.index.get_entry(p2);
160 let h2 = match e2 {
169 let h2 = match e2 {
161 Some(ref entry) => entry.hash(),
170 Some(ref entry) => entry.hash(),
162 None => &NULL_NODE_ID,
171 None => &NULL_NODE_ID,
163 };
172 };
164
173
165 hash(data, &h1, &h2).as_slice() == expected
174 hash(data, &h1, &h2).as_slice() == expected
166 }
175 }
167
176
168 /// Build the full data of a revision out its snapshot
177 /// Build the full data of a revision out its snapshot
169 /// and its deltas.
178 /// and its deltas.
170 #[timed]
179 #[timed]
171 fn build_data_from_deltas(
180 fn build_data_from_deltas(
172 snapshot: RevlogEntry,
181 snapshot: RevlogEntry,
173 deltas: &[RevlogEntry],
182 deltas: &[RevlogEntry],
174 ) -> Result<Vec<u8>, RevlogError> {
183 ) -> Result<Vec<u8>, RevlogError> {
175 let snapshot = snapshot.data()?;
184 let snapshot = snapshot.data()?;
176 let deltas = deltas
185 let deltas = deltas
177 .iter()
186 .iter()
178 .rev()
187 .rev()
179 .map(RevlogEntry::data)
188 .map(RevlogEntry::data)
180 .collect::<Result<Vec<Cow<'_, [u8]>>, RevlogError>>()?;
189 .collect::<Result<Vec<Cow<'_, [u8]>>, RevlogError>>()?;
181 let patches: Vec<_> =
190 let patches: Vec<_> =
182 deltas.iter().map(|d| patch::PatchList::new(d)).collect();
191 deltas.iter().map(|d| patch::PatchList::new(d)).collect();
183 let patch = patch::fold_patch_lists(&patches);
192 let patch = patch::fold_patch_lists(&patches);
184 Ok(patch.apply(&snapshot))
193 Ok(patch.apply(&snapshot))
185 }
194 }
186
195
187 /// Return the revlog data.
196 /// Return the revlog data.
188 fn data(&self) -> &[u8] {
197 fn data(&self) -> &[u8] {
189 match self.data_bytes {
198 match self.data_bytes {
190 Some(ref data_bytes) => &data_bytes,
199 Some(ref data_bytes) => &data_bytes,
191 None => panic!(
200 None => panic!(
192 "forgot to load the data or trying to access inline data"
201 "forgot to load the data or trying to access inline data"
193 ),
202 ),
194 }
203 }
195 }
204 }
196
205
197 /// Get an entry of the revlog.
206 /// Get an entry of the revlog.
198 fn get_entry(&self, rev: Revision) -> Result<RevlogEntry, RevlogError> {
207 fn get_entry(&self, rev: Revision) -> Result<RevlogEntry, RevlogError> {
199 let index_entry = self
208 let index_entry = self
200 .index
209 .index
201 .get_entry(rev)
210 .get_entry(rev)
202 .ok_or(RevlogError::InvalidRevision)?;
211 .ok_or(RevlogError::InvalidRevision)?;
203 let start = index_entry.offset();
212 let start = index_entry.offset();
204 let end = start + index_entry.compressed_len();
213 let end = start + index_entry.compressed_len();
205 let data = if self.index.is_inline() {
214 let data = if self.index.is_inline() {
206 self.index.data(start, end)
215 self.index.data(start, end)
207 } else {
216 } else {
208 &self.data()[start..end]
217 &self.data()[start..end]
209 };
218 };
210 let entry = RevlogEntry {
219 let entry = RevlogEntry {
211 rev,
220 rev,
212 bytes: data,
221 bytes: data,
213 compressed_len: index_entry.compressed_len(),
222 compressed_len: index_entry.compressed_len(),
214 uncompressed_len: index_entry.uncompressed_len(),
223 uncompressed_len: index_entry.uncompressed_len(),
215 base_rev: if index_entry.base_revision() == rev {
224 base_rev: if index_entry.base_revision() == rev {
216 None
225 None
217 } else {
226 } else {
218 Some(index_entry.base_revision())
227 Some(index_entry.base_revision())
219 },
228 },
220 };
229 };
221 Ok(entry)
230 Ok(entry)
222 }
231 }
223 }
232 }
224
233
225 /// The revlog entry's bytes and the necessary informations to extract
234 /// The revlog entry's bytes and the necessary informations to extract
226 /// the entry's data.
235 /// the entry's data.
227 #[derive(Debug)]
236 #[derive(Debug)]
228 pub struct RevlogEntry<'a> {
237 pub struct RevlogEntry<'a> {
229 rev: Revision,
238 rev: Revision,
230 bytes: &'a [u8],
239 bytes: &'a [u8],
231 compressed_len: usize,
240 compressed_len: usize,
232 uncompressed_len: usize,
241 uncompressed_len: usize,
233 base_rev: Option<Revision>,
242 base_rev: Option<Revision>,
234 }
243 }
235
244
236 impl<'a> RevlogEntry<'a> {
245 impl<'a> RevlogEntry<'a> {
237 /// Extract the data contained in the entry.
246 /// Extract the data contained in the entry.
238 pub fn data(&self) -> Result<Cow<'_, [u8]>, RevlogError> {
247 pub fn data(&self) -> Result<Cow<'_, [u8]>, RevlogError> {
239 if self.bytes.is_empty() {
248 if self.bytes.is_empty() {
240 return Ok(Cow::Borrowed(&[]));
249 return Ok(Cow::Borrowed(&[]));
241 }
250 }
242 match self.bytes[0] {
251 match self.bytes[0] {
243 // Revision data is the entirety of the entry, including this
252 // Revision data is the entirety of the entry, including this
244 // header.
253 // header.
245 b'\0' => Ok(Cow::Borrowed(self.bytes)),
254 b'\0' => Ok(Cow::Borrowed(self.bytes)),
246 // Raw revision data follows.
255 // Raw revision data follows.
247 b'u' => Ok(Cow::Borrowed(&self.bytes[1..])),
256 b'u' => Ok(Cow::Borrowed(&self.bytes[1..])),
248 // zlib (RFC 1950) data.
257 // zlib (RFC 1950) data.
249 b'x' => Ok(Cow::Owned(self.uncompressed_zlib_data()?)),
258 b'x' => Ok(Cow::Owned(self.uncompressed_zlib_data()?)),
250 // zstd data.
259 // zstd data.
251 b'\x28' => Ok(Cow::Owned(self.uncompressed_zstd_data()?)),
260 b'\x28' => Ok(Cow::Owned(self.uncompressed_zstd_data()?)),
252 format_type => Err(RevlogError::UnknowDataFormat(format_type)),
261 format_type => Err(RevlogError::UnknowDataFormat(format_type)),
253 }
262 }
254 }
263 }
255
264
256 fn uncompressed_zlib_data(&self) -> Result<Vec<u8>, RevlogError> {
265 fn uncompressed_zlib_data(&self) -> Result<Vec<u8>, RevlogError> {
257 let mut decoder = ZlibDecoder::new(self.bytes);
266 let mut decoder = ZlibDecoder::new(self.bytes);
258 if self.is_delta() {
267 if self.is_delta() {
259 let mut buf = Vec::with_capacity(self.compressed_len);
268 let mut buf = Vec::with_capacity(self.compressed_len);
260 decoder
269 decoder
261 .read_to_end(&mut buf)
270 .read_to_end(&mut buf)
262 .or(Err(RevlogError::Corrupted))?;
271 .or(Err(RevlogError::Corrupted))?;
263 Ok(buf)
272 Ok(buf)
264 } else {
273 } else {
265 let mut buf = vec![0; self.uncompressed_len];
274 let mut buf = vec![0; self.uncompressed_len];
266 decoder
275 decoder
267 .read_exact(&mut buf)
276 .read_exact(&mut buf)
268 .or(Err(RevlogError::Corrupted))?;
277 .or(Err(RevlogError::Corrupted))?;
269 Ok(buf)
278 Ok(buf)
270 }
279 }
271 }
280 }
272
281
273 fn uncompressed_zstd_data(&self) -> Result<Vec<u8>, RevlogError> {
282 fn uncompressed_zstd_data(&self) -> Result<Vec<u8>, RevlogError> {
274 if self.is_delta() {
283 if self.is_delta() {
275 let mut buf = Vec::with_capacity(self.compressed_len);
284 let mut buf = Vec::with_capacity(self.compressed_len);
276 zstd::stream::copy_decode(self.bytes, &mut buf)
285 zstd::stream::copy_decode(self.bytes, &mut buf)
277 .or(Err(RevlogError::Corrupted))?;
286 .or(Err(RevlogError::Corrupted))?;
278 Ok(buf)
287 Ok(buf)
279 } else {
288 } else {
280 let mut buf = vec![0; self.uncompressed_len];
289 let mut buf = vec![0; self.uncompressed_len];
281 let len = zstd::block::decompress_to_buffer(self.bytes, &mut buf)
290 let len = zstd::block::decompress_to_buffer(self.bytes, &mut buf)
282 .or(Err(RevlogError::Corrupted))?;
291 .or(Err(RevlogError::Corrupted))?;
283 if len != self.uncompressed_len {
292 if len != self.uncompressed_len {
284 Err(RevlogError::Corrupted)
293 Err(RevlogError::Corrupted)
285 } else {
294 } else {
286 Ok(buf)
295 Ok(buf)
287 }
296 }
288 }
297 }
289 }
298 }
290
299
291 /// Tell if the entry is a snapshot or a delta
300 /// Tell if the entry is a snapshot or a delta
292 /// (influences on decompression).
301 /// (influences on decompression).
293 fn is_delta(&self) -> bool {
302 fn is_delta(&self) -> bool {
294 self.base_rev.is_some()
303 self.base_rev.is_some()
295 }
304 }
296 }
305 }
297
306
298 /// Format version of the revlog.
307 /// Format version of the revlog.
299 pub fn get_version(index_bytes: &[u8]) -> u16 {
308 pub fn get_version(index_bytes: &[u8]) -> u16 {
300 BigEndian::read_u16(&index_bytes[2..=3])
309 BigEndian::read_u16(&index_bytes[2..=3])
301 }
310 }
302
311
303 /// Calculate the hash of a revision given its data and its parents.
312 /// Calculate the hash of a revision given its data and its parents.
304 fn hash(data: &[u8], p1_hash: &[u8], p2_hash: &[u8]) -> Vec<u8> {
313 fn hash(data: &[u8], p1_hash: &[u8], p2_hash: &[u8]) -> Vec<u8> {
305 let mut hasher = Sha1::new();
314 let mut hasher = Sha1::new();
306 let (a, b) = (p1_hash, p2_hash);
315 let (a, b) = (p1_hash, p2_hash);
307 if a > b {
316 if a > b {
308 hasher.input(b);
317 hasher.input(b);
309 hasher.input(a);
318 hasher.input(a);
310 } else {
319 } else {
311 hasher.input(a);
320 hasher.input(a);
312 hasher.input(b);
321 hasher.input(b);
313 }
322 }
314 hasher.input(data);
323 hasher.input(data);
315 let mut hash = vec![0; NODE_BYTES_LENGTH];
324 let mut hash = vec![0; NODE_BYTES_LENGTH];
316 hasher.result(&mut hash);
325 hasher.result(&mut hash);
317 hash
326 hash
318 }
327 }
319
328
320 #[cfg(test)]
329 #[cfg(test)]
321 mod tests {
330 mod tests {
322 use super::*;
331 use super::*;
323
332
324 use super::super::index::IndexEntryBuilder;
333 use super::super::index::IndexEntryBuilder;
325
334
326 #[test]
335 #[test]
327 fn version_test() {
336 fn version_test() {
328 let bytes = IndexEntryBuilder::new()
337 let bytes = IndexEntryBuilder::new()
329 .is_first(true)
338 .is_first(true)
330 .with_version(1)
339 .with_version(1)
331 .build();
340 .build();
332
341
333 assert_eq!(get_version(&bytes), 1)
342 assert_eq!(get_version(&bytes), 1)
334 }
343 }
335 }
344 }
@@ -1,101 +1,108 b''
1 use crate::commands::Command;
1 use crate::commands::Command;
2 use crate::error::{CommandError, CommandErrorKind};
2 use crate::error::{CommandError, CommandErrorKind};
3 use crate::ui::utf8_to_local;
3 use crate::ui::utf8_to_local;
4 use crate::ui::Ui;
4 use crate::ui::Ui;
5 use hg::operations::FindRoot;
5 use hg::operations::FindRoot;
6 use hg::operations::{CatRev, CatRevError, CatRevErrorKind};
6 use hg::operations::{CatRev, CatRevError, CatRevErrorKind};
7 use hg::requirements;
7 use hg::requirements;
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
11
12 pub const HELP_TEXT: &str = "
12 pub const HELP_TEXT: &str = "
13 Output the current or given revision of files
13 Output the current or given revision of files
14 ";
14 ";
15
15
16 pub struct CatCommand<'a> {
16 pub struct CatCommand<'a> {
17 rev: Option<&'a str>,
17 rev: Option<&'a str>,
18 files: Vec<&'a str>,
18 files: Vec<&'a str>,
19 }
19 }
20
20
21 impl<'a> CatCommand<'a> {
21 impl<'a> CatCommand<'a> {
22 pub fn new(rev: Option<&'a str>, files: Vec<&'a str>) -> Self {
22 pub fn new(rev: Option<&'a str>, files: Vec<&'a str>) -> Self {
23 Self { rev, files }
23 Self { rev, files }
24 }
24 }
25
25
26 fn display(&self, ui: &Ui, data: &[u8]) -> Result<(), CommandError> {
26 fn display(&self, ui: &Ui, data: &[u8]) -> Result<(), CommandError> {
27 ui.write_stdout(data)?;
27 ui.write_stdout(data)?;
28 Ok(())
28 Ok(())
29 }
29 }
30 }
30 }
31
31
32 impl<'a> Command for CatCommand<'a> {
32 impl<'a> Command for CatCommand<'a> {
33 #[timed]
33 #[timed]
34 fn run(&self, ui: &Ui) -> Result<(), CommandError> {
34 fn run(&self, ui: &Ui) -> Result<(), CommandError> {
35 let root = FindRoot::new().run()?;
35 let root = FindRoot::new().run()?;
36 requirements::check(&root)?;
36 requirements::check(&root)?;
37 let cwd = std::env::current_dir()
37 let cwd = std::env::current_dir()
38 .or_else(|e| Err(CommandErrorKind::CurrentDirNotFound(e)))?;
38 .or_else(|e| Err(CommandErrorKind::CurrentDirNotFound(e)))?;
39
39
40 let mut files = vec![];
40 let mut files = vec![];
41 for file in self.files.iter() {
41 for file in self.files.iter() {
42 let normalized = cwd.join(&file);
42 let normalized = cwd.join(&file);
43 let stripped = normalized
43 let stripped = normalized
44 .strip_prefix(&root)
44 .strip_prefix(&root)
45 .or(Err(CommandErrorKind::Abort(None)))?;
45 .or(Err(CommandErrorKind::Abort(None)))?;
46 let hg_file = HgPathBuf::try_from(stripped.to_path_buf())
46 let hg_file = HgPathBuf::try_from(stripped.to_path_buf())
47 .or(Err(CommandErrorKind::Abort(None)))?;
47 .or(Err(CommandErrorKind::Abort(None)))?;
48 files.push(hg_file);
48 files.push(hg_file);
49 }
49 }
50
50
51 match self.rev {
51 match self.rev {
52 Some(rev) => {
52 Some(rev) => {
53 let mut operation = CatRev::new(&root, rev, &files)
53 let mut operation = CatRev::new(&root, rev, &files)
54 .map_err(|e| map_rev_error(rev, e))?;
54 .map_err(|e| map_rev_error(rev, e))?;
55 let data =
55 let data =
56 operation.run().map_err(|e| map_rev_error(rev, e))?;
56 operation.run().map_err(|e| map_rev_error(rev, e))?;
57 self.display(ui, &data)
57 self.display(ui, &data)
58 }
58 }
59 None => Err(CommandErrorKind::Unimplemented.into()),
59 None => Err(CommandErrorKind::Unimplemented.into()),
60 }
60 }
61 }
61 }
62 }
62 }
63
63
64 /// Convert `CatRevErrorKind` to `CommandError`
64 /// Convert `CatRevErrorKind` to `CommandError`
65 fn map_rev_error(rev: &str, err: CatRevError) -> CommandError {
65 fn map_rev_error(rev: &str, err: CatRevError) -> CommandError {
66 CommandError {
66 CommandError {
67 kind: match err.kind {
67 kind: match err.kind {
68 CatRevErrorKind::IoError(err) => CommandErrorKind::Abort(Some(
68 CatRevErrorKind::IoError(err) => CommandErrorKind::Abort(Some(
69 utf8_to_local(&format!("abort: {}\n", err)).into(),
69 utf8_to_local(&format!("abort: {}\n", err)).into(),
70 )),
70 )),
71 CatRevErrorKind::InvalidRevision => CommandErrorKind::Abort(Some(
71 CatRevErrorKind::InvalidRevision => CommandErrorKind::Abort(Some(
72 utf8_to_local(&format!(
72 utf8_to_local(&format!(
73 "abort: invalid revision identifier {}\n",
73 "abort: invalid revision identifier {}\n",
74 rev
74 rev
75 ))
75 ))
76 .into(),
76 .into(),
77 )),
77 )),
78 CatRevErrorKind::AmbiguousPrefix => CommandErrorKind::Abort(Some(
79 utf8_to_local(&format!(
80 "abort: ambiguous revision identifier {}\n",
81 rev
82 ))
83 .into(),
84 )),
78 CatRevErrorKind::UnsuportedRevlogVersion(version) => {
85 CatRevErrorKind::UnsuportedRevlogVersion(version) => {
79 CommandErrorKind::Abort(Some(
86 CommandErrorKind::Abort(Some(
80 utf8_to_local(&format!(
87 utf8_to_local(&format!(
81 "abort: unsupported revlog version {}\n",
88 "abort: unsupported revlog version {}\n",
82 version
89 version
83 ))
90 ))
84 .into(),
91 .into(),
85 ))
92 ))
86 }
93 }
87 CatRevErrorKind::CorruptedRevlog => CommandErrorKind::Abort(Some(
94 CatRevErrorKind::CorruptedRevlog => CommandErrorKind::Abort(Some(
88 "abort: corrupted revlog\n".into(),
95 "abort: corrupted revlog\n".into(),
89 )),
96 )),
90 CatRevErrorKind::UnknowRevlogDataFormat(format) => {
97 CatRevErrorKind::UnknowRevlogDataFormat(format) => {
91 CommandErrorKind::Abort(Some(
98 CommandErrorKind::Abort(Some(
92 utf8_to_local(&format!(
99 utf8_to_local(&format!(
93 "abort: unknow revlog dataformat {:?}\n",
100 "abort: unknow revlog dataformat {:?}\n",
94 format
101 format
95 ))
102 ))
96 .into(),
103 .into(),
97 ))
104 ))
98 }
105 }
99 },
106 },
100 }
107 }
101 }
108 }
@@ -1,82 +1,91 b''
1 use crate::commands::Command;
1 use crate::commands::Command;
2 use crate::error::{CommandError, CommandErrorKind};
2 use crate::error::{CommandError, CommandErrorKind};
3 use crate::ui::utf8_to_local;
3 use crate::ui::utf8_to_local;
4 use crate::ui::Ui;
4 use crate::ui::Ui;
5 use hg::operations::{
5 use hg::operations::{
6 DebugData, DebugDataError, DebugDataErrorKind, DebugDataKind,
6 DebugData, DebugDataError, DebugDataErrorKind, DebugDataKind,
7 };
7 };
8 use micro_timer::timed;
8 use micro_timer::timed;
9
9
10 pub const HELP_TEXT: &str = "
10 pub const HELP_TEXT: &str = "
11 Dump the contents of a data file revision
11 Dump the contents of a data file revision
12 ";
12 ";
13
13
14 pub struct DebugDataCommand<'a> {
14 pub struct DebugDataCommand<'a> {
15 rev: &'a str,
15 rev: &'a str,
16 kind: DebugDataKind,
16 kind: DebugDataKind,
17 }
17 }
18
18
19 impl<'a> DebugDataCommand<'a> {
19 impl<'a> DebugDataCommand<'a> {
20 pub fn new(rev: &'a str, kind: DebugDataKind) -> Self {
20 pub fn new(rev: &'a str, kind: DebugDataKind) -> Self {
21 DebugDataCommand { rev, kind }
21 DebugDataCommand { rev, kind }
22 }
22 }
23 }
23 }
24
24
25 impl<'a> Command for DebugDataCommand<'a> {
25 impl<'a> Command for DebugDataCommand<'a> {
26 #[timed]
26 #[timed]
27 fn run(&self, ui: &Ui) -> Result<(), CommandError> {
27 fn run(&self, ui: &Ui) -> Result<(), CommandError> {
28 let mut operation = DebugData::new(self.rev, self.kind);
28 let mut operation = DebugData::new(self.rev, self.kind);
29 let data =
29 let data =
30 operation.run().map_err(|e| to_command_error(self.rev, e))?;
30 operation.run().map_err(|e| to_command_error(self.rev, e))?;
31
31
32 let mut stdout = ui.stdout_buffer();
32 let mut stdout = ui.stdout_buffer();
33 stdout.write_all(&data)?;
33 stdout.write_all(&data)?;
34 stdout.flush()?;
34 stdout.flush()?;
35
35
36 Ok(())
36 Ok(())
37 }
37 }
38 }
38 }
39
39
40 /// Convert operation errors to command errors
40 /// Convert operation errors to command errors
41 fn to_command_error(rev: &str, err: DebugDataError) -> CommandError {
41 fn to_command_error(rev: &str, err: DebugDataError) -> CommandError {
42 match err.kind {
42 match err.kind {
43 DebugDataErrorKind::FindRootError(err) => CommandError::from(err),
43 DebugDataErrorKind::FindRootError(err) => CommandError::from(err),
44 DebugDataErrorKind::IoError(err) => CommandError {
44 DebugDataErrorKind::IoError(err) => CommandError {
45 kind: CommandErrorKind::Abort(Some(
45 kind: CommandErrorKind::Abort(Some(
46 utf8_to_local(&format!("abort: {}\n", err)).into(),
46 utf8_to_local(&format!("abort: {}\n", err)).into(),
47 )),
47 )),
48 },
48 },
49 DebugDataErrorKind::InvalidRevision => CommandError {
49 DebugDataErrorKind::InvalidRevision => CommandError {
50 kind: CommandErrorKind::Abort(Some(
50 kind: CommandErrorKind::Abort(Some(
51 utf8_to_local(&format!(
51 utf8_to_local(&format!(
52 "abort: invalid revision identifier{}\n",
52 "abort: invalid revision identifier{}\n",
53 rev
53 rev
54 ))
54 ))
55 .into(),
55 .into(),
56 )),
56 )),
57 },
57 },
58 DebugDataErrorKind::AmbiguousPrefix => CommandError {
59 kind: CommandErrorKind::Abort(Some(
60 utf8_to_local(&format!(
61 "abort: ambiguous revision identifier{}\n",
62 rev
63 ))
64 .into(),
65 )),
66 },
58 DebugDataErrorKind::UnsuportedRevlogVersion(version) => CommandError {
67 DebugDataErrorKind::UnsuportedRevlogVersion(version) => CommandError {
59 kind: CommandErrorKind::Abort(Some(
68 kind: CommandErrorKind::Abort(Some(
60 utf8_to_local(&format!(
69 utf8_to_local(&format!(
61 "abort: unsupported revlog version {}\n",
70 "abort: unsupported revlog version {}\n",
62 version
71 version
63 ))
72 ))
64 .into(),
73 .into(),
65 )),
74 )),
66 },
75 },
67 DebugDataErrorKind::CorruptedRevlog => CommandError {
76 DebugDataErrorKind::CorruptedRevlog => CommandError {
68 kind: CommandErrorKind::Abort(Some(
77 kind: CommandErrorKind::Abort(Some(
69 "abort: corrupted revlog\n".into(),
78 "abort: corrupted revlog\n".into(),
70 )),
79 )),
71 },
80 },
72 DebugDataErrorKind::UnknowRevlogDataFormat(format) => CommandError {
81 DebugDataErrorKind::UnknowRevlogDataFormat(format) => CommandError {
73 kind: CommandErrorKind::Abort(Some(
82 kind: CommandErrorKind::Abort(Some(
74 utf8_to_local(&format!(
83 utf8_to_local(&format!(
75 "abort: unknow revlog dataformat {:?}\n",
84 "abort: unknow revlog dataformat {:?}\n",
76 format
85 format
77 ))
86 ))
78 .into(),
87 .into(),
79 )),
88 )),
80 },
89 },
81 }
90 }
82 }
91 }
@@ -1,138 +1,147 b''
1 use crate::commands::Command;
1 use crate::commands::Command;
2 use crate::error::{CommandError, CommandErrorKind};
2 use crate::error::{CommandError, CommandErrorKind};
3 use crate::ui::utf8_to_local;
3 use crate::ui::utf8_to_local;
4 use crate::ui::Ui;
4 use crate::ui::Ui;
5 use hg::operations::FindRoot;
5 use hg::operations::FindRoot;
6 use hg::operations::{
6 use hg::operations::{
7 ListDirstateTrackedFiles, ListDirstateTrackedFilesError,
7 ListDirstateTrackedFiles, ListDirstateTrackedFilesError,
8 ListDirstateTrackedFilesErrorKind,
8 ListDirstateTrackedFilesErrorKind,
9 };
9 };
10 use hg::operations::{
10 use hg::operations::{
11 ListRevTrackedFiles, ListRevTrackedFilesError,
11 ListRevTrackedFiles, ListRevTrackedFilesError,
12 ListRevTrackedFilesErrorKind,
12 ListRevTrackedFilesErrorKind,
13 };
13 };
14 use hg::requirements;
14 use hg::requirements;
15 use hg::utils::files::{get_bytes_from_path, relativize_path};
15 use hg::utils::files::{get_bytes_from_path, relativize_path};
16 use hg::utils::hg_path::{HgPath, HgPathBuf};
16 use hg::utils::hg_path::{HgPath, HgPathBuf};
17 use std::path::PathBuf;
17 use std::path::PathBuf;
18
18
19 pub const HELP_TEXT: &str = "
19 pub const HELP_TEXT: &str = "
20 List tracked files.
20 List tracked files.
21
21
22 Returns 0 on success.
22 Returns 0 on success.
23 ";
23 ";
24
24
25 pub struct FilesCommand<'a> {
25 pub struct FilesCommand<'a> {
26 rev: Option<&'a str>,
26 rev: Option<&'a str>,
27 }
27 }
28
28
29 impl<'a> FilesCommand<'a> {
29 impl<'a> FilesCommand<'a> {
30 pub fn new(rev: Option<&'a str>) -> Self {
30 pub fn new(rev: Option<&'a str>) -> Self {
31 FilesCommand { rev }
31 FilesCommand { rev }
32 }
32 }
33
33
34 fn display_files(
34 fn display_files(
35 &self,
35 &self,
36 ui: &Ui,
36 ui: &Ui,
37 root: &PathBuf,
37 root: &PathBuf,
38 files: impl IntoIterator<Item = &'a HgPath>,
38 files: impl IntoIterator<Item = &'a HgPath>,
39 ) -> Result<(), CommandError> {
39 ) -> Result<(), CommandError> {
40 let cwd = std::env::current_dir()
40 let cwd = std::env::current_dir()
41 .or_else(|e| Err(CommandErrorKind::CurrentDirNotFound(e)))?;
41 .or_else(|e| Err(CommandErrorKind::CurrentDirNotFound(e)))?;
42 let rooted_cwd = cwd
42 let rooted_cwd = cwd
43 .strip_prefix(&root)
43 .strip_prefix(&root)
44 .expect("cwd was already checked within the repository");
44 .expect("cwd was already checked within the repository");
45 let rooted_cwd = HgPathBuf::from(get_bytes_from_path(rooted_cwd));
45 let rooted_cwd = HgPathBuf::from(get_bytes_from_path(rooted_cwd));
46
46
47 let mut stdout = ui.stdout_buffer();
47 let mut stdout = ui.stdout_buffer();
48
48
49 for file in files {
49 for file in files {
50 stdout.write_all(relativize_path(file, &rooted_cwd).as_ref())?;
50 stdout.write_all(relativize_path(file, &rooted_cwd).as_ref())?;
51 stdout.write_all(b"\n")?;
51 stdout.write_all(b"\n")?;
52 }
52 }
53 stdout.flush()?;
53 stdout.flush()?;
54 Ok(())
54 Ok(())
55 }
55 }
56 }
56 }
57
57
58 impl<'a> Command for FilesCommand<'a> {
58 impl<'a> Command for FilesCommand<'a> {
59 fn run(&self, ui: &Ui) -> Result<(), CommandError> {
59 fn run(&self, ui: &Ui) -> Result<(), CommandError> {
60 let root = FindRoot::new().run()?;
60 let root = FindRoot::new().run()?;
61 requirements::check(&root)?;
61 requirements::check(&root)?;
62 if let Some(rev) = self.rev {
62 if let Some(rev) = self.rev {
63 let mut operation = ListRevTrackedFiles::new(&root, rev)
63 let mut operation = ListRevTrackedFiles::new(&root, rev)
64 .map_err(|e| map_rev_error(rev, e))?;
64 .map_err(|e| map_rev_error(rev, e))?;
65 let files = operation.run().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)
66 self.display_files(ui, &root, files)
67 } else {
67 } else {
68 let mut operation = ListDirstateTrackedFiles::new(&root)
68 let mut operation = ListDirstateTrackedFiles::new(&root)
69 .map_err(map_dirstate_error)?;
69 .map_err(map_dirstate_error)?;
70 let files = operation.run().map_err(map_dirstate_error)?;
70 let files = operation.run().map_err(map_dirstate_error)?;
71 self.display_files(ui, &root, files)
71 self.display_files(ui, &root, files)
72 }
72 }
73 }
73 }
74 }
74 }
75
75
76 /// Convert `ListRevTrackedFilesErrorKind` to `CommandError`
76 /// Convert `ListRevTrackedFilesErrorKind` to `CommandError`
77 fn map_rev_error(rev: &str, err: ListRevTrackedFilesError) -> CommandError {
77 fn map_rev_error(rev: &str, err: ListRevTrackedFilesError) -> CommandError {
78 CommandError {
78 CommandError {
79 kind: match err.kind {
79 kind: match err.kind {
80 ListRevTrackedFilesErrorKind::IoError(err) => {
80 ListRevTrackedFilesErrorKind::IoError(err) => {
81 CommandErrorKind::Abort(Some(
81 CommandErrorKind::Abort(Some(
82 utf8_to_local(&format!("abort: {}\n", err)).into(),
82 utf8_to_local(&format!("abort: {}\n", err)).into(),
83 ))
83 ))
84 }
84 }
85 ListRevTrackedFilesErrorKind::InvalidRevision => {
85 ListRevTrackedFilesErrorKind::InvalidRevision => {
86 CommandErrorKind::Abort(Some(
86 CommandErrorKind::Abort(Some(
87 utf8_to_local(&format!(
87 utf8_to_local(&format!(
88 "abort: invalid revision identifier {}\n",
88 "abort: invalid revision identifier {}\n",
89 rev
89 rev
90 ))
90 ))
91 .into(),
91 .into(),
92 ))
92 ))
93 }
93 }
94 ListRevTrackedFilesErrorKind::AmbiguousPrefix => {
95 CommandErrorKind::Abort(Some(
96 utf8_to_local(&format!(
97 "abort: ambiguous revision identifier {}\n",
98 rev
99 ))
100 .into(),
101 ))
102 }
94 ListRevTrackedFilesErrorKind::UnsuportedRevlogVersion(version) => {
103 ListRevTrackedFilesErrorKind::UnsuportedRevlogVersion(version) => {
95 CommandErrorKind::Abort(Some(
104 CommandErrorKind::Abort(Some(
96 utf8_to_local(&format!(
105 utf8_to_local(&format!(
97 "abort: unsupported revlog version {}\n",
106 "abort: unsupported revlog version {}\n",
98 version
107 version
99 ))
108 ))
100 .into(),
109 .into(),
101 ))
110 ))
102 }
111 }
103 ListRevTrackedFilesErrorKind::CorruptedRevlog => {
112 ListRevTrackedFilesErrorKind::CorruptedRevlog => {
104 CommandErrorKind::Abort(Some(
113 CommandErrorKind::Abort(Some(
105 "abort: corrupted revlog\n".into(),
114 "abort: corrupted revlog\n".into(),
106 ))
115 ))
107 }
116 }
108 ListRevTrackedFilesErrorKind::UnknowRevlogDataFormat(format) => {
117 ListRevTrackedFilesErrorKind::UnknowRevlogDataFormat(format) => {
109 CommandErrorKind::Abort(Some(
118 CommandErrorKind::Abort(Some(
110 utf8_to_local(&format!(
119 utf8_to_local(&format!(
111 "abort: unknow revlog dataformat {:?}\n",
120 "abort: unknow revlog dataformat {:?}\n",
112 format
121 format
113 ))
122 ))
114 .into(),
123 .into(),
115 ))
124 ))
116 }
125 }
117 },
126 },
118 }
127 }
119 }
128 }
120
129
121 /// Convert `ListDirstateTrackedFilesError` to `CommandError`
130 /// Convert `ListDirstateTrackedFilesError` to `CommandError`
122 fn map_dirstate_error(err: ListDirstateTrackedFilesError) -> CommandError {
131 fn map_dirstate_error(err: ListDirstateTrackedFilesError) -> CommandError {
123 CommandError {
132 CommandError {
124 kind: match err.kind {
133 kind: match err.kind {
125 ListDirstateTrackedFilesErrorKind::IoError(err) => {
134 ListDirstateTrackedFilesErrorKind::IoError(err) => {
126 CommandErrorKind::Abort(Some(
135 CommandErrorKind::Abort(Some(
127 utf8_to_local(&format!("abort: {}\n", err)).into(),
136 utf8_to_local(&format!("abort: {}\n", err)).into(),
128 ))
137 ))
129 }
138 }
130 ListDirstateTrackedFilesErrorKind::ParseError(_) => {
139 ListDirstateTrackedFilesErrorKind::ParseError(_) => {
131 CommandErrorKind::Abort(Some(
140 CommandErrorKind::Abort(Some(
132 // TODO find a better error message
141 // TODO find a better error message
133 b"abort: parse error\n".to_vec(),
142 b"abort: parse error\n".to_vec(),
134 ))
143 ))
135 }
144 }
136 },
145 },
137 }
146 }
138 }
147 }
@@ -1,167 +1,165 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/debug/rhg" ]; then
5 > if [ -f "$RUNTESTDIR/../rust/target/debug/rhg" ]; then
6 > "$RUNTESTDIR/../rust/target/debug/rhg" "$@"
6 > "$RUNTESTDIR/../rust/target/debug/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; 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 e36fa63d37a576b27a69057598351db6ee5746bd
83 8d0267cb034247ebfa5ee58ce59e22e57a492297
84 test
84 test
85 0 0
85 0 0
86 file3
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 file1\x00b8e02f6433738021a065f94175c7cd23db5f05be (esc)
90 file-1\x00b8e02f6433738021a065f94175c7cd23db5f05be (esc)
91 file2\x005d9299349fc01ddd25d0070d149b124d8f10411e (esc)
91 file-2\x005d9299349fc01ddd25d0070d149b124d8f10411e (esc)
92 file3\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 c8e64718e1ca0312eeee0f59d37f8dc612793856
96 d1d1c679d3053e8926061b6f45ca52009f011e3f
97 test
97 test
98 0 0
98 0 0
99 file1
99 file-1
100
101 Commit 1 (no-eol)
100
102
101 commit 1 (no-eol)
103 Specifying revisions by changeset ID
104 $ hg log -T '{node}\n'
105 c6ad58c44207b6ff8a4fbbca7045a5edaa7e908b
106 d654274993d0149eecc3cc03214f598320211900
107 f646af7e96481d3a5470b695cf30ad8e3ab6c575
108 cf8b83f14ead62b374b6e91a0e9303b85dfd9ed7
109 91c6f6e73e39318534dc415ea4e8a09c99cd74d6
110 6ae9681c6d30389694d8701faf24b583cf3ccafe
111 $ rhg files -r cf8b83
112 file-1
113 file-2
114 file-3
115 $ rhg cat -r cf8b83 file-2
116 2
117 $ rhg cat -r c file-2
118 abort: invalid revision identifier c
119 [255]
102
120
103 Cat files
121 Cat files
104 $ cd $TESTTMP
122 $ cd $TESTTMP
105 $ rm -rf repository
123 $ rm -rf repository
106 $ hg init repository
124 $ hg init repository
107 $ cd repository
125 $ cd repository
108 $ echo "original content" > original
126 $ echo "original content" > original
109 $ hg add original
127 $ hg add original
110 $ hg commit -m "add original" original
128 $ hg commit -m "add original" original
111 $ rhg cat -r 0 original
129 $ rhg cat -r 0 original
112 original content
130 original content
113 Cat copied file should not display copy metadata
131 Cat copied file should not display copy metadata
114 $ hg copy original copy_of_original
132 $ hg copy original copy_of_original
115 $ hg commit -m "add copy of original"
133 $ hg commit -m "add copy of original"
116 $ rhg cat -r 1 copy_of_original
134 $ rhg cat -r 1 copy_of_original
117 original content
135 original content
118
136
119 Specifying revisions by changeset ID
120 $ hg log
121 changeset: 1:41263439dc17
122 tag: tip
123 user: test
124 date: Thu Jan 01 00:00:00 1970 +0000
125 summary: add copy of original
126
127 changeset: 0:1c9e69808da7
128 user: test
129 date: Thu Jan 01 00:00:00 1970 +0000
130 summary: add original
131
132 $ rhg files -r 41263439dc17
133 abort: invalid revision identifier 41263439dc17
134 [255]
135 $ rhg cat -r 41263439dc17 original
136 abort: invalid revision identifier 41263439dc17
137 [255]
138
139 Requirements
137 Requirements
140 $ rhg debugrequirements
138 $ rhg debugrequirements
141 dotencode
139 dotencode
142 fncache
140 fncache
143 generaldelta
141 generaldelta
144 revlogv1
142 revlogv1
145 sparserevlog
143 sparserevlog
146 store
144 store
147
145
148 $ echo indoor-pool >> .hg/requires
146 $ echo indoor-pool >> .hg/requires
149 $ rhg files
147 $ rhg files
150 [252]
148 [252]
151
149
152 $ rhg cat -r 1 copy_of_original
150 $ rhg cat -r 1 copy_of_original
153 [252]
151 [252]
154
152
155 $ rhg debugrequirements
153 $ rhg debugrequirements
156 dotencode
154 dotencode
157 fncache
155 fncache
158 generaldelta
156 generaldelta
159 revlogv1
157 revlogv1
160 sparserevlog
158 sparserevlog
161 store
159 store
162 indoor-pool
160 indoor-pool
163
161
164 $ echo -e '\xFF' >> .hg/requires
162 $ echo -e '\xFF' >> .hg/requires
165 $ rhg debugrequirements
163 $ rhg debugrequirements
166 abort: .hg/requires is corrupted
164 abort: .hg/requires is corrupted
167 [255]
165 [255]
General Comments 0
You need to be logged in to leave comments. Login now