Show More
@@ -1,145 +1,158 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::PathBuf; |
|
9 | use std::path::PathBuf; | |
10 |
|
10 | |||
11 | use crate::revlog::changelog::Changelog; |
|
11 | use crate::revlog::changelog::Changelog; | |
12 | use crate::revlog::manifest::{Manifest, ManifestEntry}; |
|
12 | use crate::revlog::manifest::{Manifest, ManifestEntry}; | |
13 | use crate::revlog::path_encode::path_encode; |
|
13 | use crate::revlog::path_encode::path_encode; | |
14 | use crate::revlog::revlog::Revlog; |
|
14 | use crate::revlog::revlog::Revlog; | |
15 | use crate::revlog::revlog::RevlogError; |
|
15 | use crate::revlog::revlog::RevlogError; | |
16 | use crate::revlog::Revision; |
|
16 | use crate::revlog::Revision; | |
17 | use crate::utils::hg_path::HgPathBuf; |
|
17 | use crate::utils::hg_path::HgPathBuf; | |
18 |
|
18 | |||
|
19 | const METADATA_DELIMITER: [u8; 2] = [b'\x01', b'\n']; | |||
|
20 | ||||
19 | /// Kind of error encountered by `CatRev` |
|
21 | /// Kind of error encountered by `CatRev` | |
20 | #[derive(Debug)] |
|
22 | #[derive(Debug)] | |
21 | pub enum CatRevErrorKind { |
|
23 | pub enum CatRevErrorKind { | |
22 | /// Error when reading a `revlog` file. |
|
24 | /// Error when reading a `revlog` file. | |
23 | IoError(std::io::Error), |
|
25 | IoError(std::io::Error), | |
24 | /// The revision has not been found. |
|
26 | /// The revision has not been found. | |
25 | InvalidRevision, |
|
27 | InvalidRevision, | |
26 | /// A `revlog` file is corrupted. |
|
28 | /// A `revlog` file is corrupted. | |
27 | CorruptedRevlog, |
|
29 | CorruptedRevlog, | |
28 | /// The `revlog` format version is not supported. |
|
30 | /// The `revlog` format version is not supported. | |
29 | UnsuportedRevlogVersion(u16), |
|
31 | UnsuportedRevlogVersion(u16), | |
30 | /// The `revlog` data format is not supported. |
|
32 | /// The `revlog` data format is not supported. | |
31 | UnknowRevlogDataFormat(u8), |
|
33 | UnknowRevlogDataFormat(u8), | |
32 | } |
|
34 | } | |
33 |
|
35 | |||
34 | /// A `CatRev` error |
|
36 | /// A `CatRev` error | |
35 | #[derive(Debug)] |
|
37 | #[derive(Debug)] | |
36 | pub struct CatRevError { |
|
38 | pub struct CatRevError { | |
37 | /// Kind of error encountered by `CatRev` |
|
39 | /// Kind of error encountered by `CatRev` | |
38 | pub kind: CatRevErrorKind, |
|
40 | pub kind: CatRevErrorKind, | |
39 | } |
|
41 | } | |
40 |
|
42 | |||
41 | impl From<CatRevErrorKind> for CatRevError { |
|
43 | impl From<CatRevErrorKind> for CatRevError { | |
42 | fn from(kind: CatRevErrorKind) -> Self { |
|
44 | fn from(kind: CatRevErrorKind) -> Self { | |
43 | CatRevError { kind } |
|
45 | CatRevError { kind } | |
44 | } |
|
46 | } | |
45 | } |
|
47 | } | |
46 |
|
48 | |||
47 | impl From<RevlogError> for CatRevError { |
|
49 | impl From<RevlogError> for CatRevError { | |
48 | fn from(err: RevlogError) -> Self { |
|
50 | fn from(err: RevlogError) -> Self { | |
49 | match err { |
|
51 | match err { | |
50 | RevlogError::IoError(err) => CatRevErrorKind::IoError(err), |
|
52 | RevlogError::IoError(err) => CatRevErrorKind::IoError(err), | |
51 | RevlogError::UnsuportedVersion(version) => { |
|
53 | RevlogError::UnsuportedVersion(version) => { | |
52 | CatRevErrorKind::UnsuportedRevlogVersion(version) |
|
54 | CatRevErrorKind::UnsuportedRevlogVersion(version) | |
53 | } |
|
55 | } | |
54 | RevlogError::InvalidRevision => CatRevErrorKind::InvalidRevision, |
|
56 | RevlogError::InvalidRevision => CatRevErrorKind::InvalidRevision, | |
55 | RevlogError::Corrupted => CatRevErrorKind::CorruptedRevlog, |
|
57 | RevlogError::Corrupted => CatRevErrorKind::CorruptedRevlog, | |
56 | RevlogError::UnknowDataFormat(format) => { |
|
58 | RevlogError::UnknowDataFormat(format) => { | |
57 | CatRevErrorKind::UnknowRevlogDataFormat(format) |
|
59 | CatRevErrorKind::UnknowRevlogDataFormat(format) | |
58 | } |
|
60 | } | |
59 | } |
|
61 | } | |
60 | .into() |
|
62 | .into() | |
61 | } |
|
63 | } | |
62 | } |
|
64 | } | |
63 |
|
65 | |||
64 | /// List files under Mercurial control at a given revision. |
|
66 | /// List files under Mercurial control at a given revision. | |
65 | pub struct CatRev<'a> { |
|
67 | pub struct CatRev<'a> { | |
66 | root: &'a PathBuf, |
|
68 | root: &'a PathBuf, | |
67 | /// The revision to cat the files from. |
|
69 | /// The revision to cat the files from. | |
68 | rev: &'a str, |
|
70 | rev: &'a str, | |
69 | /// The files to output. |
|
71 | /// The files to output. | |
70 | files: &'a [HgPathBuf], |
|
72 | files: &'a [HgPathBuf], | |
71 | /// The changelog file |
|
73 | /// The changelog file | |
72 | changelog: Changelog, |
|
74 | changelog: Changelog, | |
73 | /// The manifest file |
|
75 | /// The manifest file | |
74 | manifest: Manifest, |
|
76 | manifest: Manifest, | |
75 | /// The manifest entry corresponding to the revision. |
|
77 | /// The manifest entry corresponding to the revision. | |
76 | /// |
|
78 | /// | |
77 | /// Used to hold the owner of the returned references. |
|
79 | /// Used to hold the owner of the returned references. | |
78 | manifest_entry: Option<ManifestEntry>, |
|
80 | manifest_entry: Option<ManifestEntry>, | |
79 | } |
|
81 | } | |
80 |
|
82 | |||
81 | impl<'a> CatRev<'a> { |
|
83 | impl<'a> CatRev<'a> { | |
82 | pub fn new( |
|
84 | pub fn new( | |
83 | root: &'a PathBuf, |
|
85 | root: &'a PathBuf, | |
84 | rev: &'a str, |
|
86 | rev: &'a str, | |
85 | files: &'a [HgPathBuf], |
|
87 | files: &'a [HgPathBuf], | |
86 | ) -> Result<Self, CatRevError> { |
|
88 | ) -> Result<Self, CatRevError> { | |
87 | let changelog = Changelog::open(&root)?; |
|
89 | let changelog = Changelog::open(&root)?; | |
88 | let manifest = Manifest::open(&root)?; |
|
90 | let manifest = Manifest::open(&root)?; | |
89 | let manifest_entry = None; |
|
91 | let manifest_entry = None; | |
90 |
|
92 | |||
91 | Ok(Self { |
|
93 | Ok(Self { | |
92 | root, |
|
94 | root, | |
93 | rev, |
|
95 | rev, | |
94 | files, |
|
96 | files, | |
95 | changelog, |
|
97 | changelog, | |
96 | manifest, |
|
98 | manifest, | |
97 | manifest_entry, |
|
99 | manifest_entry, | |
98 | }) |
|
100 | }) | |
99 | } |
|
101 | } | |
100 |
|
102 | |||
101 | pub fn run(&mut self) -> Result<Vec<u8>, CatRevError> { |
|
103 | pub fn run(&mut self) -> Result<Vec<u8>, CatRevError> { | |
102 | let changelog_entry = match self.rev.parse::<Revision>() { |
|
104 | let changelog_entry = match self.rev.parse::<Revision>() { | |
103 | Ok(rev) => self.changelog.get_rev(rev)?, |
|
105 | Ok(rev) => self.changelog.get_rev(rev)?, | |
104 | _ => { |
|
106 | _ => { | |
105 | let changelog_node = hex::decode(&self.rev) |
|
107 | let changelog_node = hex::decode(&self.rev) | |
106 | .map_err(|_| CatRevErrorKind::InvalidRevision)?; |
|
108 | .map_err(|_| CatRevErrorKind::InvalidRevision)?; | |
107 | self.changelog.get_node(&changelog_node)? |
|
109 | self.changelog.get_node(&changelog_node)? | |
108 | } |
|
110 | } | |
109 | }; |
|
111 | }; | |
110 | let manifest_node = hex::decode(&changelog_entry.manifest_node()?) |
|
112 | let manifest_node = hex::decode(&changelog_entry.manifest_node()?) | |
111 | .map_err(|_| CatRevErrorKind::CorruptedRevlog)?; |
|
113 | .map_err(|_| CatRevErrorKind::CorruptedRevlog)?; | |
112 |
|
114 | |||
113 | self.manifest_entry = Some(self.manifest.get_node(&manifest_node)?); |
|
115 | self.manifest_entry = Some(self.manifest.get_node(&manifest_node)?); | |
114 | if let Some(ref manifest_entry) = self.manifest_entry { |
|
116 | if let Some(ref manifest_entry) = self.manifest_entry { | |
115 | let mut bytes = vec![]; |
|
117 | let mut bytes = vec![]; | |
116 |
|
118 | |||
117 | for (manifest_file, node_bytes) in |
|
119 | for (manifest_file, node_bytes) in | |
118 | manifest_entry.files_with_nodes() |
|
120 | manifest_entry.files_with_nodes() | |
119 | { |
|
121 | { | |
120 | for cat_file in self.files.iter() { |
|
122 | for cat_file in self.files.iter() { | |
121 | if cat_file.as_bytes() == manifest_file.as_bytes() { |
|
123 | if cat_file.as_bytes() == manifest_file.as_bytes() { | |
122 | let encoded_bytes = |
|
124 | let encoded_bytes = | |
123 | path_encode(manifest_file.as_bytes()); |
|
125 | path_encode(manifest_file.as_bytes()); | |
124 | let revlog_index_string = format!( |
|
126 | let revlog_index_string = format!( | |
125 | ".hg/store/data/{}.i", |
|
127 | ".hg/store/data/{}.i", | |
126 | String::from_utf8_lossy(&encoded_bytes), |
|
128 | String::from_utf8_lossy(&encoded_bytes), | |
127 | ); |
|
129 | ); | |
128 | let revlog_index_path = |
|
130 | let revlog_index_path = | |
129 | self.root.join(&revlog_index_string); |
|
131 | self.root.join(&revlog_index_string); | |
130 | let file_log = Revlog::open(&revlog_index_path)?; |
|
132 | let file_log = Revlog::open(&revlog_index_path)?; | |
131 | let file_node = hex::decode(&node_bytes) |
|
133 | let file_node = hex::decode(&node_bytes) | |
132 | .map_err(|_| CatRevErrorKind::CorruptedRevlog)?; |
|
134 | .map_err(|_| CatRevErrorKind::CorruptedRevlog)?; | |
133 | let file_rev = file_log.get_node_rev(&file_node)?; |
|
135 | let file_rev = file_log.get_node_rev(&file_node)?; | |
134 | let data = file_log.get_rev_data(file_rev)?; |
|
136 | let data = file_log.get_rev_data(file_rev)?; | |
|
137 | if data.starts_with(&METADATA_DELIMITER) { | |||
|
138 | let end_delimiter_position = data | |||
|
139 | [METADATA_DELIMITER.len()..] | |||
|
140 | .windows(METADATA_DELIMITER.len()) | |||
|
141 | .position(|bytes| bytes == METADATA_DELIMITER); | |||
|
142 | if let Some(position) = end_delimiter_position { | |||
|
143 | let offset = METADATA_DELIMITER.len() * 2; | |||
|
144 | bytes.extend(data[position + offset..].iter()); | |||
|
145 | } | |||
|
146 | } else { | |||
135 | bytes.extend(data); |
|
147 | bytes.extend(data); | |
136 | } |
|
148 | } | |
137 | } |
|
149 | } | |
138 | } |
|
150 | } | |
|
151 | } | |||
139 |
|
152 | |||
140 | Ok(bytes) |
|
153 | Ok(bytes) | |
141 | } else { |
|
154 | } else { | |
142 | unreachable!("manifest_entry should have been stored"); |
|
155 | unreachable!("manifest_entry should have been stored"); | |
143 | } |
|
156 | } | |
144 | } |
|
157 | } | |
145 | } |
|
158 | } |
@@ -1,92 +1,108 b'' | |||||
1 | #require rust |
|
1 | #require rust | |
2 |
|
2 | |||
3 | Define an rhg function that will only run if rhg exists |
|
3 | Define an rhg function that will only run if rhg exists | |
4 | $ rhg() { |
|
4 | $ rhg() { | |
5 | > if [ -f "$RUNTESTDIR/../rust/target/debug/rhg" ]; then |
|
5 | > if [ -f "$RUNTESTDIR/../rust/target/debug/rhg" ]; then | |
6 | > "$RUNTESTDIR/../rust/target/debug/rhg" "$@" |
|
6 | > "$RUNTESTDIR/../rust/target/debug/rhg" "$@" | |
7 | > else |
|
7 | > else | |
8 | > echo "skipped: Cannot find rhg. Try to run cargo build in rust/rhg." |
|
8 | > echo "skipped: Cannot find rhg. Try to run cargo build in rust/rhg." | |
9 | > exit 80 |
|
9 | > exit 80 | |
10 | > fi |
|
10 | > fi | |
11 | > } |
|
11 | > } | |
12 |
|
12 | |||
13 | Unimplemented command |
|
13 | Unimplemented command | |
14 | $ rhg unimplemented-command |
|
14 | $ rhg unimplemented-command | |
15 | error: Found argument 'unimplemented-command' which wasn't expected, or isn't valid in this context |
|
15 | error: Found argument 'unimplemented-command' which wasn't expected, or isn't valid in this context | |
16 |
|
16 | |||
17 | USAGE: |
|
17 | USAGE: | |
18 | rhg <SUBCOMMAND> |
|
18 | rhg <SUBCOMMAND> | |
19 |
|
19 | |||
20 | For more information try --help |
|
20 | For more information try --help | |
21 | [252] |
|
21 | [252] | |
22 |
|
22 | |||
23 | Finding root |
|
23 | Finding root | |
24 | $ rhg root |
|
24 | $ rhg root | |
25 | abort: no repository found in '$TESTTMP' (.hg not found)! |
|
25 | abort: no repository found in '$TESTTMP' (.hg not found)! | |
26 | [255] |
|
26 | [255] | |
27 |
|
27 | |||
28 | $ hg init repository |
|
28 | $ hg init repository | |
29 | $ cd repository |
|
29 | $ cd repository | |
30 | $ rhg root |
|
30 | $ rhg root | |
31 | $TESTTMP/repository |
|
31 | $TESTTMP/repository | |
32 |
|
32 | |||
33 | Unwritable file descriptor |
|
33 | Unwritable file descriptor | |
34 | $ rhg root > /dev/full |
|
34 | $ rhg root > /dev/full | |
35 | abort: No space left on device (os error 28) |
|
35 | abort: No space left on device (os error 28) | |
36 | [255] |
|
36 | [255] | |
37 |
|
37 | |||
38 | Deleted repository |
|
38 | Deleted repository | |
39 | $ rm -rf `pwd` |
|
39 | $ rm -rf `pwd` | |
40 | $ rhg root |
|
40 | $ rhg root | |
41 | abort: error getting current working directory: $ENOENT$ |
|
41 | abort: error getting current working directory: $ENOENT$ | |
42 | [255] |
|
42 | [255] | |
43 |
|
43 | |||
44 | Listing tracked files |
|
44 | Listing tracked files | |
45 | $ cd $TESTTMP |
|
45 | $ cd $TESTTMP | |
46 | $ hg init repository |
|
46 | $ hg init repository | |
47 | $ cd repository |
|
47 | $ cd repository | |
48 | $ for i in 1 2 3; do |
|
48 | $ for i in 1 2 3; do | |
49 | > echo $i >> file$i |
|
49 | > echo $i >> file$i | |
50 | > hg add file$i |
|
50 | > hg add file$i | |
51 | > done |
|
51 | > done | |
52 | > hg commit -m "commit $i" -q |
|
52 | > hg commit -m "commit $i" -q | |
53 |
|
53 | |||
54 | Listing tracked files from root |
|
54 | Listing tracked files from root | |
55 | $ rhg files |
|
55 | $ rhg files | |
56 | file1 |
|
56 | file1 | |
57 | file2 |
|
57 | file2 | |
58 | file3 |
|
58 | file3 | |
59 |
|
59 | |||
60 | Listing tracked files from subdirectory |
|
60 | Listing tracked files from subdirectory | |
61 | $ mkdir -p path/to/directory |
|
61 | $ mkdir -p path/to/directory | |
62 | $ cd path/to/directory |
|
62 | $ cd path/to/directory | |
63 | $ rhg files |
|
63 | $ rhg files | |
64 | ../../../file1 |
|
64 | ../../../file1 | |
65 | ../../../file2 |
|
65 | ../../../file2 | |
66 | ../../../file3 |
|
66 | ../../../file3 | |
67 |
|
67 | |||
68 | Listing tracked files through broken pipe |
|
68 | Listing tracked files through broken pipe | |
69 | $ rhg files | head -n 1 |
|
69 | $ rhg files | head -n 1 | |
70 | ../../../file1 |
|
70 | ../../../file1 | |
71 |
|
71 | |||
72 | Debuging data in inline index |
|
72 | Debuging data in inline index | |
73 | $ cd $TESTTMP |
|
73 | $ cd $TESTTMP | |
74 | $ rm -rf repository |
|
74 | $ rm -rf repository | |
75 | $ hg init repository |
|
75 | $ hg init repository | |
76 | $ cd repository |
|
76 | $ cd repository | |
77 | $ for i in 1 2 3; do |
|
77 | $ for i in 1 2 3; do | |
78 | > echo $i >> file$i |
|
78 | > echo $i >> file$i | |
79 | > hg add file$i |
|
79 | > hg add file$i | |
80 | > hg commit -m "commit $i" -q |
|
80 | > hg commit -m "commit $i" -q | |
81 | > done |
|
81 | > done | |
82 | $ rhg debugdata -c 2 |
|
82 | $ rhg debugdata -c 2 | |
83 | e36fa63d37a576b27a69057598351db6ee5746bd |
|
83 | e36fa63d37a576b27a69057598351db6ee5746bd | |
84 | test |
|
84 | test | |
85 | 0 0 |
|
85 | 0 0 | |
86 | file3 |
|
86 | file3 | |
87 |
|
87 | |||
88 | commit 3 (no-eol) |
|
88 | commit 3 (no-eol) | |
89 | $ rhg debugdata -m 2 |
|
89 | $ rhg debugdata -m 2 | |
90 | file1\x00b8e02f6433738021a065f94175c7cd23db5f05be (esc) |
|
90 | file1\x00b8e02f6433738021a065f94175c7cd23db5f05be (esc) | |
91 | file2\x005d9299349fc01ddd25d0070d149b124d8f10411e (esc) |
|
91 | file2\x005d9299349fc01ddd25d0070d149b124d8f10411e (esc) | |
92 | file3\x002661d26c649684b482d10f91960cc3db683c38b4 (esc) |
|
92 | file3\x002661d26c649684b482d10f91960cc3db683c38b4 (esc) | |
|
93 | ||||
|
94 | Cat files | |||
|
95 | $ cd $TESTTMP | |||
|
96 | $ rm -rf repository | |||
|
97 | $ hg init repository | |||
|
98 | $ cd repository | |||
|
99 | $ echo "original content" > original | |||
|
100 | $ hg add original | |||
|
101 | $ hg commit -m "add original" original | |||
|
102 | $ rhg cat -r 0 original | |||
|
103 | original content | |||
|
104 | Cat copied file should not display copy metadata | |||
|
105 | $ hg copy original copy_of_original | |||
|
106 | $ hg commit -m "add copy of original" | |||
|
107 | $ rhg cat -r 1 copy_of_original | |||
|
108 | original content |
General Comments 0
You need to be logged in to leave comments.
Login now