diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-04 12:41:35 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-04 12:41:35 +0000 |
commit | 7e5d7eea9c580ef4b41a765bde624af431942b96 (patch) | |
tree | 2c0d9ca12878fc4525650aa4e54d77a81a07cc09 /vendor/gix-odb/src/alternate | |
parent | Adding debian version 1.70.0+dfsg1-9. (diff) | |
download | rustc-7e5d7eea9c580ef4b41a765bde624af431942b96.tar.xz rustc-7e5d7eea9c580ef4b41a765bde624af431942b96.zip |
Merging upstream version 1.70.0+dfsg2.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
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) +} |