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