diff options
Diffstat (limited to 'vendor/gix-commitgraph/src/init.rs')
-rw-r--r-- | vendor/gix-commitgraph/src/init.rs | 130 |
1 files changed, 130 insertions, 0 deletions
diff --git a/vendor/gix-commitgraph/src/init.rs b/vendor/gix-commitgraph/src/init.rs new file mode 100644 index 000000000..f964aade0 --- /dev/null +++ b/vendor/gix-commitgraph/src/init.rs @@ -0,0 +1,130 @@ +use std::{ + convert::TryFrom, + io::{BufRead, BufReader}, + path::{Path, PathBuf}, +}; + +use crate::{file, File, Graph, MAX_COMMITS}; + +/// The error returned by initializations functions like [`Graph::at()`]. +#[derive(thiserror::Error, Debug)] +#[allow(missing_docs)] +pub enum Error { + #[error("{}", .path.display())] + File { + #[source] + err: file::Error, + path: PathBuf, + }, + #[error("Commit-graph files mismatch: '{}' uses hash {hash1:?}, but '{}' uses hash {hash2:?}", .path1.display(), .path2.display())] + HashVersionMismatch { + path1: PathBuf, + hash1: gix_hash::Kind, + path2: PathBuf, + hash2: gix_hash::Kind, + }, + #[error("Did not find any files that look like commit graphs at '{}'", .0.display())] + InvalidPath(PathBuf), + #[error("Could not open commit-graph file at '{}'", .path.display())] + Io { + #[source] + err: std::io::Error, + path: PathBuf, + }, + #[error( + "Commit-graph files contain {0} commits altogether, but only {} commits are allowed", + MAX_COMMITS + )] + TooManyCommits(u64), +} + +/// Instantiate a `Graph` from various sources. +impl Graph { + /// Instantiate a commit graph from `path` which may be a directory containing graph files or the graph file itself. + pub fn at(path: impl AsRef<Path>) -> Result<Self, Error> { + Self::try_from(path.as_ref()) + } + + /// Instantiate a commit graph from the directory containing all of its files. + pub fn from_commit_graphs_dir(path: impl AsRef<Path>) -> Result<Self, Error> { + let commit_graphs_dir = path.as_ref(); + let chain_file_path = commit_graphs_dir.join("commit-graph-chain"); + let chain_file = std::fs::File::open(&chain_file_path).map_err(|e| Error::Io { + err: e, + path: chain_file_path.clone(), + })?; + let mut files = Vec::new(); + for line in BufReader::new(chain_file).lines() { + let hash = line.map_err(|e| Error::Io { + err: e, + path: chain_file_path.clone(), + })?; + let graph_file_path = commit_graphs_dir.join(format!("graph-{hash}.graph")); + files.push(File::at(&graph_file_path).map_err(|e| Error::File { + err: e, + path: graph_file_path.clone(), + })?); + } + Self::new(files) + } + + /// Instantiate a commit graph from a `.git/objects/info/commit-graph` or + /// `.git/objects/info/commit-graphs/graph-*.graph` file. + pub fn from_file(path: impl AsRef<Path>) -> Result<Self, Error> { + let path = path.as_ref(); + let file = File::at(path).map_err(|e| Error::File { + err: e, + path: path.to_owned(), + })?; + Self::new(vec![file]) + } + + /// Instantiate a commit graph from an `.git/objects/info` directory. + pub fn from_info_dir(info_dir: impl AsRef<Path>) -> Result<Self, Error> { + Self::from_file(info_dir.as_ref().join("commit-graph")) + .or_else(|_| Self::from_commit_graphs_dir(info_dir.as_ref().join("commit-graphs"))) + } + + /// Create a new commit graph from a list of `files`. + pub fn new(files: Vec<File>) -> Result<Self, Error> { + let num_commits: u64 = files.iter().map(|f| u64::from(f.num_commits())).sum(); + if num_commits > u64::from(MAX_COMMITS) { + return Err(Error::TooManyCommits(num_commits)); + } + + for window in files.windows(2) { + let f1 = &window[0]; + let f2 = &window[1]; + if f1.object_hash() != f2.object_hash() { + return Err(Error::HashVersionMismatch { + path1: f1.path().to_owned(), + hash1: f1.object_hash(), + path2: f2.path().to_owned(), + hash2: f2.object_hash(), + }); + } + } + + Ok(Self { files }) + } +} + +impl TryFrom<&Path> for Graph { + type Error = Error; + + fn try_from(path: &Path) -> Result<Self, Self::Error> { + if path.is_file() { + // Assume we are looking at `.git/objects/info/commit-graph` or + // `.git/objects/info/commit-graphs/graph-*.graph`. + Self::from_file(path) + } else if path.is_dir() { + if path.join("commit-graph-chain").is_file() { + Self::from_commit_graphs_dir(path) + } else { + Self::from_info_dir(path) + } + } else { + Err(Error::InvalidPath(path.to_owned())) + } + } +} |