Skip to content

Commit 376ed0c

Browse files
committed
fix: if core.symlinks=false, don't misclassify actual symlinks as files.
Thus, prefer the actual observation over the stored and maybe incorrect filesystem settings. This avoids false-positives when checking for changes.
1 parent c85b92d commit 376ed0c

File tree

2 files changed

+44
-8
lines changed

2 files changed

+44
-8
lines changed

gix-status/src/index_as_worktree/function.rs

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -408,12 +408,12 @@ impl<'index> State<'_, 'index> {
408408
None => false,
409409
};
410410

411-
// Here we implement racy-git. See racy-git.txt in the git documentation for a detailed documentation.
411+
// We implement racy-git. See racy-git.txt in the git documentation for detailed documentation.
412412
//
413413
// A file is racy if:
414-
// 1. its `mtime` is at or after the last index timestamp and its entry stat information
415-
// matches the on-disk file but the file contents are actually modified
416-
// 2. it's size is 0 (set after detecting a file was racy previously)
414+
// 1. Its `mtime` is at or after the last index timestamp and its entry stat information
415+
// matches the on-disk file, but the file contents are actually modified
416+
// 2. Its size is 0 (set after detecting a file was racy previously)
417417
//
418418
// The first case is detected below by checking the timestamp if the file is marked unmodified.
419419
// The second case is usually detected either because the on-disk file is not empty, hence
@@ -449,7 +449,16 @@ impl<'index> State<'_, 'index> {
449449
file_len: file_size_bytes,
450450
filter: &mut self.filter,
451451
attr_stack: &mut self.attr_stack,
452-
options: self.options,
452+
core_symlinks:
453+
// If this is legitimately a symlink, then pretend symlinks are enabled as the option seems stale.
454+
// Otherwise, respect the option.
455+
if metadata.is_symlink()
456+
&& entry.mode.to_tree_entry_mode().map(|m| m.kind()) == Some(gix_object::tree::EntryKind::Link)
457+
{
458+
true
459+
} else {
460+
self.options.fs.symlink
461+
},
453462
id: &entry.id,
454463
objects,
455464
worktree_reads: self.worktree_reads,
@@ -517,7 +526,7 @@ where
517526
entry: &'a gix_index::Entry,
518527
filter: &'a mut gix_filter::Pipeline,
519528
attr_stack: &'a mut gix_worktree::Stack,
520-
options: &'a Options,
529+
core_symlinks: bool,
521530
id: &'a gix_hash::oid,
522531
objects: Find,
523532
worktree_bytes: &'a AtomicU64,
@@ -545,7 +554,7 @@ where
545554
//
546555
let is_symlink = self.entry.mode == gix_index::entry::Mode::SYMLINK;
547556
// TODO: what to do about precompose unicode and ignore_case for symlinks
548-
let out = if is_symlink && self.options.fs.symlink {
557+
let out = if is_symlink && self.core_symlinks {
549558
// conversion to bstr can never fail because symlinks are only used
550559
// on unix (by git) so no reason to use the try version here
551560
let symlink_path =

gix-status/tests/status/index_as_worktree.rs

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ fn nonfile_fixture(name: &str, expected_status: &[Expectation<'_>]) -> Outcome {
4848
false,
4949
Default::default(),
5050
false,
51+
None,
5152
)
5253
}
5354

@@ -65,6 +66,7 @@ fn fixture_with_index(
6566
false,
6667
Default::default(),
6768
false,
69+
None,
6870
)
6971
}
7072

@@ -78,6 +80,7 @@ fn submodule_fixture(name: &str, expected_status: &[Expectation<'_>]) -> Outcome
7880
false,
7981
Default::default(),
8082
false,
83+
None,
8184
)
8285
}
8386

@@ -91,6 +94,7 @@ fn conflict_fixture(name: &str, expected_status: &[Expectation<'_>]) -> Outcome
9194
false,
9295
Default::default(),
9396
false,
97+
None,
9498
)
9599
}
96100

@@ -104,6 +108,7 @@ fn submodule_fixture_status(name: &str, expected_status: &[Expectation<'_>], sub
104108
submodule_dirty,
105109
Default::default(),
106110
false,
111+
None,
107112
)
108113
}
109114

@@ -117,6 +122,7 @@ fn fixture_filtered(name: &str, pathspecs: &[&str], expected_status: &[Expectati
117122
false,
118123
Default::default(),
119124
false,
125+
None,
120126
)
121127
}
122128

@@ -130,6 +136,7 @@ fn fixture_filtered_detailed(
130136
submodule_dirty: bool,
131137
auto_crlf: gix_filter::eol::AutoCrlf,
132138
use_odb: bool,
139+
fs_capabilities: Option<&dyn Fn(&std::path::Path) -> gix_fs::Capabilities>,
133140
) -> Outcome {
134141
// This can easily happen in some fixtures, which can cause flakiness. It's time-dependent after all.
135142
fn ignore_racyclean(mut out: Outcome) -> Outcome {
@@ -179,7 +186,7 @@ fn fixture_filtered_detailed(
179186
should_interrupt: &AtomicBool::default(),
180187
};
181188
let options = Options {
182-
fs: gix_fs::Capabilities::probe(&git_dir),
189+
fs: fs_capabilities.map_or_else(|| gix_fs::Capabilities::probe(&git_dir), |new| new(&git_dir)),
183190
stat: TEST_OPTIONS,
184191
..Options::default()
185192
};
@@ -353,6 +360,7 @@ fn replace_dir_with_file() {
353360
false,
354361
Default::default(),
355362
false,
363+
None,
356364
);
357365
assert_eq!(
358366
out,
@@ -560,6 +568,24 @@ fn unchanged() {
560568
fixture("status_unchanged", &[]);
561569
}
562570

571+
#[test]
572+
fn unchanged_symlinks_present_but_deactivated() {
573+
fixture_filtered_detailed(
574+
"status_unchanged",
575+
"",
576+
&[],
577+
&[],
578+
|_| {},
579+
false,
580+
Default::default(),
581+
false,
582+
Some(&|dir| gix_fs::Capabilities {
583+
symlink: false,
584+
..gix_fs::Capabilities::probe(dir)
585+
}),
586+
);
587+
}
588+
563589
#[test]
564590
fn unchanged_despite_filter() {
565591
let actual_outcome = fixture_filtered_detailed(
@@ -571,6 +597,7 @@ fn unchanged_despite_filter() {
571597
false,
572598
AutoCrlf::Enabled,
573599
true, /* make ODB available */
600+
None,
574601
);
575602

576603
let expected_outcome = Outcome {

0 commit comments

Comments
 (0)