History

port PathCache over to `Stack` (#301)

Tests for PathCache still work, which means that a lot of logic should be usable right away for implementing ignore paths.

Sebastian Thiel committed 4 years ago

Commit ebfea8d1 parent 0d3ba1a2 files · +21 −67Browse files

Unified · Split

2 files
  1. git-worktree/src/fs.rs +1 −1
  2. git-worktree/src/index/checkout.rs +20 −66
git-worktree/src/fs.rs+1−1
@@ -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>,
git-worktree/src/index/checkout.rs+20−66
@@ -1,5 +1,4 @@
1use std::path::PathBuf;1use crate::fs;
2
3use bstr::BString;2use bstr::BString;
4 3
5/// A cache for directory creation to reduce the amount of stat calls when creating4/// 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)]
20pub struct PathCache {19pub 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 includes36 /// 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}