History

wire everything up to have all data where it needs to be, but… (#301)

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.

Sebastian Thiel committed 4 years ago

Commit 34d0d5c1 parent 883d78d6 files · +100 −37Browse files

Unified · Split

6 files
  1. git-worktree/src/fs/cache/mod.rs +11 −4
  2. git-worktree/src/fs/cache/platform.rs +13 −2
  3. git-worktree/src/fs/cache/state.rs +34 −9
  4. git-worktree/src/fs/mod.rs +2 −2
  5. git-worktree/src/index/entry.rs +1 −1
  6. git-worktree/tests/worktree/fs/cache.rs +39 −19
git-worktree/src/fs/cache/mod.rs+11−4
@@ -1,6 +1,7 @@
1use super::Cache;1use super::Cache;
2use crate::fs;2use crate::fs;
3use crate::fs::PathIdMapping;3use crate::fs::PathOidMapping;
4use git_hash::oid;
4use std::path::{Path, PathBuf};5use std::path::{Path, PathBuf};
5 6
6#[derive(Clone)]7#[derive(Clone)]
@@ -66,7 +67,7 @@ impl<'paths> Cache<'paths> { · 59 unmodified lines
66 state: State,67 state: State,
67 case: git_glob::pattern::Case,68 case: git_glob::pattern::Case,
68 buf: Vec<u8>,69 buf: Vec<u8>,
69 attribute_files_in_index: Vec<PathIdMapping<'paths>>,70 attribute_files_in_index: Vec<PathOidMapping<'paths>>,
70 ) -> Self {71 ) -> Self {
71 let root = worktree_root.into();72 let root = worktree_root.into();
72 Cache {73 Cache {
@@ -83,15 +84,21 @@ impl<'paths> Cache<'paths> { · 10 unmodified lines
83 /// path is created as directory. If it's not known it is assumed to be a file.84 /// path is created as directory. If it's not known it is assumed to be a file.
84 ///85 ///
85 /// Provide access to cached information for that `relative` entry via the platform returned.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 &mut self,88 &mut self,
88 relative: impl AsRef<Path>,89 relative: impl AsRef<Path>,
89 is_dir: Option<bool>,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 let mut platform = platform::StackDelegate {96 let mut platform = platform::StackDelegate {
92 state: &mut self.state,97 state: &mut self.state,
93 buf: &mut self.buf,98 buf: &mut self.buf,
94 is_dir,99 is_dir,
100 attribute_files_in_index: &self.attribute_files_in_index,
101 find,
95 };102 };
96 self.stack.make_relative_path_current(relative, &mut platform)?;103 self.stack.make_relative_path_current(relative, &mut platform)?;
97 Ok(Platform { parent: self, is_dir })104 Ok(Platform { parent: self, is_dir })
git-worktree/src/fs/cache/platform.rs+13−2
@@ -1,5 +1,7 @@
1use crate::fs;1use crate::fs;
2use crate::fs::cache::{Platform, State};2use crate::fs::cache::{Platform, State};
3use crate::fs::PathOidMapping;
4use git_hash::oid;
3use std::path::Path;5use std::path::Path;
4 6
5impl<'a, 'paths> Platform<'a, 'paths> {7impl<'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
46pub struct StackDelegate<'a> {48pub struct StackDelegate<'a, 'paths, Find> {
47 pub state: &'a mut State,49 pub state: &'a mut State,
48 pub buf: &'a mut Vec<u8>,50 pub buf: &'a mut Vec<u8>,
49 pub is_dir: Option<bool>,51 pub is_dir: Option<bool>,
52 pub attribute_files_in_index: &'a Vec<PathOidMapping<'paths>>,
53 pub find: Find,
50}54}
51 55
52impl<'a> fs::stack::Delegate for StackDelegate<'a> {56impl<'a, 'paths, Find, E> fs::stack::Delegate for StackDelegate<'a, 'paths, Find>
57where
58 Find: for<'b> FnMut(&oid, &'b mut Vec<u8>) -> Result<git_object::BlobRef<'b>, E>,
59{
53 fn push(&mut self, is_last_component: bool, stack: &fs::Stack) -> std::io::Result<()> {60 fn push(&mut self, is_last_component: bool, stack: &fs::Stack) -> std::io::Result<()> {
54 match &mut self.state {61 match &mut self.state {
55 State::CreateDirectoryAndAttributesStack {62 State::CreateDirectoryAndAttributesStack {
@@ -77,11 +84,15 @@ impl<'a> fs::stack::Delegate for StackDelegate<'a> { · 21 unmodified lines
77 &stack.root,84 &stack.root,
78 stack.current.parent().expect("component was just pushed"),85 stack.current.parent().expect("component was just pushed"),
79 self.buf,86 self.buf,
87 self.attribute_files_in_index,
88 &mut self.find,
80 )?,89 )?,
81 State::IgnoreStack(ignore) => ignore.push(90 State::IgnoreStack(ignore) => ignore.push(
82 &stack.root,91 &stack.root,
83 stack.current.parent().expect("component was just pushed"),92 stack.current.parent().expect("component was just pushed"),
84 self.buf,93 self.buf,
94 self.attribute_files_in_index,
95 &mut self.find,
85 )?,96 )?,
86 }97 }
87 Ok(())98 Ok(())
git-worktree/src/fs/cache/state.rs+34−9
@@ -1,7 +1,8 @@
1use crate::fs::cache::{state, State};1use crate::fs::cache::{state, State};
2use crate::fs::PathIdMapping;2use crate::fs::PathOidMapping;
3use bstr::{BStr, BString, ByteSlice};3use bstr::{BStr, BString, ByteSlice};
4use git_glob::pattern::Case;4use git_glob::pattern::Case;
5use git_hash::oid;
5use std::path::Path;6use std::path::Path;
6 7
7type AttributeMatchGroup = git_attributes::MatchGroup<git_attributes::Attributes>;8type 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 pub fn push<Find, E>(
55 let follow_symlinks = true;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 if !self72 if !self
57 .stack73 .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.76 match ignore_file_in_index {
61 self.stack.patterns.push(Default::default());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 Ok(())89 Ok(())
65 }90 }
66}91}
@@ -108,12 +133,12 @@ impl State { · 41 unmodified lines
108 /// - ignores entries which aren't blobs133 /// - ignores entries which aren't blobs
109 /// - ignores ignore entries which are not skip-worktree134 /// - ignores ignore entries which are not skip-worktree
110 /// - within merges, picks 'our' stage both for ignore and attribute files.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 &self,137 &self,
113 index: &git_index::State,138 index: &git_index::State,
114 paths: &'paths git_index::PathStorage,139 paths: &'paths git_index::PathStorage,
115 case: git_glob::pattern::Case,140 case: git_glob::pattern::Case,
116 ) -> Vec<PathIdMapping<'paths>> {141 ) -> Vec<PathOidMapping<'paths>> {
117 let a1_backing;142 let a1_backing;
118 let a2_backing;143 let a2_backing;
119 let names = match self {144 let names = match self {
git-worktree/src/fs/mod.rs+2−2
@@ -65,10 +65,10 @@ pub struct Cache<'paths> { · 64 unmodified lines
65 /// If case folding should happen when looking up attributes or exclusions.65 /// If case folding should happen when looking up attributes or exclusions.
66 case: git_glob::pattern::Case,66 case: git_glob::pattern::Case,
67 /// A lookup table for object ids to read from in some situations when looking up attributes or exclusions.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
71pub(crate) type PathIdMapping<'paths> = (&'paths BStr, git_hash::ObjectId);71pub(crate) type PathOidMapping<'paths> = (&'paths BStr, git_hash::ObjectId);
72 72
73///73///
74pub mod cache;74pub mod cache;
git-worktree/src/index/entry.rs+1−1
@@ -38,7 +38,7 @@ where · 37 unmodified lines
38 path: entry_path.to_owned(),38 path: entry_path.to_owned(),
39 })?;39 })?;
40 let is_dir = Some(entry.mode == git_index::entry::Mode::COMMIT || entry.mode == git_index::entry::Mode::DIR);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 let object_size = match entry.mode {43 let object_size = match entry.mode {
44 git_index::entry::Mode::FILE | git_index::entry::Mode::FILE_EXECUTABLE => {44 git_index::entry::Mode::FILE | git_index::entry::Mode::FILE_EXECUTABLE => {
git-worktree/tests/worktree/fs/cache.rs+39−19
@@ -4,6 +4,13 @@ mod create_directory { · 3 unmodified lines
4 use git_worktree::fs;4 use git_worktree::fs;
5 use tempfile::{tempdir, TempDir};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 #[test]14 #[test]
8 fn root_is_assumed_to_exist_and_files_in_root_do_not_create_directory() {15 fn root_is_assumed_to_exist_and_files_in_root_do_not_create_directory() {
9 let dir = tempdir().unwrap();16 let dir = tempdir().unwrap();
@@ -16,7 +23,7 @@ mod create_directory { · 6 unmodified lines
16 );23 );
17 assert_eq!(cache.num_mkdir_calls(), 0);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 assert!(!path.parent().unwrap().exists(), "prefix itself is never created");27 assert!(!path.parent().unwrap().exists(), "prefix itself is never created");
21 assert_eq!(cache.num_mkdir_calls(), 0);28 assert_eq!(cache.num_mkdir_calls(), 0);
22 }29 }
@@ -32,7 +39,10 @@ mod create_directory { · 9 unmodified lines
32 ("exe", Some(false)),39 ("exe", Some(false)),
33 ("link", None),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 assert!(path.parent().unwrap().is_dir(), "dir exists");46 assert!(path.parent().unwrap().is_dir(), "dir exists");
37 }47 }
38 48
@@ -44,7 +54,7 @@ mod create_directory { · 5 unmodified lines
44 let (mut cache, tmp) = new_cache();54 let (mut cache, tmp) = new_cache();
45 std::fs::create_dir(tmp.path().join("dir")).unwrap();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 assert!(path.parent().unwrap().is_dir(), "directory is still present");58 assert!(path.parent().unwrap().is_dir(), "directory is still present");
49 assert!(!path.exists(), "it won't create the file");59 assert!(!path.exists(), "it won't create the file");
50 assert_eq!(cache.num_mkdir_calls(), 1);60 assert_eq!(cache.num_mkdir_calls(), 1);
@@ -62,7 +72,10 @@ mod create_directory { · 11 unmodified lines
62 cache.unlink_on_collision(false);72 cache.unlink_on_collision(false);
63 let relative_path = format!("{}/file", dirname);73 let relative_path = format!("{}/file", dirname);
64 assert_eq!(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 std::io::ErrorKind::AlreadyExists79 std::io::ErrorKind::AlreadyExists
67 );80 );
68 }81 }
@@ -75,7 +88,10 @@ mod create_directory { · 6 unmodified lines
75 for dirname in &["link-to-dir", "file-in-dir"] {88 for dirname in &["link-to-dir", "file-in-dir"] {
76 cache.unlink_on_collision(true);89 cache.unlink_on_collision(true);
77 let relative_path = format!("{}/file", dirname);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 assert!(path.parent().unwrap().is_dir(), "directory was forcefully created");95 assert!(path.parent().unwrap().is_dir(), "directory was forcefully created");
80 assert!(!path.exists());96 assert!(!path.exists());
81 }97 }
@@ -105,6 +121,8 @@ mod ignore_and_attributes { · 23 unmodified lines
105 use std::path::Path;121 use std::path::Path;
106 122
107 use git_index::entry::Mode;123 use git_index::entry::Mode;
124 use git_odb::pack::bundle::write::Options;
125 use git_odb::FindExt;
108 use git_worktree::fs;126 use git_worktree::fs;
109 use tempfile::{tempdir, TempDir};127 use tempfile::{tempdir, TempDir};
110 128
@@ -134,7 +152,6 @@ mod ignore_and_attributes { · 23 unmodified lines
134 }152 }
135 153
136 #[test]154 #[test]
137 #[ignore]
138 fn check_against_baseline() {155 fn check_against_baseline() {
139 let dir = git_testtools::scripted_fixture_repo_read_only("make_ignore_and_attributes_setup.sh").unwrap();156 let dir = git_testtools::scripted_fixture_repo_read_only("make_ignore_and_attributes_setup.sh").unwrap();
140 let worktree_dir = dir.join("repo");157 let worktree_dir = dir.join("repo");
@@ -144,20 +161,21 @@ mod ignore_and_attributes { · 3 unmodified lines
144 let user_exclude_path = dir.join("user.exclude");161 let user_exclude_path = dir.join("user.exclude");
145 assert!(user_exclude_path.is_file());162 assert!(user_exclude_path.is_file());
146 163
147 let mut cache = fs::Cache::new(164 let mut index = git_index::File::at(git_dir.join("index"), Default::default()).unwrap();
148 &worktree_dir,165 let odb = git_odb::at(git_dir.join("objects")).unwrap();
149 git_worktree::fs::cache::State::for_add(166 let state = git_worktree::fs::cache::State::for_add(
150 Default::default(),167 Default::default(), // TODO: attribute tests
151 git_worktree::fs::cache::state::Ignore::new(168 git_worktree::fs::cache::state::Ignore::new(
152 git_attributes::MatchGroup::from_overrides(vec!["!force-include"]),169 git_attributes::MatchGroup::from_overrides(vec!["!force-include"]),
153 git_attributes::MatchGroup::from_git_dir(&git_dir, Some(user_exclude_path), &mut buf).unwrap(),170 git_attributes::MatchGroup::from_git_dir(&git_dir, Some(user_exclude_path), &mut buf).unwrap(),
154 None,171 None,
155 ),
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 for (relative_path, source_and_line) in (IgnoreExpectations {180 for (relative_path, source_and_line) in (IgnoreExpectations {
163 lines: baseline.lines(),181 lines: baseline.lines(),
@@ -168,7 +186,9 @@ mod ignore_and_attributes { · 4 unmodified lines
168 // TODO: ignore file in index only186 // TODO: ignore file in index only
169 // TODO: dir-excludes187 // TODO: dir-excludes
170 // TODO: a sibling dir to exercise pop() impl.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 let match_ = platform.matching_exclude_pattern();193 let match_ = platform.matching_exclude_pattern();
174 let is_excluded = platform.is_excluded();194 let is_excluded = platform.is_excluded();