Skip to content

Commit 6c1da71

Browse files
committed
fix: assure memory maps don't get in the way of updating .git/packet-refs on Windows. (#1183)
The solution entails to not use memory maps on Windows which is certainly fine on todays systems, even though it's likely to add latency.
1 parent 7b4573a commit 6c1da71

File tree

5 files changed

+250
-256
lines changed

5 files changed

+250
-256
lines changed

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

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ mod init {
3636
pub fn at(git_dir: PathBuf, write_reflog: file::WriteReflog, object_hash: gix_hash::Kind) -> Self {
3737
file::Store {
3838
git_dir,
39-
packed_buffer_mmap_threshold: 32 * 1024,
39+
packed_buffer_mmap_threshold: packed_refs_mmap_threshold(),
4040
common_dir: None,
4141
write_reflog,
4242
namespace: None,
@@ -55,7 +55,7 @@ mod init {
5555
) -> Self {
5656
file::Store {
5757
git_dir,
58-
packed_buffer_mmap_threshold: 32 * 1024,
58+
packed_buffer_mmap_threshold: packed_refs_mmap_threshold(),
5959
common_dir: Some(common_dir),
6060
write_reflog,
6161
namespace: None,
@@ -64,4 +64,12 @@ mod init {
6464
}
6565
}
6666
}
67+
68+
fn packed_refs_mmap_threshold() -> u64 {
69+
if cfg!(windows) {
70+
u64::MAX
71+
} else {
72+
32 * 1024
73+
}
74+
}
6775
}

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

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
use crate::file::store;
2+
3+
#[test]
4+
fn set_packed_buffer_mmap_threshold() -> crate::Result {
5+
let mut store = store()?;
6+
let prev = store.set_packed_buffer_mmap_threshold(0);
7+
if cfg!(windows) {
8+
assert_eq!(
9+
prev,
10+
u64::MAX,
11+
"on windows mmap are deactivated as otherwise we can't change packed-refs while it's mapped"
12+
);
13+
} else {
14+
assert_eq!(prev, 32 * 1024, "the default is the value that Git uses as well");
15+
}
16+
assert_eq!(
17+
store.set_packed_buffer_mmap_threshold(0),
18+
0,
19+
"it actually sets the value"
20+
);
21+
Ok(())
22+
}

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
mod access;
12
mod find;
23
mod iter;
34
mod reflog;

gix-ref/tests/file/transaction/prepare_and_commit/create_or_update/mod.rs

Lines changed: 128 additions & 149 deletions
Original file line numberDiff line numberDiff line change
@@ -653,52 +653,47 @@ fn write_reference_to_which_head_points_to_does_not_update_heads_reflog_even_tho
653653

654654
#[test]
655655
fn packed_refs_are_looked_up_when_checking_existing_values() -> crate::Result {
656-
for use_mmap in [false, true] {
657-
let (_keep, mut store) = store_writable("make_packed_ref_repository.sh")?;
658-
if use_mmap {
659-
store.set_packed_buffer_mmap_threshold(0);
660-
}
661-
assert!(
662-
store.try_find_loose("main")?.is_none(),
663-
"no loose main available, it's packed"
664-
);
665-
let new_id = hex_to_id("0000000000000000000000000000000000000001");
666-
let old_id = hex_to_id("134385f6d781b7e97062102c6a483440bfda2a03");
667-
let edits = store
668-
.transaction()
669-
.prepare(
670-
Some(RefEdit {
671-
change: Change::Update {
672-
log: LogChange {
673-
mode: RefLog::AndReference,
674-
force_create_reflog: false,
675-
message: "for pack".into(),
676-
},
677-
expected: PreviousValue::MustExistAndMatch(Target::Peeled(old_id)),
678-
new: Target::Peeled(new_id),
656+
let (_keep, store) = store_writable("make_packed_ref_repository.sh")?;
657+
assert!(
658+
store.try_find_loose("main")?.is_none(),
659+
"no loose main available, it's packed"
660+
);
661+
let new_id = hex_to_id("0000000000000000000000000000000000000001");
662+
let old_id = hex_to_id("134385f6d781b7e97062102c6a483440bfda2a03");
663+
let edits = store
664+
.transaction()
665+
.prepare(
666+
Some(RefEdit {
667+
change: Change::Update {
668+
log: LogChange {
669+
mode: RefLog::AndReference,
670+
force_create_reflog: false,
671+
message: "for pack".into(),
679672
},
680-
name: "refs/heads/main".try_into()?,
681-
deref: false,
682-
}),
683-
Fail::Immediately,
684-
Fail::Immediately,
685-
)?
686-
.commit(committer().to_ref())?;
673+
expected: PreviousValue::MustExistAndMatch(Target::Peeled(old_id)),
674+
new: Target::Peeled(new_id),
675+
},
676+
name: "refs/heads/main".try_into()?,
677+
deref: false,
678+
}),
679+
Fail::Immediately,
680+
Fail::Immediately,
681+
)?
682+
.commit(committer().to_ref())?;
687683

688-
assert_eq!(edits.len(), 1, "only one edit was performed in the loose refs store");
684+
assert_eq!(edits.len(), 1, "only one edit was performed in the loose refs store");
689685

690-
let packed = store.open_packed_buffer().unwrap().expect("packed refs is available");
691-
assert_eq!(
686+
let packed = store.open_packed_buffer().unwrap().expect("packed refs is available");
687+
assert_eq!(
692688
packed.find("main")?.target(),
693689
old_id,
694690
"packed refs aren't rewritten, the change goes into the loose ref instead which shadows packed refs of same name"
695691
);
696-
assert_eq!(
697-
store.find_loose("main")?.target.try_id(),
698-
Some(new_id.as_ref()),
699-
"the new id was written to the loose ref"
700-
);
701-
}
692+
assert_eq!(
693+
store.find_loose("main")?.target.try_id(),
694+
Some(new_id.as_ref()),
695+
"the new id was written to the loose ref"
696+
);
702697
Ok(())
703698
}
704699

@@ -709,130 +704,114 @@ fn packed_refs_creation_with_tag_loop_are_not_handled_and_cannot_exist_due_to_ob
709704

710705
#[test]
711706
fn packed_refs_creation_with_packed_refs_mode_prune_removes_original_loose_refs() -> crate::Result {
712-
for use_mmap in [false, true] {
713-
let (_keep, mut store) = store_writable("make_ref_repository.sh")?;
714-
if use_mmap {
715-
let prev = store.set_packed_buffer_mmap_threshold(0);
716-
assert_eq!(prev, 32 * 1024, "the default is the value that Git uses as well");
717-
assert_eq!(
718-
store.set_packed_buffer_mmap_threshold(0),
719-
0,
720-
"it actually sets the value"
721-
);
722-
}
723-
assert!(
724-
store.open_packed_buffer()?.is_none(),
725-
"there should be no packed refs to start out with"
726-
);
727-
let odb = gix_odb::at(store.git_dir().join("objects"))?;
728-
let edits = store
729-
.transaction()
730-
.packed_refs(PackedRefs::DeletionsAndNonSymbolicUpdatesRemoveLooseSourceReference(
731-
Box::new(odb),
732-
))
733-
.prepare(
734-
store
735-
.loose_iter()?
736-
.filter_map(|r| r.ok().filter(|r| r.kind() == gix_ref::Kind::Peeled))
737-
.map(|r| RefEdit {
738-
change: Change::Update {
739-
log: LogChange::default(),
740-
expected: PreviousValue::MustExistAndMatch(r.target.clone()),
741-
new: r.target,
742-
},
743-
name: r.name,
744-
deref: false,
745-
}),
746-
Fail::Immediately,
747-
Fail::Immediately,
748-
)?
749-
.commit(committer().to_ref())?;
750-
751-
assert_eq!(
752-
edits.len(),
753-
8,
754-
"there are a certain amount of loose refs that are packed"
755-
);
756-
757-
assert!(
707+
let (_keep, store) = store_writable("make_ref_repository.sh")?;
708+
assert!(
709+
store.open_packed_buffer()?.is_none(),
710+
"there should be no packed refs to start out with"
711+
);
712+
let odb = gix_odb::at(store.git_dir().join("objects"))?;
713+
let edits = store
714+
.transaction()
715+
.packed_refs(PackedRefs::DeletionsAndNonSymbolicUpdatesRemoveLooseSourceReference(
716+
Box::new(odb),
717+
))
718+
.prepare(
758719
store
759720
.loose_iter()?
760-
.filter_map(Result::ok)
761-
.all(|r| r.kind() == gix_ref::Kind::Symbolic),
762-
"only symbolic refs are left"
763-
);
721+
.filter_map(|r| r.ok().filter(|r| r.kind() == gix_ref::Kind::Peeled))
722+
.map(|r| RefEdit {
723+
change: Change::Update {
724+
log: LogChange::default(),
725+
expected: PreviousValue::MustExistAndMatch(r.target.clone()),
726+
new: r.target,
727+
},
728+
name: r.name,
729+
deref: false,
730+
}),
731+
Fail::Immediately,
732+
Fail::Immediately,
733+
)?
734+
.commit(committer().to_ref())?;
764735

765-
let other_store = store_with_packed_refs()?;
766-
let expected_pack_data: BString = std::fs::read(other_store.packed_refs_path())?.into();
767-
let actual_packed_data: BString = std::fs::read(store.packed_refs_path())?.into();
768-
assert_eq!(
769-
actual_packed_data, expected_pack_data,
770-
"both gitoxide and git must agree on the packed refs file perfectly"
771-
);
772-
}
736+
assert_eq!(
737+
edits.len(),
738+
8,
739+
"there are a certain amount of loose refs that are packed"
740+
);
741+
742+
assert!(
743+
store
744+
.loose_iter()?
745+
.filter_map(Result::ok)
746+
.all(|r| r.kind() == gix_ref::Kind::Symbolic),
747+
"only symbolic refs are left"
748+
);
749+
750+
let other_store = store_with_packed_refs()?;
751+
let expected_pack_data: BString = std::fs::read(other_store.packed_refs_path())?.into();
752+
let actual_packed_data: BString = std::fs::read(store.packed_refs_path())?.into();
753+
assert_eq!(
754+
actual_packed_data, expected_pack_data,
755+
"both gitoxide and git must agree on the packed refs file perfectly"
756+
);
773757
Ok(())
774758
}
775759

776760
#[test]
777761
fn packed_refs_creation_with_packed_refs_mode_leave_keeps_original_loose_refs() -> crate::Result {
778-
for use_mmap in [false, true] {
779-
let (_keep, mut store) = store_writable("make_packed_ref_repository_for_overlay.sh")?;
780-
if use_mmap {
781-
store.set_packed_buffer_mmap_threshold(0);
782-
}
783-
let branch = store.find("newer-as-loose")?;
784-
let packed = store.open_packed_buffer()?.expect("packed-refs");
785-
assert_ne!(
786-
packed.find("newer-as-loose")?.target(),
787-
branch.target.try_id().expect("peeled"),
788-
"the packed ref is outdated"
789-
);
790-
let previous_reflog_entries = branch.log_iter(&store).all()?.expect("log").count();
791-
let previous_packed_refs = packed.iter()?.filter_map(Result::ok).count();
792-
793-
let edits = store.loose_iter()?.map(|r| r.expect("valid ref")).map(|r| RefEdit {
794-
change: Change::Update {
795-
log: LogChange::default(),
796-
expected: PreviousValue::MustExistAndMatch(r.target.clone()),
797-
new: r.target,
798-
},
799-
name: r.name,
800-
deref: false,
801-
});
762+
let (_keep, store) = store_writable("make_packed_ref_repository_for_overlay.sh")?;
763+
let branch = store.find("newer-as-loose")?;
764+
let packed = store.open_packed_buffer()?.expect("packed-refs");
765+
assert_ne!(
766+
packed.find("newer-as-loose")?.target(),
767+
branch.target.try_id().expect("peeled"),
768+
"the packed ref is outdated"
769+
);
770+
let previous_reflog_entries = branch.log_iter(&store).all()?.expect("log").count();
771+
let previous_packed_refs = packed.iter()?.filter_map(Result::ok).count();
772+
773+
let edits = store.loose_iter()?.map(|r| r.expect("valid ref")).map(|r| RefEdit {
774+
change: Change::Update {
775+
log: LogChange::default(),
776+
expected: PreviousValue::MustExistAndMatch(r.target.clone()),
777+
new: r.target,
778+
},
779+
name: r.name,
780+
deref: false,
781+
});
802782

803-
let edits = store
804-
.transaction()
805-
.packed_refs(PackedRefs::DeletionsAndNonSymbolicUpdates(Box::new(EmptyCommit)))
806-
.prepare(edits, Fail::Immediately, Fail::Immediately)?
807-
.commit(committer().to_ref())?;
808-
assert_eq!(
783+
let edits = store
784+
.transaction()
785+
.packed_refs(PackedRefs::DeletionsAndNonSymbolicUpdates(Box::new(EmptyCommit)))
786+
.prepare(edits, Fail::Immediately, Fail::Immediately)?
787+
.commit(committer().to_ref())?;
788+
assert_eq!(
809789
edits.len(),
810790
2,
811791
"it claims to have performed all desired operations, even though some don't make it into the pack as 'side-car'"
812792
);
813793

814-
assert_eq!(
815-
store.loose_iter()?.filter_map(Result::ok).count(),
816-
edits.len(),
817-
"the amount of loose refs didn't change and having symbolic ones isn't a problem"
818-
);
819-
assert_eq!(
820-
branch.log_iter(&store).all()?.expect("log").count(),
821-
previous_reflog_entries,
822-
"reflog isn't adjusted as there is no change"
823-
);
794+
assert_eq!(
795+
store.loose_iter()?.filter_map(Result::ok).count(),
796+
edits.len(),
797+
"the amount of loose refs didn't change and having symbolic ones isn't a problem"
798+
);
799+
assert_eq!(
800+
branch.log_iter(&store).all()?.expect("log").count(),
801+
previous_reflog_entries,
802+
"reflog isn't adjusted as there is no change"
803+
);
824804

825-
let packed = store.open_packed_buffer()?.expect("packed-refs");
826-
assert_eq!(
827-
packed.iter()?.filter_map(Result::ok).count(),
828-
previous_packed_refs,
829-
"the amount of packed refs doesn't change"
830-
);
831-
assert_eq!(
832-
packed.find("newer-as-loose")?.target(),
833-
store.find("newer-as-loose")?.target.into_id(),
834-
"the packed ref is now up to date and the loose ref definitely still exists"
835-
);
836-
}
805+
let packed = store.open_packed_buffer()?.expect("packed-refs");
806+
assert_eq!(
807+
packed.iter()?.filter_map(Result::ok).count(),
808+
previous_packed_refs,
809+
"the amount of packed refs doesn't change"
810+
);
811+
assert_eq!(
812+
packed.find("newer-as-loose")?.target(),
813+
store.find("newer-as-loose")?.target.into_id(),
814+
"the packed ref is now up to date and the loose ref definitely still exists"
815+
);
837816
Ok(())
838817
}

0 commit comments

Comments
 (0)