Skip to content

Commit c8a63ab

Browse files
committed
feat: add file::Store::is_empty()
1 parent 26ae766 commit c8a63ab

File tree

8 files changed

+79
-4
lines changed

8 files changed

+79
-4
lines changed

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());

0 commit comments

Comments
 (0)