Skip to content

Commit 7ae3797

Browse files
authored
Merge pull request #2016 from GitoxideLabs/improvements
various improvements
2 parents 648022b + b985766 commit 7ae3797

File tree

19 files changed

+184
-27
lines changed

19 files changed

+184
-27
lines changed

gix-index/src/entry/mode.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ impl Mode {
4949
) -> Option<Change> {
5050
match self {
5151
Mode::FILE if !stat.is_file() => (),
52+
Mode::SYMLINK if stat.is_symlink() => return None,
5253
Mode::SYMLINK if has_symlinks && !stat.is_symlink() => (),
5354
Mode::SYMLINK if !has_symlinks && !stat.is_file() => (),
5455
Mode::COMMIT | Mode::DIR if !stat.is_dir() => (),

gix-ref/src/store/file/mod.rs

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ mod access {
5353
}
5454
}
5555

56-
use crate::file;
56+
use crate::{file, Target};
5757

5858
/// Access
5959
impl file::Store {
@@ -78,6 +78,35 @@ mod access {
7878
pub fn common_dir_resolved(&self) -> &Path {
7979
self.common_dir.as_deref().unwrap_or(&self.git_dir)
8080
}
81+
82+
/// Return `Some(true)` if this is a freshly initialized ref store without any observable changes.
83+
/// Return `None` if `HEAD` couldn't be read.
84+
///
85+
/// This is the case if:
86+
///
87+
/// * the ref-store is valid
88+
/// * `HEAD` exists
89+
/// * `HEAD` still points to `default_ref`
90+
/// * there are no packed refs
91+
/// * There are no observable references in `refs/`
92+
pub fn is_pristine(&self, default_ref: &crate::FullNameRef) -> Option<bool> {
93+
let head = self.find_loose("HEAD").ok()?;
94+
match head.target {
95+
Target::Object(_) => return Some(false),
96+
Target::Symbolic(name) => {
97+
if name.as_ref() != default_ref {
98+
return Some(false);
99+
}
100+
}
101+
}
102+
if self.loose_iter().ok()?.filter_map(Result::ok).next().is_some() {
103+
return Some(false);
104+
}
105+
if self.packed_refs_path().is_file() {
106+
return Some(false);
107+
}
108+
Some(true)
109+
}
81110
}
82111
}
83112

Binary file not shown.
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
#!/usr/bin/env bash
2+
set -eu -o pipefail
3+
4+
git init untouched
5+
6+
git init changed-headref
7+
(cd changed-headref
8+
echo "ref: refs/heads/other" >.git/HEAD
9+
)
10+
11+
git init detached
12+
(cd detached
13+
echo "abcdefabcdefabcdefabcdefabcdefabcdefabcd" >.git/HEAD
14+
)
15+
16+
git init invalid-loose-ref
17+
(cd invalid-loose-ref
18+
touch .git/refs/heads/empty
19+
)

gix-ref/tests/refs/file/mod.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,12 @@ pub fn store_with_packed_refs() -> crate::Result<Store> {
1414
}
1515

1616
pub fn store_at(name: &str) -> crate::Result<Store> {
17-
let path = gix_testtools::scripted_fixture_read_only_standalone(name)?;
18-
Ok(Store::at(path.join(".git"), Default::default()))
17+
named_store_at(name, "")
18+
}
19+
20+
pub fn named_store_at(script_name: &str, name: &str) -> crate::Result<Store> {
21+
let path = gix_testtools::scripted_fixture_read_only_standalone(script_name)?;
22+
Ok(Store::at(path.join(name).join(".git"), Default::default()))
1923
}
2024

2125
pub fn store_at_with_args(name: &str, args: impl IntoIterator<Item = impl Into<String>>) -> crate::Result<Store> {

gix-ref/tests/refs/file/store/access.rs

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use crate::file::store;
1+
use crate::file::{named_store_at, store};
22

33
#[test]
44
fn set_packed_buffer_mmap_threshold() -> crate::Result {
@@ -20,3 +20,22 @@ fn set_packed_buffer_mmap_threshold() -> crate::Result {
2020
);
2121
Ok(())
2222
}
23+
24+
#[test]
25+
fn is_pristine() -> crate::Result {
26+
let store = named_store_at("make_pristine.sh", "untouched")?;
27+
assert_eq!(store.is_pristine("refs/heads/main".try_into()?), Some(true));
28+
assert_eq!(store.is_pristine("refs/heads/other".try_into()?), Some(false));
29+
30+
let store = named_store_at("make_pristine.sh", "changed-headref")?;
31+
assert_eq!(store.is_pristine("refs/heads/other".try_into()?), Some(true));
32+
assert_eq!(store.is_pristine("refs/heads/main".try_into()?), Some(false));
33+
34+
let store = named_store_at("make_pristine.sh", "detached")?;
35+
assert_eq!(store.is_pristine("refs/heads/main".try_into()?), Some(false));
36+
37+
let store = named_store_at("make_pristine.sh", "invalid-loose-ref")?;
38+
assert_eq!(store.is_pristine("refs/heads/main".try_into()?), Some(true));
39+
40+
Ok(())
41+
}

gix-ref/tests/refs/file/store/find.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ mod existing {
99
"make_packed_ref_repository_for_overlay.sh",
1010
] {
1111
let store = store_at(fixture)?;
12+
assert_eq!(store.is_pristine("refs/heads/main".try_into()?), Some(false));
1213
let c1 = hex_to_id("134385f6d781b7e97062102c6a483440bfda2a03");
1314
let r = store.find("main")?;
1415
assert_eq!(r.target.into_id(), c1);

gix-ref/tests/refs/file/store/iter.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -576,6 +576,7 @@ fn overlay_partial_prefix_iter_when_prefix_is_dir() -> crate::Result {
576576
use gix_ref::Target::*;
577577

578578
let store = store_at("make_packed_ref_repository_for_overlay.sh")?;
579+
assert_eq!(store.is_pristine("refs/heads/main".try_into()?), Some(false));
579580
let c1 = hex_to_id("134385f6d781b7e97062102c6a483440bfda2a03");
580581

581582
let ref_names = store

gix-ref/tests/refs/file/worktree.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ mod read_only {
8484
fn linked() -> crate::Result {
8585
for packed in [false, true] {
8686
let (store, odb, _tmp) = worktree_store(packed, "w1", Mode::Read)?;
87+
assert_eq!(store.is_pristine("refs/heads/main".try_into()?), Some(false));
8788
let peel = into_peel(&store, odb);
8889

8990
let w1_head_id = peel(store.find("HEAD").unwrap());
@@ -132,6 +133,7 @@ mod read_only {
132133
fn main() -> crate::Result {
133134
for packed in [false, true] {
134135
let (store, odb, _tmp) = main_store(packed, Mode::Read)?;
136+
assert_eq!(store.is_pristine("refs/heads/main".try_into()?), Some(false));
135137
let peel = into_peel(&store, odb);
136138

137139
let head_id = peel(store.find("HEAD").unwrap());

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 =

0 commit comments

Comments
 (0)