diff options
Diffstat (limited to 'vendor/gix-odb/src/alternate')
-rw-r--r-- | vendor/gix-odb/src/alternate/mod.rs | 75 | ||||
-rw-r--r-- | vendor/gix-odb/src/alternate/parse.rs | 33 |
2 files changed, 108 insertions, 0 deletions
diff --git a/vendor/gix-odb/src/alternate/mod.rs b/vendor/gix-odb/src/alternate/mod.rs new file mode 100644 index 000000000..c343ef5aa --- /dev/null +++ b/vendor/gix-odb/src/alternate/mod.rs @@ -0,0 +1,75 @@ +//! A file with directories of other git object databases to use when reading objects. +//! +//! This inherently makes alternates read-only. +//! +//! An alternate file in `<git-dir>/info/alternates` can look as follows: +//! +//! ```text +//! # a comment, empty lines are also allowed +//! # relative paths resolve relative to the parent git repository +//! ../path/relative/to/repo/.git +//! /absolute/path/to/repo/.git +//! +//! "/a/ansi-c-quoted/path/with/tabs\t/.git" +//! +//! # each .git directory should indeed be a directory, and not a file +//! ``` +//! +//! Based on the [canonical implementation](https://github.com/git/git/blob/master/sha1-file.c#L598:L609). +use std::{fs, io, path::PathBuf}; + +use gix_path::realpath::MAX_SYMLINKS; + +/// +pub mod parse; + +/// Returned by [`resolve()`] +#[derive(thiserror::Error, Debug)] +#[allow(missing_docs)] +pub enum Error { + #[error(transparent)] + Io(#[from] io::Error), + #[error(transparent)] + Realpath(#[from] gix_path::realpath::Error), + #[error(transparent)] + Parse(#[from] parse::Error), + #[error("Alternates form a cycle: {} -> {}", .0.iter().map(|p| format!("'{}'", p.display())).collect::<Vec<_>>().join(" -> "), .0.first().expect("more than one directories").display())] + Cycle(Vec<PathBuf>), +} + +/// Given an `objects_directory`, try to resolve alternate object directories possibly located in the +/// `./info/alternates` file into canonical paths and resolve relative paths with the help of the `current_dir`. +/// If no alternate object database was resolved, the resulting `Vec` is empty (it is not an error +/// if there are no alternates). +/// It is an error once a repository is seen again as it would lead to a cycle. +pub fn resolve( + objects_directory: impl Into<PathBuf>, + current_dir: impl AsRef<std::path::Path>, +) -> Result<Vec<PathBuf>, Error> { + let relative_base = objects_directory.into(); + let mut dirs = vec![(0, relative_base.clone())]; + let mut out = Vec::new(); + let cwd = current_dir.as_ref(); + let mut seen = vec![gix_path::realpath_opts(&relative_base, cwd, MAX_SYMLINKS)?]; + while let Some((depth, dir)) = dirs.pop() { + match fs::read(dir.join("info").join("alternates")) { + Ok(input) => { + for path in parse::content(&input)?.into_iter() { + let path = relative_base.join(path); + let path_canonicalized = gix_path::realpath_opts(&path, cwd, MAX_SYMLINKS)?; + if seen.contains(&path_canonicalized) { + return Err(Error::Cycle(seen)); + } + seen.push(path_canonicalized); + dirs.push((depth + 1, path)); + } + } + Err(err) if err.kind() == io::ErrorKind::NotFound => {} + Err(err) => return Err(err.into()), + }; + if depth != 0 { + out.push(dir); + } + } + Ok(out) +} diff --git a/vendor/gix-odb/src/alternate/parse.rs b/vendor/gix-odb/src/alternate/parse.rs new file mode 100644 index 000000000..1c297d153 --- /dev/null +++ b/vendor/gix-odb/src/alternate/parse.rs @@ -0,0 +1,33 @@ +use std::{borrow::Cow, path::PathBuf}; + +use gix_object::bstr::ByteSlice; + +/// Returned as part of [`crate::alternate::Error::Parse`] +#[derive(thiserror::Error, Debug)] +#[allow(missing_docs)] +pub enum Error { + #[error("Could not obtain an object path for the alternate directory '{}'", String::from_utf8_lossy(.0))] + PathConversion(Vec<u8>), + #[error("Could not unquote alternate path")] + Unquote(#[from] gix_quote::ansi_c::undo::Error), +} + +pub(crate) fn content(input: &[u8]) -> Result<Vec<PathBuf>, Error> { + let mut out = Vec::new(); + for line in input.split(|b| *b == b'\n') { + let line = line.as_bstr(); + if line.is_empty() || line.starts_with(b"#") { + continue; + } + out.push( + gix_path::try_from_bstr(if line.starts_with(b"\"") { + gix_quote::ansi_c::undo(line)?.0 + } else { + Cow::Borrowed(line) + }) + .map_err(|_| Error::PathConversion(line.to_vec()))? + .into_owned(), + ) + } + Ok(out) +} |