1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
use std::path::PathBuf;
use git_features::threading::OwnShared;
use crate::store_impl::{file, packed};
impl file::Store {
pub(crate) fn packed_transaction(
&self,
lock_mode: git_lock::acquire::Fail,
) -> Result<packed::Transaction, transaction::Error> {
let lock = git_lock::File::acquire_to_update_resource(self.packed_refs_path(), lock_mode, None)?;
Ok(packed::Transaction::new_from_pack_and_lock(
self.assure_packed_refs_uptodate()?,
lock,
))
}
pub fn open_packed_buffer(&self) -> Result<Option<packed::Buffer>, packed::buffer::open::Error> {
let need_more_than_this_many_bytes_to_use_mmap = 32 * 1024;
match packed::Buffer::open(self.packed_refs_path(), need_more_than_this_many_bytes_to_use_mmap) {
Ok(buf) => Ok(Some(buf)),
Err(packed::buffer::open::Error::Io(err)) if err.kind() == std::io::ErrorKind::NotFound => Ok(None),
Err(err) => Err(err),
}
}
pub fn cached_packed_buffer(&self) -> Result<Option<OwnShared<packed::Buffer>>, packed::buffer::open::Error> {
self.assure_packed_refs_uptodate()
}
pub fn packed_refs_path(&self) -> PathBuf {
self.base.join("packed-refs")
}
}
pub mod transaction {
use quick_error::quick_error;
use crate::store_impl::packed;
quick_error! {
#[derive(Debug)]
#[allow(missing_docs)]
pub enum Error {
BufferOpen(err: packed::buffer::open::Error) {
display("An existing pack couldn't be opened or read when preparing a transaction")
source(err)
from()
}
TransactionLock(err: git_lock::acquire::Error) {
display("The lock for a packed transaction could not be obtained")
source(err)
from()
}
}
}
}
pub(crate) mod modifiable {
use std::time::SystemTime;
use git_features::threading::{get_mut, get_ref, OwnShared};
use crate::{file, packed};
#[derive(Debug, Default)]
pub(crate) struct State {
buffer: Option<OwnShared<packed::Buffer>>,
modified: Option<SystemTime>,
}
impl file::Store {
pub(crate) fn force_refresh_packed_buffer(&self) -> Result<(), packed::buffer::open::Error> {
let mut state = get_mut(&self.packed);
state.buffer = self.open_packed_buffer()?.map(OwnShared::new);
Ok(())
}
pub(crate) fn assure_packed_refs_uptodate(
&self,
) -> Result<Option<OwnShared<packed::Buffer>>, packed::buffer::open::Error> {
let packed_refs_path = self.packed_refs_path();
let packed_refs_modified_time = || packed_refs_path.metadata().and_then(|m| m.modified()).ok();
let state = get_ref(&self.packed);
let recent_modification = packed_refs_modified_time();
let buffer = match (&state.modified, recent_modification) {
(None, None) => state.buffer.clone(),
(Some(_), None) => {
drop(state);
let mut state = get_mut(&self.packed);
if let (Some(_), None) = (state.modified, packed_refs_modified_time()) {
state.buffer = None;
state.modified = None;
}
state.buffer.clone()
}
(Some(cached_time), Some(modified_time)) => {
if *cached_time < modified_time {
drop(state);
let mut state = get_mut(&self.packed);
match (state.modified, packed_refs_modified_time()) {
(Some(cached_time), Some(modified_time)) if cached_time < modified_time => {
state.buffer = self.open_packed_buffer()?.map(OwnShared::new);
state.modified = Some(modified_time);
}
_ => {}
}
state.buffer.clone()
} else {
state.buffer.clone()
}
}
(None, Some(_modified_time)) => {
drop(state);
let mut state = get_mut(&self.packed);
if let (None, Some(modified_time)) = (state.modified, packed_refs_modified_time()) {
state.buffer = self.open_packed_buffer()?.map(OwnShared::new);
state.modified = Some(modified_time);
}
state.buffer.clone()
}
};
Ok(buffer)
}
}
}