Browse Source

Allow for more screen space when formatting

pull/2/head
Sebastian Thiel 11 months ago
parent
commit
67943002e7
No known key found for this signature in database GPG Key ID: EE029D1E5EB40300
  1. 9
      demos/git-count.rs
  2. 10
      git-features/src/parallel.rs
  3. 4
      git-features/src/progress/log.rs
  4. 30
      git-object/src/borrowed/commit.rs
  5. 24
      git-object/src/borrowed/tag.rs
  6. 34
      git-object/src/borrowed/util/mod.rs
  7. 32
      git-object/src/borrowed/util/tests.rs
  8. 17
      git-object/tests/borrowed/commit.rs
  9. 5
      git-object/tests/borrowed/tag.rs
  10. 7
      git-odb/src/loose/db/mod.rs
  11. 15
      git-odb/src/loose/db/serde.rs
  12. 9
      git-odb/src/loose/object.rs
  13. 9
      git-odb/src/pack/cache.rs
  14. 9
      git-odb/src/pack/file/mod.rs
  15. 53
      git-odb/src/pack/file/read.rs
  16. 11
      git-odb/src/pack/index/file.rs
  17. 48
      git-odb/src/pack/index/verify.rs
  18. 56
      git-odb/src/zlib.rs
  19. 11
      git-odb/tests/loose/mod.rs
  20. 26
      git-odb/tests/pack/file.rs
  21. 44
      git-odb/tests/pack/index.rs
  22. 33
      git-repository/src/init.rs
  23. 44
      gitoxide-core/src/lib.rs
  24. 1
      rustfmt.toml
  25. 4
      src/plumbing/pretty.rs

9
demos/git-count.rs

