cat.rs
105 lines
| 3.7 KiB
| application/rls-services+xml
|
RustLexer
Antoine Cezar
|
r46112 | // list_tracked_files.rs | ||
// | ||||
// Copyright 2020 Antoine Cezar <antoine.cezar@octobus.net> | ||||
// | ||||
// This software may be used and distributed according to the terms of the | ||||
// GNU General Public License version 2 or any later version. | ||||
Simon Sapin
|
r46782 | use std::path::PathBuf; | ||
Antoine Cezar
|
r46112 | |||
Simon Sapin
|
r46782 | use crate::repo::Repo; | ||
Antoine Cezar
|
r46112 | use crate::revlog::changelog::Changelog; | ||
Simon Sapin
|
r46751 | use crate::revlog::manifest::Manifest; | ||
Antoine Cezar
|
r46112 | use crate::revlog::path_encode::path_encode; | ||
use crate::revlog::revlog::Revlog; | ||||
use crate::revlog::revlog::RevlogError; | ||||
Simon Sapin
|
r46647 | use crate::revlog::Node; | ||
Antoine cezar
|
r46408 | use crate::utils::files::get_path_from_bytes; | ||
use crate::utils::hg_path::{HgPath, HgPathBuf}; | ||||
Antoine Cezar
|
r46112 | |||
Simon Sapin
|
r47478 | pub struct CatOutput { | ||
/// Whether any file in the manifest matched the paths given as CLI | ||||
/// arguments | ||||
pub found_any: bool, | ||||
/// The contents of matching files, in manifest order | ||||
pub concatenated: Vec<u8>, | ||||
/// Which of the CLI arguments did not match any manifest file | ||||
pub missing: Vec<HgPathBuf>, | ||||
/// The node ID that the given revset was resolved to | ||||
pub node: Node, | ||||
} | ||||
Antoine cezar
|
r46406 | const METADATA_DELIMITER: [u8; 2] = [b'\x01', b'\n']; | ||
Simon Sapin
|
r47478 | /// Output the given revision of files | ||
Simon Sapin
|
r46751 | /// | ||
/// * `root`: Repository root | ||||
/// * `rev`: The revision to cat the files from. | ||||
/// * `files`: The files to output. | ||||
Simon Sapin
|
r47478 | pub fn cat<'a>( | ||
Simon Sapin
|
r46782 | repo: &Repo, | ||
Simon Sapin
|
r47162 | revset: &str, | ||
Simon Sapin
|
r47478 | files: &'a [HgPathBuf], | ||
) -> Result<CatOutput, RevlogError> { | ||||
Simon Sapin
|
r47162 | let rev = crate::revset::resolve_single(revset, repo)?; | ||
Simon Sapin
|
r46782 | let changelog = Changelog::open(repo)?; | ||
let manifest = Manifest::open(repo)?; | ||||
Simon Sapin
|
r47162 | let changelog_entry = changelog.get_rev(rev)?; | ||
Simon Sapin
|
r47478 | let node = *changelog | ||
.node_from_rev(rev) | ||||
.expect("should succeed when changelog.get_rev did"); | ||||
Simon Sapin
|
r47172 | let manifest_node = | ||
Node::from_hex_for_repo(&changelog_entry.manifest_node()?)?; | ||||
Simon Sapin
|
r47160 | let manifest_entry = manifest.get_node(manifest_node.into())?; | ||
Simon Sapin
|
r46751 | let mut bytes = vec![]; | ||
Simon Sapin
|
r47478 | let mut matched = vec![false; files.len()]; | ||
let mut found_any = false; | ||||
Antoine Cezar
|
r46112 | |||
Simon Sapin
|
r46751 | for (manifest_file, node_bytes) in manifest_entry.files_with_nodes() { | ||
Simon Sapin
|
r47478 | for (cat_file, is_matched) in files.iter().zip(&mut matched) { | ||
Simon Sapin
|
r46751 | if cat_file.as_bytes() == manifest_file.as_bytes() { | ||
Simon Sapin
|
r47478 | *is_matched = true; | ||
found_any = true; | ||||
Simon Sapin
|
r46782 | let index_path = store_path(manifest_file, b".i"); | ||
let data_path = store_path(manifest_file, b".d"); | ||||
Antoine Cezar
|
r46112 | |||
Simon Sapin
|
r46782 | let file_log = | ||
Revlog::open(repo, &index_path, Some(&data_path))?; | ||||
Simon Sapin
|
r47172 | let file_node = Node::from_hex_for_repo(node_bytes)?; | ||
Simon Sapin
|
r47160 | let file_rev = file_log.get_node_rev(file_node.into())?; | ||
Simon Sapin
|
r46751 | let data = file_log.get_rev_data(file_rev)?; | ||
if data.starts_with(&METADATA_DELIMITER) { | ||||
let end_delimiter_position = data | ||||
[METADATA_DELIMITER.len()..] | ||||
.windows(METADATA_DELIMITER.len()) | ||||
.position(|bytes| bytes == METADATA_DELIMITER); | ||||
if let Some(position) = end_delimiter_position { | ||||
let offset = METADATA_DELIMITER.len() * 2; | ||||
bytes.extend(data[position + offset..].iter()); | ||||
} | ||||
} else { | ||||
bytes.extend(data); | ||||
} | ||||
} | ||||
} | ||||
Antoine Cezar
|
r46112 | } | ||
Simon Sapin
|
r47478 | let missing: Vec<_> = files | ||
.iter() | ||||
.zip(&matched) | ||||
.filter(|pair| !*pair.1) | ||||
.map(|pair| pair.0.clone()) | ||||
.collect(); | ||||
Ok(CatOutput { | ||||
found_any, | ||||
concatenated: bytes, | ||||
missing, | ||||
node, | ||||
}) | ||||
Antoine Cezar
|
r46112 | } | ||
Antoine cezar
|
r46408 | |||
Simon Sapin
|
r46782 | fn store_path(hg_path: &HgPath, suffix: &[u8]) -> PathBuf { | ||
Antoine cezar
|
r46408 | let encoded_bytes = | ||
path_encode(&[b"data/", hg_path.as_bytes(), suffix].concat()); | ||||
Simon Sapin
|
r46782 | get_path_from_bytes(&encoded_bytes).into() | ||
Antoine cezar
|
r46408 | } | ||