pub struct Buffer { /* private fields */ }
Expand description
A buffer containing a packed-ref file that is either memory mapped or fully in-memory depending on a cutoff.
The buffer is guaranteed to be sorted as per the packed-ref rules which allows some operations to be more efficient.
Implementations§
source§impl Buffer
impl Buffer
packed-refs specific functionality
sourcepub fn iter(&self) -> Result<Iter<'_>, Error>
pub fn iter(&self) -> Result<Iter<'_>, Error>
Return an iterator of references stored in this packed refs buffer, ordered by reference name.
Note
There is no namespace support in packed iterators. It can be emulated using iter_prefixed(…)
.
Examples found in repository?
380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405
fn iter_from_info<'s, 'p>(
&'s self,
git_dir_info: IterInfo<'_>,
common_dir_info: Option<IterInfo<'_>>,
packed: Option<&'p packed::Buffer>,
) -> std::io::Result<LooseThenPacked<'p, 's>> {
Ok(LooseThenPacked {
git_dir: self.git_dir(),
common_dir: self.common_dir(),
iter_packed: match packed {
Some(packed) => Some(
match git_dir_info.prefix() {
Some(prefix) => packed.iter_prefixed(path_to_name(prefix).into_owned()),
None => packed.iter(),
}
.map_err(|err| std::io::Error::new(std::io::ErrorKind::Other, err))?
.peekable(),
),
None => None,
},
iter_git_dir: git_dir_info.into_iter(),
iter_common_dir: common_dir_info.map(IterInfo::into_iter),
buf: Vec::new(),
namespace: self.namespace.as_ref(),
})
}
More examples
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 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189
pub fn commit(self) -> Result<(), commit::Error> {
let mut edits = self.edits.expect("BUG: cannot call commit() before prepare(…)");
if edits.is_empty() {
return Ok(());
}
let mut file = self.lock.expect("a write lock for applying changes");
let refs_sorted: Box<dyn Iterator<Item = Result<packed::Reference<'_>, packed::iter::Error>>> =
match self.buffer.as_ref() {
Some(buffer) => Box::new(buffer.iter()?),
None => Box::new(std::iter::empty()),
};
let mut refs_sorted = refs_sorted.peekable();
edits.sort_by(|l, r| l.inner.name.as_bstr().cmp(r.inner.name.as_bstr()));
let mut peekable_sorted_edits = edits.iter().peekable();
file.with_mut(|f| f.write_all(HEADER_LINE))?;
let mut num_written_lines = 0;
loop {
match (refs_sorted.peek(), peekable_sorted_edits.peek()) {
(Some(Err(_)), _) => {
let err = refs_sorted.next().expect("next").expect_err("err");
return Err(commit::Error::Iteration(err));
}
(None, None) => {
break;
}
(Some(Ok(_)), None) => {
let pref = refs_sorted.next().expect("next").expect("no err");
num_written_lines += 1;
file.with_mut(|out| write_packed_ref(out, pref))?;
}
(Some(Ok(pref)), Some(edit)) => {
use std::cmp::Ordering::*;
match pref.name.as_bstr().cmp(edit.inner.name.as_bstr()) {
Less => {
let pref = refs_sorted.next().expect("next").expect("valid");
num_written_lines += 1;
file.with_mut(|out| write_packed_ref(out, pref))?;
}
Greater => {
let edit = peekable_sorted_edits.next().expect("next");
file.with_mut(|out| write_edit(out, edit, &mut num_written_lines))?;
}
Equal => {
let _pref = refs_sorted.next().expect("next").expect("valid");
let edit = peekable_sorted_edits.next().expect("next");
file.with_mut(|out| write_edit(out, edit, &mut num_written_lines))?;
}
}
}
(None, Some(_)) => {
let edit = peekable_sorted_edits.next().expect("next");
file.with_mut(|out| write_edit(out, edit, &mut num_written_lines))?;
}
}
}
if num_written_lines == 0 {
std::fs::remove_file(file.resource_path())?;
} else {
file.commit()?;
}
drop(refs_sorted);
Ok(())
}
sourcepub fn iter_prefixed(
&self,
prefix: impl Into<BString>
) -> Result<Iter<'_>, Error>
pub fn iter_prefixed(
&self,
prefix: impl Into<BString>
) -> Result<Iter<'_>, Error>
Return an iterator yielding only references matching the given prefix, ordered by reference name.
Examples found in repository?
380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405
fn iter_from_info<'s, 'p>(
&'s self,
git_dir_info: IterInfo<'_>,
common_dir_info: Option<IterInfo<'_>>,
packed: Option<&'p packed::Buffer>,
) -> std::io::Result<LooseThenPacked<'p, 's>> {
Ok(LooseThenPacked {
git_dir: self.git_dir(),
common_dir: self.common_dir(),
iter_packed: match packed {
Some(packed) => Some(
match git_dir_info.prefix() {
Some(prefix) => packed.iter_prefixed(path_to_name(prefix).into_owned()),
None => packed.iter(),
}
.map_err(|err| std::io::Error::new(std::io::ErrorKind::Other, err))?
.peekable(),
),
None => None,
},
iter_git_dir: git_dir_info.into_iter(),
iter_common_dir: common_dir_info.map(IterInfo::into_iter),
buf: Vec::new(),
namespace: self.namespace.as_ref(),
})
}
source§impl Buffer
impl Buffer
Initialization
sourcepub fn open(
path: impl Into<PathBuf>,
use_memory_map_if_larger_than_bytes: u64
) -> Result<Self, Error>
pub fn open(
path: impl Into<PathBuf>,
use_memory_map_if_larger_than_bytes: u64
) -> Result<Self, Error>
Open the file at path
and map it into memory if the file size is larger than use_memory_map_if_larger_than_bytes
.
In order to allow fast lookups and optimizations, the contents of the packed refs must be sorted. If that’s not the case, they will be sorted on the fly with the data being written into a memory buffer.
source§impl Buffer
impl Buffer
packed-refs specific functionality
sourcepub fn try_find<'a, Name, E>(
&self,
name: Name
) -> Result<Option<Reference<'_>>, Error>where
Name: TryInto<&'a PartialNameRef, Error = E>,
Error: From<E>,
pub fn try_find<'a, Name, E>(
&self,
name: Name
) -> Result<Option<Reference<'_>>, Error>where
Name: TryInto<&'a PartialNameRef, Error = E>,
Error: From<E>,
Find a reference with the given name
and return it.
Note that it will look it up verbatim and does not deal with namespaces or special prefixes like
main-worktree/
or worktrees/<name>/
, as this is left to the caller.
Examples found in repository?
More examples
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 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197
fn lock_ref_and_apply_change(
store: &file::Store,
lock_fail_mode: git_lock::acquire::Fail,
packed: Option<&packed::Buffer>,
change: &mut Edit,
has_global_lock: bool,
direct_to_packed_refs: bool,
) -> Result<(), Error> {
use std::io::Write;
assert!(
change.lock.is_none(),
"locks can only be acquired once and it's all or nothing"
);
let existing_ref = store
.ref_contents(change.update.name.as_ref())
.map_err(Error::from)
.and_then(|maybe_loose| {
maybe_loose
.map(|buf| {
loose::Reference::try_from_path(change.update.name.clone(), &buf)
.map(Reference::from)
.map_err(Error::from)
})
.transpose()
})
.or_else(|err| match err {
Error::ReferenceDecode(_) => Ok(None),
other => Err(other),
})
.and_then(|maybe_loose| match (maybe_loose, packed) {
(None, Some(packed)) => packed
.try_find(change.update.name.as_ref())
.map(|opt| opt.map(Into::into))
.map_err(Error::from),
(None, None) => Ok(None),
(maybe_loose, _) => Ok(maybe_loose),
});
let lock = match &mut change.update.change {
Change::Delete { expected, .. } => {
let (base, relative_path) = store.reference_path_with_base(change.update.name.as_ref());
let lock = if has_global_lock {
None
} else {
git_lock::Marker::acquire_to_hold_resource(
base.join(relative_path.as_ref()),
lock_fail_mode,
Some(base.clone().into_owned()),
)
.map_err(|err| Error::LockAcquire {
source: err,
full_name: "borrowchk won't allow change.name()".into(),
})?
.into()
};
let existing_ref = existing_ref?;
match (&expected, &existing_ref) {
(PreviousValue::MustNotExist, _) => {
panic!("BUG: MustNotExist constraint makes no sense if references are to be deleted")
}
(PreviousValue::ExistingMustMatch(_), None)
| (PreviousValue::MustExist, Some(_))
| (PreviousValue::Any, Some(_))
| (PreviousValue::Any, None) => {}
(PreviousValue::MustExist, None) | (PreviousValue::MustExistAndMatch(_), None) => {
return Err(Error::DeleteReferenceMustExist {
full_name: change.name(),
})
}
(PreviousValue::MustExistAndMatch(previous), Some(existing))
| (PreviousValue::ExistingMustMatch(previous), Some(existing)) => {
let actual = existing.target.clone();
if *previous != actual {
let expected = previous.clone();
return Err(Error::ReferenceOutOfDate {
full_name: change.name(),
expected,
actual,
});
}
}
}
// Keep the previous value for the caller and ourselves. Maybe they want to keep a log of sorts.
if let Some(existing) = existing_ref {
*expected = PreviousValue::MustExistAndMatch(existing.target);
}
lock
}
Change::Update { expected, new, .. } => {
let (base, relative_path) = store.reference_path_with_base(change.update.name.as_ref());
let obtain_lock = || {
git_lock::File::acquire_to_update_resource(
base.join(relative_path.as_ref()),
lock_fail_mode,
Some(base.clone().into_owned()),
)
.map_err(|err| Error::LockAcquire {
source: err,
full_name: "borrowchk won't allow change.name() and this will be corrected by caller".into(),
})
};
let mut lock = (!has_global_lock).then(obtain_lock).transpose()?;
let existing_ref = existing_ref?;
match (&expected, &existing_ref) {
(PreviousValue::Any, _)
| (PreviousValue::MustExist, Some(_))
| (PreviousValue::MustNotExist, None)
| (PreviousValue::ExistingMustMatch(_), None) => {}
(PreviousValue::MustExist, None) => {
let expected = Target::Peeled(store.object_hash.null());
let full_name = change.name();
return Err(Error::MustExist { full_name, expected });
}
(PreviousValue::MustNotExist, Some(existing)) => {
if existing.target != *new {
let new = new.clone();
return Err(Error::MustNotExist {
full_name: change.name(),
actual: existing.target.clone(),
new,
});
}
}
(PreviousValue::MustExistAndMatch(previous), Some(existing))
| (PreviousValue::ExistingMustMatch(previous), Some(existing)) => {
if *previous != existing.target {
let actual = existing.target.clone();
let expected = previous.to_owned();
let full_name = change.name();
return Err(Error::ReferenceOutOfDate {
full_name,
actual,
expected,
});
}
}
(PreviousValue::MustExistAndMatch(previous), None) => {
let expected = previous.to_owned();
let full_name = change.name();
return Err(Error::MustExist { full_name, expected });
}
};
fn new_would_change_existing(new: &Target, existing: &Target) -> (bool, bool) {
match (new, existing) {
(Target::Peeled(new), Target::Peeled(old)) => (old != new, false),
(Target::Symbolic(new), Target::Symbolic(old)) => (old != new, true),
(Target::Peeled(_), _) => (true, false),
(Target::Symbolic(_), _) => (true, true),
}
}
let (is_effective, is_symbolic) = if let Some(existing) = existing_ref {
let (effective, is_symbolic) = new_would_change_existing(new, &existing.target);
*expected = PreviousValue::MustExistAndMatch(existing.target);
(effective, is_symbolic)
} else {
(true, matches!(new, Target::Symbolic(_)))
};
if (is_effective && !direct_to_packed_refs) || is_symbolic {
let mut lock = lock.take().map(Ok).unwrap_or_else(obtain_lock)?;
lock.with_mut(|file| match new {
Target::Peeled(oid) => write!(file, "{}", oid),
Target::Symbolic(name) => write!(file, "ref: {}", name.0),
})?;
Some(lock.close()?)
} else {
None
}
}
};
change.lock = lock;
Ok(())
}
sourcepub fn find<'a, Name, E>(&self, name: Name) -> Result<Reference<'_>, Error>where
Name: TryInto<&'a PartialNameRef, Error = E>,
Error: From<E>,
pub fn find<'a, Name, E>(&self, name: Name) -> Result<Reference<'_>, Error>where
Name: TryInto<&'a PartialNameRef, Error = E>,
Error: From<E>,
Find a reference with the given name
and return it.
Examples found in repository?
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
pub fn prepare(
mut self,
edits: impl IntoIterator<Item = RefEdit>,
find: &mut FindObjectFn<'_>,
) -> Result<Self, prepare::Error> {
assert!(self.edits.is_none(), "BUG: cannot call prepare(…) more than once");
let buffer = &self.buffer;
// Remove all edits which are deletions that aren't here in the first place
let mut edits: Vec<Edit> = edits
.into_iter()
.filter(|edit| {
if let Change::Delete { .. } = edit.change {
buffer.as_ref().map_or(true, |b| b.find(edit.name.as_ref()).is_ok())
} else {
true
}
})
.map(|change| Edit {
inner: change,
peeled: None,
})
.collect();
let mut buf = Vec::new();
for edit in edits.iter_mut() {
if let Change::Update {
new: Target::Peeled(new),
..
} = edit.inner.change
{
let mut next_id = new;
edit.peeled = loop {
let kind = find(next_id, &mut buf)?;
match kind {
Some(kind) if kind == git_object::Kind::Tag => {
next_id = git_object::TagRefIter::from_bytes(&buf).target_id().map_err(|_| {
prepare::Error::Resolve(
format!("Couldn't get target object id from tag {}", next_id).into(),
)
})?;
}
Some(_) => {
break if next_id == new { None } else { Some(next_id) };
}
None => {
return Err(prepare::Error::Resolve(
format!("Couldn't find object with id {}", next_id).into(),
))
}
}
};
}
}
if edits.is_empty() {
self.closed_lock = self
.lock
.take()
.map(|l| l.close())
.transpose()
.map_err(prepare::Error::CloseLock)?;
} else {
// NOTE that we don't do any additional checks here but apply all edits unconditionally.
// This is because this transaction system is internal and will be used correctly from the
// loose ref store transactions, which do the necessary checking.
}
self.edits = Some(edits);
Ok(self)
}
Trait Implementations§
Auto Trait Implementations§
impl RefUnwindSafe for Buffer
impl Send for Buffer
impl Sync for Buffer
impl Unpin for Buffer
impl UnwindSafe for Buffer
Blanket Implementations§
source§impl<T> ToHex for Twhere
T: AsRef<[u8]>,
impl<T> ToHex for Twhere
T: AsRef<[u8]>,
source§fn encode_hex<U>(&self) -> Uwhere
U: FromIterator<char>,
fn encode_hex<U>(&self) -> Uwhere
U: FromIterator<char>,
self
into the result. Lower case
letters are used (e.g. f9b4ca
)source§fn encode_hex_upper<U>(&self) -> Uwhere
U: FromIterator<char>,
fn encode_hex_upper<U>(&self) -> Uwhere
U: FromIterator<char>,
self
into the result. Upper case
letters are used (e.g. F9B4CA
)