@ -44,14 +44,7 @@ fn run() -> Result<()> {
.par_iter()
.fold(
|| (0, 0, 0, 0, 0),
|(mut deltas, mut commits, mut trees, mut blobs, mut tags): (
u32,
u32,
u32,
u32,
u32,
),
entry| {
|(mut deltas, mut commits, mut trees, mut blobs, mut tags): (u32, u32, u32, u32, u32), entry| {
match pack.entry(entry.pack_offset).header {
Commit => commits += 1,
Tag => tags += 1,

10
git-features/src/parallel.rs

@ -10,10 +10,7 @@ mod serial {
use crate::parallel::Reducer;
#[cfg(not(feature = "parallel"))]
pub fn join<O1: Send, O2: Send>(
left: impl FnOnce() -> O1 + Send,
right: impl FnOnce() -> O2 + Send,
) -> (O1, O2) {
pub fn join<O1: Send, O2: Send>(left: impl FnOnce() -> O1 + Send, right: impl FnOnce() -> O2 + Send) -> (O1, O2) {
(left(), right())
}
@ -41,10 +38,7 @@ mod in_parallel {
use crate::parallel::Reducer;
use crossbeam_utils::thread;
pub fn join<O1: Send, O2: Send>(
left: impl FnOnce() -> O1 + Send,
right: impl FnOnce() -> O2 + Send,
) -> (O1, O2) {
pub fn join<O1: Send, O2: Send>(left: impl FnOnce() -> O1 + Send, right: impl FnOnce() -> O2 + Send) -> (O1, O2) {
thread::scope(|s| {
let left = s.spawn(|_| left());
let right = s.spawn(|_| right());

4
git-features/src/progress/log.rs

@ -47,9 +47,7 @@ impl Progress for Log {
{
self.last_set = Some(now);
match (self.max, self.unit) {
(Some(max), Some(unit)) => {
log::info!("{} → {} / {} {}", self.name, step, max, unit)
}
(Some(max), Some(unit)) => log::info!("{} → {} / {} {}", self.name, step, max, unit),
(None, Some(unit)) => log::info!("{} → {} {}", self.name, step, unit),
(Some(max), None) => log::info!("{} → {} / {}", self.name, step, max),
(None, None) => log::info!("{} → {}", self.name, step),

30
git-object/src/borrowed/commit.rs

@ -31,30 +31,24 @@ pub struct Commit<'data> {
pub fn parse_message(i: &[u8]) -> IResult<&[u8], &BStr, Error> {
if i.is_empty() {
// newline + [message]
return Err(nom::Err::Error(Error::NomDetail(
i.into(),
"commit message is missing",
)));
return Err(nom::Err::Error(Error::NomDetail(i.into(), "commit message is missing")));
}
let (i, _) = tag(NL)(i).map_err(Error::context(
"a newline separates headers from the message",
))?;
let (i, _) = tag(NL)(i).map_err(Error::context("a newline separates headers from the message"))?;
debug_assert!(!i.is_empty());
Ok((&[], &i.as_bstr()))
}
pub fn parse(i: &[u8]) -> IResult<&[u8], Commit, Error> {
let (i, tree) = parse_header_field(i, b"tree", parse_hex_sha1)
.map_err(Error::context("tree <40 lowercase hex char>"))?;
let (i, parents) = many0(|i| parse_header_field(i, b"parent", parse_hex_sha1))(i).map_err(
Error::context("zero or more 'parent <40 lowercase hex char>'"),
)?;
let (i, author) = parse_header_field(i, b"author", parse_signature)
.map_err(Error::context("author <signature>"))?;
let (i, committer) = parse_header_field(i, b"committer", parse_signature)
.map_err(Error::context("author <signature>"))?;
let (i, encoding) = opt(|i| parse_header_field(i, b"encoding", is_not(NL)))(i)
.map_err(Error::context("author <signature>"))?;
let (i, tree) =
parse_header_field(i, b"tree", parse_hex_sha1).map_err(Error::context("tree <40 lowercase hex char>"))?;
let (i, parents) = many0(|i| parse_header_field(i, b"parent", parse_hex_sha1))(i)
.map_err(Error::context("zero or more 'parent <40 lowercase hex char>'"))?;
let (i, author) =
parse_header_field(i, b"author", parse_signature).map_err(Error::context("author <signature>"))?;
let (i, committer) =
parse_header_field(i, b"committer", parse_signature).map_err(Error::context("author <signature>"))?;
let (i, encoding) =
opt(|i| parse_header_field(i, b"encoding", is_not(NL)))(i).map_err(Error::context("author <signature>"))?;
let (i, pgp_signature) = opt(alt((
|i| parse_header_field_multiline(i, b"gpgsig"),
|i| parse_header_field(i, b"gpgsig", is_not(NL)),

24
git-object/src/borrowed/tag.rs

@ -28,19 +28,18 @@ pub struct Tag<'data> {
}
fn parse(i: &[u8]) -> IResult<&[u8], Tag, Error> {
let (i, target) = parse_header_field(i, b"object", parse_hex_sha1)
.map_err(Error::context("object <40 lowercase hex char>"))?;
let (i, target) =
parse_header_field(i, b"object", parse_hex_sha1).map_err(Error::context("object <40 lowercase hex char>"))?;
let (i, kind) = parse_header_field(i, b"type", take_while1(is_alphabetic))
.map_err(Error::context("type <object kind>"))?;
let kind =
crate::Kind::from_bytes(kind).map_err(|e| nom::Err::Error(Error::ParseKindError(e)))?;
let (i, kind) =
parse_header_field(i, b"type", take_while1(is_alphabetic)).map_err(Error::context("type <object kind>"))?;
let kind = crate::Kind::from_bytes(kind).map_err(|e| nom::Err::Error(Error::ParseKindError(e)))?;
let (i, tag_version) = parse_header_field(i, b"tag", take_while1(|b| b != NL[0]))
.map_err(Error::context("tag <version>"))?;
let (i, tag_version) =
parse_header_field(i, b"tag", take_while1(|b| b != NL[0])).map_err(Error::context("tag <version>"))?;
let (i, signature) = parse_header_field(i, b"tagger", parse_signature)
.map_err(Error::context("tagger <signature>"))?;
let (i, signature) =
parse_header_field(i, b"tagger", parse_signature).map_err(Error::context("tagger <signature>"))?;
let (i, (message, pgp_signature)) = all_consuming(parse_message)(i)?;
Ok((
i,
@ -65,10 +64,7 @@ fn parse_message(i: &[u8]) -> IResult<&[u8], (&BStr, Option<&BStr>), Error> {
let (i, _) = tag(NL)(i)?;
fn all_to_end(i: &[u8]) -> IResult<&[u8], (&[u8], &[u8]), Error> {
if i.is_empty() {
return Err(nom::Err::Error(Error::NomDetail(
i.into(),
"tag message is missing",
)));
return Err(nom::Err::Error(Error::NomDetail(i.into(), "tag message is missing")));
}
// an empty signature message signals that there is none - the function signature is needed
// to work with 'alt(…)'. PGP signatures are never empty

34
git-object/src/borrowed/util/mod.rs

@ -15,10 +15,7 @@ use nom::{
pub(crate) const NL: &[u8] = b"\n";
pub(crate) const SPACE: &[u8] = b" ";
pub(crate) fn parse_header_field_multiline<'a>(
i: &'a [u8],
name: &'static [u8],
) -> IResult<&'a [u8], &'a [u8], Error> {
pub(crate) fn parse_header_field_multiline<'a>(i: &'a [u8], name: &'static [u8]) -> IResult<&'a [u8], &'a [u8], Error> {
let (i, o) = peek(preceded(
terminated(tag(name), tag(SPACE)),
recognize(tuple((
@ -38,10 +35,7 @@ pub(crate) fn parse_header_field<'a, T>(
name: &'static [u8],
parse_value: impl Fn(&'a [u8]) -> IResult<&'a [u8], T, Error>,
) -> IResult<&'a [u8], T, Error> {
terminated(
preceded(terminated(tag(name), tag(SPACE)), parse_value),
tag(NL),
)(i)
terminated(preceded(terminated(tag(name), tag(SPACE)), parse_value), tag(NL))(i)
}
fn is_hex_digit_lc(b: u8) -> bool {
@ -69,25 +63,11 @@ pub(crate) fn parse_signature(i: &[u8]) -> IResult<&[u8], Signature, Error> {
"tagger <name> <<email>> <time seconds since epoch> <+|-><HHMM>",
))?;
let sign = if tzsign[0] == b'-' {
Sign::Minus
} else {
Sign::Plus
};
let hours = btoi::<i32>(&tzhour).map_err(|e| {
nom::Err::Error(Error::ParseIntegerError(
"invalid 'hours' string",
tzhour.into(),
e,
))
})?;
let minutes = btoi::<i32>(&tzminute).map_err(|e| {
nom::Err::Error(Error::ParseIntegerError(
"invalid 'minutes' string",
tzminute.into(),
e,
))
})?;
let sign = if tzsign[0] == b'-' { Sign::Minus } else { Sign::Plus };
let hours = btoi::<i32>(&tzhour)
.map_err(|e| nom::Err::Error(Error::ParseIntegerError("invalid 'hours' string", tzhour.into(), e)))?;
let minutes = btoi::<i32>(&tzminute)
.map_err(|e| nom::Err::Error(Error::ParseIntegerError("invalid 'minutes' string", tzminute.into(), e)))?;
let offset = (hours * 3600 + minutes * 60) * if sign == Sign::Minus { -1 } else { 1 };
Ok((

32
git-object/src/borrowed/util/tests.rs

@ -4,13 +4,7 @@ mod parse_signature {
use crate::{Sign, Time};
use bstr::ByteSlice;
fn signature(
name: &'static str,
email: &'static str,
time: u32,
sign: Sign,
offset: i32,
) -> Signature<'static> {
fn signature(name: &'static str, email: &'static str, time: u32, sign: Sign, offset: i32) -> Signature<'static> {
Signature {
name: name.as_bytes().as_bstr(),
email: email.as_bytes().as_bstr(),
@ -24,13 +18,7 @@ mod parse_signature {
parse_signature(b"Sebastian Thiel <byronimo@gmail.com> 1528473343 -0230")
.unwrap()
.1,
signature(
"Sebastian Thiel",
"byronimo@gmail.com",
1528473343,
Sign::Minus,
-9000
)
signature("Sebastian Thiel", "byronimo@gmail.com", 1528473343, Sign::Minus, -9000)
);
}
@ -40,13 +28,7 @@ mod parse_signature {
parse_signature(b"Sebastian Thiel <byronimo@gmail.com> 1528473343 +0230")
.unwrap()
.1,
signature(
"Sebastian Thiel",
"byronimo@gmail.com",
1528473343,
Sign::Plus,
9000
)
signature("Sebastian Thiel", "byronimo@gmail.com", 1528473343, Sign::Plus, 9000)
);
}
@ -56,13 +38,7 @@ mod parse_signature {
parse_signature(b"Sebastian Thiel <byronimo@gmail.com> 1528473343 -0000")
.unwrap()
.1,
signature(
"Sebastian Thiel",
"byronimo@gmail.com",
1528473343,
Sign::Minus,
0
)
signature("Sebastian Thiel", "byronimo@gmail.com", 1528473343, Sign::Minus, 0)
);
}

17
git-object/tests/borrowed/commit.rs

@ -7,10 +7,7 @@ mod method {
fn tree() {
let fixture = fixture_bytes("commit", "unsigned.txt");
let commit = Commit::from_bytes(&fixture).unwrap();
assert_eq!(
commit.tree(),
hex_to_id("1b2dfb4ac5e42080b682fc676e9738c94ce6d54d")
);
assert_eq!(commit.tree(), hex_to_id("1b2dfb4ac5e42080b682fc676e9738c94ce6d54d"));
assert_eq!(commit.tree, "1b2dfb4ac5e42080b682fc676e9738c94ce6d54d");
}
}
@ -43,9 +40,7 @@ mod from_bytes {
Commit::from_bytes(&fixture_bytes("commit", "whitespace.txt")).unwrap(),
Commit {
tree: b"9bed6275068a0575243ba8409253e61af81ab2ff".as_bstr(),
parents: SmallVec::from(
vec![b"26b4df046d1776c123ac69d918f5aec247b58cc6".as_bstr()]
),
parents: SmallVec::from(vec![b"26b4df046d1776c123ac69d918f5aec247b58cc6".as_bstr()]),
author: signature(1592448450),
committer: signature(1592448450),
encoding: None,
@ -61,9 +56,7 @@ mod from_bytes {
Commit::from_bytes(&fixture_bytes("commit", "signed-singleline.txt")).unwrap(),
Commit {
tree: b"00fc39317701176e326974ce44f5bd545a32ec0b".as_bstr(),
parents: SmallVec::from(
vec![b"09d8d3a12e161a7f6afb522dbe8900a9c09bce06".as_bstr()]
),
parents: SmallVec::from(vec![b"09d8d3a12e161a7f6afb522dbe8900a9c09bce06".as_bstr()]),
author: signature(1592391367),
committer: signature(1592391367),
encoding: None,
@ -111,9 +104,7 @@ mod from_bytes {
Commit::from_bytes(&fixture_bytes("commit", "with-encoding.txt")).unwrap(),
Commit {
tree: b"4a1c03029e7407c0afe9fc0320b3258e188b115e".as_bstr(),
parents: SmallVec::from(
vec![b"7ca98aad461a5c302cb4c9e3acaaa6053cc67a62".as_bstr()]
),
parents: SmallVec::from(vec![b"7ca98aad461a5c302cb4c9e3acaaa6053cc67a62".as_bstr()]),
author: signature(1592438199),
committer: signature(1592438199),
encoding: Some("ISO-8859-1".into()),

5
git-object/tests/borrowed/tag.rs

@ -13,10 +13,7 @@ mod method {
fn target() {
let fixture = fixture_bytes("tag", "signed.txt");
let tag = Tag::from_bytes(&fixture).unwrap();
assert_eq!(
tag.target(),
hex_to_id("ffa700b4aca13b80cb6b98a078e7c96804f8e0ec")
);
assert_eq!(tag.target(), hex_to_id("ffa700b4aca13b80cb6b98a078e7c96804f8e0ec"));
assert_eq!(tag.target, "ffa700b4aca13b80cb6b98a078e7c96804f8e0ec")
}
}

7
git-odb/src/loose/db/mod.rs

@ -70,9 +70,7 @@ impl Db {
let mut is_valid_path = false;
let e = res.map_err(Error::WalkDir).map(|e| {
let p = e.path();
let (c1, c2) = p
.components()
.fold((None, None), |(_c1, c2), cn| (c2, Some(cn)));
let (c1, c2) = p.components().fold((None, None), |(_c1, c2), cn| (c2, Some(cn)));
if let (Some(Normal(c1)), Some(Normal(c2))) = (c1, c2) {
if c1.len() == 2 && c2.len() == 38 {
if let (Some(c1), Some(c2)) = (c1.to_str(), c2.to_str()) {
@ -106,8 +104,7 @@ impl Db {
let mut decompressed = [0; HEADER_READ_UNCOMPRESSED_BYTES];
let mut compressed = [0; HEADER_READ_COMPRESSED_BYTES];
let ((_status, _consumed_in, consumed_out), bytes_read, mut input_stream) = {
let mut istream =
fs::File::open(&path).map_err(|e| Error::Io(e, "open", path.to_owned()))?;
let mut istream = fs::File::open(&path).map_err(|e| Error::Io(e, "open", path.to_owned()))?;
let bytes_read = istream
.read(&mut compressed[..])
.map_err(|e| Error::Io(e, "read", path.to_owned()))?;

15
git-odb/src/loose/db/serde.rs

@ -13,11 +13,7 @@ pub fn parse_header(input: &[u8]) -> Result<(object::Kind, usize, usize), Error>
(Some(kind), Some(size)) => Ok((
object::Kind::from_bytes(kind)?,
btoi::btoi(size).map_err(|e| {
Error::ParseIntegerError(
"Object size in header could not be parsed",
size.to_owned(),
e,
)
Error::ParseIntegerError("Object size in header could not be parsed", size.to_owned(), e)
})?,
header_end + 1, // account for 0 byte
)),
@ -35,11 +31,7 @@ fn kind_to_bytes_with_space(object: object::Kind) -> &'static [u8] {
}
}
pub fn write_header(
object: object::Kind,
size: usize,
mut out: impl std::io::Write,
) -> Result<usize, std::io::Error> {
pub fn write_header(object: object::Kind, size: usize, mut out: impl std::io::Write) -> Result<usize, std::io::Error> {
let mut written = out.write(kind_to_bytes_with_space(object))?;
written += itoa::write(&mut out, size)?;
out.write_u8(0)?;
@ -63,8 +55,7 @@ mod tests {
] {
let written = write_header(*kind, *size, &mut buf[..]).unwrap();
assert_eq!(buf[..written].as_bstr(), expected.as_bstr());
let (actual_kind, actual_size, actual_read) =
parse_header(&buf[..written]).unwrap();
let (actual_kind, actual_size, actual_read) = parse_header(&buf[..written]).unwrap();
assert_eq!(actual_kind, *kind);
assert_eq!(actual_size, *size);
assert_eq!(actual_read, written);

9
git-odb/src/loose/object.rs

@ -80,19 +80,16 @@ impl Object {
if let Some(path) = self.path.take() {
// NOTE: For now we just re-read everything from the beginning without seeking, as our buffer
// is small so the seek might be more expensive than just reading everything.
let mut file =
std::fs::File::open(&path).map_err(|e| Error::Io(e, "open", path.clone()))?;
let mut file = std::fs::File::open(&path).map_err(|e| Error::Io(e, "open", path.clone()))?;
let file_size = file
.metadata()
.map_err(|e| Error::Io(e, "read metadata", path.clone()))?
.len() as usize;
let mut buf = Vec::with_capacity(file_size);
file.read_to_end(&mut buf)
.map_err(|e| Error::Io(e, "read", path))?;
file.read_to_end(&mut buf).map_err(|e| Error::Io(e, "read", path))?;
self.compressed_data = SmallVec::from(buf);
}
self.decompressed_data =
SmallVec::from(decompress_to_vec_zlib(&self.compressed_data[..]).unwrap());
self.decompressed_data = SmallVec::from(decompress_to_vec_zlib(&self.compressed_data[..]).unwrap());
self.decompressed_data.shrink_to_fit();
assert!(self.decompressed_data.len() == total_size);
self.decompression_complete = true;

9
git-odb/src/pack/cache.rs

@ -6,14 +6,7 @@ pub trait DecodeEntry {
pub struct DecodeEntryNoop;
impl DecodeEntry for DecodeEntryNoop {
fn put(
&mut self,
_offset: u64,
_data: &[u8],
_kind: git_object::Kind,
_compressed_size: usize,
) {
}
fn put(&mut self, _offset: u64, _data: &[u8], _kind: git_object::Kind, _compressed_size: usize) {}
fn get(&mut self, _offset: u64, _out: &mut Vec<u8>) -> Option<(git_object::Kind, usize)> {
None
}

9
git-odb/src/pack/file/mod.rs

@ -120,11 +120,7 @@ impl File {
fn assure_v2(&self) {
assert!(
if let Kind::V2 = self.kind.clone() {
true
} else {
false
},
if let Kind::V2 = self.kind.clone() { true } else { false },
"Only V2 is implemented"
);
}
@ -135,8 +131,7 @@ impl File {
assert!(pack_offset <= self.data.len(), "offset out of bounds");
let object_data = &self.data[pack_offset..];
let (object, decompressed_size, consumed_bytes) =
decoded::Header::from_bytes(object_data, offset);
let (object, decompressed_size, consumed_bytes) = decoded::Header::from_bytes(object_data, offset);
decoded::Entry {
header: object,
decompressed_size,

53
git-odb/src/pack/file/read.rs

@ -73,14 +73,8 @@ impl File {
/// Note that this method does not resolve deltified objects, but merely decompresses their content
/// `out` is expected to be large enough to hold `entry.size` bytes.
/// Returns the amount of packed bytes there were decompressed into `out`
fn decompress_entry_from_data_offset(
&self,
data_offset: u64,
out: &mut [u8],
) -> Result<usize, Error> {
let offset: usize = data_offset
.try_into()
.expect("offset representable by machine");
fn decompress_entry_from_data_offset(&self, data_offset: u64, out: &mut [u8]) -> Result<usize, Error> {
let offset: usize = data_offset.try_into().expect("offset representable by machine");
assert!(offset < self.data.len(), "entry offset out of bounds");
Inflate::default()
@ -110,14 +104,13 @@ impl File {
.expect("size representable by machine"),
0,
);
self.decompress_entry(&entry, out.as_mut_slice())
.map(|consumed_input| {
DecodeEntryResult::from_object_entry(
entry.header.to_kind().expect("a non-delta entry"),
&entry,
consumed_input,
)
})
self.decompress_entry(&entry, out.as_mut_slice()).map(|consumed_input| {
DecodeEntryResult::from_object_entry(
entry.header.to_kind().expect("a non-delta entry"),
&entry,
consumed_input,
)
})
}
OfsDelta { .. } | RefDelta { .. } => self.resolve_deltas(entry, resolve, out, cache),
}
@ -195,9 +188,7 @@ impl File {
// First pass will decompress all delta data and keep it in our output buffer
// [<possibly resolved base object>]<delta-1..delta-n>...
// so that we can find the biggest result size.
let total_delta_data_size: usize = total_delta_data_size
.try_into()
.expect("delta data to fit in memory");
let total_delta_data_size: usize = total_delta_data_size.try_into().expect("delta data to fit in memory");
let chain_len = chain.len();
let (first_buffer_end, second_buffer_end) = {
@ -246,10 +237,7 @@ impl File {
.expect("biggest result size small enough to fit into usize");
let first_buffer_size = biggest_result_size;
let second_buffer_size = first_buffer_size;
out.resize(
first_buffer_size + second_buffer_size + total_delta_data_size,
0,
);
out.resize(first_buffer_size + second_buffer_size + total_delta_data_size, 0);
// Now 'rescue' the deltas, because in the next step we possibly overwrite that portion
// of memory with the base object (in the majority of cases)
@ -276,8 +264,7 @@ impl File {
let base_entry = cursor;
debug_assert!(!base_entry.header.is_delta());
object_kind = base_entry.header.to_kind();
let packed_size =
self.decompress_entry_from_data_offset(base_entry.data_offset, out)?;
let packed_size = self.decompress_entry_from_data_offset(base_entry.data_offset, out)?;
cache.put(
base_entry.data_offset,
&out[..base_entry
@ -315,11 +302,7 @@ impl File {
if delta_idx + 1 == chain_len {
last_result_size = Some(*result_size);
}
apply_delta(
&source_buf[..*base_size],
&mut target_buf[..*result_size],
data,
);
apply_delta(&source_buf[..*base_size], &mut target_buf[..*result_size], data);
// use the target as source for the next delta
std::mem::swap(&mut source_buf, &mut target_buf);
}
@ -332,15 +315,9 @@ impl File {
}
out.resize(last_result_size, 0);
let object_kind = object_kind
.expect("a base object as root of any delta chain that we are here to resolve");
let object_kind = object_kind.expect("a base object as root of any delta chain that we are here to resolve");
let consumed_input = consumed_input.expect("at least one decompressed delta object");
cache.put(
first_entry.data_offset,
out.as_slice(),
object_kind,
consumed_input,
);
cache.put(first_entry.data_offset, out.as_slice(), object_kind, consumed_input);
Ok(DecodeEntryResult {
kind: object_kind,
// technically depending on the cache, the chain size is not correct as it might

11
git-odb/src/pack/index/file.rs

@ -122,10 +122,7 @@ impl File {
match self.kind {
Kind::V2 => {
let start = self.offset_pack_offset_v2() + index * N32_SIZE;
self.pack_offset_from_offset_v2(
&self.data[start..start + N32_SIZE],
self.offset_pack_offset64_v2(),
)
self.pack_offset_from_offset_v2(&self.data[start..start + N32_SIZE], self.offset_pack_offset64_v2())
}
Kind::V1 => {
let start = V1_HEADER_SIZE + index * (N32_SIZE + SHA1_SIZE);
@ -151,11 +148,7 @@ impl File {
pub fn lookup_index(&self, id: &[u8]) -> Option<u32> {
let first_byte = id[0] as usize;
let mut upper_bound = self.fan[first_byte];
let mut lower_bound = if first_byte != 0 {
self.fan[first_byte - 1]
} else {
0
};
let mut lower_bound = if first_byte != 0 { self.fan[first_byte - 1] } else { 0 };
// Bisect using indices
// TODO: Performance of V2 could possibly be better if we would be able to do a binary search

48
git-odb/src/pack/index/verify.rs

@ -49,9 +49,7 @@ impl TimeThroughput {
impl Into<String> for TimeThroughput {
fn into(self) -> String {
let time_taken = std::time::Instant::now()
.duration_since(self.then)
.as_secs_f32();
let time_taken = std::time::Instant::now().duration_since(self.then).as_secs_f32();
format!(
"finished in {:.2}s at {}/s",
time_taken,
@ -128,8 +126,7 @@ impl index::File {
expected: self.checksum_of_pack(),
});
}
let mut progress =
root.add_child(format!("Sha1 of pack at '{}'", pack.path().display()));
let mut progress = root.add_child(format!("Sha1 of pack at '{}'", pack.path().display()));
let (pack_res, id) = parallel::join(
move || {
let throughput = TimeThroughput::new(pack.data_len());
@ -187,15 +184,9 @@ impl index::File {
let mut chunk_average = chunk_stats.into_iter().fold(
DecodeEntryResult::default_from_kind(git_object::Kind::Tree),
|mut average, stats| {
*self
.stats
.objects_per_chain_length
.entry(stats.num_deltas)
.or_insert(0) += 1;
self.stats.total_decompressed_entries_size +=
stats.decompressed_size;
self.stats.total_compressed_entries_size +=
stats.compressed_size as u64;
*self.stats.objects_per_chain_length.entry(stats.num_deltas).or_insert(0) += 1;
self.stats.total_decompressed_entries_size += stats.decompressed_size;
self.stats.total_compressed_entries_size += stats.compressed_size as u64;
self.stats.total_object_size += stats.object_size as u64;
add_decode_result(&mut average, stats);
average
@ -236,10 +227,7 @@ impl index::File {
(
make_cache(),
Vec::with_capacity(2048),
reduce_progress
.lock()
.unwrap()
.add_child(format!("thread {}", index)),
reduce_progress.lock().unwrap().add_child(format!("thread {}", index)),
)
};
@ -261,31 +249,20 @@ impl index::File {
buf,
|id, _| {
self.lookup_index(&id).map(|index| {
ResolvedBase::InPack(
pack.entry(self.pack_offset_at_index(index)),
)
ResolvedBase::InPack(pack.entry(self.pack_offset_at_index(index)))
})
},
cache,
)
.map_err(|e| {
ChecksumError::PackDecode(
e,
index_entry.oid,
index_entry.pack_offset,
)
})?;
.map_err(|e| ChecksumError::PackDecode(e, index_entry.oid, index_entry.pack_offset))?;
let object_kind = entry_stats.kind;
let consumed_input = entry_stats.compressed_size;
stats.push(entry_stats);
let mut header_buf = [0u8; 64];
let header_size = crate::loose::db::serde::write_header(
object_kind,
buf.len(),
&mut header_buf[..],
)
.expect("header buffer to be big enough");
let header_size =
crate::loose::db::serde::write_header(object_kind, buf.len(), &mut header_buf[..])
.expect("header buffer to be big enough");
let mut hasher = git_features::hash::Sha1::default();
hasher.update(&header_buf[..header_size]);
hasher.update(buf.as_slice());
@ -302,8 +279,7 @@ impl index::File {
if let Some(desired_crc32) = index_entry.crc32 {
let actual_crc32 = pack.entry_crc32(
index_entry.pack_offset,
(pack_entry_data_offset - index_entry.pack_offset) as usize
+ consumed_input,
(pack_entry_data_offset - index_entry.pack_offset) as usize + consumed_input,
);
if actual_crc32 != desired_crc32 {
return Err(ChecksumError::Crc32Mismatch {

56
git-odb/src/zlib.rs

@ -4,8 +4,7 @@ use miniz_oxide::{
core::{
decompress,
inflate_flags::{
TINFL_FLAG_HAS_MORE_INPUT, TINFL_FLAG_PARSE_ZLIB_HEADER,
TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF,
TINFL_FLAG_HAS_MORE_INPUT, TINFL_FLAG_PARSE_ZLIB_HEADER, TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF,
},
},
TINFLStatus,
@ -55,19 +54,14 @@ impl Inflate {
&mut self.state,
input,
out,
if parse_header {
TINFL_FLAG_PARSE_ZLIB_HEADER
} else {
0
} | TINFL_FLAG_HAS_MORE_INPUT
if parse_header { TINFL_FLAG_PARSE_ZLIB_HEADER } else { 0 }
| TINFL_FLAG_HAS_MORE_INPUT
| TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF,
);
use miniz_oxide::inflate::TINFLStatus::*;
match status {
Failed | FailedCannotMakeProgress | BadParam | Adler32Mismatch => {
return Err(Error::Inflate(status))
}
Failed | FailedCannotMakeProgress | BadParam | Adler32Mismatch => return Err(Error::Inflate(status)),
HasMoreOutput | NeedsMoreInput => {}
Done => {
self.is_done = true;
@ -78,9 +72,7 @@ impl Inflate {
}
pub mod stream {
use miniz_oxide::{
inflate, inflate::stream::InflateState, DataFormat, MZError, MZFlush, MZStatus,
};
use miniz_oxide::{inflate, inflate::stream::InflateState, DataFormat, MZError, MZFlush, MZStatus};
use quick_error::quick_error;
use std::io;
use std::io::BufRead;
@ -173,12 +165,7 @@ pub mod stream {
}
impl Inflate {
fn decompress(
&mut self,
input: &[u8],
output: &mut [u8],
flush: MZFlush,
) -> Result<Status, Error> {
fn decompress(&mut self, input: &[u8], output: &mut [u8], flush: MZFlush) -> Result<Status, Error> {
let res = inflate::stream::inflate(&mut self.state, input, output, flush);
self.total_in += res.bytes_consumed as u64;
self.total_out += res.bytes_written as u64;
@ -187,9 +174,7 @@ pub mod stream {
Ok(status) => match status {
MZStatus::Ok => Ok(Status::Ok),
MZStatus::StreamEnd => Ok(Status::StreamEnd),
MZStatus::NeedDict => Err(Error::ZLibNeedDict(
self.state.decompressor().adler32().unwrap_or(0),
)),
MZStatus::NeedDict => Err(Error::ZLibNeedDict(self.state.decompressor().adler32().unwrap_or(0))),
},
Err(status) => match status {
MZError::Buf => Ok(Status::BufError),
@ -223,17 +208,10 @@ pub mod stream {
// then we need to keep asking for more data because if we
// return that 0 bytes of data have been read then it will
// be interpreted as EOF.
Ok(Status::Ok) | Ok(Status::BufError) if read == 0 && !eof && dst.len() > 0 => {
continue
}
Ok(Status::Ok) | Ok(Status::BufError) if read == 0 && !eof && dst.len() > 0 => continue,
Ok(Status::Ok) | Ok(Status::BufError) | Ok(Status::StreamEnd) => return Ok(read),
Err(..) => {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
"corrupt deflate stream",
))
}
Err(..) => return Err(io::Error::new(io::ErrorKind::InvalidInput, "corrupt deflate stream")),
}
}
}
@ -252,21 +230,11 @@ pub mod stream {
#[test]
fn small_file_decompress() {
let r = InflateReader::new(
std::fs::File::open(fixture_path(
"objects/37/d4e6c5c48ba0d245164c4e10d5f41140cab980",
))
.unwrap(),
std::fs::File::open(fixture_path("objects/37/d4e6c5c48ba0d245164c4e10d5f41140cab980")).unwrap(),
);
let mut bytes = r.bytes();
let content = bytes
.by_ref()
.take(16)
.collect::<Result<Vec<_>, _>>()
.unwrap();
assert_eq!(
content.as_slice().as_bstr(),
b"blob 9\0hi there\n".as_bstr()
);
let content = bytes.by_ref().take(16).collect::<Result<Vec<_>, _>>().unwrap();
assert_eq!(content.as_slice().as_bstr(), b"blob 9\0hi there\n".as_bstr());
assert!(bytes.next().is_none());
}
}

11
git-odb/tests/loose/mod.rs

@ -137,10 +137,7 @@ cjHJZXWmV4CcRfmLsXzU8s2cR9A0DBvOxhPD1TlKC2JhBFXigjuL9U4Rbq9tdegB
fn blob_big_stream() {
let o = locate("a706d7cd20fc8ce71489f34b50cf01011c104193");
let size = o.size;
assert_eq!(
o.stream().unwrap().bytes().filter_map(Result::ok).count(),
size
);
assert_eq!(o.stream().unwrap().bytes().filter_map(Result::ok).count(), size);
}
#[test]
@ -170,16 +167,14 @@ cjHJZXWmV4CcRfmLsXzU8s2cR9A0DBvOxhPD1TlKC2JhBFXigjuL9U4Rbq9tdegB
mode: TreeMode::Tree,
filename: b"dir".as_bstr(),
oid: &[
150, 174, 134, 139, 53, 57, 245, 81, 200, 143, 213, 240, 35, 148, 208,
34, 88, 27, 17, 176,
150, 174, 134, 139, 53, 57, 245, 81, 200, 143, 213, 240, 35, 148, 208, 34, 88, 27, 17, 176,
],
},
TreeEntry {
mode: TreeMode::Blob,
filename: b"file.txt".as_bstr(),
oid: &[
55, 212, 230, 197, 196, 139, 160, 210, 69, 22, 76, 78, 16, 213, 244,
17, 64, 202, 185, 128,
55, 212, 230, 197, 196, 139, 160, 210, 69, 22, 76, 78, 16, 213, 244, 17, 64, 202, 185, 128,
],
},
],

26
git-odb/tests/pack/file.rs

@ -13,10 +13,7 @@ mod method {
#[test]
fn checksum() {
let p = pack_at(SMALL_PACK);
assert_eq!(
hex::encode(p.checksum().0),
"0f3ea84cd1bba10c2a03d736a460635082833e59"
);
assert_eq!(hex::encode(p.checksum().0), "0f3ea84cd1bba10c2a03d736a460635082833e59");
}
#[test]
@ -77,13 +74,8 @@ mod decode_entry {
let p = pack_at(SMALL_PACK);
let entry = p.entry(offset);
let mut buf = Vec::new();
p.decode_entry(
entry,
&mut buf,
resolve_with_panic,
&mut cache::DecodeEntryNoop,
)
.unwrap();
p.decode_entry(entry, &mut buf, resolve_with_panic, &mut cache::DecodeEntryNoop)
.unwrap();
buf
}
}
@ -102,18 +94,18 @@ mod decompress_entry {
#[test]
fn blob() {
let buf = decompress_entry_at_offset(2142);
assert_eq!(buf.as_bstr(), b"GitPython is a python library used to interact with Git repositories.\n\nHi there\n\nHello Other\n".as_bstr());
assert_eq!(
buf.as_bstr(),
b"GitPython is a python library used to interact with Git repositories.\n\nHi there\n\nHello Other\n"
.as_bstr()
);
assert_eq!(buf.len(), 93)
}
#[test]
fn blob_with_two_chain_links() {
let buf = decompress_entry_at_offset(3033);
assert_eq!(
buf.len(),
6,
"it decompresses delta objects, but won't resolve them"
)
assert_eq!(buf.len(), 6, "it decompresses delta objects, but won't resolve them")
}
#[test]

44
git-odb/tests/pack/index.rs

@ -21,21 +21,9 @@ mod method {
fn lookup() {
let idx = index::File::at(&fixture_path(INDEX_V1)).unwrap();
for (id, desired_index, assertion) in &[
(
&b"036bd66fe9b6591e959e6df51160e636ab1a682e"[..],
Some(0),
"first",
),
(
b"f7f791d96b9a34ef0f08db4b007c5309b9adc3d6",
Some(65),
"close to last",
),
(
b"ffffffffffffffffffffffffffffffffffffffff",
None,
"not in pack",
),
(&b"036bd66fe9b6591e959e6df51160e636ab1a682e"[..], Some(0), "first"),
(b"f7f791d96b9a34ef0f08db4b007c5309b9adc3d6", Some(65), "close to last"),
(b"ffffffffffffffffffffffffffffffffffffffff", None, "not in pack"),
] {
assert_eq!(
idx.lookup_index(&git_object::Id::from_hex(*id).unwrap()),
@ -62,21 +50,9 @@ mod method {
fn lookup() {
let idx = index::File::at(&fixture_path(INDEX_V2)).unwrap();
for (id, desired_index, assertion) in &[
(
&b"0ead45fc727edcf5cadca25ef922284f32bb6fc1"[..],
Some(0),
"first",
),
(
b"e800b9c207e17f9b11e321cc1fba5dfe08af4222",
Some(29),
"last",
),
(
b"ffffffffffffffffffffffffffffffffffffffff",
None,
"not in pack",
),
(&b"0ead45fc727edcf5cadca25ef922284f32bb6fc1"[..], Some(0), "first"),
(b"e800b9c207e17f9b11e321cc1fba5dfe08af4222", Some(29), "last"),
(b"ffffffffffffffffffffffffffffffffffffffff", None, "not in pack"),
] {
assert_eq!(
idx.lookup_index(&git_object::Id::from_hex(*id).unwrap()),
@ -89,13 +65,7 @@ mod method {
let index = idx.lookup_index(&entry.oid).unwrap();
assert_eq!(entry.oid.as_slice(), idx.oid_at_index(index));
assert_eq!(entry.pack_offset, idx.pack_offset_at_index(index));
assert_eq!(
entry.crc32,
idx.crc32_at_index(index),
"{} {:?}",
index,
entry
);
assert_eq!(entry.crc32, idx.crc32_at_index(index), "{} {:?}", index, entry);
}
}
}

33
git-repository/src/init.rs

@ -30,27 +30,17 @@ quick_error! {
const GIT_DIR_NAME: &str = ".git";
const TPL_INFO_EXCLUDE: &[u8] = include_bytes!("./assets/baseline-init/info/exclude");
const TPL_HOOKS_APPLYPATCH_MSG: &[u8] =
include_bytes!("./assets/baseline-init/hooks/applypatch-msg.sample");
const TPL_HOOKS_COMMIT_MSG: &[u8] =
include_bytes!("./assets/baseline-init/hooks/commit-msg.sample");
const TPL_HOOKS_FSMONITOR_WATCHMAN: &[u8] =
include_bytes!("./assets/baseline-init/hooks/fsmonitor-watchman.sample");
const TPL_HOOKS_POST_UPDATE: &[u8] =
include_bytes!("./assets/baseline-init/hooks/post-update.sample");
const TPL_HOOKS_PRE_APPLYPATCH: &[u8] =
include_bytes!("./assets/baseline-init/hooks/pre-applypatch.sample");
const TPL_HOOKS_PRE_COMMIT: &[u8] =
include_bytes!("./assets/baseline-init/hooks/pre-commit.sample");
const TPL_HOOKS_PRE_MERGE_COMMIT: &[u8] =
include_bytes!("./assets/baseline-init/hooks/pre-merge-commit.sample");
const TPL_HOOKS_APPLYPATCH_MSG: &[u8] = include_bytes!("./assets/baseline-init/hooks/applypatch-msg.sample");
const TPL_HOOKS_COMMIT_MSG: &[u8] = include_bytes!("./assets/baseline-init/hooks/commit-msg.sample");
const TPL_HOOKS_FSMONITOR_WATCHMAN: &[u8] = include_bytes!("./assets/baseline-init/hooks/fsmonitor-watchman.sample");
const TPL_HOOKS_POST_UPDATE: &[u8] = include_bytes!("./assets/baseline-init/hooks/post-update.sample");
const TPL_HOOKS_PRE_APPLYPATCH: &[u8] = include_bytes!("./assets/baseline-init/hooks/pre-applypatch.sample");
const TPL_HOOKS_PRE_COMMIT: &[u8] = include_bytes!("./assets/baseline-init/hooks/pre-commit.sample");
const TPL_HOOKS_PRE_MERGE_COMMIT: &[u8] = include_bytes!("./assets/baseline-init/hooks/pre-merge-commit.sample");
const TPL_HOOKS_PRE_PUSH: &[u8] = include_bytes!("./assets/baseline-init/hooks/pre-push.sample");
const TPL_HOOKS_PRE_REBASE: &[u8] =
include_bytes!("./assets/baseline-init/hooks/pre-rebase.sample");
const TPL_HOOKS_PRE_RECEIVE: &[u8] =
include_bytes!("./assets/baseline-init/hooks/pre-receive.sample");
const TPL_HOOKS_PREPARE_COMMIT_MSG: &[u8] =
include_bytes!("./assets/baseline-init/hooks/prepare-commit-msg.sample");
const TPL_HOOKS_PRE_REBASE: &[u8] = include_bytes!("./assets/baseline-init/hooks/pre-rebase.sample");
const TPL_HOOKS_PRE_RECEIVE: &[u8] = include_bytes!("./assets/baseline-init/hooks/pre-receive.sample");
const TPL_HOOKS_PREPARE_COMMIT_MSG: &[u8] = include_bytes!("./assets/baseline-init/hooks/prepare-commit-msg.sample");
const TPL_HOOKS_UPDATE: &[u8] = include_bytes!("./assets/baseline-init/hooks/update.sample");
const TPL_CONFIG: &[u8] = include_bytes!("./assets/baseline-init/config");
const TPL_DESCRIPTION: &[u8] = include_bytes!("./assets/baseline-init/description");
@ -97,8 +87,7 @@ fn write_file(data: &[u8], path: &Path) -> Result<(), Error> {
.append(false)
.open(path)
.map_err(|e| Error::IoOpen(e, path.to_owned()))?;
file.write_all(data)
.map_err(|e| Error::IoWrite(e, path.to_owned()))
file.write_all(data).map_err(|e| Error::IoWrite(e, path.to_owned()))
}
fn create_dir(p: &Path) -> Result<(), Error> {

44
gitoxide-core/src/lib.rs

@ -21,21 +21,29 @@ where
<P as Progress>::SubProgress: Send,
{
let path = path.as_ref();
let ext = path.extension()
.and_then(|ext| ext.to_str())
.ok_or_else(|| anyhow!("Cannot determine file type on path without extension '{}', expecting default extensions 'idx' and 'pack'", path.display()))?;
let ext = path.extension().and_then(|ext| ext.to_str()).ok_or_else(|| {
anyhow!(
"Cannot determine file type on path without extension '{}', expecting default extensions 'idx' and 'pack'",
path.display()
)
})?;
let res = match ext {
"pack" => {
let pack = git_odb::pack::File::at(path).with_context(|| "Could not open pack file")?;
pack.verify_checksum().map(|id| (id, None))?
}
"idx" => {
let idx = git_odb::pack::index::File::at(path)
.with_context(|| "Could not open pack index file")?;
let idx = git_odb::pack::index::File::at(path).with_context(|| "Could not open pack index file")?;
let packfile_path = path.with_extension("pack");
let pack = git_odb::pack::File::at(&packfile_path)
.or_else(|e| {
writeln!(err, "Could not find matching pack file at '{}' - only index file will be verified, error was: {}", packfile_path.display(), e).ok();
writeln!(
err,
"Could not find matching pack file at '{}' - only index file will be verified, error was: {}",
packfile_path.display(),
e
)
.ok();
Err(e)
})
.ok();
@ -67,12 +75,7 @@ where
}
})?
}
ext => {
return Err(anyhow!(
"Unknown extension {:?}, expecting 'idx' or 'pack'",
ext
))
}
ext => return Err(anyhow!("Unknown extension {:?}, expecting 'idx' or 'pack'", ext)),
};
if let Some(stats) = res.1.as_ref() {
if output_statistics {
@ -83,16 +86,9 @@ where
Ok(res)
}
fn print_statistics(
out: &mut impl io::Write,
stats: &index::PackFileChecksumResult,
) -> io::Result<()> {
fn print_statistics(out: &mut impl io::Write, stats: &index::PackFileChecksumResult) -> io::Result<()> {
writeln!(out, "objects per delta chain length")?;
let mut chain_length_to_object: Vec<_> = stats
.objects_per_chain_length
.iter()
.map(|(a, b)| (*a, *b))
.collect();
let mut chain_length_to_object: Vec<_> = stats.objects_per_chain_length.iter().map(|(a, b)| (*a, *b)).collect();
chain_length_to_object.sort_by_key(|e| e.0);
let mut total_object_count = 0;
for (chain_length, object_count) in chain_length_to_object.into_iter() {
@ -132,10 +128,8 @@ fn print_statistics(
"pack size", ByteSize(stats.pack_size),
width = width
)?;
let compression_ratio =
stats.total_decompressed_entries_size as f64 / stats.total_compressed_entries_size as f64;
let delta_compression_ratio =
stats.total_object_size as f64 / stats.total_compressed_entries_size as f64;
let compression_ratio = stats.total_decompressed_entries_size as f64 / stats.total_compressed_entries_size as f64;
let delta_compression_ratio = stats.total_object_size as f64 / stats.total_compressed_entries_size as f64;
#[rustfmt::skip]
writeln!(
out,

1
rustfmt.toml

@ -0,0 +1 @@
max_width = 120

4
src/plumbing/pretty.rs

@ -95,9 +95,7 @@ pub fn main() -> Result<()> {
} => {
let (handle, progress) = init_progress("verify-pack", verbose, progress);
let mut buf = Vec::new();
let res =
core::verify_pack_or_pack_index(path, progress, statistics, &mut buf, stderr())
.map(|_| ());
let res = core::verify_pack_or_pack_index(path, progress, statistics, &mut buf, stderr()).map(|_| ());
// We might have something interesting to show, which would be hidden by the alternate screen if there is a progress TUI
// We know that the printing happens at the end, so this is fine.
drop(handle);

Loading…
Cancel
Save