Skip to content

Commit

Permalink
fix: default to creating file-symlinks if it is dangling on Windows (#…
Browse files Browse the repository at this point in the history
…1354)

This behaviour is the same as in Git.
  • Loading branch information
Byron committed May 13, 2024
1 parent 185eb51 commit 8e64b40
Show file tree
Hide file tree
Showing 4 changed files with 47 additions and 6 deletions.
8 changes: 7 additions & 1 deletion gix-fs/src/symlink.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,13 @@ pub fn remove(path: &Path) -> io::Result<()> {
pub fn create(original: &Path, link: &Path) -> io::Result<()> {
use std::os::windows::fs::{symlink_dir, symlink_file};
// TODO: figure out if links to links count as files or whatever they point at
if std::fs::metadata(link.parent().expect("dir for link").join(original))?.is_dir() {
let orig_abs = link.parent().expect("dir for link").join(original);
let is_dir = match std::fs::metadata(orig_abs) {
Ok(m) => m.is_dir(),
Err(err) if err.kind() == io::ErrorKind::NotFound => false,
Err(err) => return Err(err),
};
if is_dir {
symlink_dir(original, link)
} else {
symlink_file(original, link)
Expand Down
Binary file not shown.
11 changes: 11 additions & 0 deletions gix-worktree-state/tests/fixtures/make_dangling_symlink.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#!/bin/bash
set -eu -o pipefail

git init -q

target_oid=$(echo -n "non-existing-target" | git hash-object -w --stdin)
git update-index --index-info <<-EOF
120000 $target_oid dangling
EOF

git commit -m "dangling symlink in index"
34 changes: 29 additions & 5 deletions gix-worktree-state/tests/state/checkout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,11 @@ fn driver_exe() -> String {
exe
}

fn assure_is_empty(dir: impl AsRef<Path>) -> std::io::Result<()> {
assert_eq!(std::fs::read_dir(dir)?.count(), 0);
Ok(())
}

#[test]
fn submodules_are_instantiated_as_directories() -> crate::Result {
let mut opts = opts_from_probe();
Expand All @@ -57,11 +62,6 @@ fn submodules_are_instantiated_as_directories() -> crate::Result {
Ok(())
}

fn assure_is_empty(dir: impl AsRef<Path>) -> std::io::Result<()> {
assert_eq!(std::fs::read_dir(dir)?.count(), 0);
Ok(())
}

#[test]
fn accidental_writes_through_symlinks_are_prevented_if_overwriting_is_forbidden() {
let mut opts = opts_from_probe();
Expand Down Expand Up @@ -257,6 +257,30 @@ fn symlinks_become_files_if_disabled() -> crate::Result {
Ok(())
}

#[test]
fn dangling_symlinks_can_be_created() -> crate::Result {
let opts = opts_from_probe();
if !opts.fs.symlink {
eprintln!("Skipping dangling symlink test on filesystem that doesn't support it");
return Ok(());
}

let (_source_tree, destination, _index, outcome) =
checkout_index_in_tmp_dir(opts.clone(), "make_dangling_symlink")?;
let worktree_files = dir_structure(&destination);
let worktree_files_stripped = stripped_prefix(&destination, &worktree_files);

assert_eq!(worktree_files_stripped, paths(["dangling"]));
let symlink_path = &worktree_files[0];
assert!(symlink_path
.symlink_metadata()
.expect("dangling symlink is on disk")
.is_symlink());
assert_eq!(std::fs::read_link(symlink_path)?, Path::new("non-existing-target"));
assert!(outcome.collisions.is_empty());
Ok(())
}

#[test]
fn allow_or_disallow_symlinks() -> crate::Result {
let mut opts = opts_from_probe();
Expand Down

0 comments on commit 8e64b40

Please sign in to comment.