Show More
@@ -1,105 +1,105 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::path::PathBuf; |
|
8 | use std::path::PathBuf; | |
9 |
|
9 | |||
10 | use crate::repo::Repo; |
|
10 | use crate::repo::Repo; | |
11 | use crate::revlog::changelog::Changelog; |
|
11 | use crate::revlog::changelog::Changelog; | |
12 | use crate::revlog::manifest::Manifest; |
|
12 | use crate::revlog::manifest::Manifestlog; | |
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; |
|
16 | use crate::revlog::Node; | |
17 | use crate::utils::files::get_path_from_bytes; |
|
17 | use crate::utils::files::get_path_from_bytes; | |
18 | use crate::utils::hg_path::{HgPath, HgPathBuf}; |
|
18 | use crate::utils::hg_path::{HgPath, HgPathBuf}; | |
19 |
|
19 | |||
20 | pub struct CatOutput { |
|
20 | pub struct CatOutput { | |
21 | /// Whether any file in the manifest matched the paths given as CLI |
|
21 | /// Whether any file in the manifest matched the paths given as CLI | |
22 | /// arguments |
|
22 | /// arguments | |
23 | pub found_any: bool, |
|
23 | pub found_any: bool, | |
24 | /// The contents of matching files, in manifest order |
|
24 | /// The contents of matching files, in manifest order | |
25 | pub concatenated: Vec<u8>, |
|
25 | pub concatenated: Vec<u8>, | |
26 | /// Which of the CLI arguments did not match any manifest file |
|
26 | /// Which of the CLI arguments did not match any manifest file | |
27 | pub missing: Vec<HgPathBuf>, |
|
27 | pub missing: Vec<HgPathBuf>, | |
28 | /// The node ID that the given revset was resolved to |
|
28 | /// The node ID that the given revset was resolved to | |
29 | pub node: Node, |
|
29 | pub node: Node, | |
30 | } |
|
30 | } | |
31 |
|
31 | |||
32 | const METADATA_DELIMITER: [u8; 2] = [b'\x01', b'\n']; |
|
32 | const METADATA_DELIMITER: [u8; 2] = [b'\x01', b'\n']; | |
33 |
|
33 | |||
34 | /// Output the given revision of files |
|
34 | /// Output the given revision of files | |
35 | /// |
|
35 | /// | |
36 | /// * `root`: Repository root |
|
36 | /// * `root`: Repository root | |
37 | /// * `rev`: The revision to cat the files from. |
|
37 | /// * `rev`: The revision to cat the files from. | |
38 | /// * `files`: The files to output. |
|
38 | /// * `files`: The files to output. | |
39 | pub fn cat<'a>( |
|
39 | pub fn cat<'a>( | |
40 | repo: &Repo, |
|
40 | repo: &Repo, | |
41 | revset: &str, |
|
41 | revset: &str, | |
42 | files: &'a [HgPathBuf], |
|
42 | files: &'a [HgPathBuf], | |
43 | ) -> Result<CatOutput, RevlogError> { |
|
43 | ) -> Result<CatOutput, RevlogError> { | |
44 | let rev = crate::revset::resolve_single(revset, repo)?; |
|
44 | let rev = crate::revset::resolve_single(revset, repo)?; | |
45 | let changelog = Changelog::open(repo)?; |
|
45 | let changelog = Changelog::open(repo)?; | |
46 | let manifest = Manifest::open(repo)?; |
|
46 | let manifest = Manifestlog::open(repo)?; | |
47 | let changelog_entry = changelog.get_rev(rev)?; |
|
47 | let changelog_entry = changelog.get_rev(rev)?; | |
48 | let node = *changelog |
|
48 | let node = *changelog | |
49 | .node_from_rev(rev) |
|
49 | .node_from_rev(rev) | |
50 | .expect("should succeed when changelog.get_rev did"); |
|
50 | .expect("should succeed when changelog.get_rev did"); | |
51 | let manifest_node = |
|
51 | let manifest_node = | |
52 | Node::from_hex_for_repo(&changelog_entry.manifest_node()?)?; |
|
52 | Node::from_hex_for_repo(&changelog_entry.manifest_node()?)?; | |
53 | let manifest_entry = manifest.get_node(manifest_node.into())?; |
|
53 | let manifest_entry = manifest.get_node(manifest_node.into())?; | |
54 | let mut bytes = vec![]; |
|
54 | let mut bytes = vec![]; | |
55 | let mut matched = vec![false; files.len()]; |
|
55 | let mut matched = vec![false; files.len()]; | |
56 | let mut found_any = false; |
|
56 | let mut found_any = false; | |
57 |
|
57 | |||
58 | for (manifest_file, node_bytes) in manifest_entry.files_with_nodes() { |
|
58 | for (manifest_file, node_bytes) in manifest_entry.files_with_nodes() { | |
59 | for (cat_file, is_matched) in files.iter().zip(&mut matched) { |
|
59 | for (cat_file, is_matched) in files.iter().zip(&mut matched) { | |
60 | if cat_file.as_bytes() == manifest_file.as_bytes() { |
|
60 | if cat_file.as_bytes() == manifest_file.as_bytes() { | |
61 | *is_matched = true; |
|
61 | *is_matched = true; | |
62 | found_any = true; |
|
62 | found_any = true; | |
63 | let index_path = store_path(manifest_file, b".i"); |
|
63 | let index_path = store_path(manifest_file, b".i"); | |
64 | let data_path = store_path(manifest_file, b".d"); |
|
64 | let data_path = store_path(manifest_file, b".d"); | |
65 |
|
65 | |||
66 | let file_log = |
|
66 | let file_log = | |
67 | Revlog::open(repo, &index_path, Some(&data_path))?; |
|
67 | Revlog::open(repo, &index_path, Some(&data_path))?; | |
68 | let file_node = Node::from_hex_for_repo(node_bytes)?; |
|
68 | let file_node = Node::from_hex_for_repo(node_bytes)?; | |
69 | let file_rev = file_log.get_node_rev(file_node.into())?; |
|
69 | let file_rev = file_log.get_node_rev(file_node.into())?; | |
70 | let data = file_log.get_rev_data(file_rev)?; |
|
70 | let data = file_log.get_rev_data(file_rev)?; | |
71 | if data.starts_with(&METADATA_DELIMITER) { |
|
71 | if data.starts_with(&METADATA_DELIMITER) { | |
72 | let end_delimiter_position = data |
|
72 | let end_delimiter_position = data | |
73 | [METADATA_DELIMITER.len()..] |
|
73 | [METADATA_DELIMITER.len()..] | |
74 | .windows(METADATA_DELIMITER.len()) |
|
74 | .windows(METADATA_DELIMITER.len()) | |
75 | .position(|bytes| bytes == METADATA_DELIMITER); |
|
75 | .position(|bytes| bytes == METADATA_DELIMITER); | |
76 | if let Some(position) = end_delimiter_position { |
|
76 | if let Some(position) = end_delimiter_position { | |
77 | let offset = METADATA_DELIMITER.len() * 2; |
|
77 | let offset = METADATA_DELIMITER.len() * 2; | |
78 | bytes.extend(data[position + offset..].iter()); |
|
78 | bytes.extend(data[position + offset..].iter()); | |
79 | } |
|
79 | } | |
80 | } else { |
|
80 | } else { | |
81 | bytes.extend(data); |
|
81 | bytes.extend(data); | |
82 | } |
|
82 | } | |
83 | } |
|
83 | } | |
84 | } |
|
84 | } | |
85 | } |
|
85 | } | |
86 |
|
86 | |||
87 | let missing: Vec<_> = files |
|
87 | let missing: Vec<_> = files | |
88 | .iter() |
|
88 | .iter() | |
89 | .zip(&matched) |
|
89 | .zip(&matched) | |
90 | .filter(|pair| !*pair.1) |
|
90 | .filter(|pair| !*pair.1) | |
91 | .map(|pair| pair.0.clone()) |
|
91 | .map(|pair| pair.0.clone()) | |
92 | .collect(); |
|
92 | .collect(); | |
93 | Ok(CatOutput { |
|
93 | Ok(CatOutput { | |
94 | found_any, |
|
94 | found_any, | |
95 | concatenated: bytes, |
|
95 | concatenated: bytes, | |
96 | missing, |
|
96 | missing, | |
97 | node, |
|
97 | node, | |
98 | }) |
|
98 | }) | |
99 | } |
|
99 | } | |
100 |
|
100 | |||
101 | fn store_path(hg_path: &HgPath, suffix: &[u8]) -> PathBuf { |
|
101 | fn store_path(hg_path: &HgPath, suffix: &[u8]) -> PathBuf { | |
102 | let encoded_bytes = |
|
102 | let encoded_bytes = | |
103 | path_encode(&[b"data/", hg_path.as_bytes(), suffix].concat()); |
|
103 | path_encode(&[b"data/", hg_path.as_bytes(), suffix].concat()); | |
104 | get_path_from_bytes(&encoded_bytes).into() |
|
104 | get_path_from_bytes(&encoded_bytes).into() | |
105 | } |
|
105 | } |
@@ -1,90 +1,90 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_entries; |
|
8 | use crate::dirstate::parsers::parse_dirstate_entries; | |
9 | use crate::dirstate_tree::on_disk::{for_each_tracked_path, read_docket}; |
|
9 | use crate::dirstate_tree::on_disk::{for_each_tracked_path, read_docket}; | |
10 | use crate::errors::HgError; |
|
10 | use crate::errors::HgError; | |
11 | use crate::repo::Repo; |
|
11 | use crate::repo::Repo; | |
12 | use crate::revlog::changelog::Changelog; |
|
12 | use crate::revlog::changelog::Changelog; | |
13 |
use crate::revlog::manifest::{Manifest, Manifest |
|
13 | use crate::revlog::manifest::{Manifest, Manifestlog}; | |
14 | use crate::revlog::node::Node; |
|
14 | use crate::revlog::node::Node; | |
15 | use crate::revlog::revlog::RevlogError; |
|
15 | use crate::revlog::revlog::RevlogError; | |
16 | use crate::utils::hg_path::HgPath; |
|
16 | use crate::utils::hg_path::HgPath; | |
17 | use crate::DirstateError; |
|
17 | use crate::DirstateError; | |
18 | use rayon::prelude::*; |
|
18 | use rayon::prelude::*; | |
19 |
|
19 | |||
20 | /// List files under Mercurial control in the working directory |
|
20 | /// List files under Mercurial control in the working directory | |
21 | /// by reading the dirstate |
|
21 | /// by reading the dirstate | |
22 | pub struct Dirstate { |
|
22 | pub struct Dirstate { | |
23 | /// The `dirstate` content. |
|
23 | /// The `dirstate` content. | |
24 | content: Vec<u8>, |
|
24 | content: Vec<u8>, | |
25 | v2_metadata: Option<Vec<u8>>, |
|
25 | v2_metadata: Option<Vec<u8>>, | |
26 | } |
|
26 | } | |
27 |
|
27 | |||
28 | impl Dirstate { |
|
28 | impl Dirstate { | |
29 | pub fn new(repo: &Repo) -> Result<Self, HgError> { |
|
29 | pub fn new(repo: &Repo) -> Result<Self, HgError> { | |
30 | let mut content = repo.hg_vfs().read("dirstate")?; |
|
30 | let mut content = repo.hg_vfs().read("dirstate")?; | |
31 | let v2_metadata = if repo.has_dirstate_v2() { |
|
31 | let v2_metadata = if repo.has_dirstate_v2() { | |
32 | let docket = read_docket(&content)?; |
|
32 | let docket = read_docket(&content)?; | |
33 | let meta = docket.tree_metadata().to_vec(); |
|
33 | let meta = docket.tree_metadata().to_vec(); | |
34 | content = repo.hg_vfs().read(docket.data_filename())?; |
|
34 | content = repo.hg_vfs().read(docket.data_filename())?; | |
35 | Some(meta) |
|
35 | Some(meta) | |
36 | } else { |
|
36 | } else { | |
37 | None |
|
37 | None | |
38 | }; |
|
38 | }; | |
39 | Ok(Self { |
|
39 | Ok(Self { | |
40 | content, |
|
40 | content, | |
41 | v2_metadata, |
|
41 | v2_metadata, | |
42 | }) |
|
42 | }) | |
43 | } |
|
43 | } | |
44 |
|
44 | |||
45 | pub fn tracked_files(&self) -> Result<Vec<&HgPath>, DirstateError> { |
|
45 | pub fn tracked_files(&self) -> Result<Vec<&HgPath>, DirstateError> { | |
46 | let mut files = Vec::new(); |
|
46 | let mut files = Vec::new(); | |
47 | if !self.content.is_empty() { |
|
47 | if !self.content.is_empty() { | |
48 | if let Some(meta) = &self.v2_metadata { |
|
48 | if let Some(meta) = &self.v2_metadata { | |
49 | for_each_tracked_path(&self.content, meta, |path| { |
|
49 | for_each_tracked_path(&self.content, meta, |path| { | |
50 | files.push(path) |
|
50 | files.push(path) | |
51 | })? |
|
51 | })? | |
52 | } else { |
|
52 | } else { | |
53 | let _parents = parse_dirstate_entries( |
|
53 | let _parents = parse_dirstate_entries( | |
54 | &self.content, |
|
54 | &self.content, | |
55 | |path, entry, _copy_source| { |
|
55 | |path, entry, _copy_source| { | |
56 | if entry.state.is_tracked() { |
|
56 | if entry.state.is_tracked() { | |
57 | files.push(path) |
|
57 | files.push(path) | |
58 | } |
|
58 | } | |
59 | Ok(()) |
|
59 | Ok(()) | |
60 | }, |
|
60 | }, | |
61 | )?; |
|
61 | )?; | |
62 | } |
|
62 | } | |
63 | } |
|
63 | } | |
64 | files.par_sort_unstable(); |
|
64 | files.par_sort_unstable(); | |
65 | Ok(files) |
|
65 | Ok(files) | |
66 | } |
|
66 | } | |
67 | } |
|
67 | } | |
68 |
|
68 | |||
69 | /// List files under Mercurial control at a given revision. |
|
69 | /// List files under Mercurial control at a given revision. | |
70 | pub fn list_rev_tracked_files( |
|
70 | pub fn list_rev_tracked_files( | |
71 | repo: &Repo, |
|
71 | repo: &Repo, | |
72 | revset: &str, |
|
72 | revset: &str, | |
73 | ) -> Result<FilesForRev, RevlogError> { |
|
73 | ) -> Result<FilesForRev, RevlogError> { | |
74 | let rev = crate::revset::resolve_single(revset, repo)?; |
|
74 | let rev = crate::revset::resolve_single(revset, repo)?; | |
75 | let changelog = Changelog::open(repo)?; |
|
75 | let changelog = Changelog::open(repo)?; | |
76 | let manifest = Manifest::open(repo)?; |
|
76 | let manifest = Manifestlog::open(repo)?; | |
77 | let changelog_entry = changelog.get_rev(rev)?; |
|
77 | let changelog_entry = changelog.get_rev(rev)?; | |
78 | let manifest_node = |
|
78 | let manifest_node = | |
79 | Node::from_hex_for_repo(&changelog_entry.manifest_node()?)?; |
|
79 | Node::from_hex_for_repo(&changelog_entry.manifest_node()?)?; | |
80 | let manifest_entry = manifest.get_node(manifest_node.into())?; |
|
80 | let manifest_entry = manifest.get_node(manifest_node.into())?; | |
81 | Ok(FilesForRev(manifest_entry)) |
|
81 | Ok(FilesForRev(manifest_entry)) | |
82 | } |
|
82 | } | |
83 |
|
83 | |||
84 |
pub struct FilesForRev(Manifest |
|
84 | pub struct FilesForRev(Manifest); | |
85 |
|
85 | |||
86 | impl FilesForRev { |
|
86 | impl FilesForRev { | |
87 | pub fn iter(&self) -> impl Iterator<Item = &HgPath> { |
|
87 | pub fn iter(&self) -> impl Iterator<Item = &HgPath> { | |
88 | self.0.files() |
|
88 | self.0.files() | |
89 | } |
|
89 | } | |
90 | } |
|
90 | } |
@@ -1,76 +1,70 b'' | |||||
1 | use crate::repo::Repo; |
|
1 | use crate::repo::Repo; | |
2 | use crate::revlog::revlog::{Revlog, RevlogError}; |
|
2 | use crate::revlog::revlog::{Revlog, RevlogError}; | |
3 | use crate::revlog::NodePrefix; |
|
3 | use crate::revlog::NodePrefix; | |
4 | use crate::revlog::Revision; |
|
4 | use crate::revlog::Revision; | |
5 | use crate::utils::hg_path::HgPath; |
|
5 | use crate::utils::hg_path::HgPath; | |
6 |
|
6 | |||
7 | /// A specialized `Revlog` to work with `manifest` data format. |
|
7 | /// A specialized `Revlog` to work with `manifest` data format. | |
8 | pub struct Manifest { |
|
8 | pub struct Manifestlog { | |
9 | /// The generic `revlog` format. |
|
9 | /// The generic `revlog` format. | |
10 | revlog: Revlog, |
|
10 | revlog: Revlog, | |
11 | } |
|
11 | } | |
12 |
|
12 | |||
13 | impl Manifest { |
|
13 | impl Manifestlog { | |
14 | /// Open the `manifest` of a repository given by its root. |
|
14 | /// Open the `manifest` of a repository given by its root. | |
15 | pub fn open(repo: &Repo) -> Result<Self, RevlogError> { |
|
15 | pub fn open(repo: &Repo) -> Result<Self, RevlogError> { | |
16 | let revlog = Revlog::open(repo, "00manifest.i", None)?; |
|
16 | let revlog = Revlog::open(repo, "00manifest.i", None)?; | |
17 | Ok(Self { revlog }) |
|
17 | Ok(Self { revlog }) | |
18 | } |
|
18 | } | |
19 |
|
19 | |||
20 | /// Return the `ManifestEntry` of a given node id. |
|
20 | /// Return the `ManifestEntry` of a given node id. | |
21 | pub fn get_node( |
|
21 | pub fn get_node(&self, node: NodePrefix) -> Result<Manifest, RevlogError> { | |
22 | &self, |
|
|||
23 | node: NodePrefix, |
|
|||
24 | ) -> Result<ManifestEntry, RevlogError> { |
|
|||
25 | let rev = self.revlog.get_node_rev(node)?; |
|
22 | let rev = self.revlog.get_node_rev(node)?; | |
26 | self.get_rev(rev) |
|
23 | self.get_rev(rev) | |
27 | } |
|
24 | } | |
28 |
|
25 | |||
29 | /// Return the `ManifestEntry` of a given node revision. |
|
26 | /// Return the `ManifestEntry` of a given node revision. | |
30 | pub fn get_rev( |
|
27 | pub fn get_rev(&self, rev: Revision) -> Result<Manifest, RevlogError> { | |
31 | &self, |
|
|||
32 | rev: Revision, |
|
|||
33 | ) -> Result<ManifestEntry, RevlogError> { |
|
|||
34 | let bytes = self.revlog.get_rev_data(rev)?; |
|
28 | let bytes = self.revlog.get_rev_data(rev)?; | |
35 |
Ok(Manifest |
|
29 | Ok(Manifest { bytes }) | |
36 | } |
|
30 | } | |
37 | } |
|
31 | } | |
38 |
|
32 | |||
39 | /// `Manifest` entry which knows how to interpret the `manifest` data bytes. |
|
33 | /// `Manifestlog` entry which knows how to interpret the `manifest` data bytes. | |
40 | #[derive(Debug)] |
|
34 | #[derive(Debug)] | |
41 |
pub struct Manifest |
|
35 | pub struct Manifest { | |
42 | bytes: Vec<u8>, |
|
36 | bytes: Vec<u8>, | |
43 | } |
|
37 | } | |
44 |
|
38 | |||
45 |
impl Manifest |
|
39 | impl Manifest { | |
46 | /// Return an iterator over the lines of the entry. |
|
40 | /// Return an iterator over the lines of the entry. | |
47 | pub fn lines(&self) -> impl Iterator<Item = &[u8]> { |
|
41 | pub fn lines(&self) -> impl Iterator<Item = &[u8]> { | |
48 | self.bytes |
|
42 | self.bytes | |
49 | .split(|b| b == &b'\n') |
|
43 | .split(|b| b == &b'\n') | |
50 | .filter(|line| !line.is_empty()) |
|
44 | .filter(|line| !line.is_empty()) | |
51 | } |
|
45 | } | |
52 |
|
46 | |||
53 | /// Return an iterator over the files of the entry. |
|
47 | /// Return an iterator over the files of the entry. | |
54 | pub fn files(&self) -> impl Iterator<Item = &HgPath> { |
|
48 | pub fn files(&self) -> impl Iterator<Item = &HgPath> { | |
55 | self.lines().filter(|line| !line.is_empty()).map(|line| { |
|
49 | self.lines().filter(|line| !line.is_empty()).map(|line| { | |
56 | let pos = line |
|
50 | let pos = line | |
57 | .iter() |
|
51 | .iter() | |
58 | .position(|x| x == &b'\0') |
|
52 | .position(|x| x == &b'\0') | |
59 | .expect("manifest line should contain \\0"); |
|
53 | .expect("manifest line should contain \\0"); | |
60 | HgPath::new(&line[..pos]) |
|
54 | HgPath::new(&line[..pos]) | |
61 | }) |
|
55 | }) | |
62 | } |
|
56 | } | |
63 |
|
57 | |||
64 | /// Return an iterator over the files of the entry. |
|
58 | /// Return an iterator over the files of the entry. | |
65 | pub fn files_with_nodes(&self) -> impl Iterator<Item = (&HgPath, &[u8])> { |
|
59 | pub fn files_with_nodes(&self) -> impl Iterator<Item = (&HgPath, &[u8])> { | |
66 | self.lines().filter(|line| !line.is_empty()).map(|line| { |
|
60 | self.lines().filter(|line| !line.is_empty()).map(|line| { | |
67 | let pos = line |
|
61 | let pos = line | |
68 | .iter() |
|
62 | .iter() | |
69 | .position(|x| x == &b'\0') |
|
63 | .position(|x| x == &b'\0') | |
70 | .expect("manifest line should contain \\0"); |
|
64 | .expect("manifest line should contain \\0"); | |
71 | let hash_start = pos + 1; |
|
65 | let hash_start = pos + 1; | |
72 | let hash_end = hash_start + 40; |
|
66 | let hash_end = hash_start + 40; | |
73 | (HgPath::new(&line[..pos]), &line[hash_start..hash_end]) |
|
67 | (HgPath::new(&line[..pos]), &line[hash_start..hash_end]) | |
74 | }) |
|
68 | }) | |
75 | } |
|
69 | } | |
76 | } |
|
70 | } |
General Comments 0
You need to be logged in to leave comments.
Login now