Tests for PathCache still work, which means that a lot of logic should be usable right away for implementing ignore paths.
Unified · Split
| @@ -189,7 +189,7 @@ pub mod stack { · 188 unmodified lines | |||
| 189 | /// along with the stacks state for inspection to perform an operation that produces some data. | 189 | /// along with the stacks state for inspection to perform an operation that produces some data. |
| 190 | /// | 190 | /// |
| 191 | /// The full path to `relative` will be returned along with the data returned by push_comp. | 191 | /// The full path to `relative` will be returned along with the data returned by push_comp. |
| 192 | pub fn make_relative_path_current<F>( | 192 | pub fn make_relative_path_current( |
| 193 | &mut self, | 193 | &mut self, |
| 194 | relative: impl AsRef<Path>, | 194 | relative: impl AsRef<Path>, |
| 195 | mut push_comp: impl FnMut(&mut std::iter::Peekable<std::path::Components<'_>>, &Self) -> std::io::Result<T>, | 195 | mut push_comp: impl FnMut(&mut std::iter::Peekable<std::path::Components<'_>>, &Self) -> std::io::Result<T>, |
| @@ -1,5 +1,4 @@ | |||
| 1 | use std::path::PathBuf; | 1 | use crate::fs; |
| 2 | |||
| 3 | use bstr::BString; | 2 | use bstr::BString; |
| 4 | 3 | ||
| 5 | /// A cache for directory creation to reduce the amount of stat calls when creating | 4 | /// A cache for directory creation to reduce the amount of stat calls when creating |
| @@ -18,15 +17,7 @@ use bstr::BString; · 12 unmodified lines | |||
| 18 | /// The caching is only useful if consecutive calls to create a directory are using a sorted list of entries. | 17 | /// The caching is only useful if consecutive calls to create a directory are using a sorted list of entries. |
| 19 | #[allow(unused)] | 18 | #[allow(unused)] |
| 20 | pub struct PathCache { | 19 | pub struct PathCache { |
| 21 | /// The prefix/root for all paths we handle. | 20 | stack: fs::Stack<()>, |
| 22 | root: PathBuf, | ||
| 23 | /// the most recent known cached that we know is valid. | ||
| 24 | valid: PathBuf, | ||
| 25 | /// The relative portion of `valid` that was added previously. | ||
| 26 | valid_relative: PathBuf, | ||
| 27 | /// The amount of path components of 'valid' beyond the roots components. If `root` has 2, and this is 2, `valid` has 4 components. | ||
| 28 | valid_components: usize, | ||
| 29 | |||
| 30 | /// If there is a symlink or a file in our path, try to unlink it before creating the directory. | 21 | /// If there is a symlink or a file in our path, try to unlink it before creating the directory. |
| 31 | pub unlink_on_collision: bool, | 22 | pub unlink_on_collision: bool, |
| 32 | 23 | ||
| @@ -39,7 +30,7 @@ mod cache { · 6 unmodified lines | |||
| 39 | use std::path::{Path, PathBuf}; | 30 | use std::path::{Path, PathBuf}; |
| 40 | 31 | ||
| 41 | use super::PathCache; | 32 | use super::PathCache; |
| 42 | use crate::os; | 33 | use crate::{fs, os}; |
| 43 | 34 | ||
| 44 | impl PathCache { | 35 | impl PathCache { |
| 45 | /// Create a new instance with `root` being the base for all future paths we handle, assuming it to be valid which includes | 36 | /// Create a new instance with `root` being the base for all future paths we handle, assuming it to be valid which includes |
| @@ -47,10 +38,7 @@ mod cache { · 1 unmodified line | |||
| 47 | pub fn new(root: impl Into<PathBuf>) -> Self { | 38 | pub fn new(root: impl Into<PathBuf>) -> Self { |
| 48 | let root = root.into(); | 39 | let root = root.into(); |
| 49 | PathCache { | 40 | PathCache { |
| 50 | valid: root.clone(), | 41 | stack: fs::Stack::new(root), |
| 51 | valid_relative: PathBuf::with_capacity(128), | ||
| 52 | valid_components: 0, | ||
| 53 | root, | ||
| 54 | #[cfg(debug_assertions)] | 42 | #[cfg(debug_assertions)] |
| 55 | test_mkdir_calls: 0, | 43 | test_mkdir_calls: 0, |
| 56 | unlink_on_collision: false, | 44 | unlink_on_collision: false, |
| @@ -66,58 +54,33 @@ mod cache { · 9 unmodified lines | |||
| 66 | relative: impl AsRef<Path>, | 54 | relative: impl AsRef<Path>, |
| 67 | mode: git_index::entry::Mode, | 55 | mode: git_index::entry::Mode, |
| 68 | ) -> std::io::Result<&Path> { | 56 | ) -> std::io::Result<&Path> { |
| 69 | let relative = relative.as_ref(); | 57 | #[cfg(debug_assertions)] |
| 70 | debug_assert!( | 58 | let mkdir_calls = &mut self.test_mkdir_calls; |
| 71 | relative.is_relative(), | 59 | let unlink_on_collision = self.unlink_on_collision; |
| 72 | "only index paths are handled correctly here, must be relative" | 60 | self.stack |
| 73 | ); | 61 | .make_relative_path_current(relative, |components, stack: &fs::Stack<()>| { |
| 74 | 62 | let target_is_dir = mode == git_index::entry::Mode::COMMIT || mode == git_index::entry::Mode::DIR; | |
| 75 | let mut components = relative.components().peekable(); | ||
| 76 | let mut existing_components = self.valid_relative.components(); | ||
| 77 | let mut matching_components = 0; | ||
| 78 | while let (Some(existing_comp), Some(new_comp)) = (existing_components.next(), components.peek()) { | ||
| 79 | if existing_comp == *new_comp { | ||
| 80 | components.next(); | ||
| 81 | matching_components += 1; | ||
| 82 | } else { | ||
| 83 | break; | ||
| 84 | } | ||
| 85 | } | ||
| 86 | |||
| 87 | for _ in 0..self.valid_components - matching_components { | ||
| 88 | self.valid.pop(); | ||
| 89 | self.valid_relative.pop(); | ||
| 90 | } | ||
| 91 | |||
| 92 | self.valid_components = matching_components; | ||
| 93 | |||
| 94 | let target_is_dir = mode == git_index::entry::Mode::COMMIT || mode == git_index::entry::Mode::DIR; | ||
| 95 | while let Some(comp) = components.next() { | ||
| 96 | self.valid.push(comp); | ||
| 97 | self.valid_relative.push(comp); | ||
| 98 | self.valid_components += 1; | ||
| 99 | let res = (|| { | ||
| 100 | if components.peek().is_some() || target_is_dir { | 63 | if components.peek().is_some() || target_is_dir { |
| 101 | #[cfg(debug_assertions)] | 64 | #[cfg(debug_assertions)] |
| 102 | { | 65 | { |
| 103 | self.test_mkdir_calls += 1; | 66 | *mkdir_calls += 1; |
| 104 | } | 67 | } |
| 105 | match std::fs::create_dir(&self.valid) { | 68 | match std::fs::create_dir(stack.current()) { |
| 106 | Ok(()) => {} | 69 | Ok(()) => {} |
| 107 | Err(err) if err.kind() == std::io::ErrorKind::AlreadyExists => { | 70 | Err(err) if err.kind() == std::io::ErrorKind::AlreadyExists => { |
| 108 | let meta = self.valid.symlink_metadata()?; | 71 | let meta = stack.current().symlink_metadata()?; |
| 109 | if !meta.is_dir() { | 72 | if !meta.is_dir() { |
| 110 | if self.unlink_on_collision { | 73 | if unlink_on_collision { |
| 111 | if meta.is_symlink() { | 74 | if meta.is_symlink() { |
| 112 | os::remove_symlink(&self.valid)?; | 75 | os::remove_symlink(stack.current())?; |
| 113 | } else { | 76 | } else { |
| 114 | std::fs::remove_file(&self.valid)?; | 77 | std::fs::remove_file(stack.current())?; |
| 115 | } | 78 | } |
| 116 | #[cfg(debug_assertions)] | 79 | #[cfg(debug_assertions)] |
| 117 | { | 80 | { |
| 118 | self.test_mkdir_calls += 1; | 81 | *mkdir_calls += 1; |
| 119 | } | 82 | } |
| 120 | std::fs::create_dir(&self.valid)?; | 83 | std::fs::create_dir(stack.current())?; |
| 121 | } else { | 84 | } else { |
| 122 | return Err(err); | 85 | return Err(err); |
| 123 | } | 86 | } |
| @@ -127,17 +90,8 @@ mod cache { · 3 unmodified lines | |||
| 127 | } | 90 | } |
| 128 | } | 91 | } |
| 129 | Ok(()) | 92 | Ok(()) |
| 130 | })(); | 93 | }) |
| 131 | 94 | .map(|(p, _)| p) | |
| 132 | if let Err(err) = res { | ||
| 133 | self.valid.pop(); | ||
| 134 | self.valid_relative.pop(); | ||
| 135 | self.valid_components -= 1; | ||
| 136 | return Err(err); | ||
| 137 | } | ||
| 138 | } | ||
| 139 | |||
| 140 | Ok(&self.valid) | ||
| 141 | } | 95 | } |
| 142 | } | 96 | } |
| 143 | } | 97 | } |