proper error handling needs more bounds on error E which can be done but really is a lot of typing. Nothing to be done here except for doing it I guess.
Unified · Split
| @@ -1,6 +1,7 @@ | ||
| 1 | 1 | use super::Cache; |
| 2 | 2 | use crate::fs; |
| 3 | −use crate::fs::PathIdMapping; | |
| 3 | +use crate::fs::PathOidMapping; | |
| 4 | +use git_hash::oid; | |
| 4 | 5 | use std::path::{Path, PathBuf}; |
| 5 | 6 | |
| 6 | 7 | #[derive(Clone)] |
| @@ -66,7 +67,7 @@ impl<'paths> Cache<'paths> { · 59 unmodified lines | ||
| 66 | 67 | state: State, |
| 67 | 68 | case: git_glob::pattern::Case, |
| 68 | 69 | buf: Vec<u8>, |
| 69 | − attribute_files_in_index: Vec<PathIdMapping<'paths>>, | |
| 70 | + attribute_files_in_index: Vec<PathOidMapping<'paths>>, | |
| 70 | 71 | ) -> Self { |
| 71 | 72 | let root = worktree_root.into(); |
| 72 | 73 | Cache { |
| @@ -83,15 +84,21 @@ impl<'paths> Cache<'paths> { · 10 unmodified lines | ||
| 83 | 84 | /// path is created as directory. If it's not known it is assumed to be a file. |
| 84 | 85 | /// |
| 85 | 86 | /// Provide access to cached information for that `relative` entry via the platform returned. |
| 86 | − pub fn at_entry( | |
| 87 | + pub fn at_entry<Find, E>( | |
| 87 | 88 | &mut self, |
| 88 | 89 | relative: impl AsRef<Path>, |
| 89 | 90 | is_dir: Option<bool>, |
| 90 | − ) -> std::io::Result<Platform<'_, 'paths>> { | |
| 91 | + find: Find, | |
| 92 | + ) -> std::io::Result<Platform<'_, 'paths>> | |
| 93 | + where | |
| 94 | + Find: for<'a> FnMut(&oid, &'a mut Vec<u8>) -> Result<git_object::BlobRef<'a>, E>, | |
| 95 | + { | |
| 91 | 96 | let mut platform = platform::StackDelegate { |
| 92 | 97 | state: &mut self.state, |
| 93 | 98 | buf: &mut self.buf, |
| 94 | 99 | is_dir, |
| 100 | + attribute_files_in_index: &self.attribute_files_in_index, | |
| 101 | + find, | |
| 95 | 102 | }; |
| 96 | 103 | self.stack.make_relative_path_current(relative, &mut platform)?; |
| 97 | 104 | Ok(Platform { parent: self, is_dir }) |
| @@ -1,5 +1,7 @@ | ||
| 1 | 1 | use crate::fs; |
| 2 | 2 | use crate::fs::cache::{Platform, State}; |
| 3 | +use crate::fs::PathOidMapping; | |
| 4 | +use git_hash::oid; | |
| 3 | 5 | use std::path::Path; |
| 4 | 6 | |
| 5 | 7 | impl<'a, 'paths> Platform<'a, 'paths> { |
| @@ -43,13 +45,18 @@ impl<'a, 'paths> std::fmt::Debug for Platform<'a, 'paths> { · 37 unmodified lines | ||
| 43 | 45 | } |
| 44 | 46 | } |
| 45 | 47 | |
| 46 | −pub struct StackDelegate<'a> { | |
| 48 | +pub struct StackDelegate<'a, 'paths, Find> { | |
| 47 | 49 | pub state: &'a mut State, |
| 48 | 50 | pub buf: &'a mut Vec<u8>, |
| 49 | 51 | pub is_dir: Option<bool>, |
| 52 | + pub attribute_files_in_index: &'a Vec<PathOidMapping<'paths>>, | |
| 53 | + pub find: Find, | |
| 50 | 54 | } |
| 51 | 55 | |
| 52 | −impl<'a> fs::stack::Delegate for StackDelegate<'a> { | |
| 56 | +impl<'a, 'paths, Find, E> fs::stack::Delegate for StackDelegate<'a, 'paths, Find> | |
| 57 | +where | |
| 58 | + Find: for<'b> FnMut(&oid, &'b mut Vec<u8>) -> Result<git_object::BlobRef<'b>, E>, | |
| 59 | +{ | |
| 53 | 60 | fn push(&mut self, is_last_component: bool, stack: &fs::Stack) -> std::io::Result<()> { |
| 54 | 61 | match &mut self.state { |
| 55 | 62 | State::CreateDirectoryAndAttributesStack { |
| @@ -77,11 +84,15 @@ impl<'a> fs::stack::Delegate for StackDelegate<'a> { · 21 unmodified lines | ||
| 77 | 84 | &stack.root, |
| 78 | 85 | stack.current.parent().expect("component was just pushed"), |
| 79 | 86 | self.buf, |
| 87 | + self.attribute_files_in_index, | |
| 88 | + &mut self.find, | |
| 80 | 89 | )?, |
| 81 | 90 | State::IgnoreStack(ignore) => ignore.push( |
| 82 | 91 | &stack.root, |
| 83 | 92 | stack.current.parent().expect("component was just pushed"), |
| 84 | 93 | self.buf, |
| 94 | + self.attribute_files_in_index, | |
| 95 | + &mut self.find, | |
| 85 | 96 | )?, |
| 86 | 97 | } |
| 87 | 98 | Ok(()) |
| @@ -1,7 +1,8 @@ | ||
| 1 | 1 | use crate::fs::cache::{state, State}; |
| 2 | −use crate::fs::PathIdMapping; | |
| 2 | +use crate::fs::PathOidMapping; | |
| 3 | 3 | use bstr::{BStr, BString, ByteSlice}; |
| 4 | 4 | use git_glob::pattern::Case; |
| 5 | +use git_hash::oid; | |
| 5 | 6 | use std::path::Path; |
| 6 | 7 | |
| 7 | 8 | type AttributeMatchGroup = git_attributes::MatchGroup<git_attributes::Attributes>; |
| @@ -51,16 +52,40 @@ impl Ignore { · 43 unmodified lines | ||
| 51 | 52 | } |
| 52 | 53 | } |
| 53 | 54 | |
| 54 | − pub fn push(&mut self, root: &Path, dir: &Path, buf: &mut Vec<u8>) -> std::io::Result<()> { | |
| 55 | − let follow_symlinks = true; | |
| 55 | + pub fn push<Find, E>( | |
| 56 | + &mut self, | |
| 57 | + root: &Path, | |
| 58 | + dir: &Path, | |
| 59 | + buf: &mut Vec<u8>, | |
| 60 | + attribute_files_in_index: &Vec<PathOidMapping<'_>>, | |
| 61 | + _find: Find, | |
| 62 | + ) -> std::io::Result<()> | |
| 63 | + where | |
| 64 | + Find: for<'b> FnMut(&oid, &'b mut Vec<u8>) -> Result<git_object::BlobRef<'b>, E>, | |
| 65 | + { | |
| 66 | + let ignore_path = git_features::path::convert::to_unix_separators( | |
| 67 | + git_features::path::into_bytes_or_panic_on_windows(dir.join(".gitignore")), | |
| 68 | + ); | |
| 69 | + let ignore_file_in_index = attribute_files_in_index.binary_search_by(|t| t.0.cmp(ignore_path.as_bstr())); | |
| 70 | + let ignore_path = git_features::path::from_byte_vec_or_panic_on_windows(ignore_path); | |
| 71 | + let follow_symlinks = ignore_file_in_index.is_err(); | |
| 56 | 72 | if !self |
| 57 | 73 | .stack |
| 58 | − .add_patterns_file(dir.join(".gitignore"), follow_symlinks, Some(root), buf)? | |
| 74 | + .add_patterns_file(ignore_path, follow_symlinks, Some(root), buf)? | |
| 59 | 75 | { |
| 60 | − // Need one stack level per component so push and pop matches. | |
| 61 | − self.stack.patterns.push(Default::default()); | |
| 76 | + match ignore_file_in_index { | |
| 77 | + Ok(_idx) => { | |
| 78 | + // let ignore_blob = find(&attribute_files_in_index[0].1, buf) | |
| 79 | + // .map_err(|err| std::io::Error::new(std::io::ErrorKind::Other, err))?; | |
| 80 | + // self.stack.add_patterns_buffer(ignore_blob.data); | |
| 81 | + todo!() | |
| 82 | + } | |
| 83 | + Err(_) => { | |
| 84 | + // Need one stack level per component so push and pop matches. | |
| 85 | + self.stack.patterns.push(Default::default()) | |
| 86 | + } | |
| 87 | + } | |
| 62 | 88 | } |
| 63 | − // TODO: from index | |
| 64 | 89 | Ok(()) |
| 65 | 90 | } |
| 66 | 91 | } |
| @@ -108,12 +133,12 @@ impl State { · 41 unmodified lines | ||
| 108 | 133 | /// - ignores entries which aren't blobs |
| 109 | 134 | /// - ignores ignore entries which are not skip-worktree |
| 110 | 135 | /// - within merges, picks 'our' stage both for ignore and attribute files. |
| 111 | − pub(crate) fn build_attribute_list<'paths>( | |
| 136 | + pub fn build_attribute_list<'paths>( | |
| 112 | 137 | &self, |
| 113 | 138 | index: &git_index::State, |
| 114 | 139 | paths: &'paths git_index::PathStorage, |
| 115 | 140 | case: git_glob::pattern::Case, |
| 116 | − ) -> Vec<PathIdMapping<'paths>> { | |
| 141 | + ) -> Vec<PathOidMapping<'paths>> { | |
| 117 | 142 | let a1_backing; |
| 118 | 143 | let a2_backing; |
| 119 | 144 | let names = match self { |
| @@ -65,10 +65,10 @@ pub struct Cache<'paths> { · 64 unmodified lines | ||
| 65 | 65 | /// If case folding should happen when looking up attributes or exclusions. |
| 66 | 66 | case: git_glob::pattern::Case, |
| 67 | 67 | /// A lookup table for object ids to read from in some situations when looking up attributes or exclusions. |
| 68 | − attribute_files_in_index: Vec<PathIdMapping<'paths>>, | |
| 68 | + attribute_files_in_index: Vec<PathOidMapping<'paths>>, | |
| 69 | 69 | } |
| 70 | 70 | |
| 71 | −pub(crate) type PathIdMapping<'paths> = (&'paths BStr, git_hash::ObjectId); | |
| 71 | +pub(crate) type PathOidMapping<'paths> = (&'paths BStr, git_hash::ObjectId); | |
| 72 | 72 | |
| 73 | 73 | /// |
| 74 | 74 | pub mod cache; |
| @@ -38,7 +38,7 @@ where · 37 unmodified lines | ||
| 38 | 38 | path: entry_path.to_owned(), |
| 39 | 39 | })?; |
| 40 | 40 | let is_dir = Some(entry.mode == git_index::entry::Mode::COMMIT || entry.mode == git_index::entry::Mode::DIR); |
| 41 | − let dest = path_cache.at_entry(dest_relative, is_dir)?.path(); | |
| 41 | + let dest = path_cache.at_entry(dest_relative, is_dir, &mut *find)?.path(); | |
| 42 | 42 | |
| 43 | 43 | let object_size = match entry.mode { |
| 44 | 44 | git_index::entry::Mode::FILE | git_index::entry::Mode::FILE_EXECUTABLE => { |
| @@ -4,6 +4,13 @@ mod create_directory { · 3 unmodified lines | ||
| 4 | 4 | use git_worktree::fs; |
| 5 | 5 | use tempfile::{tempdir, TempDir}; |
| 6 | 6 | |
| 7 | + fn panic_on_find<'buf>( | |
| 8 | + _oid: &git_hash::oid, | |
| 9 | + _buf: &'buf mut Vec<u8>, | |
| 10 | + ) -> std::io::Result<git_object::BlobRef<'buf>> { | |
| 11 | + unreachable!("find should nto be called") | |
| 12 | + } | |
| 13 | + | |
| 7 | 14 | #[test] |
| 8 | 15 | fn root_is_assumed_to_exist_and_files_in_root_do_not_create_directory() { |
| 9 | 16 | let dir = tempdir().unwrap(); |
| @@ -16,7 +23,7 @@ mod create_directory { · 6 unmodified lines | ||
| 16 | 23 | ); |
| 17 | 24 | assert_eq!(cache.num_mkdir_calls(), 0); |
| 18 | 25 | |
| 19 | − let path = cache.at_entry("hello", Some(false)).unwrap().path(); | |
| 26 | + let path = cache.at_entry("hello", Some(false), panic_on_find).unwrap().path(); | |
| 20 | 27 | assert!(!path.parent().unwrap().exists(), "prefix itself is never created"); |
| 21 | 28 | assert_eq!(cache.num_mkdir_calls(), 0); |
| 22 | 29 | } |
| @@ -32,7 +39,10 @@ mod create_directory { · 9 unmodified lines | ||
| 32 | 39 | ("exe", Some(false)), |
| 33 | 40 | ("link", None), |
| 34 | 41 | ] { |
| 35 | − let path = cache.at_entry(Path::new("dir").join(name), *is_dir).unwrap().path(); | |
| 42 | + let path = cache | |
| 43 | + .at_entry(Path::new("dir").join(name), *is_dir, panic_on_find) | |
| 44 | + .unwrap() | |
| 45 | + .path(); | |
| 36 | 46 | assert!(path.parent().unwrap().is_dir(), "dir exists"); |
| 37 | 47 | } |
| 38 | 48 | |
| @@ -44,7 +54,7 @@ mod create_directory { · 5 unmodified lines | ||
| 44 | 54 | let (mut cache, tmp) = new_cache(); |
| 45 | 55 | std::fs::create_dir(tmp.path().join("dir")).unwrap(); |
| 46 | 56 | |
| 47 | − let path = cache.at_entry("dir/file", Some(false)).unwrap().path(); | |
| 57 | + let path = cache.at_entry("dir/file", Some(false), panic_on_find).unwrap().path(); | |
| 48 | 58 | assert!(path.parent().unwrap().is_dir(), "directory is still present"); |
| 49 | 59 | assert!(!path.exists(), "it won't create the file"); |
| 50 | 60 | assert_eq!(cache.num_mkdir_calls(), 1); |
| @@ -62,7 +72,10 @@ mod create_directory { · 11 unmodified lines | ||
| 62 | 72 | cache.unlink_on_collision(false); |
| 63 | 73 | let relative_path = format!("{}/file", dirname); |
| 64 | 74 | assert_eq!( |
| 65 | − cache.at_entry(&relative_path, Some(false)).unwrap_err().kind(), | |
| 75 | + cache | |
| 76 | + .at_entry(&relative_path, Some(false), panic_on_find) | |
| 77 | + .unwrap_err() | |
| 78 | + .kind(), | |
| 66 | 79 | std::io::ErrorKind::AlreadyExists |
| 67 | 80 | ); |
| 68 | 81 | } |
| @@ -75,7 +88,10 @@ mod create_directory { · 6 unmodified lines | ||
| 75 | 88 | for dirname in &["link-to-dir", "file-in-dir"] { |
| 76 | 89 | cache.unlink_on_collision(true); |
| 77 | 90 | let relative_path = format!("{}/file", dirname); |
| 78 | − let path = cache.at_entry(&relative_path, Some(false)).unwrap().path(); | |
| 91 | + let path = cache | |
| 92 | + .at_entry(&relative_path, Some(false), panic_on_find) | |
| 93 | + .unwrap() | |
| 94 | + .path(); | |
| 79 | 95 | assert!(path.parent().unwrap().is_dir(), "directory was forcefully created"); |
| 80 | 96 | assert!(!path.exists()); |
| 81 | 97 | } |
| @@ -105,6 +121,8 @@ mod ignore_and_attributes { · 23 unmodified lines | ||
| 105 | 121 | use std::path::Path; |
| 106 | 122 | |
| 107 | 123 | use git_index::entry::Mode; |
| 124 | + use git_odb::pack::bundle::write::Options; | |
| 125 | + use git_odb::FindExt; | |
| 108 | 126 | use git_worktree::fs; |
| 109 | 127 | use tempfile::{tempdir, TempDir}; |
| 110 | 128 | |
| @@ -134,7 +152,6 @@ mod ignore_and_attributes { · 23 unmodified lines | ||
| 134 | 152 | } |
| 135 | 153 | |
| 136 | 154 | #[test] |
| 137 | − #[ignore] | |
| 138 | 155 | fn check_against_baseline() { |
| 139 | 156 | let dir = git_testtools::scripted_fixture_repo_read_only("make_ignore_and_attributes_setup.sh").unwrap(); |
| 140 | 157 | let worktree_dir = dir.join("repo"); |
| @@ -144,20 +161,21 @@ mod ignore_and_attributes { · 3 unmodified lines | ||
| 144 | 161 | let user_exclude_path = dir.join("user.exclude"); |
| 145 | 162 | assert!(user_exclude_path.is_file()); |
| 146 | 163 | |
| 147 | − let mut cache = fs::Cache::new( | |
| 148 | − &worktree_dir, | |
| 149 | − git_worktree::fs::cache::State::for_add( | |
| 150 | − Default::default(), | |
| 151 | − git_worktree::fs::cache::state::Ignore::new( | |
| 152 | − git_attributes::MatchGroup::from_overrides(vec!["!force-include"]), | |
| 153 | − git_attributes::MatchGroup::from_git_dir(&git_dir, Some(user_exclude_path), &mut buf).unwrap(), | |
| 154 | − None, | |
| 155 | − ), | |
| 164 | + let mut index = git_index::File::at(git_dir.join("index"), Default::default()).unwrap(); | |
| 165 | + let odb = git_odb::at(git_dir.join("objects")).unwrap(); | |
| 166 | + let state = git_worktree::fs::cache::State::for_add( | |
| 167 | + Default::default(), // TODO: attribute tests | |
| 168 | + git_worktree::fs::cache::state::Ignore::new( | |
| 169 | + git_attributes::MatchGroup::from_overrides(vec!["!force-include"]), | |
| 170 | + git_attributes::MatchGroup::from_git_dir(&git_dir, Some(user_exclude_path), &mut buf).unwrap(), | |
| 171 | + None, | |
| 156 | 172 | ), |
| 157 | − Default::default(), | |
| 158 | − buf, | |
| 159 | − Default::default(), // TODO: get the index, use it to ask state to get the attributes files LUT | |
| 160 | 173 | ); |
| 174 | + let case = git_glob::pattern::Case::Sensitive; | |
| 175 | + let paths_storage = index.take_path_backing(); | |
| 176 | + let attribute_files_in_index = state.build_attribute_list(&index.state, &paths_storage, case); | |
| 177 | + assert_eq!(attribute_files_in_index, vec![]); | |
| 178 | + let mut cache = fs::Cache::new(&worktree_dir, state, case, buf, attribute_files_in_index); | |
| 161 | 179 | |
| 162 | 180 | for (relative_path, source_and_line) in (IgnoreExpectations { |
| 163 | 181 | lines: baseline.lines(), |
| @@ -168,7 +186,9 @@ mod ignore_and_attributes { · 4 unmodified lines | ||
| 168 | 186 | // TODO: ignore file in index only |
| 169 | 187 | // TODO: dir-excludes |
| 170 | 188 | // TODO: a sibling dir to exercise pop() impl. |
| 171 | − let platform = cache.at_entry(relative_path, is_dir).unwrap(); | |
| 189 | + let platform = cache | |
| 190 | + .at_entry(relative_path, is_dir, |oid, buf| odb.find_blob(oid, buf)) | |
| 191 | + .unwrap(); | |
| 172 | 192 | |
| 173 | 193 | let match_ = platform.matching_exclude_pattern(); |
| 174 | 194 | let is_excluded = platform.is_excluded(); |