Skip to content

Commit 749c310

Browse files
committed
Support for forceful removal of symlinks or files during dir creation (#301)
1 parent de58f50 commit 749c310

File tree

3 files changed

+39
-3
lines changed

3 files changed

+39
-3
lines changed

git-worktree/src/index/checkout.rs

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@ pub struct PathCache {
2727
/// The amount of path components of 'valid' beyond the roots components. If `root` has 2, and this is 2, `valid` has 4 components.
2828
valid_components: usize,
2929

30+
/// 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,
32+
3033
/// just for testing
3134
#[cfg(debug_assertions)]
3235
pub test_mkdir_calls: usize,
@@ -46,7 +49,9 @@ mod cache {
4649
valid_relative: PathBuf::with_capacity(128),
4750
valid_components: 0,
4851
root,
52+
#[cfg(debug_assertions)]
4953
test_mkdir_calls: 0,
54+
unlink_on_collision: false,
5055
}
5156
}
5257

@@ -90,11 +95,28 @@ mod cache {
9095
self.valid_relative.push(comp);
9196
self.valid_components += 1;
9297
if components.peek().is_some() || target_is_dir {
93-
self.test_mkdir_calls += 1;
98+
#[cfg(debug_assertions)]
99+
{
100+
self.test_mkdir_calls += 1;
101+
}
94102
match std::fs::create_dir(&self.valid) {
95103
Ok(()) => {}
96104
Err(err) if err.kind() == std::io::ErrorKind::AlreadyExists => {
97-
if !self.valid.symlink_metadata()?.is_dir() {
105+
let meta = self.valid.symlink_metadata()?;
106+
if !meta.is_dir() {
107+
if self.unlink_on_collision {
108+
if meta.is_symlink() {
109+
symlink::remove_symlink_auto(&self.valid)?;
110+
} else {
111+
std::fs::remove_file(&self.valid)?;
112+
}
113+
#[cfg(debug_assertions)]
114+
{
115+
self.test_mkdir_calls += 1;
116+
}
117+
std::fs::create_dir(&self.valid)?;
118+
continue;
119+
}
98120
return Err(err);
99121
}
100122
}

git-worktree/src/index/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,10 @@ where
2424

2525
use std::io::ErrorKind::AlreadyExists;
2626
let dir = dir.into();
27+
2728
let mut path_cache = PathCache::new(dir.clone());
29+
path_cache.unlink_on_collision = options.overwrite_existing;
30+
2831
let mut buf = Vec::new();
2932
let mut collisions = Vec::new();
3033

git-worktree/tests/index/mod.rs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ mod checkout {
4848
.unwrap();
4949
assert!(path.parent().unwrap().is_dir(), "directory is still present");
5050
assert!(!path.exists(), "it won't create the file");
51+
assert_eq!(cache.test_mkdir_calls, 1);
5152
}
5253

5354
#[test]
@@ -59,14 +60,24 @@ mod checkout {
5960
std::fs::write(tmp.path().join("file-in-dir"), &[]).unwrap();
6061

6162
for dirname in &["link-to-dir", "file-in-dir"] {
63+
cache.unlink_on_collision = false;
64+
let relative_path = format!("{}/file", dirname);
6265
assert_eq!(
6366
cache
64-
.append_relative_path_assure_leading_dir(format!("{}/file", dirname), Mode::FILE)
67+
.append_relative_path_assure_leading_dir(&relative_path, Mode::FILE)
6568
.unwrap_err()
6669
.kind(),
6770
std::io::ErrorKind::AlreadyExists
6871
);
72+
cache.unlink_on_collision = true;
73+
74+
let path = cache
75+
.append_relative_path_assure_leading_dir(&relative_path, Mode::FILE)
76+
.unwrap();
77+
assert!(path.parent().unwrap().is_dir(), "directory was forcefully created");
78+
assert!(!path.exists());
6979
}
80+
assert_eq!(cache.test_mkdir_calls, 4);
7081
}
7182

7283
fn new_cache() -> (PathCache, TempDir) {

0 commit comments

Comments
 (0)