##// END OF EJS Templates
rust: use NodePrefix::from_hex instead of hex::decode directly...
Simon Sapin -
r46647:88e741bf default
parent child Browse files
Show More
@@ -1,172 +1,176 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::Node;
17 use crate::revlog::NodePrefix;
16 use crate::revlog::Revision;
18 use crate::revlog::Revision;
17 use crate::utils::files::get_path_from_bytes;
19 use crate::utils::files::get_path_from_bytes;
18 use crate::utils::hg_path::{HgPath, HgPathBuf};
20 use crate::utils::hg_path::{HgPath, HgPathBuf};
19
21
20 const METADATA_DELIMITER: [u8; 2] = [b'\x01', b'\n'];
22 const METADATA_DELIMITER: [u8; 2] = [b'\x01', b'\n'];
21
23
22 /// Kind of error encountered by `CatRev`
24 /// Kind of error encountered by `CatRev`
23 #[derive(Debug)]
25 #[derive(Debug)]
24 pub enum CatRevErrorKind {
26 pub enum CatRevErrorKind {
25 /// Error when reading a `revlog` file.
27 /// Error when reading a `revlog` file.
26 IoError(std::io::Error),
28 IoError(std::io::Error),
27 /// The revision has not been found.
29 /// The revision has not been found.
28 InvalidRevision,
30 InvalidRevision,
29 /// Found more than one revision whose ID match the requested prefix
31 /// Found more than one revision whose ID match the requested prefix
30 AmbiguousPrefix,
32 AmbiguousPrefix,
31 /// A `revlog` file is corrupted.
33 /// A `revlog` file is corrupted.
32 CorruptedRevlog,
34 CorruptedRevlog,
33 /// The `revlog` format version is not supported.
35 /// The `revlog` format version is not supported.
34 UnsuportedRevlogVersion(u16),
36 UnsuportedRevlogVersion(u16),
35 /// The `revlog` data format is not supported.
37 /// The `revlog` data format is not supported.
36 UnknowRevlogDataFormat(u8),
38 UnknowRevlogDataFormat(u8),
37 }
39 }
38
40
39 /// A `CatRev` error
41 /// A `CatRev` error
40 #[derive(Debug)]
42 #[derive(Debug)]
41 pub struct CatRevError {
43 pub struct CatRevError {
42 /// Kind of error encountered by `CatRev`
44 /// Kind of error encountered by `CatRev`
43 pub kind: CatRevErrorKind,
45 pub kind: CatRevErrorKind,
44 }
46 }
45
47
46 impl From<CatRevErrorKind> for CatRevError {
48 impl From<CatRevErrorKind> for CatRevError {
47 fn from(kind: CatRevErrorKind) -> Self {
49 fn from(kind: CatRevErrorKind) -> Self {
48 CatRevError { kind }
50 CatRevError { kind }
49 }
51 }
50 }
52 }
51
53
52 impl From<RevlogError> for CatRevError {
54 impl From<RevlogError> for CatRevError {
53 fn from(err: RevlogError) -> Self {
55 fn from(err: RevlogError) -> Self {
54 match err {
56 match err {
55 RevlogError::IoError(err) => CatRevErrorKind::IoError(err),
57 RevlogError::IoError(err) => CatRevErrorKind::IoError(err),
56 RevlogError::UnsuportedVersion(version) => {
58 RevlogError::UnsuportedVersion(version) => {
57 CatRevErrorKind::UnsuportedRevlogVersion(version)
59 CatRevErrorKind::UnsuportedRevlogVersion(version)
58 }
60 }
59 RevlogError::InvalidRevision => CatRevErrorKind::InvalidRevision,
61 RevlogError::InvalidRevision => CatRevErrorKind::InvalidRevision,
60 RevlogError::AmbiguousPrefix => CatRevErrorKind::AmbiguousPrefix,
62 RevlogError::AmbiguousPrefix => CatRevErrorKind::AmbiguousPrefix,
61 RevlogError::Corrupted => CatRevErrorKind::CorruptedRevlog,
63 RevlogError::Corrupted => CatRevErrorKind::CorruptedRevlog,
62 RevlogError::UnknowDataFormat(format) => {
64 RevlogError::UnknowDataFormat(format) => {
63 CatRevErrorKind::UnknowRevlogDataFormat(format)
65 CatRevErrorKind::UnknowRevlogDataFormat(format)
64 }
66 }
65 }
67 }
66 .into()
68 .into()
67 }
69 }
68 }
70 }
69
71
70 /// List files under Mercurial control at a given revision.
72 /// List files under Mercurial control at a given revision.
71 pub struct CatRev<'a> {
73 pub struct CatRev<'a> {
72 root: &'a PathBuf,
74 root: &'a PathBuf,
73 /// The revision to cat the files from.
75 /// The revision to cat the files from.
74 rev: &'a str,
76 rev: &'a str,
75 /// The files to output.
77 /// The files to output.
76 files: &'a [HgPathBuf],
78 files: &'a [HgPathBuf],
77 /// The changelog file
79 /// The changelog file
78 changelog: Changelog,
80 changelog: Changelog,
79 /// The manifest file
81 /// The manifest file
80 manifest: Manifest,
82 manifest: Manifest,
81 /// The manifest entry corresponding to the revision.
83 /// The manifest entry corresponding to the revision.
82 ///
84 ///
83 /// Used to hold the owner of the returned references.
85 /// Used to hold the owner of the returned references.
84 manifest_entry: Option<ManifestEntry>,
86 manifest_entry: Option<ManifestEntry>,
85 }
87 }
86
88
87 impl<'a> CatRev<'a> {
89 impl<'a> CatRev<'a> {
88 pub fn new(
90 pub fn new(
89 root: &'a PathBuf,
91 root: &'a PathBuf,
90 rev: &'a str,
92 rev: &'a str,
91 files: &'a [HgPathBuf],
93 files: &'a [HgPathBuf],
92 ) -> Result<Self, CatRevError> {
94 ) -> Result<Self, CatRevError> {
93 let changelog = Changelog::open(&root)?;
95 let changelog = Changelog::open(&root)?;
94 let manifest = Manifest::open(&root)?;
96 let manifest = Manifest::open(&root)?;
95 let manifest_entry = None;
97 let manifest_entry = None;
96
98
97 Ok(Self {
99 Ok(Self {
98 root,
100 root,
99 rev,
101 rev,
100 files,
102 files,
101 changelog,
103 changelog,
102 manifest,
104 manifest,
103 manifest_entry,
105 manifest_entry,
104 })
106 })
105 }
107 }
106
108
107 pub fn run(&mut self) -> Result<Vec<u8>, CatRevError> {
109 pub fn run(&mut self) -> Result<Vec<u8>, CatRevError> {
108 let changelog_entry = match self.rev.parse::<Revision>() {
110 let changelog_entry = match self.rev.parse::<Revision>() {
109 Ok(rev) => self.changelog.get_rev(rev)?,
111 Ok(rev) => self.changelog.get_rev(rev)?,
110 _ => {
112 _ => {
111 let changelog_node = hex::decode(&self.rev)
113 let changelog_node = NodePrefix::from_hex(&self.rev)
112 .map_err(|_| CatRevErrorKind::InvalidRevision)?;
114 .map_err(|_| CatRevErrorKind::InvalidRevision)?;
113 self.changelog.get_node(&changelog_node)?
115 self.changelog.get_node(changelog_node.borrow())?
114 }
116 }
115 };
117 };
116 let manifest_node = hex::decode(&changelog_entry.manifest_node()?)
118 let manifest_node = Node::from_hex(&changelog_entry.manifest_node()?)
117 .map_err(|_| CatRevErrorKind::CorruptedRevlog)?;
119 .map_err(|_| CatRevErrorKind::CorruptedRevlog)?;
118
120
119 self.manifest_entry = Some(self.manifest.get_node(&manifest_node)?);
121 self.manifest_entry =
122 Some(self.manifest.get_node((&manifest_node).into())?);
120 if let Some(ref manifest_entry) = self.manifest_entry {
123 if let Some(ref manifest_entry) = self.manifest_entry {
121 let mut bytes = vec![];
124 let mut bytes = vec![];
122
125
123 for (manifest_file, node_bytes) in
126 for (manifest_file, node_bytes) in
124 manifest_entry.files_with_nodes()
127 manifest_entry.files_with_nodes()
125 {
128 {
126 for cat_file in self.files.iter() {
129 for cat_file in self.files.iter() {
127 if cat_file.as_bytes() == manifest_file.as_bytes() {
130 if cat_file.as_bytes() == manifest_file.as_bytes() {
128 let index_path =
131 let index_path =
129 store_path(self.root, manifest_file, b".i");
132 store_path(self.root, manifest_file, b".i");
130 let data_path =
133 let data_path =
131 store_path(self.root, manifest_file, b".d");
134 store_path(self.root, manifest_file, b".d");
132
135
133 let file_log =
136 let file_log =
134 Revlog::open(&index_path, Some(&data_path))?;
137 Revlog::open(&index_path, Some(&data_path))?;
135 let file_node = hex::decode(&node_bytes)
138 let file_node = Node::from_hex(node_bytes)
136 .map_err(|_| CatRevErrorKind::CorruptedRevlog)?;
139 .map_err(|_| CatRevErrorKind::CorruptedRevlog)?;
137 let file_rev = file_log.get_node_rev(&file_node)?;
140 let file_rev =
141 file_log.get_node_rev((&file_node).into())?;
138 let data = file_log.get_rev_data(file_rev)?;
142 let data = file_log.get_rev_data(file_rev)?;
139 if data.starts_with(&METADATA_DELIMITER) {
143 if data.starts_with(&METADATA_DELIMITER) {
140 let end_delimiter_position = data
144 let end_delimiter_position = data
141 [METADATA_DELIMITER.len()..]
145 [METADATA_DELIMITER.len()..]
142 .windows(METADATA_DELIMITER.len())
146 .windows(METADATA_DELIMITER.len())
143 .position(|bytes| bytes == METADATA_DELIMITER);
147 .position(|bytes| bytes == METADATA_DELIMITER);
144 if let Some(position) = end_delimiter_position {
148 if let Some(position) = end_delimiter_position {
145 let offset = METADATA_DELIMITER.len() * 2;
149 let offset = METADATA_DELIMITER.len() * 2;
146 bytes.extend(data[position + offset..].iter());
150 bytes.extend(data[position + offset..].iter());
147 }
151 }
148 } else {
152 } else {
149 bytes.extend(data);
153 bytes.extend(data);
150 }
154 }
151 }
155 }
152 }
156 }
153 }
157 }
154
158
155 Ok(bytes)
159 Ok(bytes)
156 } else {
160 } else {
157 unreachable!("manifest_entry should have been stored");
161 unreachable!("manifest_entry should have been stored");
158 }
162 }
159 }
163 }
160 }
164 }
161
165
162 fn store_path(root: &Path, hg_path: &HgPath, suffix: &[u8]) -> PathBuf {
166 fn store_path(root: &Path, hg_path: &HgPath, suffix: &[u8]) -> PathBuf {
163 let encoded_bytes =
167 let encoded_bytes =
164 path_encode(&[b"data/", hg_path.as_bytes(), suffix].concat());
168 path_encode(&[b"data/", hg_path.as_bytes(), suffix].concat());
165 [
169 [
166 root,
170 root,
167 &Path::new(".hg/store/"),
171 &Path::new(".hg/store/"),
168 get_path_from_bytes(&encoded_bytes),
172 get_path_from_bytes(&encoded_bytes),
169 ]
173 ]
170 .iter()
174 .iter()
171 .collect()
175 .collect()
172 }
176 }
@@ -1,119 +1,120 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::NodePrefix;
10 use crate::revlog::Revision;
11 use crate::revlog::Revision;
11
12
12 /// Kind of data to debug
13 /// Kind of data to debug
13 #[derive(Debug, Copy, Clone)]
14 #[derive(Debug, Copy, Clone)]
14 pub enum DebugDataKind {
15 pub enum DebugDataKind {
15 Changelog,
16 Changelog,
16 Manifest,
17 Manifest,
17 }
18 }
18
19
19 /// Kind of error encountered by DebugData
20 /// Kind of error encountered by DebugData
20 #[derive(Debug)]
21 #[derive(Debug)]
21 pub enum DebugDataErrorKind {
22 pub enum DebugDataErrorKind {
22 FindRootError(find_root::FindRootError),
23 FindRootError(find_root::FindRootError),
23 /// Error when reading a `revlog` file.
24 /// Error when reading a `revlog` file.
24 IoError(std::io::Error),
25 IoError(std::io::Error),
25 /// The revision has not been found.
26 /// The revision has not been found.
26 InvalidRevision,
27 InvalidRevision,
27 /// Found more than one revision whose ID match the requested prefix
28 /// Found more than one revision whose ID match the requested prefix
28 AmbiguousPrefix,
29 AmbiguousPrefix,
29 /// A `revlog` file is corrupted.
30 /// A `revlog` file is corrupted.
30 CorruptedRevlog,
31 CorruptedRevlog,
31 /// The `revlog` format version is not supported.
32 /// The `revlog` format version is not supported.
32 UnsuportedRevlogVersion(u16),
33 UnsuportedRevlogVersion(u16),
33 /// The `revlog` data format is not supported.
34 /// The `revlog` data format is not supported.
34 UnknowRevlogDataFormat(u8),
35 UnknowRevlogDataFormat(u8),
35 }
36 }
36
37
37 /// A DebugData error
38 /// A DebugData error
38 #[derive(Debug)]
39 #[derive(Debug)]
39 pub struct DebugDataError {
40 pub struct DebugDataError {
40 /// Kind of error encountered by DebugData
41 /// Kind of error encountered by DebugData
41 pub kind: DebugDataErrorKind,
42 pub kind: DebugDataErrorKind,
42 }
43 }
43
44
44 impl From<DebugDataErrorKind> for DebugDataError {
45 impl From<DebugDataErrorKind> for DebugDataError {
45 fn from(kind: DebugDataErrorKind) -> Self {
46 fn from(kind: DebugDataErrorKind) -> Self {
46 DebugDataError { kind }
47 DebugDataError { kind }
47 }
48 }
48 }
49 }
49
50
50 impl From<find_root::FindRootError> for DebugDataError {
51 impl From<find_root::FindRootError> for DebugDataError {
51 fn from(err: find_root::FindRootError) -> Self {
52 fn from(err: find_root::FindRootError) -> Self {
52 let kind = DebugDataErrorKind::FindRootError(err);
53 let kind = DebugDataErrorKind::FindRootError(err);
53 DebugDataError { kind }
54 DebugDataError { kind }
54 }
55 }
55 }
56 }
56
57
57 impl From<std::io::Error> for DebugDataError {
58 impl From<std::io::Error> for DebugDataError {
58 fn from(err: std::io::Error) -> Self {
59 fn from(err: std::io::Error) -> Self {
59 let kind = DebugDataErrorKind::IoError(err);
60 let kind = DebugDataErrorKind::IoError(err);
60 DebugDataError { kind }
61 DebugDataError { kind }
61 }
62 }
62 }
63 }
63
64
64 impl From<RevlogError> for DebugDataError {
65 impl From<RevlogError> for DebugDataError {
65 fn from(err: RevlogError) -> Self {
66 fn from(err: RevlogError) -> Self {
66 match err {
67 match err {
67 RevlogError::IoError(err) => DebugDataErrorKind::IoError(err),
68 RevlogError::IoError(err) => DebugDataErrorKind::IoError(err),
68 RevlogError::UnsuportedVersion(version) => {
69 RevlogError::UnsuportedVersion(version) => {
69 DebugDataErrorKind::UnsuportedRevlogVersion(version)
70 DebugDataErrorKind::UnsuportedRevlogVersion(version)
70 }
71 }
71 RevlogError::InvalidRevision => {
72 RevlogError::InvalidRevision => {
72 DebugDataErrorKind::InvalidRevision
73 DebugDataErrorKind::InvalidRevision
73 }
74 }
74 RevlogError::AmbiguousPrefix => {
75 RevlogError::AmbiguousPrefix => {
75 DebugDataErrorKind::AmbiguousPrefix
76 DebugDataErrorKind::AmbiguousPrefix
76 }
77 }
77 RevlogError::Corrupted => DebugDataErrorKind::CorruptedRevlog,
78 RevlogError::Corrupted => DebugDataErrorKind::CorruptedRevlog,
78 RevlogError::UnknowDataFormat(format) => {
79 RevlogError::UnknowDataFormat(format) => {
79 DebugDataErrorKind::UnknowRevlogDataFormat(format)
80 DebugDataErrorKind::UnknowRevlogDataFormat(format)
80 }
81 }
81 }
82 }
82 .into()
83 .into()
83 }
84 }
84 }
85 }
85
86
86 /// Dump the contents data of a revision.
87 /// Dump the contents data of a revision.
87 pub struct DebugData<'a> {
88 pub struct DebugData<'a> {
88 /// Revision or hash of the revision.
89 /// Revision or hash of the revision.
89 rev: &'a str,
90 rev: &'a str,
90 /// Kind of data to debug.
91 /// Kind of data to debug.
91 kind: DebugDataKind,
92 kind: DebugDataKind,
92 }
93 }
93
94
94 impl<'a> DebugData<'a> {
95 impl<'a> DebugData<'a> {
95 pub fn new(rev: &'a str, kind: DebugDataKind) -> Self {
96 pub fn new(rev: &'a str, kind: DebugDataKind) -> Self {
96 DebugData { rev, kind }
97 DebugData { rev, kind }
97 }
98 }
98
99
99 pub fn run(&mut self) -> Result<Vec<u8>, DebugDataError> {
100 pub fn run(&mut self) -> Result<Vec<u8>, DebugDataError> {
100 let root = find_root::FindRoot::new().run()?;
101 let root = find_root::FindRoot::new().run()?;
101 let index_file = match self.kind {
102 let index_file = match self.kind {
102 DebugDataKind::Changelog => root.join(".hg/store/00changelog.i"),
103 DebugDataKind::Changelog => root.join(".hg/store/00changelog.i"),
103 DebugDataKind::Manifest => root.join(".hg/store/00manifest.i"),
104 DebugDataKind::Manifest => root.join(".hg/store/00manifest.i"),
104 };
105 };
105 let revlog = Revlog::open(&index_file, None)?;
106 let revlog = Revlog::open(&index_file, None)?;
106
107
107 let data = match self.rev.parse::<Revision>() {
108 let data = match self.rev.parse::<Revision>() {
108 Ok(rev) => revlog.get_rev_data(rev)?,
109 Ok(rev) => revlog.get_rev_data(rev)?,
109 _ => {
110 _ => {
110 let node = hex::decode(&self.rev)
111 let node = NodePrefix::from_hex(&self.rev)
111 .map_err(|_| DebugDataErrorKind::InvalidRevision)?;
112 .map_err(|_| DebugDataErrorKind::InvalidRevision)?;
112 let rev = revlog.get_node_rev(&node)?;
113 let rev = revlog.get_node_rev(node.borrow())?;
113 revlog.get_rev_data(rev)?
114 revlog.get_rev_data(rev)?
114 }
115 }
115 };
116 };
116
117
117 Ok(data)
118 Ok(data)
118 }
119 }
119 }
120 }
@@ -1,192 +1,194 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::node::{Node, NodePrefix};
11 use crate::revlog::revlog::RevlogError;
12 use crate::revlog::revlog::RevlogError;
12 use crate::revlog::Revision;
13 use crate::revlog::Revision;
13 use crate::utils::hg_path::HgPath;
14 use crate::utils::hg_path::HgPath;
14 use crate::{DirstateParseError, EntryState};
15 use crate::{DirstateParseError, EntryState};
15 use rayon::prelude::*;
16 use rayon::prelude::*;
16 use std::convert::From;
17 use std::convert::From;
17 use std::fs;
18 use std::fs;
18 use std::path::PathBuf;
19 use std::path::PathBuf;
19
20
20 /// Kind of error encountered by `ListDirstateTrackedFiles`
21 /// Kind of error encountered by `ListDirstateTrackedFiles`
21 #[derive(Debug)]
22 #[derive(Debug)]
22 pub enum ListDirstateTrackedFilesErrorKind {
23 pub enum ListDirstateTrackedFilesErrorKind {
23 /// Error when reading the `dirstate` file
24 /// Error when reading the `dirstate` file
24 IoError(std::io::Error),
25 IoError(std::io::Error),
25 /// Error when parsing the `dirstate` file
26 /// Error when parsing the `dirstate` file
26 ParseError(DirstateParseError),
27 ParseError(DirstateParseError),
27 }
28 }
28
29
29 /// A `ListDirstateTrackedFiles` error
30 /// A `ListDirstateTrackedFiles` error
30 #[derive(Debug)]
31 #[derive(Debug)]
31 pub struct ListDirstateTrackedFilesError {
32 pub struct ListDirstateTrackedFilesError {
32 /// Kind of error encountered by `ListDirstateTrackedFiles`
33 /// Kind of error encountered by `ListDirstateTrackedFiles`
33 pub kind: ListDirstateTrackedFilesErrorKind,
34 pub kind: ListDirstateTrackedFilesErrorKind,
34 }
35 }
35
36
36 impl From<ListDirstateTrackedFilesErrorKind>
37 impl From<ListDirstateTrackedFilesErrorKind>
37 for ListDirstateTrackedFilesError
38 for ListDirstateTrackedFilesError
38 {
39 {
39 fn from(kind: ListDirstateTrackedFilesErrorKind) -> Self {
40 fn from(kind: ListDirstateTrackedFilesErrorKind) -> Self {
40 ListDirstateTrackedFilesError { kind }
41 ListDirstateTrackedFilesError { kind }
41 }
42 }
42 }
43 }
43
44
44 impl From<std::io::Error> for ListDirstateTrackedFilesError {
45 impl From<std::io::Error> for ListDirstateTrackedFilesError {
45 fn from(err: std::io::Error) -> Self {
46 fn from(err: std::io::Error) -> Self {
46 let kind = ListDirstateTrackedFilesErrorKind::IoError(err);
47 let kind = ListDirstateTrackedFilesErrorKind::IoError(err);
47 ListDirstateTrackedFilesError { kind }
48 ListDirstateTrackedFilesError { kind }
48 }
49 }
49 }
50 }
50
51
51 /// List files under Mercurial control in the working directory
52 /// List files under Mercurial control in the working directory
52 /// by reading the dirstate
53 /// by reading the dirstate
53 pub struct ListDirstateTrackedFiles {
54 pub struct ListDirstateTrackedFiles {
54 /// The `dirstate` content.
55 /// The `dirstate` content.
55 content: Vec<u8>,
56 content: Vec<u8>,
56 }
57 }
57
58
58 impl ListDirstateTrackedFiles {
59 impl ListDirstateTrackedFiles {
59 pub fn new(root: &PathBuf) -> Result<Self, ListDirstateTrackedFilesError> {
60 pub fn new(root: &PathBuf) -> Result<Self, ListDirstateTrackedFilesError> {
60 let dirstate = root.join(".hg/dirstate");
61 let dirstate = root.join(".hg/dirstate");
61 let content = fs::read(&dirstate)?;
62 let content = fs::read(&dirstate)?;
62 Ok(Self { content })
63 Ok(Self { content })
63 }
64 }
64
65
65 pub fn run(
66 pub fn run(
66 &mut self,
67 &mut self,
67 ) -> Result<Vec<&HgPath>, ListDirstateTrackedFilesError> {
68 ) -> Result<Vec<&HgPath>, ListDirstateTrackedFilesError> {
68 let (_, entries, _) = parse_dirstate(&self.content)
69 let (_, entries, _) = parse_dirstate(&self.content)
69 .map_err(ListDirstateTrackedFilesErrorKind::ParseError)?;
70 .map_err(ListDirstateTrackedFilesErrorKind::ParseError)?;
70 let mut files: Vec<&HgPath> = entries
71 let mut files: Vec<&HgPath> = entries
71 .into_iter()
72 .into_iter()
72 .filter_map(|(path, entry)| match entry.state {
73 .filter_map(|(path, entry)| match entry.state {
73 EntryState::Removed => None,
74 EntryState::Removed => None,
74 _ => Some(path),
75 _ => Some(path),
75 })
76 })
76 .collect();
77 .collect();
77 files.par_sort_unstable();
78 files.par_sort_unstable();
78 Ok(files)
79 Ok(files)
79 }
80 }
80 }
81 }
81
82
82 /// Kind of error encountered by `ListRevTrackedFiles`
83 /// Kind of error encountered by `ListRevTrackedFiles`
83 #[derive(Debug)]
84 #[derive(Debug)]
84 pub enum ListRevTrackedFilesErrorKind {
85 pub enum ListRevTrackedFilesErrorKind {
85 /// Error when reading a `revlog` file.
86 /// Error when reading a `revlog` file.
86 IoError(std::io::Error),
87 IoError(std::io::Error),
87 /// The revision has not been found.
88 /// The revision has not been found.
88 InvalidRevision,
89 InvalidRevision,
89 /// Found more than one revision whose ID match the requested prefix
90 /// Found more than one revision whose ID match the requested prefix
90 AmbiguousPrefix,
91 AmbiguousPrefix,
91 /// A `revlog` file is corrupted.
92 /// A `revlog` file is corrupted.
92 CorruptedRevlog,
93 CorruptedRevlog,
93 /// The `revlog` format version is not supported.
94 /// The `revlog` format version is not supported.
94 UnsuportedRevlogVersion(u16),
95 UnsuportedRevlogVersion(u16),
95 /// The `revlog` data format is not supported.
96 /// The `revlog` data format is not supported.
96 UnknowRevlogDataFormat(u8),
97 UnknowRevlogDataFormat(u8),
97 }
98 }
98
99
99 /// A `ListRevTrackedFiles` error
100 /// A `ListRevTrackedFiles` error
100 #[derive(Debug)]
101 #[derive(Debug)]
101 pub struct ListRevTrackedFilesError {
102 pub struct ListRevTrackedFilesError {
102 /// Kind of error encountered by `ListRevTrackedFiles`
103 /// Kind of error encountered by `ListRevTrackedFiles`
103 pub kind: ListRevTrackedFilesErrorKind,
104 pub kind: ListRevTrackedFilesErrorKind,
104 }
105 }
105
106
106 impl From<ListRevTrackedFilesErrorKind> for ListRevTrackedFilesError {
107 impl From<ListRevTrackedFilesErrorKind> for ListRevTrackedFilesError {
107 fn from(kind: ListRevTrackedFilesErrorKind) -> Self {
108 fn from(kind: ListRevTrackedFilesErrorKind) -> Self {
108 ListRevTrackedFilesError { kind }
109 ListRevTrackedFilesError { kind }
109 }
110 }
110 }
111 }
111
112
112 impl From<RevlogError> for ListRevTrackedFilesError {
113 impl From<RevlogError> for ListRevTrackedFilesError {
113 fn from(err: RevlogError) -> Self {
114 fn from(err: RevlogError) -> Self {
114 match err {
115 match err {
115 RevlogError::IoError(err) => {
116 RevlogError::IoError(err) => {
116 ListRevTrackedFilesErrorKind::IoError(err)
117 ListRevTrackedFilesErrorKind::IoError(err)
117 }
118 }
118 RevlogError::UnsuportedVersion(version) => {
119 RevlogError::UnsuportedVersion(version) => {
119 ListRevTrackedFilesErrorKind::UnsuportedRevlogVersion(version)
120 ListRevTrackedFilesErrorKind::UnsuportedRevlogVersion(version)
120 }
121 }
121 RevlogError::InvalidRevision => {
122 RevlogError::InvalidRevision => {
122 ListRevTrackedFilesErrorKind::InvalidRevision
123 ListRevTrackedFilesErrorKind::InvalidRevision
123 }
124 }
124 RevlogError::AmbiguousPrefix => {
125 RevlogError::AmbiguousPrefix => {
125 ListRevTrackedFilesErrorKind::AmbiguousPrefix
126 ListRevTrackedFilesErrorKind::AmbiguousPrefix
126 }
127 }
127 RevlogError::Corrupted => {
128 RevlogError::Corrupted => {
128 ListRevTrackedFilesErrorKind::CorruptedRevlog
129 ListRevTrackedFilesErrorKind::CorruptedRevlog
129 }
130 }
130 RevlogError::UnknowDataFormat(format) => {
131 RevlogError::UnknowDataFormat(format) => {
131 ListRevTrackedFilesErrorKind::UnknowRevlogDataFormat(format)
132 ListRevTrackedFilesErrorKind::UnknowRevlogDataFormat(format)
132 }
133 }
133 }
134 }
134 .into()
135 .into()
135 }
136 }
136 }
137 }
137
138
138 /// List files under Mercurial control at a given revision.
139 /// List files under Mercurial control at a given revision.
139 pub struct ListRevTrackedFiles<'a> {
140 pub struct ListRevTrackedFiles<'a> {
140 /// The revision to list the files from.
141 /// The revision to list the files from.
141 rev: &'a str,
142 rev: &'a str,
142 /// The changelog file
143 /// The changelog file
143 changelog: Changelog,
144 changelog: Changelog,
144 /// The manifest file
145 /// The manifest file
145 manifest: Manifest,
146 manifest: Manifest,
146 /// The manifest entry corresponding to the revision.
147 /// The manifest entry corresponding to the revision.
147 ///
148 ///
148 /// Used to hold the owner of the returned references.
149 /// Used to hold the owner of the returned references.
149 manifest_entry: Option<ManifestEntry>,
150 manifest_entry: Option<ManifestEntry>,
150 }
151 }
151
152
152 impl<'a> ListRevTrackedFiles<'a> {
153 impl<'a> ListRevTrackedFiles<'a> {
153 pub fn new(
154 pub fn new(
154 root: &PathBuf,
155 root: &PathBuf,
155 rev: &'a str,
156 rev: &'a str,
156 ) -> Result<Self, ListRevTrackedFilesError> {
157 ) -> Result<Self, ListRevTrackedFilesError> {
157 let changelog = Changelog::open(&root)?;
158 let changelog = Changelog::open(&root)?;
158 let manifest = Manifest::open(&root)?;
159 let manifest = Manifest::open(&root)?;
159
160
160 Ok(Self {
161 Ok(Self {
161 rev,
162 rev,
162 changelog,
163 changelog,
163 manifest,
164 manifest,
164 manifest_entry: None,
165 manifest_entry: None,
165 })
166 })
166 }
167 }
167
168
168 pub fn run(
169 pub fn run(
169 &mut self,
170 &mut self,
170 ) -> Result<impl Iterator<Item = &HgPath>, ListRevTrackedFilesError> {
171 ) -> Result<impl Iterator<Item = &HgPath>, ListRevTrackedFilesError> {
171 let changelog_entry = match self.rev.parse::<Revision>() {
172 let changelog_entry = match self.rev.parse::<Revision>() {
172 Ok(rev) => self.changelog.get_rev(rev)?,
173 Ok(rev) => self.changelog.get_rev(rev)?,
173 _ => {
174 _ => {
174 let changelog_node = hex::decode(&self.rev)
175 let changelog_node = NodePrefix::from_hex(&self.rev)
175 .or(Err(ListRevTrackedFilesErrorKind::InvalidRevision))?;
176 .or(Err(ListRevTrackedFilesErrorKind::InvalidRevision))?;
176 self.changelog.get_node(&changelog_node)?
177 self.changelog.get_node(changelog_node.borrow())?
177 }
178 }
178 };
179 };
179 let manifest_node = hex::decode(&changelog_entry.manifest_node()?)
180 let manifest_node = Node::from_hex(&changelog_entry.manifest_node()?)
180 .or(Err(ListRevTrackedFilesErrorKind::CorruptedRevlog))?;
181 .or(Err(ListRevTrackedFilesErrorKind::CorruptedRevlog))?;
181
182
182 self.manifest_entry = Some(self.manifest.get_node(&manifest_node)?);
183 self.manifest_entry =
184 Some(self.manifest.get_node((&manifest_node).into())?);
183
185
184 if let Some(ref manifest_entry) = self.manifest_entry {
186 if let Some(ref manifest_entry) = self.manifest_entry {
185 Ok(manifest_entry.files())
187 Ok(manifest_entry.files())
186 } else {
188 } else {
187 panic!(
189 panic!(
188 "manifest entry should have been stored in self.manifest_node to ensure its lifetime since references are returned from it"
190 "manifest entry should have been stored in self.manifest_node to ensure its lifetime since references are returned from it"
189 )
191 )
190 }
192 }
191 }
193 }
192 }
194 }
@@ -1,58 +1,59 b''
1 use crate::revlog::revlog::{Revlog, RevlogError};
1 use crate::revlog::revlog::{Revlog, RevlogError};
2 use crate::revlog::NodePrefixRef;
2 use crate::revlog::Revision;
3 use crate::revlog::Revision;
3 use std::path::PathBuf;
4 use std::path::PathBuf;
4
5
5 /// A specialized `Revlog` to work with `changelog` data format.
6 /// A specialized `Revlog` to work with `changelog` data format.
6 pub struct Changelog {
7 pub struct Changelog {
7 /// The generic `revlog` format.
8 /// The generic `revlog` format.
8 revlog: Revlog,
9 revlog: Revlog,
9 }
10 }
10
11
11 impl Changelog {
12 impl Changelog {
12 /// Open the `changelog` of a repository given by its root.
13 /// Open the `changelog` of a repository given by its root.
13 pub fn open(root: &PathBuf) -> Result<Self, RevlogError> {
14 pub fn open(root: &PathBuf) -> Result<Self, RevlogError> {
14 let index_file = root.join(".hg/store/00changelog.i");
15 let index_file = root.join(".hg/store/00changelog.i");
15 let revlog = Revlog::open(&index_file, None)?;
16 let revlog = Revlog::open(&index_file, None)?;
16 Ok(Self { revlog })
17 Ok(Self { revlog })
17 }
18 }
18
19
19 /// Return the `ChangelogEntry` a given node id.
20 /// Return the `ChangelogEntry` a given node id.
20 pub fn get_node(
21 pub fn get_node(
21 &self,
22 &self,
22 node: &[u8],
23 node: NodePrefixRef,
23 ) -> Result<ChangelogEntry, RevlogError> {
24 ) -> Result<ChangelogEntry, RevlogError> {
24 let rev = self.revlog.get_node_rev(node)?;
25 let rev = self.revlog.get_node_rev(node)?;
25 self.get_rev(rev)
26 self.get_rev(rev)
26 }
27 }
27
28
28 /// Return the `ChangelogEntry` of a given node revision.
29 /// Return the `ChangelogEntry` of a given node revision.
29 pub fn get_rev(
30 pub fn get_rev(
30 &self,
31 &self,
31 rev: Revision,
32 rev: Revision,
32 ) -> Result<ChangelogEntry, RevlogError> {
33 ) -> Result<ChangelogEntry, RevlogError> {
33 let bytes = self.revlog.get_rev_data(rev)?;
34 let bytes = self.revlog.get_rev_data(rev)?;
34 Ok(ChangelogEntry { bytes })
35 Ok(ChangelogEntry { bytes })
35 }
36 }
36 }
37 }
37
38
38 /// `Changelog` entry which knows how to interpret the `changelog` data bytes.
39 /// `Changelog` entry which knows how to interpret the `changelog` data bytes.
39 #[derive(Debug)]
40 #[derive(Debug)]
40 pub struct ChangelogEntry {
41 pub struct ChangelogEntry {
41 /// The data bytes of the `changelog` entry.
42 /// The data bytes of the `changelog` entry.
42 bytes: Vec<u8>,
43 bytes: Vec<u8>,
43 }
44 }
44
45
45 impl ChangelogEntry {
46 impl ChangelogEntry {
46 /// Return an iterator over the lines of the entry.
47 /// Return an iterator over the lines of the entry.
47 pub fn lines(&self) -> impl Iterator<Item = &[u8]> {
48 pub fn lines(&self) -> impl Iterator<Item = &[u8]> {
48 self.bytes
49 self.bytes
49 .split(|b| b == &b'\n')
50 .split(|b| b == &b'\n')
50 .filter(|line| !line.is_empty())
51 .filter(|line| !line.is_empty())
51 }
52 }
52
53
53 /// Return the node id of the `manifest` referenced by this `changelog`
54 /// Return the node id of the `manifest` referenced by this `changelog`
54 /// entry.
55 /// entry.
55 pub fn manifest_node(&self) -> Result<&[u8], RevlogError> {
56 pub fn manifest_node(&self) -> Result<&[u8], RevlogError> {
56 self.lines().next().ok_or(RevlogError::Corrupted)
57 self.lines().next().ok_or(RevlogError::Corrupted)
57 }
58 }
58 }
59 }
@@ -1,390 +1,392 b''
1 use std::convert::TryInto;
1 use std::ops::Deref;
2 use std::ops::Deref;
2
3
3 use byteorder::{BigEndian, ByteOrder};
4 use byteorder::{BigEndian, ByteOrder};
4
5
6 use crate::revlog::node::Node;
5 use crate::revlog::revlog::RevlogError;
7 use crate::revlog::revlog::RevlogError;
6 use crate::revlog::{Revision, NULL_REVISION};
8 use crate::revlog::{Revision, NULL_REVISION};
7
9
8 pub const INDEX_ENTRY_SIZE: usize = 64;
10 pub const INDEX_ENTRY_SIZE: usize = 64;
9
11
10 /// A Revlog index
12 /// A Revlog index
11 pub struct Index {
13 pub struct Index {
12 bytes: Box<dyn Deref<Target = [u8]> + Send>,
14 bytes: Box<dyn Deref<Target = [u8]> + Send>,
13 /// Offsets of starts of index blocks.
15 /// Offsets of starts of index blocks.
14 /// Only needed when the index is interleaved with data.
16 /// Only needed when the index is interleaved with data.
15 offsets: Option<Vec<usize>>,
17 offsets: Option<Vec<usize>>,
16 }
18 }
17
19
18 impl Index {
20 impl Index {
19 /// Create an index from bytes.
21 /// Create an index from bytes.
20 /// Calculate the start of each entry when is_inline is true.
22 /// Calculate the start of each entry when is_inline is true.
21 pub fn new(
23 pub fn new(
22 bytes: Box<dyn Deref<Target = [u8]> + Send>,
24 bytes: Box<dyn Deref<Target = [u8]> + Send>,
23 ) -> Result<Self, RevlogError> {
25 ) -> Result<Self, RevlogError> {
24 if is_inline(&bytes) {
26 if is_inline(&bytes) {
25 let mut offset: usize = 0;
27 let mut offset: usize = 0;
26 let mut offsets = Vec::new();
28 let mut offsets = Vec::new();
27
29
28 while offset + INDEX_ENTRY_SIZE <= bytes.len() {
30 while offset + INDEX_ENTRY_SIZE <= bytes.len() {
29 offsets.push(offset);
31 offsets.push(offset);
30 let end = offset + INDEX_ENTRY_SIZE;
32 let end = offset + INDEX_ENTRY_SIZE;
31 let entry = IndexEntry {
33 let entry = IndexEntry {
32 bytes: &bytes[offset..end],
34 bytes: &bytes[offset..end],
33 offset_override: None,
35 offset_override: None,
34 };
36 };
35
37
36 offset += INDEX_ENTRY_SIZE + entry.compressed_len();
38 offset += INDEX_ENTRY_SIZE + entry.compressed_len();
37 }
39 }
38
40
39 if offset == bytes.len() {
41 if offset == bytes.len() {
40 Ok(Self {
42 Ok(Self {
41 bytes,
43 bytes,
42 offsets: Some(offsets),
44 offsets: Some(offsets),
43 })
45 })
44 } else {
46 } else {
45 Err(RevlogError::Corrupted)
47 Err(RevlogError::Corrupted)
46 }
48 }
47 } else {
49 } else {
48 Ok(Self {
50 Ok(Self {
49 bytes,
51 bytes,
50 offsets: None,
52 offsets: None,
51 })
53 })
52 }
54 }
53 }
55 }
54
56
55 /// Value of the inline flag.
57 /// Value of the inline flag.
56 pub fn is_inline(&self) -> bool {
58 pub fn is_inline(&self) -> bool {
57 is_inline(&self.bytes)
59 is_inline(&self.bytes)
58 }
60 }
59
61
60 /// Return a slice of bytes if `revlog` is inline. Panic if not.
62 /// Return a slice of bytes if `revlog` is inline. Panic if not.
61 pub fn data(&self, start: usize, end: usize) -> &[u8] {
63 pub fn data(&self, start: usize, end: usize) -> &[u8] {
62 if !self.is_inline() {
64 if !self.is_inline() {
63 panic!("tried to access data in the index of a revlog that is not inline");
65 panic!("tried to access data in the index of a revlog that is not inline");
64 }
66 }
65 &self.bytes[start..end]
67 &self.bytes[start..end]
66 }
68 }
67
69
68 /// Return number of entries of the revlog index.
70 /// Return number of entries of the revlog index.
69 pub fn len(&self) -> usize {
71 pub fn len(&self) -> usize {
70 if let Some(offsets) = &self.offsets {
72 if let Some(offsets) = &self.offsets {
71 offsets.len()
73 offsets.len()
72 } else {
74 } else {
73 self.bytes.len() / INDEX_ENTRY_SIZE
75 self.bytes.len() / INDEX_ENTRY_SIZE
74 }
76 }
75 }
77 }
76
78
77 /// Returns `true` if the `Index` has zero `entries`.
79 /// Returns `true` if the `Index` has zero `entries`.
78 pub fn is_empty(&self) -> bool {
80 pub fn is_empty(&self) -> bool {
79 self.len() == 0
81 self.len() == 0
80 }
82 }
81
83
82 /// Return the index entry corresponding to the given revision if it
84 /// Return the index entry corresponding to the given revision if it
83 /// exists.
85 /// exists.
84 pub fn get_entry(&self, rev: Revision) -> Option<IndexEntry> {
86 pub fn get_entry(&self, rev: Revision) -> Option<IndexEntry> {
85 if rev == NULL_REVISION {
87 if rev == NULL_REVISION {
86 return None;
88 return None;
87 }
89 }
88 if let Some(offsets) = &self.offsets {
90 if let Some(offsets) = &self.offsets {
89 self.get_entry_inline(rev, offsets)
91 self.get_entry_inline(rev, offsets)
90 } else {
92 } else {
91 self.get_entry_separated(rev)
93 self.get_entry_separated(rev)
92 }
94 }
93 }
95 }
94
96
95 fn get_entry_inline(
97 fn get_entry_inline(
96 &self,
98 &self,
97 rev: Revision,
99 rev: Revision,
98 offsets: &[usize],
100 offsets: &[usize],
99 ) -> Option<IndexEntry> {
101 ) -> Option<IndexEntry> {
100 let start = *offsets.get(rev as usize)?;
102 let start = *offsets.get(rev as usize)?;
101 let end = start.checked_add(INDEX_ENTRY_SIZE)?;
103 let end = start.checked_add(INDEX_ENTRY_SIZE)?;
102 let bytes = &self.bytes[start..end];
104 let bytes = &self.bytes[start..end];
103
105
104 // See IndexEntry for an explanation of this override.
106 // See IndexEntry for an explanation of this override.
105 let offset_override = Some(end);
107 let offset_override = Some(end);
106
108
107 Some(IndexEntry {
109 Some(IndexEntry {
108 bytes,
110 bytes,
109 offset_override,
111 offset_override,
110 })
112 })
111 }
113 }
112
114
113 fn get_entry_separated(&self, rev: Revision) -> Option<IndexEntry> {
115 fn get_entry_separated(&self, rev: Revision) -> Option<IndexEntry> {
114 let max_rev = self.bytes.len() / INDEX_ENTRY_SIZE;
116 let max_rev = self.bytes.len() / INDEX_ENTRY_SIZE;
115 if rev as usize >= max_rev {
117 if rev as usize >= max_rev {
116 return None;
118 return None;
117 }
119 }
118 let start = rev as usize * INDEX_ENTRY_SIZE;
120 let start = rev as usize * INDEX_ENTRY_SIZE;
119 let end = start + INDEX_ENTRY_SIZE;
121 let end = start + INDEX_ENTRY_SIZE;
120 let bytes = &self.bytes[start..end];
122 let bytes = &self.bytes[start..end];
121
123
122 // Override the offset of the first revision as its bytes are used
124 // Override the offset of the first revision as its bytes are used
123 // for the index's metadata (saving space because it is always 0)
125 // for the index's metadata (saving space because it is always 0)
124 let offset_override = if rev == 0 { Some(0) } else { None };
126 let offset_override = if rev == 0 { Some(0) } else { None };
125
127
126 Some(IndexEntry {
128 Some(IndexEntry {
127 bytes,
129 bytes,
128 offset_override,
130 offset_override,
129 })
131 })
130 }
132 }
131 }
133 }
132
134
133 #[derive(Debug)]
135 #[derive(Debug)]
134 pub struct IndexEntry<'a> {
136 pub struct IndexEntry<'a> {
135 bytes: &'a [u8],
137 bytes: &'a [u8],
136 /// Allows to override the offset value of the entry.
138 /// Allows to override the offset value of the entry.
137 ///
139 ///
138 /// For interleaved index and data, the offset stored in the index
140 /// For interleaved index and data, the offset stored in the index
139 /// corresponds to the separated data offset.
141 /// corresponds to the separated data offset.
140 /// It has to be overridden with the actual offset in the interleaved
142 /// It has to be overridden with the actual offset in the interleaved
141 /// index which is just after the index block.
143 /// index which is just after the index block.
142 ///
144 ///
143 /// For separated index and data, the offset stored in the first index
145 /// For separated index and data, the offset stored in the first index
144 /// entry is mixed with the index headers.
146 /// entry is mixed with the index headers.
145 /// It has to be overridden with 0.
147 /// It has to be overridden with 0.
146 offset_override: Option<usize>,
148 offset_override: Option<usize>,
147 }
149 }
148
150
149 impl<'a> IndexEntry<'a> {
151 impl<'a> IndexEntry<'a> {
150 /// Return the offset of the data.
152 /// Return the offset of the data.
151 pub fn offset(&self) -> usize {
153 pub fn offset(&self) -> usize {
152 if let Some(offset_override) = self.offset_override {
154 if let Some(offset_override) = self.offset_override {
153 offset_override
155 offset_override
154 } else {
156 } else {
155 let mut bytes = [0; 8];
157 let mut bytes = [0; 8];
156 bytes[2..8].copy_from_slice(&self.bytes[0..=5]);
158 bytes[2..8].copy_from_slice(&self.bytes[0..=5]);
157 BigEndian::read_u64(&bytes[..]) as usize
159 BigEndian::read_u64(&bytes[..]) as usize
158 }
160 }
159 }
161 }
160
162
161 /// Return the compressed length of the data.
163 /// Return the compressed length of the data.
162 pub fn compressed_len(&self) -> usize {
164 pub fn compressed_len(&self) -> usize {
163 BigEndian::read_u32(&self.bytes[8..=11]) as usize
165 BigEndian::read_u32(&self.bytes[8..=11]) as usize
164 }
166 }
165
167
166 /// Return the uncompressed length of the data.
168 /// Return the uncompressed length of the data.
167 pub fn uncompressed_len(&self) -> usize {
169 pub fn uncompressed_len(&self) -> usize {
168 BigEndian::read_u32(&self.bytes[12..=15]) as usize
170 BigEndian::read_u32(&self.bytes[12..=15]) as usize
169 }
171 }
170
172
171 /// Return the revision upon which the data has been derived.
173 /// Return the revision upon which the data has been derived.
172 pub fn base_revision(&self) -> Revision {
174 pub fn base_revision(&self) -> Revision {
173 // TODO Maybe return an Option when base_revision == rev?
175 // TODO Maybe return an Option when base_revision == rev?
174 // Requires to add rev to IndexEntry
176 // Requires to add rev to IndexEntry
175
177
176 BigEndian::read_i32(&self.bytes[16..])
178 BigEndian::read_i32(&self.bytes[16..])
177 }
179 }
178
180
179 pub fn p1(&self) -> Revision {
181 pub fn p1(&self) -> Revision {
180 BigEndian::read_i32(&self.bytes[24..])
182 BigEndian::read_i32(&self.bytes[24..])
181 }
183 }
182
184
183 pub fn p2(&self) -> Revision {
185 pub fn p2(&self) -> Revision {
184 BigEndian::read_i32(&self.bytes[28..])
186 BigEndian::read_i32(&self.bytes[28..])
185 }
187 }
186
188
187 /// Return the hash of revision's full text.
189 /// Return the hash of revision's full text.
188 ///
190 ///
189 /// Currently, SHA-1 is used and only the first 20 bytes of this field
191 /// Currently, SHA-1 is used and only the first 20 bytes of this field
190 /// are used.
192 /// are used.
191 pub fn hash(&self) -> &[u8] {
193 pub fn hash(&self) -> &Node {
192 &self.bytes[32..52]
194 (&self.bytes[32..52]).try_into().unwrap()
193 }
195 }
194 }
196 }
195
197
196 /// Value of the inline flag.
198 /// Value of the inline flag.
197 pub fn is_inline(index_bytes: &[u8]) -> bool {
199 pub fn is_inline(index_bytes: &[u8]) -> bool {
198 match &index_bytes[0..=1] {
200 match &index_bytes[0..=1] {
199 [0, 0] | [0, 2] => false,
201 [0, 0] | [0, 2] => false,
200 _ => true,
202 _ => true,
201 }
203 }
202 }
204 }
203
205
204 #[cfg(test)]
206 #[cfg(test)]
205 mod tests {
207 mod tests {
206 use super::*;
208 use super::*;
207
209
208 #[cfg(test)]
210 #[cfg(test)]
209 #[derive(Debug, Copy, Clone)]
211 #[derive(Debug, Copy, Clone)]
210 pub struct IndexEntryBuilder {
212 pub struct IndexEntryBuilder {
211 is_first: bool,
213 is_first: bool,
212 is_inline: bool,
214 is_inline: bool,
213 is_general_delta: bool,
215 is_general_delta: bool,
214 version: u16,
216 version: u16,
215 offset: usize,
217 offset: usize,
216 compressed_len: usize,
218 compressed_len: usize,
217 uncompressed_len: usize,
219 uncompressed_len: usize,
218 base_revision: Revision,
220 base_revision: Revision,
219 }
221 }
220
222
221 #[cfg(test)]
223 #[cfg(test)]
222 impl IndexEntryBuilder {
224 impl IndexEntryBuilder {
223 pub fn new() -> Self {
225 pub fn new() -> Self {
224 Self {
226 Self {
225 is_first: false,
227 is_first: false,
226 is_inline: false,
228 is_inline: false,
227 is_general_delta: true,
229 is_general_delta: true,
228 version: 2,
230 version: 2,
229 offset: 0,
231 offset: 0,
230 compressed_len: 0,
232 compressed_len: 0,
231 uncompressed_len: 0,
233 uncompressed_len: 0,
232 base_revision: 0,
234 base_revision: 0,
233 }
235 }
234 }
236 }
235
237
236 pub fn is_first(&mut self, value: bool) -> &mut Self {
238 pub fn is_first(&mut self, value: bool) -> &mut Self {
237 self.is_first = value;
239 self.is_first = value;
238 self
240 self
239 }
241 }
240
242
241 pub fn with_inline(&mut self, value: bool) -> &mut Self {
243 pub fn with_inline(&mut self, value: bool) -> &mut Self {
242 self.is_inline = value;
244 self.is_inline = value;
243 self
245 self
244 }
246 }
245
247
246 pub fn with_general_delta(&mut self, value: bool) -> &mut Self {
248 pub fn with_general_delta(&mut self, value: bool) -> &mut Self {
247 self.is_general_delta = value;
249 self.is_general_delta = value;
248 self
250 self
249 }
251 }
250
252
251 pub fn with_version(&mut self, value: u16) -> &mut Self {
253 pub fn with_version(&mut self, value: u16) -> &mut Self {
252 self.version = value;
254 self.version = value;
253 self
255 self
254 }
256 }
255
257
256 pub fn with_offset(&mut self, value: usize) -> &mut Self {
258 pub fn with_offset(&mut self, value: usize) -> &mut Self {
257 self.offset = value;
259 self.offset = value;
258 self
260 self
259 }
261 }
260
262
261 pub fn with_compressed_len(&mut self, value: usize) -> &mut Self {
263 pub fn with_compressed_len(&mut self, value: usize) -> &mut Self {
262 self.compressed_len = value;
264 self.compressed_len = value;
263 self
265 self
264 }
266 }
265
267
266 pub fn with_uncompressed_len(&mut self, value: usize) -> &mut Self {
268 pub fn with_uncompressed_len(&mut self, value: usize) -> &mut Self {
267 self.uncompressed_len = value;
269 self.uncompressed_len = value;
268 self
270 self
269 }
271 }
270
272
271 pub fn with_base_revision(&mut self, value: Revision) -> &mut Self {
273 pub fn with_base_revision(&mut self, value: Revision) -> &mut Self {
272 self.base_revision = value;
274 self.base_revision = value;
273 self
275 self
274 }
276 }
275
277
276 pub fn build(&self) -> Vec<u8> {
278 pub fn build(&self) -> Vec<u8> {
277 let mut bytes = Vec::with_capacity(INDEX_ENTRY_SIZE);
279 let mut bytes = Vec::with_capacity(INDEX_ENTRY_SIZE);
278 if self.is_first {
280 if self.is_first {
279 bytes.extend(&match (self.is_general_delta, self.is_inline) {
281 bytes.extend(&match (self.is_general_delta, self.is_inline) {
280 (false, false) => [0u8, 0],
282 (false, false) => [0u8, 0],
281 (false, true) => [0u8, 1],
283 (false, true) => [0u8, 1],
282 (true, false) => [0u8, 2],
284 (true, false) => [0u8, 2],
283 (true, true) => [0u8, 3],
285 (true, true) => [0u8, 3],
284 });
286 });
285 bytes.extend(&self.version.to_be_bytes());
287 bytes.extend(&self.version.to_be_bytes());
286 // Remaining offset bytes.
288 // Remaining offset bytes.
287 bytes.extend(&[0u8; 2]);
289 bytes.extend(&[0u8; 2]);
288 } else {
290 } else {
289 // Offset is only 6 bytes will usize is 8.
291 // Offset is only 6 bytes will usize is 8.
290 bytes.extend(&self.offset.to_be_bytes()[2..]);
292 bytes.extend(&self.offset.to_be_bytes()[2..]);
291 }
293 }
292 bytes.extend(&[0u8; 2]); // Revision flags.
294 bytes.extend(&[0u8; 2]); // Revision flags.
293 bytes.extend(&self.compressed_len.to_be_bytes()[4..]);
295 bytes.extend(&self.compressed_len.to_be_bytes()[4..]);
294 bytes.extend(&self.uncompressed_len.to_be_bytes()[4..]);
296 bytes.extend(&self.uncompressed_len.to_be_bytes()[4..]);
295 bytes.extend(&self.base_revision.to_be_bytes());
297 bytes.extend(&self.base_revision.to_be_bytes());
296 bytes
298 bytes
297 }
299 }
298 }
300 }
299
301
300 #[test]
302 #[test]
301 fn is_not_inline_when_no_inline_flag_test() {
303 fn is_not_inline_when_no_inline_flag_test() {
302 let bytes = IndexEntryBuilder::new()
304 let bytes = IndexEntryBuilder::new()
303 .is_first(true)
305 .is_first(true)
304 .with_general_delta(false)
306 .with_general_delta(false)
305 .with_inline(false)
307 .with_inline(false)
306 .build();
308 .build();
307
309
308 assert_eq!(is_inline(&bytes), false)
310 assert_eq!(is_inline(&bytes), false)
309 }
311 }
310
312
311 #[test]
313 #[test]
312 fn is_inline_when_inline_flag_test() {
314 fn is_inline_when_inline_flag_test() {
313 let bytes = IndexEntryBuilder::new()
315 let bytes = IndexEntryBuilder::new()
314 .is_first(true)
316 .is_first(true)
315 .with_general_delta(false)
317 .with_general_delta(false)
316 .with_inline(true)
318 .with_inline(true)
317 .build();
319 .build();
318
320
319 assert_eq!(is_inline(&bytes), true)
321 assert_eq!(is_inline(&bytes), true)
320 }
322 }
321
323
322 #[test]
324 #[test]
323 fn is_inline_when_inline_and_generaldelta_flags_test() {
325 fn is_inline_when_inline_and_generaldelta_flags_test() {
324 let bytes = IndexEntryBuilder::new()
326 let bytes = IndexEntryBuilder::new()
325 .is_first(true)
327 .is_first(true)
326 .with_general_delta(true)
328 .with_general_delta(true)
327 .with_inline(true)
329 .with_inline(true)
328 .build();
330 .build();
329
331
330 assert_eq!(is_inline(&bytes), true)
332 assert_eq!(is_inline(&bytes), true)
331 }
333 }
332
334
333 #[test]
335 #[test]
334 fn test_offset() {
336 fn test_offset() {
335 let bytes = IndexEntryBuilder::new().with_offset(1).build();
337 let bytes = IndexEntryBuilder::new().with_offset(1).build();
336 let entry = IndexEntry {
338 let entry = IndexEntry {
337 bytes: &bytes,
339 bytes: &bytes,
338 offset_override: None,
340 offset_override: None,
339 };
341 };
340
342
341 assert_eq!(entry.offset(), 1)
343 assert_eq!(entry.offset(), 1)
342 }
344 }
343
345
344 #[test]
346 #[test]
345 fn test_with_overridden_offset() {
347 fn test_with_overridden_offset() {
346 let bytes = IndexEntryBuilder::new().with_offset(1).build();
348 let bytes = IndexEntryBuilder::new().with_offset(1).build();
347 let entry = IndexEntry {
349 let entry = IndexEntry {
348 bytes: &bytes,
350 bytes: &bytes,
349 offset_override: Some(2),
351 offset_override: Some(2),
350 };
352 };
351
353
352 assert_eq!(entry.offset(), 2)
354 assert_eq!(entry.offset(), 2)
353 }
355 }
354
356
355 #[test]
357 #[test]
356 fn test_compressed_len() {
358 fn test_compressed_len() {
357 let bytes = IndexEntryBuilder::new().with_compressed_len(1).build();
359 let bytes = IndexEntryBuilder::new().with_compressed_len(1).build();
358 let entry = IndexEntry {
360 let entry = IndexEntry {
359 bytes: &bytes,
361 bytes: &bytes,
360 offset_override: None,
362 offset_override: None,
361 };
363 };
362
364
363 assert_eq!(entry.compressed_len(), 1)
365 assert_eq!(entry.compressed_len(), 1)
364 }
366 }
365
367
366 #[test]
368 #[test]
367 fn test_uncompressed_len() {
369 fn test_uncompressed_len() {
368 let bytes = IndexEntryBuilder::new().with_uncompressed_len(1).build();
370 let bytes = IndexEntryBuilder::new().with_uncompressed_len(1).build();
369 let entry = IndexEntry {
371 let entry = IndexEntry {
370 bytes: &bytes,
372 bytes: &bytes,
371 offset_override: None,
373 offset_override: None,
372 };
374 };
373
375
374 assert_eq!(entry.uncompressed_len(), 1)
376 assert_eq!(entry.uncompressed_len(), 1)
375 }
377 }
376
378
377 #[test]
379 #[test]
378 fn test_base_revision() {
380 fn test_base_revision() {
379 let bytes = IndexEntryBuilder::new().with_base_revision(1).build();
381 let bytes = IndexEntryBuilder::new().with_base_revision(1).build();
380 let entry = IndexEntry {
382 let entry = IndexEntry {
381 bytes: &bytes,
383 bytes: &bytes,
382 offset_override: None,
384 offset_override: None,
383 };
385 };
384
386
385 assert_eq!(entry.base_revision(), 1)
387 assert_eq!(entry.base_revision(), 1)
386 }
388 }
387 }
389 }
388
390
389 #[cfg(test)]
391 #[cfg(test)]
390 pub use tests::IndexEntryBuilder;
392 pub use tests::IndexEntryBuilder;
@@ -1,73 +1,77 b''
1 use crate::revlog::revlog::{Revlog, RevlogError};
1 use crate::revlog::revlog::{Revlog, RevlogError};
2 use crate::revlog::NodePrefixRef;
2 use crate::revlog::Revision;
3 use crate::revlog::Revision;
3 use crate::utils::hg_path::HgPath;
4 use crate::utils::hg_path::HgPath;
4 use std::path::PathBuf;
5 use std::path::PathBuf;
5
6
6 /// A specialized `Revlog` to work with `manifest` data format.
7 /// A specialized `Revlog` to work with `manifest` data format.
7 pub struct Manifest {
8 pub struct Manifest {
8 /// The generic `revlog` format.
9 /// The generic `revlog` format.
9 revlog: Revlog,
10 revlog: Revlog,
10 }
11 }
11
12
12 impl Manifest {
13 impl Manifest {
13 /// Open the `manifest` of a repository given by its root.
14 /// Open the `manifest` of a repository given by its root.
14 pub fn open(root: &PathBuf) -> Result<Self, RevlogError> {
15 pub fn open(root: &PathBuf) -> Result<Self, RevlogError> {
15 let index_file = root.join(".hg/store/00manifest.i");
16 let index_file = root.join(".hg/store/00manifest.i");
16 let revlog = Revlog::open(&index_file, None)?;
17 let revlog = Revlog::open(&index_file, None)?;
17 Ok(Self { revlog })
18 Ok(Self { revlog })
18 }
19 }
19
20
20 /// Return the `ManifestEntry` of a given node id.
21 /// Return the `ManifestEntry` of a given node id.
21 pub fn get_node(&self, node: &[u8]) -> Result<ManifestEntry, RevlogError> {
22 pub fn get_node(
23 &self,
24 node: NodePrefixRef,
25 ) -> Result<ManifestEntry, RevlogError> {
22 let rev = self.revlog.get_node_rev(node)?;
26 let rev = self.revlog.get_node_rev(node)?;
23 self.get_rev(rev)
27 self.get_rev(rev)
24 }
28 }
25
29
26 /// Return the `ManifestEntry` of a given node revision.
30 /// Return the `ManifestEntry` of a given node revision.
27 pub fn get_rev(
31 pub fn get_rev(
28 &self,
32 &self,
29 rev: Revision,
33 rev: Revision,
30 ) -> Result<ManifestEntry, RevlogError> {
34 ) -> Result<ManifestEntry, RevlogError> {
31 let bytes = self.revlog.get_rev_data(rev)?;
35 let bytes = self.revlog.get_rev_data(rev)?;
32 Ok(ManifestEntry { bytes })
36 Ok(ManifestEntry { bytes })
33 }
37 }
34 }
38 }
35
39
36 /// `Manifest` entry which knows how to interpret the `manifest` data bytes.
40 /// `Manifest` entry which knows how to interpret the `manifest` data bytes.
37 #[derive(Debug)]
41 #[derive(Debug)]
38 pub struct ManifestEntry {
42 pub struct ManifestEntry {
39 bytes: Vec<u8>,
43 bytes: Vec<u8>,
40 }
44 }
41
45
42 impl ManifestEntry {
46 impl ManifestEntry {
43 /// Return an iterator over the lines of the entry.
47 /// Return an iterator over the lines of the entry.
44 pub fn lines(&self) -> impl Iterator<Item = &[u8]> {
48 pub fn lines(&self) -> impl Iterator<Item = &[u8]> {
45 self.bytes
49 self.bytes
46 .split(|b| b == &b'\n')
50 .split(|b| b == &b'\n')
47 .filter(|line| !line.is_empty())
51 .filter(|line| !line.is_empty())
48 }
52 }
49
53
50 /// Return an iterator over the files of the entry.
54 /// Return an iterator over the files of the entry.
51 pub fn files(&self) -> impl Iterator<Item = &HgPath> {
55 pub fn files(&self) -> impl Iterator<Item = &HgPath> {
52 self.lines().filter(|line| !line.is_empty()).map(|line| {
56 self.lines().filter(|line| !line.is_empty()).map(|line| {
53 let pos = line
57 let pos = line
54 .iter()
58 .iter()
55 .position(|x| x == &b'\0')
59 .position(|x| x == &b'\0')
56 .expect("manifest line should contain \\0");
60 .expect("manifest line should contain \\0");
57 HgPath::new(&line[..pos])
61 HgPath::new(&line[..pos])
58 })
62 })
59 }
63 }
60
64
61 /// Return an iterator over the files of the entry.
65 /// Return an iterator over the files of the entry.
62 pub fn files_with_nodes(&self) -> impl Iterator<Item = (&HgPath, &[u8])> {
66 pub fn files_with_nodes(&self) -> impl Iterator<Item = (&HgPath, &[u8])> {
63 self.lines().filter(|line| !line.is_empty()).map(|line| {
67 self.lines().filter(|line| !line.is_empty()).map(|line| {
64 let pos = line
68 let pos = line
65 .iter()
69 .iter()
66 .position(|x| x == &b'\0')
70 .position(|x| x == &b'\0')
67 .expect("manifest line should contain \\0");
71 .expect("manifest line should contain \\0");
68 let hash_start = pos + 1;
72 let hash_start = pos + 1;
69 let hash_end = hash_start + 40;
73 let hash_end = hash_start + 40;
70 (HgPath::new(&line[..pos]), &line[hash_start..hash_end])
74 (HgPath::new(&line[..pos]), &line[hash_start..hash_end])
71 })
75 })
72 }
76 }
73 }
77 }
@@ -1,438 +1,456 b''
1 // Copyright 2019-2020 Georges Racinet <georges.racinet@octobus.net>
1 // Copyright 2019-2020 Georges Racinet <georges.racinet@octobus.net>
2 //
2 //
3 // This software may be used and distributed according to the terms of the
3 // This software may be used and distributed according to the terms of the
4 // GNU General Public License version 2 or any later version.
4 // GNU General Public License version 2 or any later version.
5
5
6 //! Definitions and utilities for Revision nodes
6 //! Definitions and utilities for Revision nodes
7 //!
7 //!
8 //! In Mercurial code base, it is customary to call "a node" the binary SHA
8 //! In Mercurial code base, it is customary to call "a node" the binary SHA
9 //! of a revision.
9 //! of a revision.
10
10
11 use hex::{self, FromHex, FromHexError};
11 use hex::{self, FromHex, FromHexError};
12 use std::convert::{TryFrom, TryInto};
12
13
13 /// The length in bytes of a `Node`
14 /// The length in bytes of a `Node`
14 ///
15 ///
15 /// This constant is meant to ease refactors of this module, and
16 /// This constant is meant to ease refactors of this module, and
16 /// are private so that calling code does not expect all nodes have
17 /// are private so that calling code does not expect all nodes have
17 /// the same size, should we support several formats concurrently in
18 /// the same size, should we support several formats concurrently in
18 /// the future.
19 /// the future.
19 pub const NODE_BYTES_LENGTH: usize = 20;
20 pub const NODE_BYTES_LENGTH: usize = 20;
20
21
21 /// Id of the null node.
22 /// Id of the null node.
22 ///
23 ///
23 /// Used to indicate the absence of node.
24 /// Used to indicate the absence of node.
24 pub const NULL_NODE_ID: [u8; NODE_BYTES_LENGTH] = [0u8; NODE_BYTES_LENGTH];
25 pub const NULL_NODE_ID: [u8; NODE_BYTES_LENGTH] = [0u8; NODE_BYTES_LENGTH];
25
26
26 /// The length in bytes of a `Node`
27 /// The length in bytes of a `Node`
27 ///
28 ///
28 /// see also `NODES_BYTES_LENGTH` about it being private.
29 /// see also `NODES_BYTES_LENGTH` about it being private.
29 const NODE_NYBBLES_LENGTH: usize = 2 * NODE_BYTES_LENGTH;
30 const NODE_NYBBLES_LENGTH: usize = 2 * NODE_BYTES_LENGTH;
30
31
31 /// Private alias for readability and to ease future change
32 /// Private alias for readability and to ease future change
32 type NodeData = [u8; NODE_BYTES_LENGTH];
33 type NodeData = [u8; NODE_BYTES_LENGTH];
33
34
34 /// Binary revision SHA
35 /// Binary revision SHA
35 ///
36 ///
36 /// ## Future changes of hash size
37 /// ## Future changes of hash size
37 ///
38 ///
38 /// To accomodate future changes of hash size, Rust callers
39 /// To accomodate future changes of hash size, Rust callers
39 /// should use the conversion methods at the boundaries (FFI, actual
40 /// should use the conversion methods at the boundaries (FFI, actual
40 /// computation of hashes and I/O) only, and only if required.
41 /// computation of hashes and I/O) only, and only if required.
41 ///
42 ///
42 /// All other callers outside of unit tests should just handle `Node` values
43 /// All other callers outside of unit tests should just handle `Node` values
43 /// and never make any assumption on the actual length, using [`nybbles_len`]
44 /// and never make any assumption on the actual length, using [`nybbles_len`]
44 /// if they need a loop boundary.
45 /// if they need a loop boundary.
45 ///
46 ///
46 /// All methods that create a `Node` either take a type that enforces
47 /// All methods that create a `Node` either take a type that enforces
47 /// the size or fail immediately at runtime with [`ExactLengthRequired`].
48 /// the size or fail immediately at runtime with [`ExactLengthRequired`].
48 ///
49 ///
49 /// [`nybbles_len`]: #method.nybbles_len
50 /// [`nybbles_len`]: #method.nybbles_len
50 /// [`ExactLengthRequired`]: struct.NodeError#variant.ExactLengthRequired
51 /// [`ExactLengthRequired`]: struct.NodeError#variant.ExactLengthRequired
51 #[derive(Clone, Debug, PartialEq)]
52 #[derive(Clone, Debug, PartialEq)]
52 #[repr(transparent)]
53 #[repr(transparent)]
53 pub struct Node {
54 pub struct Node {
54 data: NodeData,
55 data: NodeData,
55 }
56 }
56
57
57 /// The node value for NULL_REVISION
58 /// The node value for NULL_REVISION
58 pub const NULL_NODE: Node = Node {
59 pub const NULL_NODE: Node = Node {
59 data: [0; NODE_BYTES_LENGTH],
60 data: [0; NODE_BYTES_LENGTH],
60 };
61 };
61
62
62 impl From<NodeData> for Node {
63 impl From<NodeData> for Node {
63 fn from(data: NodeData) -> Node {
64 fn from(data: NodeData) -> Node {
64 Node { data }
65 Node { data }
65 }
66 }
66 }
67 }
67
68
69 /// Return an error if the slice has an unexpected length
70 impl<'a> TryFrom<&'a [u8]> for &'a Node {
71 type Error = std::array::TryFromSliceError;
72
73 #[inline]
74 fn try_from(bytes: &'a [u8]) -> Result<&'a Node, Self::Error> {
75 let data = bytes.try_into()?;
76 // Safety: `#[repr(transparent)]` makes it ok to "wrap" the target
77 // of a reference to the type of the single field.
78 Ok(unsafe { std::mem::transmute::<&NodeData, &Node>(data) })
79 }
80 }
81
68 #[derive(Debug, PartialEq)]
82 #[derive(Debug, PartialEq)]
69 pub enum NodeError {
83 pub enum NodeError {
70 ExactLengthRequired(usize, String),
84 ExactLengthRequired(usize, String),
71 PrefixTooLong(String),
85 PrefixTooLong(String),
72 HexError(FromHexError, String),
86 HexError(FromHexError, String),
73 }
87 }
74
88
75 /// Low level utility function, also for prefixes
89 /// Low level utility function, also for prefixes
76 fn get_nybble(s: &[u8], i: usize) -> u8 {
90 fn get_nybble(s: &[u8], i: usize) -> u8 {
77 if i % 2 == 0 {
91 if i % 2 == 0 {
78 s[i / 2] >> 4
92 s[i / 2] >> 4
79 } else {
93 } else {
80 s[i / 2] & 0x0f
94 s[i / 2] & 0x0f
81 }
95 }
82 }
96 }
83
97
84 impl Node {
98 impl Node {
85 /// Retrieve the `i`th half-byte of the binary data.
99 /// Retrieve the `i`th half-byte of the binary data.
86 ///
100 ///
87 /// This is also the `i`th hexadecimal digit in numeric form,
101 /// This is also the `i`th hexadecimal digit in numeric form,
88 /// also called a [nybble](https://en.wikipedia.org/wiki/Nibble).
102 /// also called a [nybble](https://en.wikipedia.org/wiki/Nibble).
89 pub fn get_nybble(&self, i: usize) -> u8 {
103 pub fn get_nybble(&self, i: usize) -> u8 {
90 get_nybble(&self.data, i)
104 get_nybble(&self.data, i)
91 }
105 }
92
106
93 /// Length of the data, in nybbles
107 /// Length of the data, in nybbles
94 pub fn nybbles_len(&self) -> usize {
108 pub fn nybbles_len(&self) -> usize {
95 // public exposure as an instance method only, so that we can
109 // public exposure as an instance method only, so that we can
96 // easily support several sizes of hashes if needed in the future.
110 // easily support several sizes of hashes if needed in the future.
97 NODE_NYBBLES_LENGTH
111 NODE_NYBBLES_LENGTH
98 }
112 }
99
113
100 /// Convert from hexadecimal string representation
114 /// Convert from hexadecimal string representation
101 ///
115 ///
102 /// Exact length is required.
116 /// Exact length is required.
103 ///
117 ///
104 /// To be used in FFI and I/O only, in order to facilitate future
118 /// To be used in FFI and I/O only, in order to facilitate future
105 /// changes of hash format.
119 /// changes of hash format.
106 pub fn from_hex(hex: &str) -> Result<Node, NodeError> {
120 pub fn from_hex(hex: impl AsRef<[u8]>) -> Result<Node, NodeError> {
107 Ok(NodeData::from_hex(hex)
121 Ok(NodeData::from_hex(hex.as_ref())
108 .map_err(|e| NodeError::from((e, hex)))?
122 .map_err(|e| NodeError::from((e, hex)))?
109 .into())
123 .into())
110 }
124 }
111
125
112 /// Convert to hexadecimal string representation
126 /// Convert to hexadecimal string representation
113 ///
127 ///
114 /// To be used in FFI and I/O only, in order to facilitate future
128 /// To be used in FFI and I/O only, in order to facilitate future
115 /// changes of hash format.
129 /// changes of hash format.
116 pub fn encode_hex(&self) -> String {
130 pub fn encode_hex(&self) -> String {
117 hex::encode(self.data)
131 hex::encode(self.data)
118 }
132 }
119
133
120 /// Provide access to binary data
134 /// Provide access to binary data
121 ///
135 ///
122 /// This is needed by FFI layers, for instance to return expected
136 /// This is needed by FFI layers, for instance to return expected
123 /// binary values to Python.
137 /// binary values to Python.
124 pub fn as_bytes(&self) -> &[u8] {
138 pub fn as_bytes(&self) -> &[u8] {
125 &self.data
139 &self.data
126 }
140 }
127 }
141 }
128
142
129 impl<T: AsRef<str>> From<(FromHexError, T)> for NodeError {
143 impl<T: AsRef<[u8]>> From<(FromHexError, T)> for NodeError {
130 fn from(err_offender: (FromHexError, T)) -> Self {
144 fn from(err_offender: (FromHexError, T)) -> Self {
131 let (err, offender) = err_offender;
145 let (err, offender) = err_offender;
146 let offender = String::from_utf8_lossy(offender.as_ref()).into_owned();
132 match err {
147 match err {
133 FromHexError::InvalidStringLength => {
148 FromHexError::InvalidStringLength => {
134 NodeError::ExactLengthRequired(
149 NodeError::ExactLengthRequired(NODE_NYBBLES_LENGTH, offender)
135 NODE_NYBBLES_LENGTH,
136 offender.as_ref().to_owned(),
137 )
138 }
150 }
139 _ => NodeError::HexError(err, offender.as_ref().to_owned()),
151 _ => NodeError::HexError(err, offender),
140 }
152 }
141 }
153 }
142 }
154 }
143
155
144 /// The beginning of a binary revision SHA.
156 /// The beginning of a binary revision SHA.
145 ///
157 ///
146 /// Since it can potentially come from an hexadecimal representation with
158 /// Since it can potentially come from an hexadecimal representation with
147 /// odd length, it needs to carry around whether the last 4 bits are relevant
159 /// odd length, it needs to carry around whether the last 4 bits are relevant
148 /// or not.
160 /// or not.
149 #[derive(Debug, PartialEq)]
161 #[derive(Debug, PartialEq)]
150 pub struct NodePrefix {
162 pub struct NodePrefix {
151 buf: Vec<u8>,
163 buf: Vec<u8>,
152 is_odd: bool,
164 is_odd: bool,
153 }
165 }
154
166
155 impl NodePrefix {
167 impl NodePrefix {
156 /// Convert from hexadecimal string representation
168 /// Convert from hexadecimal string representation
157 ///
169 ///
158 /// Similarly to `hex::decode`, can be used with Unicode string types
170 /// Similarly to `hex::decode`, can be used with Unicode string types
159 /// (`String`, `&str`) as well as bytes.
171 /// (`String`, `&str`) as well as bytes.
160 ///
172 ///
161 /// To be used in FFI and I/O only, in order to facilitate future
173 /// To be used in FFI and I/O only, in order to facilitate future
162 /// changes of hash format.
174 /// changes of hash format.
163 pub fn from_hex(hex: impl AsRef<[u8]>) -> Result<Self, NodeError> {
175 pub fn from_hex(hex: impl AsRef<[u8]>) -> Result<Self, NodeError> {
164 let hex = hex.as_ref();
176 let hex = hex.as_ref();
165 let len = hex.len();
177 let len = hex.len();
166 if len > NODE_NYBBLES_LENGTH {
178 if len > NODE_NYBBLES_LENGTH {
167 return Err(NodeError::PrefixTooLong(
179 return Err(NodeError::PrefixTooLong(
168 String::from_utf8_lossy(hex).to_owned().to_string(),
180 String::from_utf8_lossy(hex).to_owned().to_string(),
169 ));
181 ));
170 }
182 }
171
183
172 let is_odd = len % 2 == 1;
184 let is_odd = len % 2 == 1;
173 let even_part = if is_odd { &hex[..len - 1] } else { hex };
185 let even_part = if is_odd { &hex[..len - 1] } else { hex };
174 let mut buf: Vec<u8> = Vec::from_hex(&even_part)
186 let mut buf: Vec<u8> =
175 .map_err(|e| (e, String::from_utf8_lossy(hex)))?;
187 Vec::from_hex(&even_part).map_err(|e| (e, hex))?;
176
188
177 if is_odd {
189 if is_odd {
178 let latest_char = char::from(hex[len - 1]);
190 let latest_char = char::from(hex[len - 1]);
179 let latest_nybble = latest_char.to_digit(16).ok_or_else(|| {
191 let latest_nybble = latest_char.to_digit(16).ok_or_else(|| {
180 (
192 (
181 FromHexError::InvalidHexCharacter {
193 FromHexError::InvalidHexCharacter {
182 c: latest_char,
194 c: latest_char,
183 index: len - 1,
195 index: len - 1,
184 },
196 },
185 String::from_utf8_lossy(hex),
197 hex,
186 )
198 )
187 })? as u8;
199 })? as u8;
188 buf.push(latest_nybble << 4);
200 buf.push(latest_nybble << 4);
189 }
201 }
190 Ok(NodePrefix { buf, is_odd })
202 Ok(NodePrefix { buf, is_odd })
191 }
203 }
192
204
193 pub fn borrow(&self) -> NodePrefixRef {
205 pub fn borrow(&self) -> NodePrefixRef {
194 NodePrefixRef {
206 NodePrefixRef {
195 buf: &self.buf,
207 buf: &self.buf,
196 is_odd: self.is_odd,
208 is_odd: self.is_odd,
197 }
209 }
198 }
210 }
199 }
211 }
200
212
201 #[derive(Clone, Debug, PartialEq)]
213 #[derive(Clone, Debug, PartialEq)]
202 pub struct NodePrefixRef<'a> {
214 pub struct NodePrefixRef<'a> {
203 buf: &'a [u8],
215 buf: &'a [u8],
204 is_odd: bool,
216 is_odd: bool,
205 }
217 }
206
218
207 impl<'a> NodePrefixRef<'a> {
219 impl<'a> NodePrefixRef<'a> {
208 pub fn len(&self) -> usize {
220 pub fn len(&self) -> usize {
209 if self.is_odd {
221 if self.is_odd {
210 self.buf.len() * 2 - 1
222 self.buf.len() * 2 - 1
211 } else {
223 } else {
212 self.buf.len() * 2
224 self.buf.len() * 2
213 }
225 }
214 }
226 }
215
227
216 pub fn is_empty(&self) -> bool {
228 pub fn is_empty(&self) -> bool {
217 self.len() == 0
229 self.len() == 0
218 }
230 }
219
231
220 pub fn is_prefix_of(&self, node: &Node) -> bool {
232 pub fn is_prefix_of(&self, node: &Node) -> bool {
221 if self.is_odd {
233 if self.is_odd {
222 let buf = self.buf;
234 let buf = self.buf;
223 let last_pos = buf.len() - 1;
235 let last_pos = buf.len() - 1;
224 node.data.starts_with(buf.split_at(last_pos).0)
236 node.data.starts_with(buf.split_at(last_pos).0)
225 && node.data[last_pos] >> 4 == buf[last_pos] >> 4
237 && node.data[last_pos] >> 4 == buf[last_pos] >> 4
226 } else {
238 } else {
227 node.data.starts_with(self.buf)
239 node.data.starts_with(self.buf)
228 }
240 }
229 }
241 }
230
242
231 /// Retrieve the `i`th half-byte from the prefix.
243 /// Retrieve the `i`th half-byte from the prefix.
232 ///
244 ///
233 /// This is also the `i`th hexadecimal digit in numeric form,
245 /// This is also the `i`th hexadecimal digit in numeric form,
234 /// also called a [nybble](https://en.wikipedia.org/wiki/Nibble).
246 /// also called a [nybble](https://en.wikipedia.org/wiki/Nibble).
235 pub fn get_nybble(&self, i: usize) -> u8 {
247 pub fn get_nybble(&self, i: usize) -> u8 {
236 assert!(i < self.len());
248 assert!(i < self.len());
237 get_nybble(self.buf, i)
249 get_nybble(self.buf, i)
238 }
250 }
239
251
240 /// Return the index first nybble that's different from `node`
252 /// Return the index first nybble that's different from `node`
241 ///
253 ///
242 /// If the return value is `None` that means that `self` is
254 /// If the return value is `None` that means that `self` is
243 /// a prefix of `node`, but the current method is a bit slower
255 /// a prefix of `node`, but the current method is a bit slower
244 /// than `is_prefix_of`.
256 /// than `is_prefix_of`.
245 ///
257 ///
246 /// Returned index is as in `get_nybble`, i.e., starting at 0.
258 /// Returned index is as in `get_nybble`, i.e., starting at 0.
247 pub fn first_different_nybble(&self, node: &Node) -> Option<usize> {
259 pub fn first_different_nybble(&self, node: &Node) -> Option<usize> {
248 let buf = self.buf;
260 let buf = self.buf;
249 let until = if self.is_odd {
261 let until = if self.is_odd {
250 buf.len() - 1
262 buf.len() - 1
251 } else {
263 } else {
252 buf.len()
264 buf.len()
253 };
265 };
254 for (i, item) in buf.iter().enumerate().take(until) {
266 for (i, item) in buf.iter().enumerate().take(until) {
255 if *item != node.data[i] {
267 if *item != node.data[i] {
256 return if *item & 0xf0 == node.data[i] & 0xf0 {
268 return if *item & 0xf0 == node.data[i] & 0xf0 {
257 Some(2 * i + 1)
269 Some(2 * i + 1)
258 } else {
270 } else {
259 Some(2 * i)
271 Some(2 * i)
260 };
272 };
261 }
273 }
262 }
274 }
263 if self.is_odd && buf[until] & 0xf0 != node.data[until] & 0xf0 {
275 if self.is_odd && buf[until] & 0xf0 != node.data[until] & 0xf0 {
264 Some(until * 2)
276 Some(until * 2)
265 } else {
277 } else {
266 None
278 None
267 }
279 }
268 }
280 }
269 }
281 }
270
282
271 /// A shortcut for full `Node` references
283 /// A shortcut for full `Node` references
272 impl<'a> From<&'a Node> for NodePrefixRef<'a> {
284 impl<'a> From<&'a Node> for NodePrefixRef<'a> {
273 fn from(node: &'a Node) -> Self {
285 fn from(node: &'a Node) -> Self {
274 NodePrefixRef {
286 NodePrefixRef {
275 buf: &node.data,
287 buf: &node.data,
276 is_odd: false,
288 is_odd: false,
277 }
289 }
278 }
290 }
279 }
291 }
280
292
293 impl PartialEq<Node> for NodePrefixRef<'_> {
294 fn eq(&self, other: &Node) -> bool {
295 !self.is_odd && self.buf == other.data
296 }
297 }
298
281 #[cfg(test)]
299 #[cfg(test)]
282 mod tests {
300 mod tests {
283 use super::*;
301 use super::*;
284
302
285 fn sample_node() -> Node {
303 fn sample_node() -> Node {
286 let mut data = [0; NODE_BYTES_LENGTH];
304 let mut data = [0; NODE_BYTES_LENGTH];
287 data.copy_from_slice(&[
305 data.copy_from_slice(&[
288 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xfe, 0xdc, 0xba,
306 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xfe, 0xdc, 0xba,
289 0x98, 0x76, 0x54, 0x32, 0x10, 0xde, 0xad, 0xbe, 0xef,
307 0x98, 0x76, 0x54, 0x32, 0x10, 0xde, 0xad, 0xbe, 0xef,
290 ]);
308 ]);
291 data.into()
309 data.into()
292 }
310 }
293
311
294 /// Pad an hexadecimal string to reach `NODE_NYBBLES_LENGTH`
312 /// Pad an hexadecimal string to reach `NODE_NYBBLES_LENGTH`
295 ///
313 ///check_hash
296 /// The padding is made with zeros
314 /// The padding is made with zeros
297 pub fn hex_pad_right(hex: &str) -> String {
315 pub fn hex_pad_right(hex: &str) -> String {
298 let mut res = hex.to_string();
316 let mut res = hex.to_string();
299 while res.len() < NODE_NYBBLES_LENGTH {
317 while res.len() < NODE_NYBBLES_LENGTH {
300 res.push('0');
318 res.push('0');
301 }
319 }
302 res
320 res
303 }
321 }
304
322
305 fn sample_node_hex() -> String {
323 fn sample_node_hex() -> String {
306 hex_pad_right("0123456789abcdeffedcba9876543210deadbeef")
324 hex_pad_right("0123456789abcdeffedcba9876543210deadbeef")
307 }
325 }
308
326
309 #[test]
327 #[test]
310 fn test_node_from_hex() {
328 fn test_node_from_hex() {
311 assert_eq!(Node::from_hex(&sample_node_hex()), Ok(sample_node()));
329 assert_eq!(Node::from_hex(&sample_node_hex()), Ok(sample_node()));
312
330
313 let mut short = hex_pad_right("0123");
331 let mut short = hex_pad_right("0123");
314 short.pop();
332 short.pop();
315 short.pop();
333 short.pop();
316 assert_eq!(
334 assert_eq!(
317 Node::from_hex(&short),
335 Node::from_hex(&short),
318 Err(NodeError::ExactLengthRequired(NODE_NYBBLES_LENGTH, short)),
336 Err(NodeError::ExactLengthRequired(NODE_NYBBLES_LENGTH, short)),
319 );
337 );
320
338
321 let not_hex = hex_pad_right("012... oops");
339 let not_hex = hex_pad_right("012... oops");
322 assert_eq!(
340 assert_eq!(
323 Node::from_hex(&not_hex),
341 Node::from_hex(&not_hex),
324 Err(NodeError::HexError(
342 Err(NodeError::HexError(
325 FromHexError::InvalidHexCharacter { c: '.', index: 3 },
343 FromHexError::InvalidHexCharacter { c: '.', index: 3 },
326 not_hex,
344 not_hex,
327 )),
345 )),
328 );
346 );
329 }
347 }
330
348
331 #[test]
349 #[test]
332 fn test_node_encode_hex() {
350 fn test_node_encode_hex() {
333 assert_eq!(sample_node().encode_hex(), sample_node_hex());
351 assert_eq!(sample_node().encode_hex(), sample_node_hex());
334 }
352 }
335
353
336 #[test]
354 #[test]
337 fn test_prefix_from_hex() -> Result<(), NodeError> {
355 fn test_prefix_from_hex() -> Result<(), NodeError> {
338 assert_eq!(
356 assert_eq!(
339 NodePrefix::from_hex("0e1")?,
357 NodePrefix::from_hex("0e1")?,
340 NodePrefix {
358 NodePrefix {
341 buf: vec![14, 16],
359 buf: vec![14, 16],
342 is_odd: true
360 is_odd: true
343 }
361 }
344 );
362 );
345 assert_eq!(
363 assert_eq!(
346 NodePrefix::from_hex("0e1a")?,
364 NodePrefix::from_hex("0e1a")?,
347 NodePrefix {
365 NodePrefix {
348 buf: vec![14, 26],
366 buf: vec![14, 26],
349 is_odd: false
367 is_odd: false
350 }
368 }
351 );
369 );
352
370
353 // checking limit case
371 // checking limit case
354 let node_as_vec = sample_node().data.iter().cloned().collect();
372 let node_as_vec = sample_node().data.iter().cloned().collect();
355 assert_eq!(
373 assert_eq!(
356 NodePrefix::from_hex(sample_node_hex())?,
374 NodePrefix::from_hex(sample_node_hex())?,
357 NodePrefix {
375 NodePrefix {
358 buf: node_as_vec,
376 buf: node_as_vec,
359 is_odd: false
377 is_odd: false
360 }
378 }
361 );
379 );
362
380
363 Ok(())
381 Ok(())
364 }
382 }
365
383
366 #[test]
384 #[test]
367 fn test_prefix_from_hex_errors() {
385 fn test_prefix_from_hex_errors() {
368 assert_eq!(
386 assert_eq!(
369 NodePrefix::from_hex("testgr"),
387 NodePrefix::from_hex("testgr"),
370 Err(NodeError::HexError(
388 Err(NodeError::HexError(
371 FromHexError::InvalidHexCharacter { c: 't', index: 0 },
389 FromHexError::InvalidHexCharacter { c: 't', index: 0 },
372 "testgr".to_string()
390 "testgr".to_string()
373 ))
391 ))
374 );
392 );
375 let mut long = NULL_NODE.encode_hex();
393 let mut long = NULL_NODE.encode_hex();
376 long.push('c');
394 long.push('c');
377 match NodePrefix::from_hex(&long)
395 match NodePrefix::from_hex(&long)
378 .expect_err("should be refused as too long")
396 .expect_err("should be refused as too long")
379 {
397 {
380 NodeError::PrefixTooLong(s) => assert_eq!(s, long),
398 NodeError::PrefixTooLong(s) => assert_eq!(s, long),
381 err => panic!(format!("Should have been TooLong, got {:?}", err)),
399 err => panic!(format!("Should have been TooLong, got {:?}", err)),
382 }
400 }
383 }
401 }
384
402
385 #[test]
403 #[test]
386 fn test_is_prefix_of() -> Result<(), NodeError> {
404 fn test_is_prefix_of() -> Result<(), NodeError> {
387 let mut node_data = [0; NODE_BYTES_LENGTH];
405 let mut node_data = [0; NODE_BYTES_LENGTH];
388 node_data[0] = 0x12;
406 node_data[0] = 0x12;
389 node_data[1] = 0xca;
407 node_data[1] = 0xca;
390 let node = Node::from(node_data);
408 let node = Node::from(node_data);
391 assert!(NodePrefix::from_hex("12")?.borrow().is_prefix_of(&node));
409 assert!(NodePrefix::from_hex("12")?.borrow().is_prefix_of(&node));
392 assert!(!NodePrefix::from_hex("1a")?.borrow().is_prefix_of(&node));
410 assert!(!NodePrefix::from_hex("1a")?.borrow().is_prefix_of(&node));
393 assert!(NodePrefix::from_hex("12c")?.borrow().is_prefix_of(&node));
411 assert!(NodePrefix::from_hex("12c")?.borrow().is_prefix_of(&node));
394 assert!(!NodePrefix::from_hex("12d")?.borrow().is_prefix_of(&node));
412 assert!(!NodePrefix::from_hex("12d")?.borrow().is_prefix_of(&node));
395 Ok(())
413 Ok(())
396 }
414 }
397
415
398 #[test]
416 #[test]
399 fn test_get_nybble() -> Result<(), NodeError> {
417 fn test_get_nybble() -> Result<(), NodeError> {
400 let prefix = NodePrefix::from_hex("dead6789cafe")?;
418 let prefix = NodePrefix::from_hex("dead6789cafe")?;
401 assert_eq!(prefix.borrow().get_nybble(0), 13);
419 assert_eq!(prefix.borrow().get_nybble(0), 13);
402 assert_eq!(prefix.borrow().get_nybble(7), 9);
420 assert_eq!(prefix.borrow().get_nybble(7), 9);
403 Ok(())
421 Ok(())
404 }
422 }
405
423
406 #[test]
424 #[test]
407 fn test_first_different_nybble_even_prefix() {
425 fn test_first_different_nybble_even_prefix() {
408 let prefix = NodePrefix::from_hex("12ca").unwrap();
426 let prefix = NodePrefix::from_hex("12ca").unwrap();
409 let prefref = prefix.borrow();
427 let prefref = prefix.borrow();
410 let mut node = Node::from([0; NODE_BYTES_LENGTH]);
428 let mut node = Node::from([0; NODE_BYTES_LENGTH]);
411 assert_eq!(prefref.first_different_nybble(&node), Some(0));
429 assert_eq!(prefref.first_different_nybble(&node), Some(0));
412 node.data[0] = 0x13;
430 node.data[0] = 0x13;
413 assert_eq!(prefref.first_different_nybble(&node), Some(1));
431 assert_eq!(prefref.first_different_nybble(&node), Some(1));
414 node.data[0] = 0x12;
432 node.data[0] = 0x12;
415 assert_eq!(prefref.first_different_nybble(&node), Some(2));
433 assert_eq!(prefref.first_different_nybble(&node), Some(2));
416 node.data[1] = 0xca;
434 node.data[1] = 0xca;
417 // now it is a prefix
435 // now it is a prefix
418 assert_eq!(prefref.first_different_nybble(&node), None);
436 assert_eq!(prefref.first_different_nybble(&node), None);
419 }
437 }
420
438
421 #[test]
439 #[test]
422 fn test_first_different_nybble_odd_prefix() {
440 fn test_first_different_nybble_odd_prefix() {
423 let prefix = NodePrefix::from_hex("12c").unwrap();
441 let prefix = NodePrefix::from_hex("12c").unwrap();
424 let prefref = prefix.borrow();
442 let prefref = prefix.borrow();
425 let mut node = Node::from([0; NODE_BYTES_LENGTH]);
443 let mut node = Node::from([0; NODE_BYTES_LENGTH]);
426 assert_eq!(prefref.first_different_nybble(&node), Some(0));
444 assert_eq!(prefref.first_different_nybble(&node), Some(0));
427 node.data[0] = 0x13;
445 node.data[0] = 0x13;
428 assert_eq!(prefref.first_different_nybble(&node), Some(1));
446 assert_eq!(prefref.first_different_nybble(&node), Some(1));
429 node.data[0] = 0x12;
447 node.data[0] = 0x12;
430 assert_eq!(prefref.first_different_nybble(&node), Some(2));
448 assert_eq!(prefref.first_different_nybble(&node), Some(2));
431 node.data[1] = 0xca;
449 node.data[1] = 0xca;
432 // now it is a prefix
450 // now it is a prefix
433 assert_eq!(prefref.first_different_nybble(&node), None);
451 assert_eq!(prefref.first_different_nybble(&node), None);
434 }
452 }
435 }
453 }
436
454
437 #[cfg(test)]
455 #[cfg(test)]
438 pub use tests::hex_pad_right;
456 pub use tests::hex_pad_right;
@@ -1,344 +1,347 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::{NodePrefixRef, NODE_BYTES_LENGTH, NULL_NODE};
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
24 /// Found more than one entry whose ID match the requested prefix
25 AmbiguousPrefix,
25 AmbiguousPrefix,
26 Corrupted,
26 Corrupted,
27 UnknowDataFormat(u8),
27 UnknowDataFormat(u8),
28 }
28 }
29
29
30 fn mmap_open(path: &Path) -> Result<Mmap, std::io::Error> {
30 fn mmap_open(path: &Path) -> Result<Mmap, std::io::Error> {
31 let file = File::open(path)?;
31 let file = File::open(path)?;
32 let mmap = unsafe { MmapOptions::new().map(&file) }?;
32 let mmap = unsafe { MmapOptions::new().map(&file) }?;
33 Ok(mmap)
33 Ok(mmap)
34 }
34 }
35
35
36 /// Read only implementation of revlog.
36 /// Read only implementation of revlog.
37 pub struct Revlog {
37 pub struct Revlog {
38 /// 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.
39 /// 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
40 /// data.
40 /// data.
41 index: Index,
41 index: Index,
42 /// 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
43 data_bytes: Option<Box<dyn Deref<Target = [u8]> + Send>>,
43 data_bytes: Option<Box<dyn Deref<Target = [u8]> + Send>>,
44 }
44 }
45
45
46 impl Revlog {
46 impl Revlog {
47 /// Open a revlog index file.
47 /// Open a revlog index file.
48 ///
48 ///
49 /// 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
50 /// interleaved.
50 /// interleaved.
51 #[timed]
51 #[timed]
52 pub fn open(
52 pub fn open(
53 index_path: &Path,
53 index_path: &Path,
54 data_path: Option<&Path>,
54 data_path: Option<&Path>,
55 ) -> Result<Self, RevlogError> {
55 ) -> Result<Self, RevlogError> {
56 let index_mmap =
56 let index_mmap =
57 mmap_open(&index_path).map_err(RevlogError::IoError)?;
57 mmap_open(&index_path).map_err(RevlogError::IoError)?;
58
58
59 let version = get_version(&index_mmap);
59 let version = get_version(&index_mmap);
60 if version != 1 {
60 if version != 1 {
61 return Err(RevlogError::UnsuportedVersion(version));
61 return Err(RevlogError::UnsuportedVersion(version));
62 }
62 }
63
63
64 let index = Index::new(Box::new(index_mmap))?;
64 let index = Index::new(Box::new(index_mmap))?;
65
65
66 let default_data_path = index_path.with_extension("d");
66 let default_data_path = index_path.with_extension("d");
67
67
68 // type annotation required
68 // type annotation required
69 // won't recognize Mmap as Deref<Target = [u8]>
69 // won't recognize Mmap as Deref<Target = [u8]>
70 let data_bytes: Option<Box<dyn Deref<Target = [u8]> + Send>> =
70 let data_bytes: Option<Box<dyn Deref<Target = [u8]> + Send>> =
71 if index.is_inline() {
71 if index.is_inline() {
72 None
72 None
73 } else {
73 } else {
74 let data_path = data_path.unwrap_or(&default_data_path);
74 let data_path = data_path.unwrap_or(&default_data_path);
75 let data_mmap =
75 let data_mmap =
76 mmap_open(data_path).map_err(RevlogError::IoError)?;
76 mmap_open(data_path).map_err(RevlogError::IoError)?;
77 Some(Box::new(data_mmap))
77 Some(Box::new(data_mmap))
78 };
78 };
79
79
80 Ok(Revlog { index, data_bytes })
80 Ok(Revlog { index, data_bytes })
81 }
81 }
82
82
83 /// Return number of entries of the `Revlog`.
83 /// Return number of entries of the `Revlog`.
84 pub fn len(&self) -> usize {
84 pub fn len(&self) -> usize {
85 self.index.len()
85 self.index.len()
86 }
86 }
87
87
88 /// Returns `true` if the `Revlog` has zero `entries`.
88 /// Returns `true` if the `Revlog` has zero `entries`.
89 pub fn is_empty(&self) -> bool {
89 pub fn is_empty(&self) -> bool {
90 self.index.is_empty()
90 self.index.is_empty()
91 }
91 }
92
92
93 /// Return the full data associated to a node.
93 /// Return the full data associated to a node.
94 #[timed]
94 #[timed]
95 pub fn get_node_rev(&self, node: &[u8]) -> Result<Revision, RevlogError> {
95 pub fn get_node_rev(
96 &self,
97 node: NodePrefixRef,
98 ) -> Result<Revision, RevlogError> {
96 // This is brute force. But it is fast enough for now.
99 // This is brute force. But it is fast enough for now.
97 // Optimization will come later.
100 // Optimization will come later.
98 let mut found_by_prefix = None;
101 let mut found_by_prefix = None;
99 for rev in (0..self.len() as Revision).rev() {
102 for rev in (0..self.len() as Revision).rev() {
100 let index_entry =
103 let index_entry =
101 self.index.get_entry(rev).ok_or(RevlogError::Corrupted)?;
104 self.index.get_entry(rev).ok_or(RevlogError::Corrupted)?;
102 if index_entry.hash() == node {
105 if node == *index_entry.hash() {
103 return Ok(rev);
106 return Ok(rev);
104 }
107 }
105 if index_entry.hash().starts_with(node) {
108 if node.is_prefix_of(index_entry.hash()) {
106 if found_by_prefix.is_some() {
109 if found_by_prefix.is_some() {
107 return Err(RevlogError::AmbiguousPrefix);
110 return Err(RevlogError::AmbiguousPrefix);
108 }
111 }
109 found_by_prefix = Some(rev)
112 found_by_prefix = Some(rev)
110 }
113 }
111 }
114 }
112 found_by_prefix.ok_or(RevlogError::InvalidRevision)
115 found_by_prefix.ok_or(RevlogError::InvalidRevision)
113 }
116 }
114
117
115 /// Return the full data associated to a revision.
118 /// Return the full data associated to a revision.
116 ///
119 ///
117 /// All entries required to build the final data out of deltas will be
120 /// All entries required to build the final data out of deltas will be
118 /// retrieved as needed, and the deltas will be applied to the inital
121 /// retrieved as needed, and the deltas will be applied to the inital
119 /// snapshot to rebuild the final data.
122 /// snapshot to rebuild the final data.
120 #[timed]
123 #[timed]
121 pub fn get_rev_data(&self, rev: Revision) -> Result<Vec<u8>, RevlogError> {
124 pub fn get_rev_data(&self, rev: Revision) -> Result<Vec<u8>, RevlogError> {
122 // Todo return -> Cow
125 // Todo return -> Cow
123 let mut entry = self.get_entry(rev)?;
126 let mut entry = self.get_entry(rev)?;
124 let mut delta_chain = vec![];
127 let mut delta_chain = vec![];
125 while let Some(base_rev) = entry.base_rev {
128 while let Some(base_rev) = entry.base_rev {
126 delta_chain.push(entry);
129 delta_chain.push(entry);
127 entry =
130 entry =
128 self.get_entry(base_rev).or(Err(RevlogError::Corrupted))?;
131 self.get_entry(base_rev).or(Err(RevlogError::Corrupted))?;
129 }
132 }
130
133
131 // TODO do not look twice in the index
134 // TODO do not look twice in the index
132 let index_entry = self
135 let index_entry = self
133 .index
136 .index
134 .get_entry(rev)
137 .get_entry(rev)
135 .ok_or(RevlogError::InvalidRevision)?;
138 .ok_or(RevlogError::InvalidRevision)?;
136
139
137 let data: Vec<u8> = if delta_chain.is_empty() {
140 let data: Vec<u8> = if delta_chain.is_empty() {
138 entry.data()?.into()
141 entry.data()?.into()
139 } else {
142 } else {
140 Revlog::build_data_from_deltas(entry, &delta_chain)?
143 Revlog::build_data_from_deltas(entry, &delta_chain)?
141 };
144 };
142
145
143 if self.check_hash(
146 if self.check_hash(
144 index_entry.p1(),
147 index_entry.p1(),
145 index_entry.p2(),
148 index_entry.p2(),
146 index_entry.hash(),
149 index_entry.hash().as_bytes(),
147 &data,
150 &data,
148 ) {
151 ) {
149 Ok(data)
152 Ok(data)
150 } else {
153 } else {
151 Err(RevlogError::Corrupted)
154 Err(RevlogError::Corrupted)
152 }
155 }
153 }
156 }
154
157
155 /// Check the hash of some given data against the recorded hash.
158 /// Check the hash of some given data against the recorded hash.
156 pub fn check_hash(
159 pub fn check_hash(
157 &self,
160 &self,
158 p1: Revision,
161 p1: Revision,
159 p2: Revision,
162 p2: Revision,
160 expected: &[u8],
163 expected: &[u8],
161 data: &[u8],
164 data: &[u8],
162 ) -> bool {
165 ) -> bool {
163 let e1 = self.index.get_entry(p1);
166 let e1 = self.index.get_entry(p1);
164 let h1 = match e1 {
167 let h1 = match e1 {
165 Some(ref entry) => entry.hash(),
168 Some(ref entry) => entry.hash(),
166 None => &NULL_NODE_ID,
169 None => &NULL_NODE,
167 };
170 };
168 let e2 = self.index.get_entry(p2);
171 let e2 = self.index.get_entry(p2);
169 let h2 = match e2 {
172 let h2 = match e2 {
170 Some(ref entry) => entry.hash(),
173 Some(ref entry) => entry.hash(),
171 None => &NULL_NODE_ID,
174 None => &NULL_NODE,
172 };
175 };
173
176
174 hash(data, &h1, &h2).as_slice() == expected
177 hash(data, h1.as_bytes(), h2.as_bytes()).as_slice() == expected
175 }
178 }
176
179
177 /// Build the full data of a revision out its snapshot
180 /// Build the full data of a revision out its snapshot
178 /// and its deltas.
181 /// and its deltas.
179 #[timed]
182 #[timed]
180 fn build_data_from_deltas(
183 fn build_data_from_deltas(
181 snapshot: RevlogEntry,
184 snapshot: RevlogEntry,
182 deltas: &[RevlogEntry],
185 deltas: &[RevlogEntry],
183 ) -> Result<Vec<u8>, RevlogError> {
186 ) -> Result<Vec<u8>, RevlogError> {
184 let snapshot = snapshot.data()?;
187 let snapshot = snapshot.data()?;
185 let deltas = deltas
188 let deltas = deltas
186 .iter()
189 .iter()
187 .rev()
190 .rev()
188 .map(RevlogEntry::data)
191 .map(RevlogEntry::data)
189 .collect::<Result<Vec<Cow<'_, [u8]>>, RevlogError>>()?;
192 .collect::<Result<Vec<Cow<'_, [u8]>>, RevlogError>>()?;
190 let patches: Vec<_> =
193 let patches: Vec<_> =
191 deltas.iter().map(|d| patch::PatchList::new(d)).collect();
194 deltas.iter().map(|d| patch::PatchList::new(d)).collect();
192 let patch = patch::fold_patch_lists(&patches);
195 let patch = patch::fold_patch_lists(&patches);
193 Ok(patch.apply(&snapshot))
196 Ok(patch.apply(&snapshot))
194 }
197 }
195
198
196 /// Return the revlog data.
199 /// Return the revlog data.
197 fn data(&self) -> &[u8] {
200 fn data(&self) -> &[u8] {
198 match self.data_bytes {
201 match self.data_bytes {
199 Some(ref data_bytes) => &data_bytes,
202 Some(ref data_bytes) => &data_bytes,
200 None => panic!(
203 None => panic!(
201 "forgot to load the data or trying to access inline data"
204 "forgot to load the data or trying to access inline data"
202 ),
205 ),
203 }
206 }
204 }
207 }
205
208
206 /// Get an entry of the revlog.
209 /// Get an entry of the revlog.
207 fn get_entry(&self, rev: Revision) -> Result<RevlogEntry, RevlogError> {
210 fn get_entry(&self, rev: Revision) -> Result<RevlogEntry, RevlogError> {
208 let index_entry = self
211 let index_entry = self
209 .index
212 .index
210 .get_entry(rev)
213 .get_entry(rev)
211 .ok_or(RevlogError::InvalidRevision)?;
214 .ok_or(RevlogError::InvalidRevision)?;
212 let start = index_entry.offset();
215 let start = index_entry.offset();
213 let end = start + index_entry.compressed_len();
216 let end = start + index_entry.compressed_len();
214 let data = if self.index.is_inline() {
217 let data = if self.index.is_inline() {
215 self.index.data(start, end)
218 self.index.data(start, end)
216 } else {
219 } else {
217 &self.data()[start..end]
220 &self.data()[start..end]
218 };
221 };
219 let entry = RevlogEntry {
222 let entry = RevlogEntry {
220 rev,
223 rev,
221 bytes: data,
224 bytes: data,
222 compressed_len: index_entry.compressed_len(),
225 compressed_len: index_entry.compressed_len(),
223 uncompressed_len: index_entry.uncompressed_len(),
226 uncompressed_len: index_entry.uncompressed_len(),
224 base_rev: if index_entry.base_revision() == rev {
227 base_rev: if index_entry.base_revision() == rev {
225 None
228 None
226 } else {
229 } else {
227 Some(index_entry.base_revision())
230 Some(index_entry.base_revision())
228 },
231 },
229 };
232 };
230 Ok(entry)
233 Ok(entry)
231 }
234 }
232 }
235 }
233
236
234 /// The revlog entry's bytes and the necessary informations to extract
237 /// The revlog entry's bytes and the necessary informations to extract
235 /// the entry's data.
238 /// the entry's data.
236 #[derive(Debug)]
239 #[derive(Debug)]
237 pub struct RevlogEntry<'a> {
240 pub struct RevlogEntry<'a> {
238 rev: Revision,
241 rev: Revision,
239 bytes: &'a [u8],
242 bytes: &'a [u8],
240 compressed_len: usize,
243 compressed_len: usize,
241 uncompressed_len: usize,
244 uncompressed_len: usize,
242 base_rev: Option<Revision>,
245 base_rev: Option<Revision>,
243 }
246 }
244
247
245 impl<'a> RevlogEntry<'a> {
248 impl<'a> RevlogEntry<'a> {
246 /// Extract the data contained in the entry.
249 /// Extract the data contained in the entry.
247 pub fn data(&self) -> Result<Cow<'_, [u8]>, RevlogError> {
250 pub fn data(&self) -> Result<Cow<'_, [u8]>, RevlogError> {
248 if self.bytes.is_empty() {
251 if self.bytes.is_empty() {
249 return Ok(Cow::Borrowed(&[]));
252 return Ok(Cow::Borrowed(&[]));
250 }
253 }
251 match self.bytes[0] {
254 match self.bytes[0] {
252 // Revision data is the entirety of the entry, including this
255 // Revision data is the entirety of the entry, including this
253 // header.
256 // header.
254 b'\0' => Ok(Cow::Borrowed(self.bytes)),
257 b'\0' => Ok(Cow::Borrowed(self.bytes)),
255 // Raw revision data follows.
258 // Raw revision data follows.
256 b'u' => Ok(Cow::Borrowed(&self.bytes[1..])),
259 b'u' => Ok(Cow::Borrowed(&self.bytes[1..])),
257 // zlib (RFC 1950) data.
260 // zlib (RFC 1950) data.
258 b'x' => Ok(Cow::Owned(self.uncompressed_zlib_data()?)),
261 b'x' => Ok(Cow::Owned(self.uncompressed_zlib_data()?)),
259 // zstd data.
262 // zstd data.
260 b'\x28' => Ok(Cow::Owned(self.uncompressed_zstd_data()?)),
263 b'\x28' => Ok(Cow::Owned(self.uncompressed_zstd_data()?)),
261 format_type => Err(RevlogError::UnknowDataFormat(format_type)),
264 format_type => Err(RevlogError::UnknowDataFormat(format_type)),
262 }
265 }
263 }
266 }
264
267
265 fn uncompressed_zlib_data(&self) -> Result<Vec<u8>, RevlogError> {
268 fn uncompressed_zlib_data(&self) -> Result<Vec<u8>, RevlogError> {
266 let mut decoder = ZlibDecoder::new(self.bytes);
269 let mut decoder = ZlibDecoder::new(self.bytes);
267 if self.is_delta() {
270 if self.is_delta() {
268 let mut buf = Vec::with_capacity(self.compressed_len);
271 let mut buf = Vec::with_capacity(self.compressed_len);
269 decoder
272 decoder
270 .read_to_end(&mut buf)
273 .read_to_end(&mut buf)
271 .or(Err(RevlogError::Corrupted))?;
274 .or(Err(RevlogError::Corrupted))?;
272 Ok(buf)
275 Ok(buf)
273 } else {
276 } else {
274 let mut buf = vec![0; self.uncompressed_len];
277 let mut buf = vec![0; self.uncompressed_len];
275 decoder
278 decoder
276 .read_exact(&mut buf)
279 .read_exact(&mut buf)
277 .or(Err(RevlogError::Corrupted))?;
280 .or(Err(RevlogError::Corrupted))?;
278 Ok(buf)
281 Ok(buf)
279 }
282 }
280 }
283 }
281
284
282 fn uncompressed_zstd_data(&self) -> Result<Vec<u8>, RevlogError> {
285 fn uncompressed_zstd_data(&self) -> Result<Vec<u8>, RevlogError> {
283 if self.is_delta() {
286 if self.is_delta() {
284 let mut buf = Vec::with_capacity(self.compressed_len);
287 let mut buf = Vec::with_capacity(self.compressed_len);
285 zstd::stream::copy_decode(self.bytes, &mut buf)
288 zstd::stream::copy_decode(self.bytes, &mut buf)
286 .or(Err(RevlogError::Corrupted))?;
289 .or(Err(RevlogError::Corrupted))?;
287 Ok(buf)
290 Ok(buf)
288 } else {
291 } else {
289 let mut buf = vec![0; self.uncompressed_len];
292 let mut buf = vec![0; self.uncompressed_len];
290 let len = zstd::block::decompress_to_buffer(self.bytes, &mut buf)
293 let len = zstd::block::decompress_to_buffer(self.bytes, &mut buf)
291 .or(Err(RevlogError::Corrupted))?;
294 .or(Err(RevlogError::Corrupted))?;
292 if len != self.uncompressed_len {
295 if len != self.uncompressed_len {
293 Err(RevlogError::Corrupted)
296 Err(RevlogError::Corrupted)
294 } else {
297 } else {
295 Ok(buf)
298 Ok(buf)
296 }
299 }
297 }
300 }
298 }
301 }
299
302
300 /// Tell if the entry is a snapshot or a delta
303 /// Tell if the entry is a snapshot or a delta
301 /// (influences on decompression).
304 /// (influences on decompression).
302 fn is_delta(&self) -> bool {
305 fn is_delta(&self) -> bool {
303 self.base_rev.is_some()
306 self.base_rev.is_some()
304 }
307 }
305 }
308 }
306
309
307 /// Format version of the revlog.
310 /// Format version of the revlog.
308 pub fn get_version(index_bytes: &[u8]) -> u16 {
311 pub fn get_version(index_bytes: &[u8]) -> u16 {
309 BigEndian::read_u16(&index_bytes[2..=3])
312 BigEndian::read_u16(&index_bytes[2..=3])
310 }
313 }
311
314
312 /// Calculate the hash of a revision given its data and its parents.
315 /// Calculate the hash of a revision given its data and its parents.
313 fn hash(data: &[u8], p1_hash: &[u8], p2_hash: &[u8]) -> Vec<u8> {
316 fn hash(data: &[u8], p1_hash: &[u8], p2_hash: &[u8]) -> Vec<u8> {
314 let mut hasher = Sha1::new();
317 let mut hasher = Sha1::new();
315 let (a, b) = (p1_hash, p2_hash);
318 let (a, b) = (p1_hash, p2_hash);
316 if a > b {
319 if a > b {
317 hasher.input(b);
320 hasher.input(b);
318 hasher.input(a);
321 hasher.input(a);
319 } else {
322 } else {
320 hasher.input(a);
323 hasher.input(a);
321 hasher.input(b);
324 hasher.input(b);
322 }
325 }
323 hasher.input(data);
326 hasher.input(data);
324 let mut hash = vec![0; NODE_BYTES_LENGTH];
327 let mut hash = vec![0; NODE_BYTES_LENGTH];
325 hasher.result(&mut hash);
328 hasher.result(&mut hash);
326 hash
329 hash
327 }
330 }
328
331
329 #[cfg(test)]
332 #[cfg(test)]
330 mod tests {
333 mod tests {
331 use super::*;
334 use super::*;
332
335
333 use super::super::index::IndexEntryBuilder;
336 use super::super::index::IndexEntryBuilder;
334
337
335 #[test]
338 #[test]
336 fn version_test() {
339 fn version_test() {
337 let bytes = IndexEntryBuilder::new()
340 let bytes = IndexEntryBuilder::new()
338 .is_first(true)
341 .is_first(true)
339 .with_version(1)
342 .with_version(1)
340 .build();
343 .build();
341
344
342 assert_eq!(get_version(&bytes), 1)
345 assert_eq!(get_version(&bytes), 1)
343 }
346 }
344 }
347 }
@@ -1,165 +1,167 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 4 5 6; do
77 $ for i in 1 2 3 4 5 6; do
78 > echo $i >> file-$i
78 > echo $i >> file-$i
79 > hg add file-$i
79 > hg add file-$i
80 > hg commit -m "Commit $i" -q
80 > hg commit -m "Commit $i" -q
81 > done
81 > done
82 $ rhg debugdata -c 2
82 $ rhg debugdata -c 2
83 8d0267cb034247ebfa5ee58ce59e22e57a492297
83 8d0267cb034247ebfa5ee58ce59e22e57a492297
84 test
84 test
85 0 0
85 0 0
86 file-3
86 file-3
87
87
88 Commit 3 (no-eol)
88 Commit 3 (no-eol)
89 $ rhg debugdata -m 2
89 $ rhg debugdata -m 2
90 file-1\x00b8e02f6433738021a065f94175c7cd23db5f05be (esc)
90 file-1\x00b8e02f6433738021a065f94175c7cd23db5f05be (esc)
91 file-2\x005d9299349fc01ddd25d0070d149b124d8f10411e (esc)
91 file-2\x005d9299349fc01ddd25d0070d149b124d8f10411e (esc)
92 file-3\x002661d26c649684b482d10f91960cc3db683c38b4 (esc)
92 file-3\x002661d26c649684b482d10f91960cc3db683c38b4 (esc)
93
93
94 Debuging with full node id
94 Debuging with full node id
95 $ rhg debugdata -c `hg log -r 0 -T '{node}'`
95 $ rhg debugdata -c `hg log -r 0 -T '{node}'`
96 d1d1c679d3053e8926061b6f45ca52009f011e3f
96 d1d1c679d3053e8926061b6f45ca52009f011e3f
97 test
97 test
98 0 0
98 0 0
99 file-1
99 file-1
100
100
101 Commit 1 (no-eol)
101 Commit 1 (no-eol)
102
102
103 Specifying revisions by changeset ID
103 Specifying revisions by changeset ID
104 $ hg log -T '{node}\n'
104 $ hg log -T '{node}\n'
105 c6ad58c44207b6ff8a4fbbca7045a5edaa7e908b
105 c6ad58c44207b6ff8a4fbbca7045a5edaa7e908b
106 d654274993d0149eecc3cc03214f598320211900
106 d654274993d0149eecc3cc03214f598320211900
107 f646af7e96481d3a5470b695cf30ad8e3ab6c575
107 f646af7e96481d3a5470b695cf30ad8e3ab6c575
108 cf8b83f14ead62b374b6e91a0e9303b85dfd9ed7
108 cf8b83f14ead62b374b6e91a0e9303b85dfd9ed7
109 91c6f6e73e39318534dc415ea4e8a09c99cd74d6
109 91c6f6e73e39318534dc415ea4e8a09c99cd74d6
110 6ae9681c6d30389694d8701faf24b583cf3ccafe
110 6ae9681c6d30389694d8701faf24b583cf3ccafe
111 $ rhg files -r cf8b83
111 $ rhg files -r cf8b83
112 file-1
112 file-1
113 file-2
113 file-2
114 file-3
114 file-3
115 $ rhg cat -r cf8b83 file-2
115 $ rhg cat -r cf8b83 file-2
116 2
116 2
117 $ rhg cat -r c file-2
117 $ rhg cat -r c file-2
118 abort: invalid revision identifier c
118 abort: ambiguous revision identifier c
119 [255]
119 [255]
120 $ rhg cat -r d file-2
121 2
120
122
121 Cat files
123 Cat files
122 $ cd $TESTTMP
124 $ cd $TESTTMP
123 $ rm -rf repository
125 $ rm -rf repository
124 $ hg init repository
126 $ hg init repository
125 $ cd repository
127 $ cd repository
126 $ echo "original content" > original
128 $ echo "original content" > original
127 $ hg add original
129 $ hg add original
128 $ hg commit -m "add original" original
130 $ hg commit -m "add original" original
129 $ rhg cat -r 0 original
131 $ rhg cat -r 0 original
130 original content
132 original content
131 Cat copied file should not display copy metadata
133 Cat copied file should not display copy metadata
132 $ hg copy original copy_of_original
134 $ hg copy original copy_of_original
133 $ hg commit -m "add copy of original"
135 $ hg commit -m "add copy of original"
134 $ rhg cat -r 1 copy_of_original
136 $ rhg cat -r 1 copy_of_original
135 original content
137 original content
136
138
137 Requirements
139 Requirements
138 $ rhg debugrequirements
140 $ rhg debugrequirements
139 dotencode
141 dotencode
140 fncache
142 fncache
141 generaldelta
143 generaldelta
142 revlogv1
144 revlogv1
143 sparserevlog
145 sparserevlog
144 store
146 store
145
147
146 $ echo indoor-pool >> .hg/requires
148 $ echo indoor-pool >> .hg/requires
147 $ rhg files
149 $ rhg files
148 [252]
150 [252]
149
151
150 $ rhg cat -r 1 copy_of_original
152 $ rhg cat -r 1 copy_of_original
151 [252]
153 [252]
152
154
153 $ rhg debugrequirements
155 $ rhg debugrequirements
154 dotencode
156 dotencode
155 fncache
157 fncache
156 generaldelta
158 generaldelta
157 revlogv1
159 revlogv1
158 sparserevlog
160 sparserevlog
159 store
161 store
160 indoor-pool
162 indoor-pool
161
163
162 $ echo -e '\xFF' >> .hg/requires
164 $ echo -e '\xFF' >> .hg/requires
163 $ rhg debugrequirements
165 $ rhg debugrequirements
164 abort: .hg/requires is corrupted
166 abort: .hg/requires is corrupted
165 [255]
167 [255]
General Comments 0
You need to be logged in to leave comments. Login now