This is just the first pass at ensuring we can read commit-graph files correctly. It does not support anything interesting like reachability, merge-base, or ahead-behind queries.
Showing the changes to git-commitgraph/src/graph/init.rs (1 of 53 files). Show all
Unified · Split
| @@ -0,0 +1,104 @@ | ||
| 1 | +use crate::{graph_file, Graph, GraphFile, MAX_COMMITS}; | |
| 2 | +use git_object::HashKind; | |
| 3 | +use quick_error::quick_error; | |
| 4 | +use std::io::{BufRead, BufReader}; | |
| 5 | +use std::path::{Path, PathBuf}; | |
| 6 | + | |
| 7 | +quick_error! { | |
| 8 | + #[derive(Debug)] | |
| 9 | + pub enum Error { | |
| 10 | + Corrupt(msg: String, path: PathBuf) { | |
| 11 | + display("{}", msg) | |
| 12 | + } | |
| 13 | + GraphFile(err: graph_file::Error, path: PathBuf) { | |
| 14 | + display("{}", path.display()) | |
| 15 | + source(err) | |
| 16 | + } | |
| 17 | + HashVersionMismatch(path1: PathBuf, hash1: HashKind, path2: PathBuf, hash2: HashKind) { | |
| 18 | + display( | |
| 19 | + "Commit-graph files mismatch: '{}' uses hash {:?}, but '{}' uses hash {:?}", | |
| 20 | + path1.display(), | |
| 21 | + hash1, | |
| 22 | + path2.display(), | |
| 23 | + hash2, | |
| 24 | + ) | |
| 25 | + } | |
| 26 | + Io(err: std::io::Error, path: PathBuf) { | |
| 27 | + display("Could not open commit-graph graph_file at '{}'", path.display()) | |
| 28 | + source(err) | |
| 29 | + } | |
| 30 | + TooManyCommits(num_commits: u64) { | |
| 31 | + display( | |
| 32 | + "Commit-graph files contain {} commits altogether, but only {} commits are allowed", | |
| 33 | + num_commits, | |
| 34 | + MAX_COMMITS, | |
| 35 | + ) | |
| 36 | + } | |
| 37 | + // This might actually be legal... | |
| 38 | + VersionMismatch(path1: PathBuf, version1: graph_file::Kind, path2: PathBuf, version2: graph_file::Kind) { | |
| 39 | + display( | |
| 40 | + "Commit-graph files mismatch: '{}' is version {:?}, but '{}' is version {:?}", | |
| 41 | + path1.display(), | |
| 42 | + version1, | |
| 43 | + path2.display(), | |
| 44 | + version2, | |
| 45 | + ) | |
| 46 | + } | |
| 47 | + } | |
| 48 | +} | |
| 49 | + | |
| 50 | +impl Graph { | |
| 51 | + pub fn from_object_dir(object_dir: impl AsRef<Path>) -> Result<Self, Error> { | |
| 52 | + Self::from_single_file(object_dir.as_ref()).or_else(|_| Self::from_split_chain(object_dir.as_ref())) | |
| 53 | + } | |
| 54 | + | |
| 55 | + pub fn from_single_file(object_dir: impl AsRef<Path>) -> Result<Self, Error> { | |
| 56 | + let single_graph_file = object_dir.as_ref().join("info").join("commit-graph"); | |
| 57 | + let file = GraphFile::at(&single_graph_file).map_err(|e| Error::GraphFile(e, single_graph_file.clone()))?; | |
| 58 | + Self::new(vec![file]) | |
| 59 | + } | |
| 60 | + | |
| 61 | + pub fn from_split_chain(object_dir: impl AsRef<Path>) -> Result<Self, Error> { | |
| 62 | + let commit_graphs_dir = object_dir.as_ref().join("info").join("commit-graphs"); | |
| 63 | + let chain_file_path = commit_graphs_dir.join("commit-graph-chain"); | |
| 64 | + let chain_file = std::fs::File::open(&chain_file_path).map_err(|e| Error::Io(e, chain_file_path.clone()))?; | |
| 65 | + let mut files: Vec<GraphFile> = Vec::new(); | |
| 66 | + for line in BufReader::new(chain_file).lines() { | |
| 67 | + let line = line.map_err(|e| Error::Io(e, chain_file_path.clone()))?; | |
| 68 | + let graph_filename = format!("graph-{}.graph", line); | |
| 69 | + let graph_file_path = commit_graphs_dir.join(graph_filename); | |
| 70 | + files.push(GraphFile::at(&graph_file_path).map_err(|e| Error::GraphFile(e, graph_file_path.clone()))?); | |
| 71 | + } | |
| 72 | + Self::new(files) | |
| 73 | + } | |
| 74 | + | |
| 75 | + pub fn new(files: Vec<GraphFile>) -> Result<Self, Error> { | |
| 76 | + let num_commits: u64 = files.iter().map(|f| f.num_commits() as u64).sum(); | |
| 77 | + if num_commits > MAX_COMMITS as u64 { | |
| 78 | + return Err(Error::TooManyCommits(num_commits)); | |
| 79 | + } | |
| 80 | + | |
| 81 | + for window in files.windows(2) { | |
| 82 | + let f1 = &window[0]; | |
| 83 | + let f2 = &window[1]; | |
| 84 | + if f1.kind() != f2.kind() { | |
| 85 | + return Err(Error::VersionMismatch( | |
| 86 | + f1.path().to_owned(), | |
| 87 | + f1.kind(), | |
| 88 | + f2.path().to_owned(), | |
| 89 | + f2.kind(), | |
| 90 | + )); | |
| 91 | + } | |
| 92 | + if f1.hash_kind() != f2.hash_kind() { | |
| 93 | + return Err(Error::HashVersionMismatch( | |
| 94 | + f1.path().to_owned(), | |
| 95 | + f1.hash_kind(), | |
| 96 | + f2.path().to_owned(), | |
| 97 | + f2.hash_kind(), | |
| 98 | + )); | |
| 99 | + } | |
| 100 | + } | |
| 101 | + | |
| 102 | + Ok(Self { files }) | |
| 103 | + } | |
| 104 | +} | |