summaryrefslogtreecommitdiffstats
path: root/vendor/gix-commitgraph/src/init.rs
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/gix-commitgraph/src/init.rs')
-rw-r--r--vendor/gix-commitgraph/src/init.rs130
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()))
+ }
+ }
+}