From 6ebb5d1839cd5ab4d8aff78acbccebaa66f439c7 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Thu, 17 Sep 2020 11:44:37 +0800 Subject: [PATCH 01/51] refactor --- git-odb/src/pack/data/verify.rs | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/git-odb/src/pack/data/verify.rs b/git-odb/src/pack/data/verify.rs index b1ad3cd8617..26cd1f3b276 100644 --- a/git-odb/src/pack/data/verify.rs +++ b/git-odb/src/pack/data/verify.rs @@ -1,20 +1,13 @@ use crate::pack::data::File; use git_features::progress::Progress; use git_object::{owned, SHA1_SIZE}; -use quick_error::quick_error; -quick_error! { - #[derive(Debug)] - pub enum Error { - Mismatch { expected: owned::Id, actual: owned::Id } { - display("pack checksum mismatch: expected {}, got {}", expected, actual) - } - Io(err: std::io::Error) { - display("could not read pack file") - from() - source(err) - } - } +#[derive(thiserror::Error, Debug)] +pub enum Error { + #[error("pack checksum mismatch: expected {expected}, got {actual}")] + Mismatch { expected: owned::Id, actual: owned::Id }, + #[error("could not read pack file")] + Io(#[from] std::io::Error), } /// Checksums and verify checksums From 91d9f78a9af04b44b2cead30d6e1cbaaeb76a522 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Thu, 17 Sep 2020 11:48:06 +0800 Subject: [PATCH 02/51] refactor --- git-odb/src/loose/db/write.rs | 69 ++++++++++++++++++++--------------- 1 file changed, 39 insertions(+), 30 deletions(-) diff --git a/git-odb/src/loose/db/write.rs b/git-odb/src/loose/db/write.rs index fc742af4077..2e97e5c5c44 100644 --- a/git-odb/src/loose/db/write.rs +++ b/git-odb/src/loose/db/write.rs @@ -1,27 +1,24 @@ use super::Db; use crate::{hash, loose, zlib::stream::DeflateWriter}; use git_object::{owned, HashKind}; -use quick_error::quick_error; use std::{fs, io, io::Write, path::PathBuf}; use tempfile::NamedTempFile; -quick_error! { - #[derive(Debug)] - pub enum Error { - Io(err: io::Error, msg: &'static str, path: PathBuf) { - display("Could not {} '{}'", msg, path.display()) - source(err) - } - IoRaw(err: io::Error) { - display("An IO error occurred while writing an object") - source(err) - from() - } - Persist(err: tempfile::PersistError, target: PathBuf) { - display("Could not turn temporary file into persisted file at '{}'", target.display()) - source(err) - } - } +#[derive(thiserror::Error, Debug)] +pub enum Error { + #[error("Could not {message} '{path}'")] + Io { + source: io::Error, + message: &'static str, + path: PathBuf, + }, + #[error("An IO error occurred while writing an object")] + IoRaw(#[from] io::Error), + #[error("Could not turn temporary file into persisted file at '{target}'")] + Persist { + source: tempfile::PersistError, + target: PathBuf, + }, } impl crate::Write for Db { @@ -31,8 +28,11 @@ impl crate::Write for Db { match hash { HashKind::Sha1 => { let mut to = self.write_header(kind, from.len() as u64, hash)?; - to.write_all(from) - .map_err(|err| Error::Io(err, "stream all data into tempfile in", self.path.to_owned()))?; + to.write_all(from).map_err(|err| Error::Io { + source: err, + message: "stream all data into tempfile in", + path: self.path.to_owned(), + })?; to.flush()?; self.finalize_object(to) } @@ -49,8 +49,11 @@ impl crate::Write for Db { match hash { HashKind::Sha1 => { let mut to = self.write_header(kind, size, hash)?; - io::copy(&mut from, &mut to) - .map_err(|err| Error::Io(err, "stream all data into tempfile in", self.path.to_owned()))?; + io::copy(&mut from, &mut to).map_err(|err| Error::Io { + source: err, + message: "stream all data into tempfile in", + path: self.path.to_owned(), + })?; to.flush()?; self.finalize_object(to) } @@ -68,15 +71,19 @@ impl Db { hash: HashKind, ) -> Result, Error> { let mut to = hash::Write::new( - DeflateWriter::new( - NamedTempFile::new_in(&self.path) - .map_err(|err| Error::Io(err, "create named temp file in", self.path.to_owned()))?, - ), + DeflateWriter::new(NamedTempFile::new_in(&self.path).map_err(|err| Error::Io { + source: err, + message: "create named temp file in", + path: self.path.to_owned(), + })?), hash, ); - loose::object::header::encode(kind, size, &mut to) - .map_err(|err| Error::Io(err, "write header to tempfile in", self.path.to_owned()))?; + loose::object::header::encode(kind, size, &mut to).map_err(|err| Error::Io { + source: err, + message: "write header to tempfile in", + path: self.path.to_owned(), + })?; Ok(to) } @@ -96,8 +103,10 @@ impl Db { } } let file = file.into_inner(); - file.persist(&object_path) - .map_err(|err| Error::Persist(err, object_path))?; + file.persist(&object_path).map_err(|err| Error::Persist { + source: err, + target: object_path, + })?; Ok(id) } } From ad17bfdc07e1301693fdfa3d09df3b39f675a36f Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Fri, 18 Sep 2020 15:50:11 +0800 Subject: [PATCH 03/51] refactor --- git-odb/src/pack/tree/traverse/mod.rs | 33 +++++++++-------------- git-odb/src/pack/tree/traverse/resolve.rs | 9 +++++-- 2 files changed, 20 insertions(+), 22 deletions(-) diff --git a/git-odb/src/pack/tree/traverse/mod.rs b/git-odb/src/pack/tree/traverse/mod.rs index c398c5dc0b9..2b66abc24c3 100644 --- a/git-odb/src/pack/tree/traverse/mod.rs +++ b/git-odb/src/pack/tree/traverse/mod.rs @@ -9,29 +9,22 @@ use git_features::{ parallel::in_parallel_if, progress::{self, Progress}, }; -use quick_error::quick_error; mod resolve; -quick_error! { - #[derive(Debug)] - pub enum Error { - ZlibInflate(err: crate::zlib::Error, msg: &'static str) { - display("{}", msg) - source(err) - } - ResolveFailed(pack_offset: u64) { - display("The resolver failed to obtain the pack entry bytes for the entry at {}", pack_offset) - } - Inspect(err: Box) { - display("One of the object inspectors failed") - source(&**err) - from() - } - Interrupted { - display("Interrupted") - } - } +#[derive(thiserror::Error, Debug)] +pub enum Error { + #[error("{message}")] + ZlibInflate { + source: crate::zlib::Error, + message: &'static str, + }, + #[error("The resolver failed to obtain the pack entry bytes for the entry at {pack_offset}")] + ResolveFailed { pack_offset: u64 }, + #[error("One of the object inspectors failed")] + Inspect(#[from] Box), + #[error("Interrupted")] + Interrupted, } pub struct Context<'a, S> { diff --git a/git-odb/src/pack/tree/traverse/resolve.rs b/git-odb/src/pack/tree/traverse/resolve.rs index a50d2f49a72..cb3fc8043b8 100644 --- a/git-odb/src/pack/tree/traverse/resolve.rs +++ b/git-odb/src/pack/tree/traverse/resolve.rs @@ -25,7 +25,9 @@ where let decompress_from_resolver = |slice: EntrySlice| -> Result<(pack::data::Entry, u64, Vec), Error> { let mut bytes_buf = bytes_buf.borrow_mut(); bytes_buf.resize((slice.end - slice.start) as usize, 0); - resolve(slice.clone(), &mut bytes_buf).ok_or_else(|| Error::ResolveFailed(slice.start))?; + resolve(slice.clone(), &mut bytes_buf).ok_or_else(|| Error::ResolveFailed { + pack_offset: slice.start, + })?; let entry = pack::data::Entry::from_bytes(&bytes_buf, slice.start); let compressed = &bytes_buf[entry.header_size() as usize..]; let decompressed_len = entry.decompressed_size as usize; @@ -104,6 +106,9 @@ fn decompress_all_at_once(b: &[u8], decompressed_len: usize) -> Result, out.resize(decompressed_len, 0); zlib::Inflate::default() .once(&b, &mut out, true) - .map_err(|err| Error::ZlibInflate(err, "Failed to decompress entry"))?; + .map_err(|err| Error::ZlibInflate { + source: err, + message: "Failed to decompress entry", + })?; Ok(out) } From 60ac8b0a7545fff6ef293fd48716e9a19175517c Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Fri, 18 Sep 2020 16:03:54 +0800 Subject: [PATCH 04/51] (cargo-release) version 0.4.1 --- Cargo.lock | 2 +- git-odb/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3af60bdc899..0c16d0da2b9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -635,7 +635,7 @@ dependencies = [ [[package]] name = "git-odb" -version = "0.4.0" +version = "0.4.1" dependencies = [ "bstr", "btoi", diff --git a/git-odb/Cargo.toml b/git-odb/Cargo.toml index 85fcc94864b..0ea220aed8b 100644 --- a/git-odb/Cargo.toml +++ b/git-odb/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "git-odb" -version = "0.4.0" +version = "0.4.1" repository = "https://github.com/Byron/git-oxide" authors = ["Sebastian Thiel "] license = "MIT/Apache-2.0" From 105c50132c8ad1f15ace0821278a11b06c81103c Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Fri, 18 Sep 2020 16:23:37 +0800 Subject: [PATCH 05/51] (cargo-release) version 0.4.1 --- Cargo.lock | 2 +- gitoxide-core/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0c16d0da2b9..e724c5872b3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -749,7 +749,7 @@ dependencies = [ [[package]] name = "gitoxide-core" -version = "0.4.0" +version = "0.4.1" dependencies = [ "anyhow", "bytesize", diff --git a/gitoxide-core/Cargo.toml b/gitoxide-core/Cargo.toml index 0e3e30ae697..5edeb2434da 100644 --- a/gitoxide-core/Cargo.toml +++ b/gitoxide-core/Cargo.toml @@ -2,7 +2,7 @@ name = "gitoxide-core" description = "The library implementating all capabilities of the gitoxide CLI" repository = "https://github.com/Byron/git-oxide" -version = "0.4.0" +version = "0.4.1" authors = ["Sebastian Thiel "] license = "MIT/Apache-2.0" edition = "2018" From 64fff36dcfdade887b7f417605d81b9d5750f000 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Fri, 18 Sep 2020 16:25:09 +0800 Subject: [PATCH 06/51] (cargo-release) version 0.4.1 --- CHANGELOG.md | 4 ++++ Cargo.lock | 2 +- Cargo.toml | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c5261a30325..3ccd6aaca6b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +### v0.4.1 + +* fix installation via `cargo install` + ### v0.4 * add `remote-ref-list` and `pack-receive` subcommands to **gixp** diff --git a/Cargo.lock b/Cargo.lock index e724c5872b3..76559e81b0c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -732,7 +732,7 @@ dependencies = [ [[package]] name = "gitoxide" -version = "0.4.0" +version = "0.4.1" dependencies = [ "anyhow", "argh", diff --git a/Cargo.toml b/Cargo.toml index 48144ab18bf..d295a79947e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,7 +5,7 @@ repository = "https://github.com/Byron/git-oxide" authors = ["Sebastian Thiel "] edition = "2018" license = "MIT OR Apache-2.0" -version = "0.4.0" +version = "0.4.1" default-run = "gix" include = ["src/**/*", "LICENSE-*", "README.md", "CHANGELOG.md"] From 0752b45e95dd5378b7fca5b70bd11b9100ba2946 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Sat, 19 Sep 2020 11:15:14 +0800 Subject: [PATCH 07/51] refactor --- git-odb/src/pack/tree/from_offsets.rs | 70 ++++++++++++--------------- 1 file changed, 32 insertions(+), 38 deletions(-) diff --git a/git-odb/src/pack/tree/from_offsets.rs b/git-odb/src/pack/tree/from_offsets.rs index 3f6f3a6a961..9d34e7674ed 100644 --- a/git-odb/src/pack/tree/from_offsets.rs +++ b/git-odb/src/pack/tree/from_offsets.rs @@ -3,36 +3,24 @@ use git_features::{ interrupt::is_triggered, progress::{self, Progress}, }; -use quick_error::quick_error; use std::{ fs, io, io::{BufRead, Read}, time::Instant, }; -quick_error! { - #[derive(Debug)] - pub enum Error { - Io(err: io::Error, msg: &'static str) { - display("{}", msg) - source(err) - } - Header(err: pack::data::parse::Error) { - source(err) - from() - } - UnresolvedRefDelta(id: git_object::owned::Id) { - display("Could find object with id {} in this pack. Thin packs are not supported", id) - } - Tree(err: pack::tree::Error) { - display("An error occurred when handling the delta tree") - source(err) - from() - } - Interrupted { - display("Interrupted") - } - } +#[derive(thiserror::Error, Debug)] +pub enum Error { + #[error("{message}")] + Io { source: io::Error, message: &'static str }, + #[error(transparent)] + Header(#[from] pack::data::parse::Error), + #[error("Could find object with id {id} in this pack. Thin packs are not supported")] + UnresolvedRefDelta { id: git_object::owned::Id }, + #[error(transparent)] + Tree(#[from] pack::tree::Error), + #[error("Interrupted")] + Interrupted, } const PACK_HEADER_LEN: usize = 12; @@ -49,7 +37,10 @@ impl Tree { ) -> Result { let mut r = io::BufReader::with_capacity( 8192 * 8, // this value directly corresponds to performance, 8k (default) is about 4x slower than 64k - fs::File::open(pack_path).map_err(|err| Error::Io(err, "open pack path"))?, + fs::File::open(pack_path).map_err(|err| Error::Io { + source: err, + message: "open pack path", + })?, ); let anticpiated_num_objects = if let Some(num_objects) = data_sorted_by_offsets.size_hint().1 { @@ -63,11 +54,9 @@ impl Tree { { // safety check - assure ourselves it's a pack we can handle let mut buf = [0u8; PACK_HEADER_LEN]; - r.read_exact(&mut buf).map_err(|err| { - Error::Io( - err, - "reading header buffer with at least 12 bytes failed - pack file truncated?", - ) + r.read_exact(&mut buf).map_err(|err| Error::Io { + source: err, + message: "reading header buffer with at least 12 bytes failed - pack file truncated?", })?; pack::data::parse::header(&buf)?; } @@ -81,8 +70,10 @@ impl Tree { if let Some(previous_offset) = previous_cursor_position { Self::advance_cursor_to_pack_offset(&mut r, pack_offset, previous_offset)?; }; - let entry = pack::data::Entry::from_read(&mut r, pack_offset) - .map_err(|err| Error::Io(err, "EOF while parsing header"))?; + let entry = pack::data::Entry::from_read(&mut r, pack_offset).map_err(|err| Error::Io { + source: err, + message: "EOF while parsing header", + })?; previous_cursor_position = Some(pack_offset + entry.header_size() as u64); use pack::data::Header::*; @@ -92,7 +83,7 @@ impl Tree { } RefDelta { base_id } => { resolve_in_pack_id(base_id.to_borrowed()) - .ok_or_else(|| Error::UnresolvedRefDelta(base_id)) + .ok_or_else(|| Error::UnresolvedRefDelta { id: base_id }) .and_then(|base_pack_offset| { tree.add_child(base_pack_offset, pack_offset, data).map_err(Into::into) })?; @@ -123,17 +114,20 @@ impl Tree { .checked_sub(previous_offset) .expect("continuously ascending pack offets") as usize; while bytes_to_skip != 0 { - let buf = r.fill_buf().map_err(|err| Error::Io(err, "skip bytes"))?; + let buf = r.fill_buf().map_err(|err| Error::Io { + source: err, + message: "skip bytes", + })?; if buf.is_empty() { // This means we have reached the end of file and can't make progress anymore, before we have satisfied our need // for more - return Err(Error::Io( - io::Error::new( + return Err(Error::Io { + source: io::Error::new( io::ErrorKind::UnexpectedEof, "ran out of bytes before reading desired amount of bytes", ), - "index file is damaged or corrupt", - )); + message: "index file is damaged or corrupt", + }); } let bytes = buf.len().min(bytes_to_skip); r.consume(bytes); From 69f7930e00e78ff561ab5f599f9832ba7699da55 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Sat, 19 Sep 2020 11:18:09 +0800 Subject: [PATCH 08/51] announcement: lower velocity --- README.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/README.md b/README.md index 32e06636efc..3fa426a0524 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,13 @@ Please see _'Development Status'_ for a listing of all crates and their capabili ## Development Status +**Please note** that from 2020-09-17, the development speed will be reduced greatly. I will do my best to create at least +one commit per day ramp it up from there to eventually arrive at a new baseline velocity. It will be lower than what it was before, and +I hope 1/2 to 2/3 of that will be realistic. + +This is entirely unrelated to the project and I still can't wait to use `gitoxide` on a daily basis once the first high-level commands +become available. + ### gitoxide _(CLI)_ * please note that all functionality comes from the `gitoxide-core` library, which mirrors these capabilities and itself relies on all `git-*` crates. From 7bc321e96ecce0aae5063eb7008ecbac7d2ca31c Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Sat, 19 Sep 2020 21:07:57 +0800 Subject: [PATCH 09/51] refactor --- git-odb/src/loose/db/iter.rs | 12 ++++-------- git-odb/src/zlib/stream/deflate.rs | 20 ++++++-------------- 2 files changed, 10 insertions(+), 22 deletions(-) diff --git a/git-odb/src/loose/db/iter.rs b/git-odb/src/loose/db/iter.rs index 4baa2bcea62..46886f12c8d 100644 --- a/git-odb/src/loose/db/iter.rs +++ b/git-odb/src/loose/db/iter.rs @@ -1,15 +1,11 @@ use crate::loose::Db; use git_object::owned; -use quick_error::quick_error; use walkdir::WalkDir; -quick_error! { - #[derive(Debug)] - pub enum Error { - WalkDir(err: walkdir::Error) { - source(err) - } - } +#[derive(thiserror::Error, Debug)] +pub enum Error { + #[error(transparent)] + WalkDir(#[from] walkdir::Error), } /// Iteration and traversal diff --git a/git-odb/src/zlib/stream/deflate.rs b/git-odb/src/zlib/stream/deflate.rs index 8667e36a263..3f23da9fc07 100644 --- a/git-odb/src/zlib/stream/deflate.rs +++ b/git-odb/src/zlib/stream/deflate.rs @@ -1,21 +1,13 @@ use super::Status; use miniz_oxide::{deflate, deflate::core::CompressorOxide, MZError, MZFlush, MZStatus}; -use quick_error::quick_error; use std::io; -quick_error! { - #[derive(Debug)] - pub enum Error { - Compression { - display("The compression failed due to an unknown error") - } - ZLibNeedDict { - display("Need dictionary") - } - Error(err: MZError) { - display("A compression error occurred: {:?}", err) - } - } +#[derive(thiserror::Error, Debug)] +pub enum Error { + #[error("Need dictionary")] + ZLibNeedDict, + #[error("A compression error occurred: {0:?}")] + Error(MZError), } pub struct Deflate { From 669b726da305ce4520c792d62b4344b04fe5f996 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Sat, 19 Sep 2020 21:24:14 +0800 Subject: [PATCH 10/51] refactor --- git-odb/src/pack/index/init.rs | 48 ++++++++++++++++------------------ 1 file changed, 23 insertions(+), 25 deletions(-) diff --git a/git-odb/src/pack/index/init.rs b/git-odb/src/pack/index/init.rs index da98eb399bd..3b0dc7c1b37 100644 --- a/git-odb/src/pack/index/init.rs +++ b/git-odb/src/pack/index/init.rs @@ -2,23 +2,19 @@ use crate::pack::index::{self, Kind, FAN_LEN, V2_SIGNATURE}; use byteorder::{BigEndian, ByteOrder}; use filebuffer::FileBuffer; use git_object::SHA1_SIZE; -use quick_error::quick_error; use std::{convert::TryFrom, mem::size_of, path::Path}; -quick_error! { - #[derive(Debug)] - pub enum Error { - Io(err: std::io::Error, path: std::path::PathBuf) { - display("Could not open pack index file at '{}'", path.display()) - source(err) - } - Corrupt(msg: String) { - display("{}", msg) - } - UnsupportedVersion(version: u32) { - display("Unsupported index version: {}", version) - } - } +#[derive(thiserror::Error, Debug)] +pub enum Error { + #[error("Could not open pack index file at '{path}'")] + Io { + source: std::io::Error, + path: std::path::PathBuf, + }, + #[error("{message}")] + Corrupt { message: String }, + #[error("Unsupported index version: {version})")] + UnsupportedVersion { version: u32 }, } const N32_SIZE: usize = size_of::(); @@ -35,13 +31,15 @@ impl TryFrom<&Path> for index::File { type Error = Error; fn try_from(path: &Path) -> Result { - let data = FileBuffer::open(&path).map_err(|e| Error::Io(e, path.to_owned()))?; + let data = FileBuffer::open(&path).map_err(|e| Error::Io { + source: e, + path: path.to_owned(), + })?; let idx_len = data.len(); if idx_len < FAN_LEN * N32_SIZE + FOOTER_SIZE { - return Err(Error::Corrupt(format!( - "Pack index of size {} is too small for even an empty index", - idx_len - ))); + return Err(Error::Corrupt { + message: format!("Pack index of size {} is too small for even an empty index", idx_len), + }); } let (kind, version, fan, num_objects) = { let (kind, d) = { @@ -53,16 +51,16 @@ impl TryFrom<&Path> for index::File { } }; let (version, d) = { - let (mut v, mut d) = (1, d); + let (mut version, mut d) = (1, d); if let Kind::V2 = kind { let (vd, dr) = d.split_at(N32_SIZE); d = dr; - v = BigEndian::read_u32(vd); - if v != Kind::V2 as u32 { - return Err(Error::UnsupportedVersion(v)); + version = BigEndian::read_u32(vd); + if version != Kind::V2 as u32 { + return Err(Error::UnsupportedVersion { version }); } } - (v, d) + (version, d) }; let (fan, bytes_read) = read_fan(d); let (_, _d) = d.split_at(bytes_read); From 127b8b2949476b38ef6f8ea0fb68bae6d473adcc Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Sun, 20 Sep 2020 10:41:40 +0800 Subject: [PATCH 11/51] refactor --- git-odb/src/pack/bundle/locate.rs | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/git-odb/src/pack/bundle/locate.rs b/git-odb/src/pack/bundle/locate.rs index ac8aed6ffe6..2c8dce59812 100644 --- a/git-odb/src/pack/bundle/locate.rs +++ b/git-odb/src/pack/bundle/locate.rs @@ -1,14 +1,10 @@ use crate::pack; use git_object::borrowed; -use quick_error::quick_error; -quick_error! { - #[derive(Debug)] - pub enum Error { - Decode(err: pack::data::decode::Error) { - display("Could not decode object") - } - } +#[derive(thiserror::Error, Debug)] +pub enum Error { + #[error(transparent)] + Decode(pack::data::decode::Error), } impl pack::Bundle { From e897b50f457d8eaccbda4860671b4266bdbe7a41 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Sun, 20 Sep 2020 10:43:47 +0800 Subject: [PATCH 12/51] dependency update --- Cargo.lock | 44 +++++++++++++++++--------------------------- 1 file changed, 17 insertions(+), 27 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 76559e81b0c..acf82cbefe8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -228,9 +228,9 @@ checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" [[package]] name = "clap" -version = "3.0.0-beta.1" +version = "3.0.0-beta.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "860643c53f980f0d38a5e25dfab6c3c93b2cb3aa1fe192643d17a293c6c41936" +checksum = "4bd1061998a501ee7d4b6d449020df3266ca3124b941ec56cf2005c3779ca142" dependencies = [ "atty", "bitflags", @@ -247,9 +247,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "3.0.0-beta.1" +version = "3.0.0-beta.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb51c9e75b94452505acd21d929323f5a5c6c4735a852adbd39ef5fb1b014f30" +checksum = "370f715b81112975b1b69db93e0b56ea4cd4e5002ac43b2da8474106a54096a1" dependencies = [ "heck", "proc-macro-error", @@ -536,9 +536,9 @@ checksum = "de27142b013a8e869c14957e6d2edeef89e97c289e69d042ee3a49acd8b51789" [[package]] name = "futures-lite" -version = "1.6.0" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69fcd36beea698b82db5b66b6a6a3053632be70d8455123150b4713e85349618" +checksum = "5b77e08e656f472d8ea84c472fa8b0a7a917883048e1cf2d4e34a323cd0aaf63" dependencies = [ "fastrand", "futures-core", @@ -846,9 +846,12 @@ dependencies = [ [[package]] name = "instant" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b141fdc7836c525d4d594027d318c84161ca17aaf8113ab1f81ab93ae897485" +checksum = "63312a18f7ea8760cdd0a7c5aac1a619752a246b833545e3e36d1f81f7cd9e66" +dependencies = [ + "cfg-if", +] [[package]] name = "itoa" @@ -1215,9 +1218,9 @@ dependencies = [ [[package]] name = "proc-macro-error" -version = "0.4.12" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18f33027081eba0a6d8aba6d1b1c3a3be58cbb12106341c2d5759fcd9b5277e7" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" dependencies = [ "proc-macro-error-attr", "proc-macro2", @@ -1228,14 +1231,12 @@ dependencies = [ [[package]] name = "proc-macro-error-attr" -version = "0.4.12" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a5b4b77fdb63c1eca72173d68d24501c54ab1269409f6b672c85deb18af69de" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" dependencies = [ "proc-macro2", "quote", - "syn", - "syn-mid", "version_check", ] @@ -1585,17 +1586,6 @@ dependencies = [ "unicode-xid", ] -[[package]] -name = "syn-mid" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7be3539f6c128a931cf19dcee741c1af532c7fd387baa739c03dd2e96479338a" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "tempfile" version = "3.1.0" @@ -1633,9 +1623,9 @@ dependencies = [ [[package]] name = "textwrap" -version = "0.11.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" +checksum = "203008d98caf094106cfaba70acfed15e18ed3ddb7d94e49baec153a2b462789" dependencies = [ "unicode-width", ] From 98b3f4a9cc65e76aa09280065ab1d151f637e692 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Sun, 20 Sep 2020 10:50:28 +0800 Subject: [PATCH 13/51] refactor --- git-odb/src/pack/index/write/error.rs | 65 +++++++++------------------ git-odb/src/pack/index/write/mod.rs | 5 ++- 2 files changed, 26 insertions(+), 44 deletions(-) diff --git a/git-odb/src/pack/index/write/error.rs b/git-odb/src/pack/index/write/error.rs index 64f4e643d73..a80ff12cdf8 100644 --- a/git-odb/src/pack/index/write/error.rs +++ b/git-odb/src/pack/index/write/error.rs @@ -1,47 +1,26 @@ use crate::pack; -use quick_error::quick_error; use std::io; -quick_error! { - #[derive(Debug)] - pub enum Error { - Io(err: io::Error) { - display("An IO error occurred when reading the pack or creating a temporary file") - from() - source(err) - } - PackEntryDecode(err: pack::data::iter::Error) { - display("A pack entry could not be extracted") - from() - source(err) - } - Unsupported(kind: pack::index::Kind) { - display("Indices of type {} cannot be written, only {} are supported", *kind as usize, pack::index::Kind::default() as usize) - } - IteratorInvariantNoRefDelta { - display("Ref delta objects are not supported as there is no way to look them up. Resolve them beforehand.") - } - IteratorInvariantTrailer { - display("The iterator failed to set a trailing hash over all prior pack entries in the last provided entry") - } - IteratorInvariantBasesPresent { - display("Did not encounter a single base") - } - IteratorInvariantTooManyObjects(num_objects: usize) { - display("Only u32::MAX objects can be stored in a pack, found {}", num_objects) - } - IteratorInvariantBaseOffset(pack_offset: u64, distance: u64) { - display("{} is not a valid offset for pack offset {}", distance, pack_offset) - } - Tree(err: pack::tree::Error) { - display("An invariant regarding the delta tree did not hold") - source(err) - from() - } - TreeTraversal(err: pack::tree::traverse::Error) { - display("Tree traversal failed") - source(err) - from() - } - } +#[derive(thiserror::Error, Debug)] +pub enum Error { + #[error("An IO error occurred when reading the pack or creating a temporary file")] + Io(#[from] io::Error), + #[error("A pack entry could not be extracted")] + PackEntryDecode(#[from] pack::data::iter::Error), + #[error("Indices of type {} cannot be written, only {} are supported", *.0 as usize, pack::index::Kind::default() as usize)] + Unsupported(pack::index::Kind), + #[error("Ref delta objects are not supported as there is no way to look them up. Resolve them beforehand.")] + IteratorInvariantNoRefDelta, + #[error("The iterator failed to set a trailing hash over all prior pack entries in the last provided entry")] + IteratorInvariantTrailer, + #[error("Did not encounter a single base")] + IteratorInvariantBasesPresent, + #[error("Only u32::MAX objects can be stored in a pack, found {0}")] + IteratorInvariantTooManyObjects(usize), + #[error("{pack_offset} is not a valid offset for pack offset {distance}")] + IteratorInvariantBaseOffset { pack_offset: u64, distance: u64 }, + #[error(transparent)] + Tree(#[from] pack::tree::Error), + #[error(transparent)] + TreeTraversal(#[from] pack::tree::traverse::Error), } diff --git a/git-odb/src/pack/index/write/mod.rs b/git-odb/src/pack/index/write/mod.rs index dd67d02805a..2b5ac6873e8 100644 --- a/git-odb/src/pack/index/write/mod.rs +++ b/git-odb/src/pack/index/write/mod.rs @@ -105,7 +105,10 @@ impl pack::index::File { RefDelta { .. } => return Err(Error::IteratorInvariantNoRefDelta), OfsDelta { base_distance } => { let base_pack_offset = pack::data::Header::verified_base_pack_offset(pack_offset, base_distance) - .ok_or_else(|| Error::IteratorInvariantBaseOffset(pack_offset, base_distance))?; + .ok_or_else(|| Error::IteratorInvariantBaseOffset { + pack_offset, + distance: base_distance, + })?; tree.add_child( base_pack_offset, pack_offset, From 3fcdc5d21554417a4a47f464c68cd6bca06beb0d Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Sun, 20 Sep 2020 10:52:14 +0800 Subject: [PATCH 14/51] Fixes for clap beta-2 --- src/plumbing/pretty/options.rs | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/plumbing/pretty/options.rs b/src/plumbing/pretty/options.rs index 67d75e15427..1c18e591155 100644 --- a/src/plumbing/pretty/options.rs +++ b/src/plumbing/pretty/options.rs @@ -7,14 +7,14 @@ use std::path::PathBuf; #[clap(setting = AppSettings::SubcommandRequired)] #[clap(setting = AppSettings::ColoredHelp)] pub struct Args { - #[clap(long, short = "t")] + #[clap(long, short = 't')] /// The amount of threads to use for some operations. /// /// If unset, or the value is 0, there is no limit and all logical cores can be used. pub threads: Option, /// Display verbose messages and progress information - #[clap(long, short = "v")] + #[clap(long, short = 'v')] pub verbose: bool, /// Bring up a terminal user interface displaying progress visually @@ -30,7 +30,7 @@ pub struct Args { /// Determine the format to use when outputting statistics. #[clap( long, - short = "f", + short = 'f', default_value = "human", possible_values(core::OutputFormat::variants()) )] @@ -46,13 +46,13 @@ pub enum Subcommands { #[clap(setting = AppSettings::DisableVersion)] PackReceive { /// The protocol version to use. Valid values are 1 and 2 - #[clap(long, short = "p")] + #[clap(long, short = 'p')] protocol: Option, /// the directory into which to write references. Existing files will be overwritten. /// /// Note that the directory will be created if needed. - #[clap(long, short = "r")] + #[clap(long, short = 'r')] refs_directory: Option, /// The URLs or path from which to receive the pack. @@ -73,7 +73,7 @@ pub enum Subcommands { #[clap(setting = AppSettings::DisableVersion)] RemoteRefList { /// The protocol version to use. Valid values are 1 and 2 - #[clap(long, short = "p")] + #[clap(long, short = 'p')] protocol: Option, /// the URLs or path from which to receive references @@ -93,7 +93,7 @@ pub enum Subcommands { /// **restore** hash the input ourselves and ignore failing entries, instead finish the pack with the hash we computed #[clap( long, - short = "i", + short = 'i', default_value = "verify", possible_values(core::pack::index::IterationMode::variants()) )] @@ -102,7 +102,7 @@ pub enum Subcommands { /// Path to the pack file to read (with .pack extension). /// /// If unset, the pack file is expected on stdin. - #[clap(long, short = "p")] + #[clap(long, short = 'p')] pack_path: Option, /// The folder into which to place the pack and the generated index file @@ -128,7 +128,7 @@ pub enum Subcommands { /// The amount of checks to run #[clap( long, - short = "c", + short = 'c', default_value = "all", possible_values(core::pack::explode::SafetyCheck::variants()) )] @@ -155,12 +155,12 @@ pub enum Subcommands { #[clap(setting = AppSettings::DisableVersion)] PackVerify { /// output statistical information about the pack - #[clap(long, short = "s")] + #[clap(long, short = 's')] statistics: bool, /// The algorithm used to verify the pack. They differ in costs. #[clap( long, - short = "a", + short = 'a', default_value = "less-time", possible_values(core::pack::verify::Algorithm::variants()) )] From 4a762f6c2d9a94b1424c0b1c5f8a38f8df2fbbb6 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Mon, 21 Sep 2020 22:53:02 +0800 Subject: [PATCH 15/51] dependency update --- Cargo.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index acf82cbefe8..60ba271dc63 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1187,9 +1187,9 @@ checksum = "d36492546b6af1463394d46f0c834346f31548646f6ba10849802c9c9a27ac33" [[package]] name = "polling" -version = "1.0.3" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0307b8c7f438902536321f63c28cab0362f6ee89f1c7da47e3642ff956641c8b" +checksum = "e0720e0b9ea9d52451cf29d3413ba8a9303f8815d9d9653ef70e03ff73e65566" dependencies = [ "cfg-if", "libc", @@ -1652,9 +1652,9 @@ dependencies = [ [[package]] name = "time" -version = "0.2.20" +version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d4953c513c9bf1b97e9cdd83f11d60c4b0a83462880a360d80d96953a953fee" +checksum = "2c2e31fb28e2a9f01f5ed6901b066c1ba2333c04b64dc61254142bafcb3feb2c" dependencies = [ "const_fn", "libc", From 3a8fb61067c210d4db6d515f21b2e28425c52e8c Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Mon, 21 Sep 2020 22:59:25 +0800 Subject: [PATCH 16/51] refactor --- git-odb/src/pack/bundle/mod.rs | 26 ++++++++------------------ 1 file changed, 8 insertions(+), 18 deletions(-) diff --git a/git-odb/src/pack/bundle/mod.rs b/git-odb/src/pack/bundle/mod.rs index 45047afd425..fb3922cd559 100644 --- a/git-odb/src/pack/bundle/mod.rs +++ b/git-odb/src/pack/bundle/mod.rs @@ -1,5 +1,4 @@ use crate::pack; -use quick_error::quick_error; use std::{ convert::TryFrom, path::{Path, PathBuf}, @@ -8,23 +7,14 @@ use std::{ pub mod locate; pub mod write; -quick_error! { - #[derive(Debug)] - pub enum Error { - InvalidPath(path: PathBuf) { - display("An 'idx' extension is expected of an index file: '{}'", path.display()) - } - Pack(err: pack::data::parse::Error) { - display("Could not instantiate pack") - from() - source(err) - } - Index(err: pack::index::init::Error) { - display("Could not instantiate pack index") - from() - source(err) - } - } +#[derive(thiserror::Error, Debug)] +pub enum Error { + #[error("An 'idx' extension is expected of an index file: '{0}'")] + InvalidPath(PathBuf), + #[error(transparent)] + Pack(#[from] pack::data::parse::Error), + #[error(transparent)] + Index(#[from] pack::index::init::Error), } /// A packfile with an index From d4f288ceb2436b292993df74ed07d4d7e578edf1 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Mon, 21 Sep 2020 23:01:06 +0800 Subject: [PATCH 17/51] refactor --- git-odb/src/compound/init.rs | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/git-odb/src/compound/init.rs b/git-odb/src/compound/init.rs index 96b797e1691..f311c34646d 100644 --- a/git-odb/src/compound/init.rs +++ b/git-odb/src/compound/init.rs @@ -1,16 +1,10 @@ use crate::{compound, loose, pack}; -use quick_error::quick_error; use std::path::PathBuf; -quick_error! { - #[derive(Debug)] - pub enum Error { - Pack(err: pack::bundle::Error) { - display("Failed to instantiate a pack bundle") - source(err) - from() - } - } +#[derive(thiserror::Error, Debug)] +pub enum Error { + #[error(transparent)] + Pack(#[from] pack::bundle::Error), } /// Instantiation From 7ac21536b3cee6708489011731216b9b831509e4 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Mon, 21 Sep 2020 23:04:37 +0800 Subject: [PATCH 18/51] refacator --- git-odb/src/loose/object/header.rs | 35 ++++++++++++++---------------- 1 file changed, 16 insertions(+), 19 deletions(-) diff --git a/git-odb/src/loose/object/header.rs b/git-odb/src/loose/object/header.rs index c012eb7b661..22b6eb77ae7 100644 --- a/git-odb/src/loose/object/header.rs +++ b/git-odb/src/loose/object/header.rs @@ -1,23 +1,18 @@ use byteorder::WriteBytesExt; use git_object as object; -use quick_error::quick_error; -quick_error! { - #[derive(Debug)] - pub enum Error { - ParseIntegerError(msg: &'static str, number: Vec, err: btoi::ParseIntegerError) { - display("{}: {:?}", msg, std::str::from_utf8(number)) - source(err) - } - InvalidHeader(msg: &'static str) { - display("{}", msg) - } - ObjectHeader(err: object::Error) { - display("Could not parse object kind") - from() - source(err) - } - } +#[derive(thiserror::Error, Debug)] +pub enum Error { + #[error("{message}: {:?}", std::str::from_utf8(.number))] + ParseIntegerError { + source: btoi::ParseIntegerError, + message: &'static str, + number: Vec, + }, + #[error("{0}")] + InvalidHeader(&'static str), + #[error(transparent)] + ObjectHeader(#[from] object::Error), } pub fn decode(input: &[u8]) -> Result<(object::Kind, u64, usize), Error> { @@ -30,8 +25,10 @@ pub fn decode(input: &[u8]) -> Result<(object::Kind, u64, usize), Error> { match (split.next(), split.next()) { (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) + btoi::btoi(size).map_err(|source| Error::ParseIntegerError { + message: "Object size in header could not be parsed", + number: size.to_owned(), + source, })?, header_end + 1, // account for 0 byte )), From 519dd12f2bf58dd3048bc12e5b058236ad727853 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Mon, 21 Sep 2020 23:07:24 +0800 Subject: [PATCH 19/51] refactor --- git-odb/src/pack/bundle/write/error.rs | 35 ++++++++------------------ 1 file changed, 10 insertions(+), 25 deletions(-) diff --git a/git-odb/src/pack/bundle/write/error.rs b/git-odb/src/pack/bundle/write/error.rs index f59416e2ae8..5be254a7770 100644 --- a/git-odb/src/pack/bundle/write/error.rs +++ b/git-odb/src/pack/bundle/write/error.rs @@ -1,29 +1,14 @@ use crate::pack; -use quick_error::quick_error; use std::io; -quick_error! { - #[derive(Debug)] - pub enum Error { - Io(err: io::Error) { - display("An IO error occurred when reading the pack or creating a temporary file") - from() - source(err) - } - PackIter(err: pack::data::iter::Error) { - display("Pack iteration failed") - from() - source(err) - } - PeristError(err: tempfile::PersistError) { - display("Could not move a temporary file into its desired place") - from() - source(err) - } - IndexWrite(err: pack::index::write::Error) { - display("The index file could not be written") - from() - source(err) - } - } +#[derive(thiserror::Error, Debug)] +pub enum Error { + #[error("An IO error occurred when reading the pack or creating a temporary file")] + Io(#[from] io::Error), + #[error(transparent)] + PackIter(#[from] pack::data::iter::Error), + #[error("Could not move a temporary file into its desired place")] + PeristError(#[from] tempfile::PersistError), + #[error(transparent)] + IndexWrite(#[from] pack::index::write::Error), } From 3ec99dc7360c01b4f3c4593ff5049361e7043254 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Mon, 21 Sep 2020 23:10:29 +0800 Subject: [PATCH 20/51] refactor --- git-odb/src/loose/object/verify.rs | 35 ++++++++++++------------------ 1 file changed, 14 insertions(+), 21 deletions(-) diff --git a/git-odb/src/loose/object/verify.rs b/git-odb/src/loose/object/verify.rs index 23c0fc24c2f..4e2f74fd1f0 100644 --- a/git-odb/src/loose/object/verify.rs +++ b/git-odb/src/loose/object/verify.rs @@ -1,25 +1,15 @@ use crate::{hash::Write as HashWrite, loose}; use git_object::{borrowed, owned}; -use quick_error::quick_error; use std::io; -quick_error! { - #[derive(Debug)] - pub enum Error { - Io(err: io::Error) { - display("reading of object failed") - from() - source(err) - } - Decode(err: super::decode::Error) { - display("Decoding of object failed") - from() - source(err) - } - ChecksumMismatch(desired: owned::Id, actual: owned::Id) { - display("Object expected to have id {}, but actual id was {}", desired, actual) - } - } +#[derive(thiserror::Error, Debug)] +pub enum Error { + #[error("reading of object failed")] + Io(#[from] io::Error), + #[error("Decoding of object failed")] + Decode(#[from] super::decode::Error), + #[error("Object expected to have id {desired}, but actual id was {actual}")] + ChecksumMismatch { desired: owned::Id, actual: owned::Id }, } impl loose::Object { @@ -31,9 +21,12 @@ impl loose::Object { loose::object::header::encode(kind, size as u64, &mut sink).expect("hash to always work"); io::copy(&mut reader, &mut sink)?; - let actual_id = owned::Id::from(sink.hash.digest()); - if desired != actual_id.to_borrowed() { - return Err(Error::ChecksumMismatch(desired.into(), actual_id)); + let actual = owned::Id::from(sink.hash.digest()); + if desired != actual.to_borrowed() { + return Err(Error::ChecksumMismatch { + desired: desired.into(), + actual, + }); } Ok(()) } From 7874c35bccb74ae7670335e633efa7eaebc72630 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Mon, 21 Sep 2020 23:19:39 +0800 Subject: [PATCH 21/51] refactor --- git-odb/src/loose/object/decode.rs | 55 +++++++++++++++++------------- 1 file changed, 32 insertions(+), 23 deletions(-) diff --git a/git-odb/src/loose/object/decode.rs b/git-odb/src/loose/object/decode.rs index df517adcab1..f77325869a6 100644 --- a/git-odb/src/loose/object/decode.rs +++ b/git-odb/src/loose/object/decode.rs @@ -3,28 +3,21 @@ use crate::{loose, zlib}; use git_object as object; use miniz_oxide::inflate::decompress_to_vec_zlib; use object::borrowed; -use quick_error::quick_error; use smallvec::SmallVec; use std::{io::Read, path::PathBuf}; -quick_error! { - #[derive(Debug)] - pub enum Error { - Decompress(err: zlib::Error) { - display("decompression of object data failed") - from() - source(err) - } - Parse(err: borrowed::Error) { - display("Could not parse object object") - from() - source(err) - } - Io(err: std::io::Error, action: &'static str, path: PathBuf) { - display("Could not {} data at '{}'", action, path.display()) - source(err) - } - } +#[derive(thiserror::Error, Debug)] +pub enum Error { + #[error("decompression of object data failed")] + Decompress(#[from] zlib::Error), + #[error(transparent)] + Parse(#[from] borrowed::Error), + #[error("Could not {action} data at '{path}'")] + Io { + source: std::io::Error, + action: &'static str, + path: PathBuf, + }, } impl loose::Object { @@ -40,7 +33,11 @@ impl loose::Object { match &self.path { Some(path) => Ok(stream::Reader::from_read( self.header_size, - std::fs::File::open(path).map_err(|e| Error::Io(e, "open", path.to_owned()))?, + std::fs::File::open(path).map_err(|source| Error::Io { + source, + action: "open", + path: path.to_owned(), + })?, )), None => { self.decompress_all()?; @@ -64,13 +61,25 @@ impl loose::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(|source| Error::Io { + source, + action: "open", + path: path.clone(), + })?; let file_size = file .metadata() - .map_err(|e| Error::Io(e, "read metadata", path.clone()))? + .map_err(|source| Error::Io { + source, + action: "read metadata", + path: 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(|source| Error::Io { + source, + action: "read", + path, + })?; self.compressed_data = SmallVec::from(buf); } self.decompressed_data = SmallVec::from(decompress_to_vec_zlib(&self.compressed_data[..]).unwrap()); From 7e2749521b6c873766a2f6f96e6c91a0c6a9dbf3 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Mon, 21 Sep 2020 23:21:37 +0800 Subject: [PATCH 22/51] remove quickerror dependency from git-odb --- Cargo.lock | 1 - git-odb/Cargo.toml | 1 - git-odb/src/compound/locate.rs | 21 ++++++--------------- 3 files changed, 6 insertions(+), 17 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 60ba271dc63..2425839a03c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -650,7 +650,6 @@ dependencies = [ "miniz_oxide", "parking_lot 0.11.0", "pretty_assertions", - "quick-error 2.0.0", "serde", "smallvec", "tempfile", diff --git a/git-odb/Cargo.toml b/git-odb/Cargo.toml index 0ea220aed8b..6ac936e775c 100644 --- a/git-odb/Cargo.toml +++ b/git-odb/Cargo.toml @@ -21,7 +21,6 @@ all-features = true git-object = { version = "^0.4.0", path = "../git-object" } git-features = { version = "^0.6.0", path = "../git-features" } -quick-error = "2.0.0" walkdir = "2.1.4" miniz_oxide = "0.4.1" smallvec = "1.3.0" diff --git a/git-odb/src/compound/locate.rs b/git-odb/src/compound/locate.rs index 001982f8ec0..53238947823 100644 --- a/git-odb/src/compound/locate.rs +++ b/git-odb/src/compound/locate.rs @@ -1,21 +1,12 @@ use crate::{compound, loose, pack}; use git_object::borrowed; -use quick_error::quick_error; -quick_error! { - #[derive(Debug)] - pub enum Error { - Loose(err: loose::db::locate::Error) { - display("An error occurred while obtaining an object from the loose object store") - source(err) - from() - } - Pack(err: pack::bundle::locate::Error) { - display("An error occurred while obtaining an object from the packed object store") - source(err) - from() - } - } +#[derive(thiserror::Error, Debug)] +pub enum Error { + #[error("An error occurred while obtaining an object from the loose object store")] + Loose(#[from] loose::db::locate::Error), + #[error("An error occurred while obtaining an object from the packed object store")] + Pack(#[from] pack::bundle::locate::Error), } impl compound::Db { From fd6457f3a006873506543846d9400b4a66833e48 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Tue, 22 Sep 2020 06:03:37 +0800 Subject: [PATCH 23/51] Enforce using the correct version of clap Cargo doesn't treat suffix upgrades as breaking changes, fair enough I guess. This should fix #23, please let me know if it doesn't. --- Cargo.lock | 2 +- Cargo.toml | 4 ++-- tasks.md | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2425839a03c..6ce76478294 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -731,7 +731,7 @@ dependencies = [ [[package]] name = "gitoxide" -version = "0.4.1" +version = "0.4.2" dependencies = [ "anyhow", "argh", diff --git a/Cargo.toml b/Cargo.toml index d295a79947e..9905a8f4636 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,7 +5,7 @@ repository = "https://github.com/Byron/git-oxide" authors = ["Sebastian Thiel "] edition = "2018" license = "MIT OR Apache-2.0" -version = "0.4.1" +version = "0.4.2" default-run = "gix" include = ["src/**/*", "LICENSE-*", "README.md", "CHANGELOG.md"] @@ -64,7 +64,7 @@ git-features = { version = "^0.6.0", path = "git-features" } # just for feature configuration git-transport = { optional = true, version = "^0.2.0", path = "git-transport" } -clap = { version = "3.0.0-beta.1", optional = true } +clap = { version = "=3.0.0-beta.2", optional = true } argh = { version = "0.1.3", optional = true, default-features = false } prodash = { version = "10.0.0", optional = true, default-features = false } atty = { version = "0.2.14", optional = true, default-features = false } diff --git a/tasks.md b/tasks.md index 978daf9f07d..a767e3954ae 100644 --- a/tasks.md +++ b/tasks.md @@ -7,7 +7,7 @@ * [x] make NLL issue work * [x] Nice access to compound::Object * [x] Add #![deny(rust_2018_idioms)] everywhere - * [ ] Where 'thiserror' is available, use it for all Errors. It is more powerful, and if we paid for it already, let's use it. + * [x] Where 'thiserror' is available, use it for all Errors. It is more powerful, and if we paid for it already, let's use it. * [ ] alternate DB (location - it's really must following the chain until a compound DB can be created) * [ ] loose upgrade: jwalk powered iteration behind a feature flag * **git-config** From 5b47a1a051243ec2aa407297a19d41b30447bfab Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Tue, 22 Sep 2020 06:05:04 +0800 Subject: [PATCH 24/51] (cargo-release) version 0.4.3 --- Cargo.lock | 2 +- Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6ce76478294..e89eb23115f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -731,7 +731,7 @@ dependencies = [ [[package]] name = "gitoxide" -version = "0.4.2" +version = "0.4.3" dependencies = [ "anyhow", "argh", diff --git a/Cargo.toml b/Cargo.toml index 9905a8f4636..153198026c1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,7 +5,7 @@ repository = "https://github.com/Byron/git-oxide" authors = ["Sebastian Thiel "] edition = "2018" license = "MIT OR Apache-2.0" -version = "0.4.2" +version = "0.4.3" default-run = "gix" include = ["src/**/*", "LICENSE-*", "README.md", "CHANGELOG.md"] From 8f9570c602503f8689240a790766712dc1c4ec71 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Wed, 23 Sep 2020 23:19:14 +0800 Subject: [PATCH 25/51] take not of a few more obscure features --- README.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/README.md b/README.md index 3fa426a0524..4bbdde88a70 100644 --- a/README.md +++ b/README.md @@ -171,17 +171,24 @@ become available. * handle git index files for primary use by the git-repository while crafting new commits * [ ] API documentation with examples +### git-bundle + * [ ] create a bundle from an archive + * [ ] extract a branch from a bundle into a repository + ### git-repository * [x] initialize * [ ] Proper configuration depending on platform (e.g. ignorecase, filemode, …) * [ ] [Signed commits and tags](https://github.com/Byron/gitoxide/issues/12) * [ ] clone * [ ] shallow + * [ ] namespaces support * [ ] sparse checkout support + * [ ] execute hooks * [ ] .gitignore handling * [ ] checkout/stage conversions clean + smudge as in .gitattributes * [ ] read and write all data types * [ ] rev-parsing and ref history + * [ ] worktree * [ ] remotes with push and pull * [ ] configuration * [ ] merging From 6cc8a947df776aeeb031de627f84b7bc85207235 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Wed, 23 Sep 2020 23:52:38 +0800 Subject: [PATCH 26/51] first sketch of alternate resolution --- git-odb/src/alternate.rs | 40 +++++++++++++++++++++++++++++++++++++ git-odb/src/compound/mod.rs | 4 ++-- git-odb/src/lib.rs | 1 + 3 files changed, 43 insertions(+), 2 deletions(-) create mode 100644 git-odb/src/alternate.rs diff --git a/git-odb/src/alternate.rs b/git-odb/src/alternate.rs new file mode 100644 index 00000000000..2aa15e3b5ff --- /dev/null +++ b/git-odb/src/alternate.rs @@ -0,0 +1,40 @@ +use crate::compound; +use git_object::bstr::ByteSlice; +use std::{fs, io, path::PathBuf}; + +#[derive(thiserror::Error, Debug)] +pub enum Error { + #[error(transparent)] + Io(#[from] io::Error), + #[error("Could not obtain an object path for the alternate directory '{}'", String::from_utf8_lossy(&.0))] + PathConversion(Vec), + #[error(transparent)] + Init(#[from] compound::init::Error), +} + +pub fn resolve(objects_directory: impl Into) -> Result, Error> { + let mut directories = vec![objects_directory.into()]; + let mut count = 0; + while let Some(dir) = directories.pop() { + let content = match fs::read(dir.join("info").join("alternates")) { + Ok(d) => d, + Err(err) if err.kind() == io::ErrorKind::NotFound => { + if count == 0 { + return Ok(None); + } else { + return Ok(Some(compound::Db::at(dir)?)); + } + } + Err(err) => return Err(err.into()), + }; + directories.push( + content + .as_bstr() + .to_path() + .map(ToOwned::to_owned) + .map_err(|_| Error::PathConversion(content.into()))?, + ); + count += 1; + } + unreachable!("must either find an alternate, or not") +} diff --git a/git-odb/src/compound/mod.rs b/git-odb/src/compound/mod.rs index b1f0986ac05..37436f9822e 100644 --- a/git-odb/src/compound/mod.rs +++ b/git-odb/src/compound/mod.rs @@ -90,6 +90,6 @@ pub mod object { } pub use object::Object; -mod init; -mod locate; +pub mod init; +pub mod locate; mod write; diff --git a/git-odb/src/lib.rs b/git-odb/src/lib.rs index 8ff53570621..4f9b6473450 100644 --- a/git-odb/src/lib.rs +++ b/git-odb/src/lib.rs @@ -2,6 +2,7 @@ mod zlib; +pub mod alternate; pub mod compound; pub mod loose; pub mod pack; From c1eff58cd28e45a2d5f46481551724b81735ede3 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Wed, 23 Sep 2020 23:56:07 +0800 Subject: [PATCH 27/51] refactor --- git-odb/src/alternate.rs | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/git-odb/src/alternate.rs b/git-odb/src/alternate.rs index 2aa15e3b5ff..461dbc2da60 100644 --- a/git-odb/src/alternate.rs +++ b/git-odb/src/alternate.rs @@ -13,28 +13,25 @@ pub enum Error { } pub fn resolve(objects_directory: impl Into) -> Result, Error> { - let mut directories = vec![objects_directory.into()]; + let mut dir = objects_directory.into(); let mut count = 0; - while let Some(dir) = directories.pop() { + loop { let content = match fs::read(dir.join("info").join("alternates")) { Ok(d) => d, Err(err) if err.kind() == io::ErrorKind::NotFound => { - if count == 0 { - return Ok(None); + return if count == 0 { + Ok(None) } else { - return Ok(Some(compound::Db::at(dir)?)); + Ok(Some(compound::Db::at(dir)?)) } } Err(err) => return Err(err.into()), }; - directories.push( - content - .as_bstr() - .to_path() - .map(ToOwned::to_owned) - .map_err(|_| Error::PathConversion(content.into()))?, - ); + dir = content + .as_bstr() + .to_path() + .map(ToOwned::to_owned) + .map_err(|_| Error::PathConversion(content.into()))?; count += 1; } - unreachable!("must either find an alternate, or not") } From 9be7aed7bd4b939d98b9a8d1db8a6ffc85044ca9 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Thu, 24 Sep 2020 00:00:18 +0800 Subject: [PATCH 28/51] Actually resolve alternates when creating a compound DB --- git-odb/src/compound/init.rs | 7 ++++++- git-odb/src/compound/mod.rs | 1 + tasks.md | 4 +++- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/git-odb/src/compound/init.rs b/git-odb/src/compound/init.rs index f311c34646d..3006c24dcc2 100644 --- a/git-odb/src/compound/init.rs +++ b/git-odb/src/compound/init.rs @@ -5,6 +5,8 @@ use std::path::PathBuf; pub enum Error { #[error(transparent)] Pack(#[from] pack::bundle::Error), + #[error(transparent)] + Alternate(#[from] Box), } /// Instantiation @@ -26,8 +28,11 @@ impl compound::Db { }; Ok(compound::Db { - loose: loose::Db::at(loose_objects), + loose: loose::Db::at(loose_objects.clone()), packs, + alternate: crate::alternate::resolve(loose_objects) + .map_err(Box::new)? + .map(Box::new), }) } } diff --git a/git-odb/src/compound/mod.rs b/git-odb/src/compound/mod.rs index 37436f9822e..0baecabf7f4 100644 --- a/git-odb/src/compound/mod.rs +++ b/git-odb/src/compound/mod.rs @@ -3,6 +3,7 @@ use crate::{loose, pack}; pub struct Db { pub loose: loose::Db, pub packs: Vec, + pub alternate: Option>, } pub mod object { diff --git a/tasks.md b/tasks.md index a767e3954ae..564c9b825b4 100644 --- a/tasks.md +++ b/tasks.md @@ -8,7 +8,9 @@ * [x] Nice access to compound::Object * [x] Add #![deny(rust_2018_idioms)] everywhere * [x] Where 'thiserror' is available, use it for all Errors. It is more powerful, and if we paid for it already, let's use it. - * [ ] alternate DB (location - it's really must following the chain until a compound DB can be created) + * [x] alternate DB (location - it's really must following the chain until a compound DB can be created) + * [ ] circular dependencies test + * [ ] basic test * [ ] loose upgrade: jwalk powered iteration behind a feature flag * **git-config** * A complete implementation, writing a the git remote configuration is needed for finalizing the clone From 4ddc64fd71d3d1260e001f89c379c46fe157e5ce Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Thu, 24 Sep 2020 00:01:27 +0800 Subject: [PATCH 29/51] thanks clippy --- git-odb/src/alternate.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/git-odb/src/alternate.rs b/git-odb/src/alternate.rs index 461dbc2da60..4a65c34cbc7 100644 --- a/git-odb/src/alternate.rs +++ b/git-odb/src/alternate.rs @@ -31,7 +31,7 @@ pub fn resolve(objects_directory: impl Into) -> Result Date: Thu, 24 Sep 2020 07:29:17 +0800 Subject: [PATCH 30/51] dependency update --- Cargo.lock | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e89eb23115f..1532c2dec3c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -93,9 +93,9 @@ dependencies = [ [[package]] name = "async-io" -version = "1.1.2" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64c629684e697f58c0e99e5e2d84a840e3b336afbcfdbac7b44c3b1e222c2fd8" +checksum = "a9951f92a2b4f7793f8fc06a80bdb89b62c618c993497d4606474fb8c34941b5" dependencies = [ "concurrent-queue", "fastrand", @@ -398,9 +398,9 @@ dependencies = [ [[package]] name = "ctor" -version = "0.1.15" +version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39858aa5bac06462d4dd4b9164848eb81ffc4aa5c479746393598fd193afa227" +checksum = "7fbaabec2c953050352311293be5c6aba8e141ba19d6811862b232d6fd020484" dependencies = [ "quote", "syn", @@ -492,9 +492,9 @@ dependencies = [ [[package]] name = "event-listener" -version = "2.4.0" +version = "2.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1cd41440ae7e4734bbd42302f63eaba892afc93a3912dad84006247f0dedb0e" +checksum = "f7531096570974c3a9dcf9e4b8e1cede1ec26cf5046219fb3b9d897503b9be59" [[package]] name = "fastrand" @@ -536,9 +536,9 @@ checksum = "de27142b013a8e869c14957e6d2edeef89e97c289e69d042ee3a49acd8b51789" [[package]] name = "futures-lite" -version = "1.7.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b77e08e656f472d8ea84c472fa8b0a7a917883048e1cf2d4e34a323cd0aaf63" +checksum = "0db18c5f58083b54b0c416638ea73066722c2815c1e54dd8ba85ee3def593c3a" dependencies = [ "fastrand", "futures-core", @@ -779,9 +779,9 @@ dependencies = [ [[package]] name = "hermit-abi" -version = "0.1.15" +version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3deed196b6e7f9e44a2ae8d94225d80302d81208b1bb673fd21fe634645c85a9" +checksum = "4c30f6d0bc6b00693347368a67d41b58f2fb851215ff1da49e90fe2c5c667151" dependencies = [ "libc", ] From fc927091d69196a930c0cea4611af8d96b7b84d8 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Thu, 24 Sep 2020 16:48:41 +0800 Subject: [PATCH 31/51] test for circular alternates --- Cargo.lock | 62 +++++++++++++++++++++++++++++++--- git-odb/Cargo.toml | 1 + git-odb/tests/alternate/mod.rs | 6 ++++ git-odb/tests/odb.rs | 3 ++ 4 files changed, 68 insertions(+), 4 deletions(-) create mode 100644 git-odb/tests/alternate/mod.rs diff --git a/Cargo.lock b/Cargo.lock index 1532c2dec3c..5f7b9440428 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -512,6 +512,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "fuchsia-cprng" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" + [[package]] name = "futures-channel" version = "0.3.5" @@ -652,6 +658,7 @@ dependencies = [ "pretty_assertions", "serde", "smallvec", + "tempdir", "tempfile", "thiserror", "uluru", @@ -1298,6 +1305,19 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "rand" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293" +dependencies = [ + "fuchsia-cprng", + "libc", + "rand_core 0.3.1", + "rdrand", + "winapi", +] + [[package]] name = "rand" version = "0.7.3" @@ -1307,7 +1327,7 @@ dependencies = [ "getrandom", "libc", "rand_chacha", - "rand_core", + "rand_core 0.5.1", "rand_hc", ] @@ -1318,9 +1338,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" dependencies = [ "ppv-lite86", - "rand_core", + "rand_core 0.5.1", ] +[[package]] +name = "rand_core" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" +dependencies = [ + "rand_core 0.4.2", +] + +[[package]] +name = "rand_core" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" + [[package]] name = "rand_core" version = "0.5.1" @@ -1336,7 +1371,16 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" dependencies = [ - "rand_core", + "rand_core 0.5.1", +] + +[[package]] +name = "rdrand" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" +dependencies = [ + "rand_core 0.3.1", ] [[package]] @@ -1585,6 +1629,16 @@ dependencies = [ "unicode-xid", ] +[[package]] +name = "tempdir" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15f2b5fb00ccdf689e0149d1b1b3c03fead81c2b37735d812fa8bddbbf41b6d8" +dependencies = [ + "rand 0.4.6", + "remove_dir_all", +] + [[package]] name = "tempfile" version = "3.1.0" @@ -1593,7 +1647,7 @@ checksum = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9" dependencies = [ "cfg-if", "libc", - "rand", + "rand 0.7.3", "redox_syscall", "remove_dir_all", "winapi", diff --git a/git-odb/Cargo.toml b/git-odb/Cargo.toml index 6ac936e775c..77f17f0d0eb 100644 --- a/git-odb/Cargo.toml +++ b/git-odb/Cargo.toml @@ -40,3 +40,4 @@ pretty_assertions = "0.6.1" bstr = { version = "0.2.13", default-features = false, features = ["std"] } hex = "0.4.2" common_macros = "0.1.1" +tempdir = "0.3.7" diff --git a/git-odb/tests/alternate/mod.rs b/git-odb/tests/alternate/mod.rs new file mode 100644 index 00000000000..cb2dbbc601e --- /dev/null +++ b/git-odb/tests/alternate/mod.rs @@ -0,0 +1,6 @@ +#[test] +fn circular_alternates_are_detected() -> crate::Result { + tempdir::TempDir::new("alternates")?; + + Ok(()) +} diff --git a/git-odb/tests/odb.rs b/git-odb/tests/odb.rs index a09f272ab81..152481eb835 100644 --- a/git-odb/tests/odb.rs +++ b/git-odb/tests/odb.rs @@ -1,6 +1,8 @@ use git_object::owned; use std::path::PathBuf; +type Result = std::result::Result<(), Box>; + #[cfg(not(windows))] fn fixup(v: Vec) -> Vec { v @@ -22,6 +24,7 @@ pub fn fixture_path(path: &str) -> PathBuf { PathBuf::from("tests").join("fixtures").join(path) } +mod alternate; mod loose; mod pack; mod sink; From 73721185cfd646c6e83b2548280fad8f480f8324 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Thu, 24 Sep 2020 20:57:53 +0800 Subject: [PATCH 32/51] first simple alternate tests --- git-odb/src/alternate.rs | 4 ++-- git-odb/tests/alternate/mod.rs | 35 +++++++++++++++++++++++++++++++++- 2 files changed, 36 insertions(+), 3 deletions(-) diff --git a/git-odb/src/alternate.rs b/git-odb/src/alternate.rs index 4a65c34cbc7..034efa34975 100644 --- a/git-odb/src/alternate.rs +++ b/git-odb/src/alternate.rs @@ -19,13 +19,13 @@ pub fn resolve(objects_directory: impl Into) -> Result d, Err(err) if err.kind() == io::ErrorKind::NotFound => { - return if count == 0 { + break if count == 0 { Ok(None) } else { Ok(Some(compound::Db::at(dir)?)) } } - Err(err) => return Err(err.into()), + Err(err) => break Err(err.into()), }; dir = content .as_bstr() diff --git a/git-odb/tests/alternate/mod.rs b/git-odb/tests/alternate/mod.rs index cb2dbbc601e..f250cf4743f 100644 --- a/git-odb/tests/alternate/mod.rs +++ b/git-odb/tests/alternate/mod.rs @@ -1,6 +1,39 @@ +use git_odb::alternate; +use std::{fs, io, path::PathBuf}; + +fn alternate(objects_at: impl Into, objects_to: impl Into) -> Result<(PathBuf, PathBuf), io::Error> { + let at = objects_at.into(); + let to = objects_to.into(); + let at_info = at.join("info"); + fs::create_dir_all(&at_info)?; + fs::create_dir(&to)?; + fs::write(at_info.join("alternates"), to.to_string_lossy().as_bytes())?; + Ok((at, to)) +} + #[test] +#[ignore] fn circular_alternates_are_detected() -> crate::Result { - tempdir::TempDir::new("alternates")?; + let tmp = tempdir::TempDir::new("alternates")?; + alternate(tmp.path().join("a"), tmp.path().join("b"))?; + alternate(tmp.path().join("b"), tmp.path().join("a"))?; + + Ok(()) +} +#[test] +fn single_link() -> crate::Result { + let tmp = tempdir::TempDir::new("alternates")?; + let non_alternate = tmp.path().join("actual"); + + let (from, to) = alternate(tmp.path().join("a"), non_alternate)?; + assert_eq!(alternate::resolve(from).unwrap().unwrap().loose.path, to); + Ok(()) +} + +#[test] +fn no_alternate_in_first_objects_dir() -> crate::Result { + let tmp = tempdir::TempDir::new("alternates")?; + assert!(alternate::resolve(tmp.path()).unwrap().is_none()); Ok(()) } From 71167e4e50efa8a097c3b09a249004e97aeaf2c8 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Thu, 24 Sep 2020 21:35:29 +0800 Subject: [PATCH 33/51] alternate now handles cycles --- git-odb/src/alternate.rs | 7 +++++++ git-odb/tests/alternate/mod.rs | 17 ++++++++++++++--- tasks.md | 5 +++-- 3 files changed, 24 insertions(+), 5 deletions(-) diff --git a/git-odb/src/alternate.rs b/git-odb/src/alternate.rs index 034efa34975..e8f8c7e9478 100644 --- a/git-odb/src/alternate.rs +++ b/git-odb/src/alternate.rs @@ -10,12 +10,19 @@ pub enum Error { PathConversion(Vec), #[error(transparent)] Init(#[from] compound::init::Error), + #[error("Alternates form a cycle: {} -> {}", .0.iter().map(|p| format!("'{}'", p.display())).collect::>().join(" -> "), .0.first().expect("more than one directories").display())] + Cycle(Vec), } pub fn resolve(objects_directory: impl Into) -> Result, Error> { let mut dir = objects_directory.into(); let mut count = 0; + let mut seen = Vec::new(); loop { + if seen.contains(&dir) { + break Err(Error::Cycle(seen)); + } + seen.push(dir.clone()); let content = match fs::read(dir.join("info").join("alternates")) { Ok(d) => d, Err(err) if err.kind() == io::ErrorKind::NotFound => { diff --git a/git-odb/tests/alternate/mod.rs b/git-odb/tests/alternate/mod.rs index f250cf4743f..4629aa3cf3e 100644 --- a/git-odb/tests/alternate/mod.rs +++ b/git-odb/tests/alternate/mod.rs @@ -6,18 +6,29 @@ fn alternate(objects_at: impl Into, objects_to: impl Into) -> let to = objects_to.into(); let at_info = at.join("info"); fs::create_dir_all(&at_info)?; - fs::create_dir(&to)?; + fs::create_dir_all(&to)?; fs::write(at_info.join("alternates"), to.to_string_lossy().as_bytes())?; Ok((at, to)) } #[test] -#[ignore] fn circular_alternates_are_detected() -> crate::Result { let tmp = tempdir::TempDir::new("alternates")?; - alternate(tmp.path().join("a"), tmp.path().join("b"))?; + let (from, _) = alternate(tmp.path().join("a"), tmp.path().join("b"))?; alternate(tmp.path().join("b"), tmp.path().join("a"))?; + match alternate::resolve(&from) { + Err(alternate::Error::Cycle(chain)) => { + assert_eq!( + chain + .into_iter() + .map(|p| p.file_name().expect("non-root").to_str().expect("utf8").to_owned()) + .collect::>(), + vec!["a", "b"] + ); + } + _ => unreachable!("should be a specific kind of error"), + } Ok(()) } diff --git a/tasks.md b/tasks.md index 564c9b825b4..e41d61735c7 100644 --- a/tasks.md +++ b/tasks.md @@ -9,9 +9,10 @@ * [x] Add #![deny(rust_2018_idioms)] everywhere * [x] Where 'thiserror' is available, use it for all Errors. It is more powerful, and if we paid for it already, let's use it. * [x] alternate DB (location - it's really must following the chain until a compound DB can be created) - * [ ] circular dependencies test - * [ ] basic test + * [x] circular dependencies test + * [x] basic test * [ ] loose upgrade: jwalk powered iteration behind a feature flag + * [ ] full docs * **git-config** * A complete implementation, writing a the git remote configuration is needed for finalizing the clone * **git-ref** From f444c859f5b215ea70a46d5493a2babbf7a98235 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Fri, 25 Sep 2020 17:00:40 +0800 Subject: [PATCH 34/51] Use parallel walkdir (via jwalk) when parallel feature is enabled --- Cargo.lock | 107 ++++++++++++++++++++++++++++++++++- git-features/Cargo.toml | 5 +- git-features/src/fs.rs | 11 ++++ git-features/src/lib.rs | 1 + git-odb/src/loose/db/iter.rs | 5 +- tasks.md | 2 +- 6 files changed, 124 insertions(+), 7 deletions(-) create mode 100644 git-features/src/fs.rs diff --git a/Cargo.lock b/Cargo.lock index 5f7b9440428..3be6a8179d1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -332,6 +332,20 @@ dependencies = [ "build_const", ] +[[package]] +name = "crossbeam" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69323bff1fb41c635347b8ead484a5ca6c3f11914d784170b158d8449ab07f8e" +dependencies = [ + "cfg-if", + "crossbeam-channel", + "crossbeam-deque", + "crossbeam-epoch", + "crossbeam-queue", + "crossbeam-utils", +] + [[package]] name = "crossbeam-channel" version = "0.4.4" @@ -342,6 +356,43 @@ dependencies = [ "maybe-uninit", ] +[[package]] +name = "crossbeam-deque" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f02af974daeee82218205558e51ec8768b48cf524bd01d550abe5573a608285" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", + "maybe-uninit", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "058ed274caafc1f60c4997b5fc07bf7dc7cca454af7c6e81edffe5f33f70dace" +dependencies = [ + "autocfg", + "cfg-if", + "crossbeam-utils", + "lazy_static", + "maybe-uninit", + "memoffset", + "scopeguard", +] + +[[package]] +name = "crossbeam-queue" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "774ba60a54c213d409d5353bda12d49cd68d14e45036a285234c8d6f91f92570" +dependencies = [ + "cfg-if", + "crossbeam-utils", + "maybe-uninit", +] + [[package]] name = "crossbeam-utils" version = "0.7.2" @@ -478,6 +529,12 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "212d0f5754cb6769937f4501cc0e67f4f4483c8d2c3e1e922ee9edbe4ab4c7c0" +[[package]] +name = "either" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" + [[package]] name = "env_logger" version = "0.7.1" @@ -613,10 +670,12 @@ dependencies = [ "crossbeam-channel", "crossbeam-utils", "ctrlc", + "jwalk", "num_cpus", "prodash", "sha-1", "sha1", + "walkdir", ] [[package]] @@ -865,6 +924,16 @@ version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc6f3ad7b9d11a0c00842ff8de1b60ee58661048eb8049ed33c73594f359d7e6" +[[package]] +name = "jwalk" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88746778a47f54f83bc0d3d8ba40ce83808024405356b4d521c2bf93c1273cd4" +dependencies = [ + "crossbeam", + "rayon", +] + [[package]] name = "lazy_static" version = "1.4.0" @@ -934,6 +1003,15 @@ version = "2.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400" +[[package]] +name = "memoffset" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "043175f069eda7b85febe4a74abbaeff828d9f8b448515d3151a14a3542811aa" +dependencies = [ + "autocfg", +] + [[package]] name = "miniz_oxide" version = "0.4.2" @@ -1055,9 +1133,9 @@ checksum = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de" [[package]] name = "openssl-src" -version = "111.10.2+1.1.1g" +version = "111.11.0+1.1.1h" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a287fdb22e32b5b60624d4a5a7a02dbe82777f730ec0dbc42a0554326fef5a70" +checksum = "380fe324132bea01f45239fadfec9343adb044615f29930d039bec1ae7b9fa5b" dependencies = [ "cc", ] @@ -1374,6 +1452,31 @@ dependencies = [ "rand_core 0.5.1", ] +[[package]] +name = "rayon" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfd016f0c045ad38b5251be2c9c0ab806917f82da4d36b2a327e5166adad9270" +dependencies = [ + "autocfg", + "crossbeam-deque", + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8c4fec834fb6e6d2dd5eece3c7b432a52f0ba887cf40e595190c4107edc08bf" +dependencies = [ + "crossbeam-channel", + "crossbeam-deque", + "crossbeam-utils", + "lazy_static", + "num_cpus", +] + [[package]] name = "rdrand" version = "0.4.0" diff --git a/git-features/Cargo.toml b/git-features/Cargo.toml index a33a98f8438..e95e6995228 100644 --- a/git-features/Cargo.toml +++ b/git-features/Cargo.toml @@ -12,7 +12,7 @@ doctest = false test = false [features] -parallel = ["crossbeam-utils", "crossbeam-channel", "num_cpus"] +parallel = ["crossbeam-utils", "crossbeam-channel", "num_cpus", "jwalk"] fast-sha1 = ["fastsha1"] interrupt-handler = ["ctrlc"] disable-interrupts = [] @@ -34,6 +34,9 @@ crossbeam-utils = { version = "0.7.2", optional = true } crossbeam-channel = { version = "0.4.2", optional = true } num_cpus = { version = "1.13.0", optional = true } +jwalk = { version = "0.5.1", optional = true } +walkdir = { version = "2.3.1" } # used when parallel is off + # hashing and 'fast-sha1' feature sha1 = "0.6.0" crc = "1.8.1" diff --git a/git-features/src/fs.rs b/git-features/src/fs.rs new file mode 100644 index 00000000000..9848ac64284 --- /dev/null +++ b/git-features/src/fs.rs @@ -0,0 +1,11 @@ +#[cfg(feature = "parallel")] +pub mod walkdir { + pub use jwalk::{Error, WalkDir}; +} + +#[cfg(not(feature = "parallel"))] +pub mod walkdir { + pub use walkdir::{Error, WalkDir}; +} + +pub use self::walkdir::WalkDir; diff --git a/git-features/src/lib.rs b/git-features/src/lib.rs index 20f3986933b..85b07041613 100644 --- a/git-features/src/lib.rs +++ b/git-features/src/lib.rs @@ -1,5 +1,6 @@ #![forbid(unsafe_code, rust_2018_idioms)] +pub mod fs; pub mod hash; pub mod interrupt; pub mod parallel; diff --git a/git-odb/src/loose/db/iter.rs b/git-odb/src/loose/db/iter.rs index 46886f12c8d..41fc9df1cd4 100644 --- a/git-odb/src/loose/db/iter.rs +++ b/git-odb/src/loose/db/iter.rs @@ -1,18 +1,17 @@ use crate::loose::Db; +use git_features::fs::WalkDir; use git_object::owned; -use walkdir::WalkDir; #[derive(thiserror::Error, Debug)] pub enum Error { #[error(transparent)] - WalkDir(#[from] walkdir::Error), + WalkDir(#[from] git_features::fs::walkdir::Error), } /// Iteration and traversal impl Db { pub fn iter(&self) -> impl Iterator> { use std::path::Component::Normal; - // TODO: Put this behind a feature flag in git-features and allow iterating with jwalk WalkDir::new(&self.path) .min_depth(2) .max_depth(3) diff --git a/tasks.md b/tasks.md index e41d61735c7..ab97cd9f830 100644 --- a/tasks.md +++ b/tasks.md @@ -11,7 +11,7 @@ * [x] alternate DB (location - it's really must following the chain until a compound DB can be created) * [x] circular dependencies test * [x] basic test - * [ ] loose upgrade: jwalk powered iteration behind a feature flag + * [x] loose upgrade: jwalk powered iteration behind a feature flag * [ ] full docs * **git-config** * A complete implementation, writing a the git remote configuration is needed for finalizing the clone From 6dc57b31d0bc5abfca100ab1d4b5dff68852aad8 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Sat, 26 Sep 2020 17:56:40 +0800 Subject: [PATCH 35/51] =?UTF-8?q?Make=20compound=20DB=20initialization=20l?= =?UTF-8?q?ess=20lazy=E2=80=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit …previously it was possible to initialize a valid instance with an invalid path as things were very flexible. --- git-odb/src/compound/init.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/git-odb/src/compound/init.rs b/git-odb/src/compound/init.rs index 3006c24dcc2..f26a49821f3 100644 --- a/git-odb/src/compound/init.rs +++ b/git-odb/src/compound/init.rs @@ -3,6 +3,8 @@ use std::path::PathBuf; #[derive(thiserror::Error, Debug)] pub enum Error { + #[error("The objects directory at '{0}' is not an accessible directory")] + Inaccessible(PathBuf), #[error(transparent)] Pack(#[from] pack::bundle::Error), #[error(transparent)] @@ -13,6 +15,9 @@ pub enum Error { impl compound::Db { pub fn at(objects_directory: impl Into) -> Result { let loose_objects = objects_directory.into(); + if !loose_objects.is_dir() { + return Err(Error::Inaccessible(loose_objects)); + } let packs = if let Ok(entries) = std::fs::read_dir(loose_objects.join("packs")) { let mut packs_and_sizes = entries .filter_map(Result::ok) From 08f9ec41feee56fe0ff2b057bb50391100bdb84e Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Sat, 26 Sep 2020 18:27:27 +0800 Subject: [PATCH 36/51] prepare for multi-line parsing and all the bells and whistles --- README.md | 6 ++--- git-odb/src/alternate.rs | 40 +++++++++++++++++++--------------- git-odb/src/compound/init.rs | 4 +--- git-odb/src/compound/mod.rs | 2 +- git-odb/tests/alternate/mod.rs | 24 +++++++++++++++++--- tasks.md | 5 +++++ 6 files changed, 53 insertions(+), 28 deletions(-) diff --git a/README.md b/README.md index 4bbdde88a70..6f1d354ab41 100644 --- a/README.md +++ b/README.md @@ -91,9 +91,9 @@ become available. * **sink** * [x] write objects and obtain id * **alternates** - * [ ] _database that act as link to other known ODB types on disk_ - * [ ] handles cycles - * [ ] handles recursive configurations + * [ ] _database that act as link to other known git ODBs on disk_ + * [ ] safe with cycles and recursive configurations + * [ ] multi-line with comments and quotes * **multi-odb** * [ ] _an ODB for object lookup from multiple lower level ODB at once_ * **promisor** diff --git a/git-odb/src/alternate.rs b/git-odb/src/alternate.rs index e8f8c7e9478..c9e6c3dc6f7 100644 --- a/git-odb/src/alternate.rs +++ b/git-odb/src/alternate.rs @@ -14,31 +14,35 @@ pub enum Error { Cycle(Vec), } -pub fn resolve(objects_directory: impl Into) -> Result, Error> { - let mut dir = objects_directory.into(); - let mut count = 0; +pub fn resolve(objects_directory: impl Into) -> Result, Error> { + let mut dirs = vec![(0, objects_directory.into())]; + let mut out = Vec::new(); let mut seen = Vec::new(); - loop { + while let Some((depth, dir)) = dirs.pop() { if seen.contains(&dir) { - break Err(Error::Cycle(seen)); + return Err(Error::Cycle(seen)); } seen.push(dir.clone()); - let content = match fs::read(dir.join("info").join("alternates")) { - Ok(d) => d, + match fs::read(dir.join("info").join("alternates")) { + Ok(content) => { + dirs.push(( + depth + 1, + content + .as_bstr() + .to_path() + .map(ToOwned::to_owned) + .map_err(|_| Error::PathConversion(content))?, + )); + } Err(err) if err.kind() == io::ErrorKind::NotFound => { - break if count == 0 { - Ok(None) - } else { - Ok(Some(compound::Db::at(dir)?)) + // Only resolve for repositories with at least one link, otherwise the line below isn't safe to call + if depth != 0 { + // The tail of a chain doesn't have alternates, and thus is the real deal + out.push(compound::Db::at(dir)?); } } - Err(err) => break Err(err.into()), + Err(err) => return Err(err.into()), }; - dir = content - .as_bstr() - .to_path() - .map(ToOwned::to_owned) - .map_err(|_| Error::PathConversion(content))?; - count += 1; } + Ok(out) } diff --git a/git-odb/src/compound/init.rs b/git-odb/src/compound/init.rs index f26a49821f3..3f49904160c 100644 --- a/git-odb/src/compound/init.rs +++ b/git-odb/src/compound/init.rs @@ -35,9 +35,7 @@ impl compound::Db { Ok(compound::Db { loose: loose::Db::at(loose_objects.clone()), packs, - alternate: crate::alternate::resolve(loose_objects) - .map_err(Box::new)? - .map(Box::new), + alternates: crate::alternate::resolve(loose_objects).map_err(Box::new)?, }) } } diff --git a/git-odb/src/compound/mod.rs b/git-odb/src/compound/mod.rs index 0baecabf7f4..6420e80f112 100644 --- a/git-odb/src/compound/mod.rs +++ b/git-odb/src/compound/mod.rs @@ -3,7 +3,7 @@ use crate::{loose, pack}; pub struct Db { pub loose: loose::Db, pub packs: Vec, - pub alternate: Option>, + pub alternates: Vec, } pub mod object { diff --git a/git-odb/tests/alternate/mod.rs b/git-odb/tests/alternate/mod.rs index 4629aa3cf3e..0e4eef6198f 100644 --- a/git-odb/tests/alternate/mod.rs +++ b/git-odb/tests/alternate/mod.rs @@ -2,12 +2,28 @@ use git_odb::alternate; use std::{fs, io, path::PathBuf}; fn alternate(objects_at: impl Into, objects_to: impl Into) -> Result<(PathBuf, PathBuf), io::Error> { + alternate_with(objects_at, objects_to, None) +} + +fn alternate_with( + objects_at: impl Into, + objects_to: impl Into, + content_before_to: Option<&str>, +) -> Result<(PathBuf, PathBuf), io::Error> { let at = objects_at.into(); let to = objects_to.into(); let at_info = at.join("info"); fs::create_dir_all(&at_info)?; fs::create_dir_all(&to)?; - fs::write(at_info.join("alternates"), to.to_string_lossy().as_bytes())?; + let contents = if let Some(content) = content_before_to { + let mut c = vec![b'\n']; + c.extend(content.as_bytes()); + c.extend(to.to_string_lossy().as_bytes()); + c + } else { + to.to_string_lossy().as_bytes().to_owned() + }; + fs::write(at_info.join("alternates"), contents)?; Ok((at, to)) } @@ -38,13 +54,15 @@ fn single_link() -> crate::Result { let non_alternate = tmp.path().join("actual"); let (from, to) = alternate(tmp.path().join("a"), non_alternate)?; - assert_eq!(alternate::resolve(from).unwrap().unwrap().loose.path, to); + let alternates = alternate::resolve(from)?; + assert_eq!(alternates.len(), 1); + assert_eq!(alternates[0].loose.path, to); Ok(()) } #[test] fn no_alternate_in_first_objects_dir() -> crate::Result { let tmp = tempdir::TempDir::new("alternates")?; - assert!(alternate::resolve(tmp.path()).unwrap().is_none()); + assert!(alternate::resolve(tmp.path())?.is_empty()); Ok(()) } diff --git a/tasks.md b/tasks.md index ab97cd9f830..045f0544f0c 100644 --- a/tasks.md +++ b/tasks.md @@ -11,6 +11,11 @@ * [x] alternate DB (location - it's really must following the chain until a compound DB can be created) * [x] circular dependencies test * [x] basic test + * [ ] are multiple alternates supported, too? YES + * [ ] comments + * [ ] quotes + * [ ] support for relative directories + * [ ] alternates lookup * [x] loose upgrade: jwalk powered iteration behind a feature flag * [ ] full docs * **git-config** From 1effdfda703d5eb9cd1333a7bae21075ef9e53cc Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Sat, 26 Sep 2020 21:46:15 +0800 Subject: [PATCH 37/51] Ignore all cycles and be happy if we have found at least one actual odb I think this will make the implementation more user friendly, while seeding out cycles that lead to nothing usable. --- git-odb/src/alternate.rs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/git-odb/src/alternate.rs b/git-odb/src/alternate.rs index c9e6c3dc6f7..b85ca826732 100644 --- a/git-odb/src/alternate.rs +++ b/git-odb/src/alternate.rs @@ -19,12 +19,12 @@ pub fn resolve(objects_directory: impl Into) -> Result { + if seen.contains(&dir) { + continue; + } + seen.push(dir.clone()); dirs.push(( depth + 1, content @@ -44,5 +44,9 @@ pub fn resolve(objects_directory: impl Into) -> Result return Err(err.into()), }; } + + if out.is_empty() && !seen.is_empty() { + return Err(Error::Cycle(seen)); + } Ok(out) } From b20e9eea423ced275781d410217110c85ddb587c Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Sat, 26 Sep 2020 22:07:28 +0800 Subject: [PATCH 38/51] support for relateive alternates --- git-odb/src/alternate.rs | 19 ++++++++++++------- git-odb/tests/alternate/mod.rs | 11 +++++++---- tasks.md | 6 +++--- 3 files changed, 22 insertions(+), 14 deletions(-) diff --git a/git-odb/src/alternate.rs b/git-odb/src/alternate.rs index b85ca826732..6f1591fba11 100644 --- a/git-odb/src/alternate.rs +++ b/git-odb/src/alternate.rs @@ -10,12 +10,13 @@ pub enum Error { PathConversion(Vec), #[error(transparent)] Init(#[from] compound::init::Error), - #[error("Alternates form a cycle: {} -> {}", .0.iter().map(|p| format!("'{}'", p.display())).collect::>().join(" -> "), .0.first().expect("more than one directories").display())] + #[error("Alternates form a cycle: {}", .0.iter().map(|p| format!("'{}'", p.display())).collect::>().join(" -> "))] Cycle(Vec), } pub fn resolve(objects_directory: impl Into) -> Result, Error> { - let mut dirs = vec![(0, objects_directory.into())]; + let relative_base = objects_directory.into(); + let mut dirs = vec![(0, relative_base.clone())]; let mut out = Vec::new(); let mut seen = Vec::new(); while let Some((depth, dir)) = dirs.pop() { @@ -27,11 +28,15 @@ pub fn resolve(objects_directory: impl Into) -> Result { diff --git a/git-odb/tests/alternate/mod.rs b/git-odb/tests/alternate/mod.rs index 0e4eef6198f..bfaf1076a20 100644 --- a/git-odb/tests/alternate/mod.rs +++ b/git-odb/tests/alternate/mod.rs @@ -1,5 +1,8 @@ use git_odb::alternate; -use std::{fs, io, path::PathBuf}; +use std::{ + fs, io, + path::{Path, PathBuf}, +}; fn alternate(objects_at: impl Into, objects_to: impl Into) -> Result<(PathBuf, PathBuf), io::Error> { alternate_with(objects_at, objects_to, None) @@ -28,10 +31,10 @@ fn alternate_with( } #[test] -fn circular_alternates_are_detected() -> crate::Result { +fn circular_alternates_are_detected_with_relative_paths() -> crate::Result { let tmp = tempdir::TempDir::new("alternates")?; let (from, _) = alternate(tmp.path().join("a"), tmp.path().join("b"))?; - alternate(tmp.path().join("b"), tmp.path().join("a"))?; + alternate(tmp.path().join("b"), Path::new("..").join("a"))?; match alternate::resolve(&from) { Err(alternate::Error::Cycle(chain)) => { @@ -40,7 +43,7 @@ fn circular_alternates_are_detected() -> crate::Result { .into_iter() .map(|p| p.file_name().expect("non-root").to_str().expect("utf8").to_owned()) .collect::>(), - vec!["a", "b"] + vec!["a", "b", "a"] ); } _ => unreachable!("should be a specific kind of error"), diff --git a/tasks.md b/tasks.md index 045f0544f0c..dcca70731c3 100644 --- a/tasks.md +++ b/tasks.md @@ -11,11 +11,11 @@ * [x] alternate DB (location - it's really must following the chain until a compound DB can be created) * [x] circular dependencies test * [x] basic test - * [ ] are multiple alternates supported, too? YES + * [ ] multiple alternates in a single file * [ ] comments * [ ] quotes - * [ ] support for relative directories - * [ ] alternates lookup + * [x] support for relative directories + * [ ] lookup uses alternates * [x] loose upgrade: jwalk powered iteration behind a feature flag * [ ] full docs * **git-config** From 1f8d36705c4568b1036b0d62b3a80ae6ec20a99c Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Sat, 26 Sep 2020 23:52:21 +0800 Subject: [PATCH 39/51] Read multiple alternates from single file; ignore comments --- git-odb/src/alternate.rs | 63 ++++++++++++++++++++++------------ git-odb/tests/alternate/mod.rs | 4 +-- tasks.md | 4 +-- 3 files changed, 45 insertions(+), 26 deletions(-) diff --git a/git-odb/src/alternate.rs b/git-odb/src/alternate.rs index 6f1591fba11..990e6a7e606 100644 --- a/git-odb/src/alternate.rs +++ b/git-odb/src/alternate.rs @@ -1,46 +1,65 @@ use crate::compound; -use git_object::bstr::ByteSlice; use std::{fs, io, path::PathBuf}; #[derive(thiserror::Error, Debug)] pub enum Error { #[error(transparent)] Io(#[from] io::Error), - #[error("Could not obtain an object path for the alternate directory '{}'", String::from_utf8_lossy(&.0))] - PathConversion(Vec), + #[error(transparent)] + Parse(#[from] parse::Error), #[error(transparent)] Init(#[from] compound::init::Error), #[error("Alternates form a cycle: {}", .0.iter().map(|p| format!("'{}'", p.display())).collect::>().join(" -> "))] Cycle(Vec), } +pub mod parse { + use git_object::bstr::ByteSlice; + use std::path::PathBuf; + + #[derive(thiserror::Error, Debug)] + pub enum Error { + #[error("Could not obtain an object path for the alternate directory '{}'", String::from_utf8_lossy(&.0))] + PathConversion(Vec), + } + + pub(crate) fn content(input: &[u8]) -> Result, Error> { + let mut out = Vec::new(); + for line in input.split(|b| *b == b'\n') { + let line = line.as_bstr(); + if line.is_empty() || line.starts_with(b"#") { + continue; + } + out.push( + line.to_path() + .map(ToOwned::to_owned) + .map_err(|_| Error::PathConversion(line.to_vec()))?, + ) + } + Ok(out) + } +} + pub fn resolve(objects_directory: impl Into) -> Result, Error> { let relative_base = objects_directory.into(); let mut dirs = vec![(0, relative_base.clone())]; let mut out = Vec::new(); - let mut seen = Vec::new(); + let mut seen = vec![relative_base.clone()]; while let Some((depth, dir)) = dirs.pop() { match fs::read(dir.join("info").join("alternates")) { - Ok(content) => { - if seen.contains(&dir) { - continue; + Ok(input) => { + for path in parse::content(&input)?.into_iter() { + let path = relative_base.join(path); + let path_canonicalized = path.canonicalize()?; + if seen.contains(&path_canonicalized) { + continue; + } + seen.push(path_canonicalized); + dirs.push((depth + 1, path)); } - seen.push(dir.clone()); - dirs.push(( - depth + 1, - relative_base - .join( - content - .as_bstr() - .to_path() - .map(ToOwned::to_owned) - .map_err(|_| Error::PathConversion(content))?, - ) - .canonicalize()?, - )); } Err(err) if err.kind() == io::ErrorKind::NotFound => { - // Only resolve for repositories with at least one link, otherwise the line below isn't safe to call + // Only resolve for repositories with at least one link, otherwise the line below causes infinite recursion if depth != 0 { // The tail of a chain doesn't have alternates, and thus is the real deal out.push(compound::Db::at(dir)?); @@ -50,7 +69,7 @@ pub fn resolve(objects_directory: impl Into) -> Result 1 { return Err(Error::Cycle(seen)); } Ok(out) diff --git a/git-odb/tests/alternate/mod.rs b/git-odb/tests/alternate/mod.rs index bfaf1076a20..1ecd5efbae6 100644 --- a/git-odb/tests/alternate/mod.rs +++ b/git-odb/tests/alternate/mod.rs @@ -52,11 +52,11 @@ fn circular_alternates_are_detected_with_relative_paths() -> crate::Result { } #[test] -fn single_link() -> crate::Result { +fn single_link_with_comment_before_path() -> crate::Result { let tmp = tempdir::TempDir::new("alternates")?; let non_alternate = tmp.path().join("actual"); - let (from, to) = alternate(tmp.path().join("a"), non_alternate)?; + let (from, to) = alternate_with(tmp.path().join("a"), non_alternate, Some("# comment\n"))?; let alternates = alternate::resolve(from)?; assert_eq!(alternates.len(), 1); assert_eq!(alternates[0].loose.path, to); diff --git a/tasks.md b/tasks.md index dcca70731c3..ca14e9a40d2 100644 --- a/tasks.md +++ b/tasks.md @@ -11,8 +11,8 @@ * [x] alternate DB (location - it's really must following the chain until a compound DB can be created) * [x] circular dependencies test * [x] basic test - * [ ] multiple alternates in a single file - * [ ] comments + * [x] multiple alternates in a single file + * [x] comments * [ ] quotes * [x] support for relative directories * [ ] lookup uses alternates From 47e2fa03a1e2fe163c5c019d52bbb0ddbdb80bf0 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Sun, 27 Sep 2020 00:36:27 +0800 Subject: [PATCH 40/51] prepare for unquoting c-strings --- git-odb/src/alternate.rs | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/git-odb/src/alternate.rs b/git-odb/src/alternate.rs index 990e6a7e606..440bcff77d7 100644 --- a/git-odb/src/alternate.rs +++ b/git-odb/src/alternate.rs @@ -14,8 +14,8 @@ pub enum Error { } pub mod parse { - use git_object::bstr::ByteSlice; - use std::path::PathBuf; + use git_object::bstr::{BStr, ByteSlice}; + use std::{borrow::Cow, path::PathBuf}; #[derive(thiserror::Error, Debug)] pub enum Error { @@ -23,6 +23,10 @@ pub mod parse { PathConversion(Vec), } + fn unquote_ansi_c(line: &BStr) -> Cow<'_, BStr> { + line.into() + } + pub(crate) fn content(input: &[u8]) -> Result, Error> { let mut out = Vec::new(); for line in input.split(|b| *b == b'\n') { @@ -31,9 +35,14 @@ pub mod parse { continue; } out.push( - line.to_path() - .map(ToOwned::to_owned) - .map_err(|_| Error::PathConversion(line.to_vec()))?, + if line.starts_with(b"\"") { + unquote_ansi_c(line) + } else { + Cow::Borrowed(line) + } + .to_path() + .map(ToOwned::to_owned) + .map_err(|_| Error::PathConversion(line.to_vec()))?, ) } Ok(out) From 75bcc85ec0fffcab3a2c8d06962ba99ab6e041e7 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Sun, 27 Sep 2020 00:42:16 +0800 Subject: [PATCH 41/51] increase git-odb crate size limit --- etc/check-package-size.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/etc/check-package-size.sh b/etc/check-package-size.sh index bb4bb341663..4e67264adec 100755 --- a/etc/check-package-size.sh +++ b/etc/check-package-size.sh @@ -20,7 +20,7 @@ indent cargo diet -n --package-size-limit 25KB (enter git-ref && indent cargo diet -n --package-size-limit 4KB) (enter git-url && indent cargo diet -n --package-size-limit 6KB) (enter git-object && indent cargo diet -n --package-size-limit 15KB) -(enter git-odb && indent cargo diet -n --package-size-limit 50KB) +(enter git-odb && indent cargo diet -n --package-size-limit 55KB) (enter git-protocol && indent cargo diet -n --package-size-limit 20KB) (enter git-packetline && indent cargo diet -n --package-size-limit 7KB) (enter git-repository && indent cargo diet -n --package-size-limit 10KB) From ade929df38e619850f73389178a2c53e1c43fa45 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Sun, 27 Sep 2020 21:32:38 +0800 Subject: [PATCH 42/51] =?UTF-8?q?Also=20use=20alternates=20for=20looking?= =?UTF-8?q?=20up=20objects=E2=80=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit …which suffers from the same limitation as looking up objects in packs. --- git-odb/src/compound/locate.rs | 8 ++++++++ performance-tasks.md | 2 +- tasks.md | 2 +- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/git-odb/src/compound/locate.rs b/git-odb/src/compound/locate.rs index 53238947823..8fff2967c19 100644 --- a/git-odb/src/compound/locate.rs +++ b/git-odb/src/compound/locate.rs @@ -15,6 +15,14 @@ impl compound::Db { id: borrowed::Id<'_>, buffer: &'a mut Vec, ) -> Option, Error>> { + for alternate in &self.alternates { + // See 8c5bd095539042d7db0e611460803cdbf172beb0 for a commit that adds polonius and makes the proper version compile. + // See https://stackoverflow.com/questions/63906425/nll-limitation-how-to-work-around-cannot-borrow-buf-as-mutable-more-than?noredirect=1#comment113007288_63906425 + // More see below! Of course we don't want to do the lookup twice… but have to until this is fixed or we compile nightly. + if alternate.locate(id, buffer).is_some() { + return alternate.locate(id, buffer); + } + } for pack in &self.packs { // See 8c5bd095539042d7db0e611460803cdbf172beb0 for a commit that adds polonius and makes the proper version compile. // See https://stackoverflow.com/questions/63906425/nll-limitation-how-to-work-around-cannot-borrow-buf-as-mutable-more-than?noredirect=1#comment113007288_63906425 diff --git a/performance-tasks.md b/performance-tasks.md index 9abf83b6c7c..98c008e3dc5 100644 --- a/performance-tasks.md +++ b/performance-tasks.md @@ -1,6 +1,6 @@ ## Potential for improving performance -### NLL/Borrowcheck limitation in git-odb +### NLL/Borrowcheck limitation git-odb::CompoundDb cause half-of-possible performance during object lookup * Once polonius is available with production-ready performance, we have to [make this code less wasteful](https://github.com/Byron/gitoxide/blob/b125c763c5d628c397dce9a5d085fbf123ce1f29/git-odb/src/compound.rs#L42) * See https://github.com/rust-lang/rust/issues/45402 for a discussion and more links diff --git a/tasks.md b/tasks.md index ca14e9a40d2..a28c28de30c 100644 --- a/tasks.md +++ b/tasks.md @@ -15,7 +15,7 @@ * [x] comments * [ ] quotes * [x] support for relative directories - * [ ] lookup uses alternates + * [x] lookup uses alternates * [x] loose upgrade: jwalk powered iteration behind a feature flag * [ ] full docs * **git-config** From a6e77650a886ac33b23af8892182c9832a86e997 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Sun, 27 Sep 2020 21:46:00 +0800 Subject: [PATCH 43/51] fix incorrect cycle detection, which worked on MacOS by accident --- git-odb/src/alternate.rs | 4 ++-- git-odb/tests/alternate/mod.rs | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/git-odb/src/alternate.rs b/git-odb/src/alternate.rs index 440bcff77d7..6d6700bdf9e 100644 --- a/git-odb/src/alternate.rs +++ b/git-odb/src/alternate.rs @@ -9,7 +9,7 @@ pub enum Error { Parse(#[from] parse::Error), #[error(transparent)] Init(#[from] compound::init::Error), - #[error("Alternates form a cycle: {}", .0.iter().map(|p| format!("'{}'", p.display())).collect::>().join(" -> "))] + #[error("Alternates form a cycle: {} -> {}", .0.iter().map(|p| format!("'{}'", p.display())).collect::>().join(" -> "), .0.first().expect("more than one directories").display())] Cycle(Vec), } @@ -53,7 +53,7 @@ pub fn resolve(objects_directory: impl Into) -> Result { diff --git a/git-odb/tests/alternate/mod.rs b/git-odb/tests/alternate/mod.rs index 1ecd5efbae6..bd975b4193d 100644 --- a/git-odb/tests/alternate/mod.rs +++ b/git-odb/tests/alternate/mod.rs @@ -43,7 +43,7 @@ fn circular_alternates_are_detected_with_relative_paths() -> crate::Result { .into_iter() .map(|p| p.file_name().expect("non-root").to_str().expect("utf8").to_owned()) .collect::>(), - vec!["a", "b", "a"] + vec!["a", "b"] ); } _ => unreachable!("should be a specific kind of error"), @@ -52,10 +52,11 @@ fn circular_alternates_are_detected_with_relative_paths() -> crate::Result { } #[test] -fn single_link_with_comment_before_path() -> crate::Result { +fn single_link_with_comment_before_path_and_ansi_c_escape() -> crate::Result { let tmp = tempdir::TempDir::new("alternates")?; let non_alternate = tmp.path().join("actual"); + // let (from, to) = alternate_with(tmp.path().join("a"), non_alternate, Some("# comment\n\"../a\"\n"))?; let (from, to) = alternate_with(tmp.path().join("a"), non_alternate, Some("# comment\n"))?; let alternates = alternate::resolve(from)?; assert_eq!(alternates.len(), 1); From f81bb038bfc8ea0d9b61012d6effae084c89335a Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Sun, 27 Sep 2020 22:18:59 +0800 Subject: [PATCH 44/51] basic infrastructure for unquoting c-style strings This might one day go into a shared space, after all it seems to be used in many places within git. --- git-odb/src/alternate.rs | 64 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 58 insertions(+), 6 deletions(-) diff --git a/git-odb/src/alternate.rs b/git-odb/src/alternate.rs index 6d6700bdf9e..7839915c435 100644 --- a/git-odb/src/alternate.rs +++ b/git-odb/src/alternate.rs @@ -13,18 +13,70 @@ pub enum Error { Cycle(Vec), } +pub mod unquote { + use git_object::bstr::BStr; + use std::borrow::Cow; + + #[derive(thiserror::Error, Debug)] + pub enum Error { + #[error("{message}: {:?}", String::from_utf8_lossy(&.input))] + InvalidInput { message: &'static str, input: Vec }, + } + + impl Error { + fn new(message: &'static str, input: &BStr) -> Error { + Error::InvalidInput { + message, + input: input.to_vec(), + } + } + } + + pub fn ansi_c(input: &BStr) -> Result, Error> { + if !input.starts_with(b"\"") { + return Ok(input.into()); + } + if input.len() < 2 { + return Err(Error::new("Input must be surrounded by double quotes", input)); + } + let input = &input[1..input.len() - 1]; + Ok(input.into()) + } + + #[cfg(test)] + mod tests { + use super::*; + use git_object::bstr::ByteSlice; + + macro_rules! test { + ($name:ident, $input:literal, $expected:literal) => { + #[test] + fn $name() { + assert_eq!( + ansi_c($input.as_bytes().as_bstr()).expect("valid input"), + std::borrow::Cow::Borrowed($expected.as_bytes().as_bstr()) + ); + } + }; + } + + test!(unquoted_remains_unchanged, "hello", "hello"); + test!(empty_surrounded_by_quotes, "\"\"", ""); + test!(surrounded_only_by_quotes, "\"hello\"", "hello"); + } +} + pub mod parse { - use git_object::bstr::{BStr, ByteSlice}; + use crate::alternate::unquote; + use git_object::bstr::ByteSlice; use std::{borrow::Cow, path::PathBuf}; #[derive(thiserror::Error, Debug)] pub enum Error { #[error("Could not obtain an object path for the alternate directory '{}'", String::from_utf8_lossy(&.0))] PathConversion(Vec), - } - - fn unquote_ansi_c(line: &BStr) -> Cow<'_, BStr> { - line.into() + #[error("Could not unquote alternate path")] + Unquote(#[from] unquote::Error), } pub(crate) fn content(input: &[u8]) -> Result, Error> { @@ -36,7 +88,7 @@ pub mod parse { } out.push( if line.starts_with(b"\"") { - unquote_ansi_c(line) + unquote::ansi_c(line)? } else { Cow::Borrowed(line) } From 284da449cae62d12ea4eea8c31f1225699c5e52e Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Sun, 27 Sep 2020 22:51:17 +0800 Subject: [PATCH 45/51] prepare for actual unescaping --- git-odb/src/alternate.rs | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/git-odb/src/alternate.rs b/git-odb/src/alternate.rs index 7839915c435..5d5f784472c 100644 --- a/git-odb/src/alternate.rs +++ b/git-odb/src/alternate.rs @@ -14,7 +14,7 @@ pub enum Error { } pub mod unquote { - use git_object::bstr::BStr; + use git_object::bstr::{BStr, BString, ByteSlice}; use std::borrow::Cow; #[derive(thiserror::Error, Debug)] @@ -39,8 +39,25 @@ pub mod unquote { if input.len() < 2 { return Err(Error::new("Input must be surrounded by double quotes", input)); } - let input = &input[1..input.len() - 1]; - Ok(input.into()) + let input = &input[1..]; + let mut out = BString::default(); + loop { + match input.find_byteset(b"\"\\") { + Some(position) => match input[position] { + b'"' => { + out.extend_from_slice(&input[..position]); + break; + } + b'\\' => unimplemented!("actual unescaping"), + _ => unreachable!("cannot find character that we didn't search for"), + }, + None => { + out.extend_from_slice(input); + break; + } + } + } + Ok(out.into()) } #[cfg(test)] From a45c5941cf426537710842917c0e715cf4c74863 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Sun, 27 Sep 2020 23:32:56 +0800 Subject: [PATCH 46/51] First bunch of simple unescapes --- git-odb/src/alternate.rs | 44 ++++++++++++++++++++++++++++++++-------- 1 file changed, 36 insertions(+), 8 deletions(-) diff --git a/git-odb/src/alternate.rs b/git-odb/src/alternate.rs index 5d5f784472c..666c16c4fcc 100644 --- a/git-odb/src/alternate.rs +++ b/git-odb/src/alternate.rs @@ -21,6 +21,10 @@ pub mod unquote { pub enum Error { #[error("{message}: {:?}", String::from_utf8_lossy(&.input))] InvalidInput { message: &'static str, input: Vec }, + #[error("Unexpected end of input when fetching the next {0} bytes")] + UnexpectedEndOfInput(usize), + #[error("Invalid escaped value {byte} in input {:?}", String::from_utf8_lossy(&.input))] + UnsupportedEscapeByte { byte: u8, input: Vec }, } impl Error { @@ -39,18 +43,41 @@ pub mod unquote { if input.len() < 2 { return Err(Error::new("Input must be surrounded by double quotes", input)); } - let input = &input[1..]; + let original = input.as_bstr(); + let mut input = &input[1..]; let mut out = BString::default(); + fn consume_one_past(input: &mut &BStr, position: usize) -> Result { + *input = input + .get(position + 1..) + .ok_or_else(|| Error::new("Unexpected end of input", input))? + .as_bstr(); + let next = input[0]; + *input = input.get(1..).unwrap_or_default().as_bstr(); + Ok(next) + } loop { match input.find_byteset(b"\"\\") { - Some(position) => match input[position] { - b'"' => { - out.extend_from_slice(&input[..position]); - break; + Some(position) => { + out.extend_from_slice(&input[..position]); + match input[position] { + b'"' => break, + b'\\' => { + let next = consume_one_past(&mut input, position)?; + match next { + b'n' => out.push(b'\n'), + b'r' => out.push(b'\r'), + b't' => out.push(b'\t'), + _ => { + return Err(Error::UnsupportedEscapeByte { + byte: next, + input: original.to_vec(), + }) + } + } + } + _ => unreachable!("cannot find character that we didn't search for"), } - b'\\' => unimplemented!("actual unescaping"), - _ => unreachable!("cannot find character that we didn't search for"), - }, + } None => { out.extend_from_slice(input); break; @@ -80,6 +107,7 @@ pub mod unquote { test!(unquoted_remains_unchanged, "hello", "hello"); test!(empty_surrounded_by_quotes, "\"\"", ""); test!(surrounded_only_by_quotes, "\"hello\"", "hello"); + test!(typical_escapes, r#""\n\r\t""#, b"\n\r\t"); } } From 18415445caaee6e9e54aabddb88bdcd2f5602508 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Sun, 27 Sep 2020 23:52:32 +0800 Subject: [PATCH 47/51] All explicit escapes --- git-odb/src/alternate.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/git-odb/src/alternate.rs b/git-odb/src/alternate.rs index 666c16c4fcc..ac3035d444a 100644 --- a/git-odb/src/alternate.rs +++ b/git-odb/src/alternate.rs @@ -67,6 +67,12 @@ pub mod unquote { b'n' => out.push(b'\n'), b'r' => out.push(b'\r'), b't' => out.push(b'\t'), + b'a' => out.push(7), + b'b' => out.push(8), + b'v' => out.push(0xb), + b'f' => out.push(0xc), + b'"' => out.push(b'"'), + b'\\' => out.push(b'\\'), _ => { return Err(Error::UnsupportedEscapeByte { byte: next, @@ -108,6 +114,8 @@ pub mod unquote { test!(empty_surrounded_by_quotes, "\"\"", ""); test!(surrounded_only_by_quotes, "\"hello\"", "hello"); test!(typical_escapes, r#""\n\r\t""#, b"\n\r\t"); + test!(untypical_escapes, r#""\a\b\f\v""#, b"\x07\x08\x0c\x0b"); + test!(literal_escape_and_double_quote, r#""\"\\""#, br#""\"#); } } From 5effc7b6daf6ff49b6d51af09f8da148602c7322 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Mon, 28 Sep 2020 00:22:27 +0800 Subject: [PATCH 48/51] And octal values unquoting works too --- git-odb/src/alternate.rs | 30 +++++++++++++++++++++++++----- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/git-odb/src/alternate.rs b/git-odb/src/alternate.rs index ac3035d444a..aaaa6fb6277 100644 --- a/git-odb/src/alternate.rs +++ b/git-odb/src/alternate.rs @@ -16,21 +16,20 @@ pub enum Error { pub mod unquote { use git_object::bstr::{BStr, BString, ByteSlice}; use std::borrow::Cow; + use std::io::Read; #[derive(thiserror::Error, Debug)] pub enum Error { #[error("{message}: {:?}", String::from_utf8_lossy(&.input))] - InvalidInput { message: &'static str, input: Vec }, - #[error("Unexpected end of input when fetching the next {0} bytes")] - UnexpectedEndOfInput(usize), + InvalidInput { message: String, input: Vec }, #[error("Invalid escaped value {byte} in input {:?}", String::from_utf8_lossy(&.input))] UnsupportedEscapeByte { byte: u8, input: Vec }, } impl Error { - fn new(message: &'static str, input: &BStr) -> Error { + fn new(message: impl ToString, input: &BStr) -> Error { Error::InvalidInput { - message, + message: message.to_string(), input: input.to_vec(), } } @@ -73,6 +72,22 @@ pub mod unquote { b'f' => out.push(0xc), b'"' => out.push(b'"'), b'\\' => out.push(b'\\'), + b'0' | b'1' | b'2' | b'3' => { + let mut buf = [next; 3]; + &input + .get(..2) + .ok_or_else(|| { + Error::new( + "Unexpected end of input when fetching two more octal bytes", + input, + ) + })? + .read(&mut buf[1..]) + .expect("impossible to fail as numbers match"); + let byte = btoi::btou_radix(&buf, 8).map_err(|e| Error::new(e, original))?; + out.push(byte); + input = &input[2..]; + } _ => { return Err(Error::UnsupportedEscapeByte { byte: next, @@ -116,6 +131,11 @@ pub mod unquote { test!(typical_escapes, r#""\n\r\t""#, b"\n\r\t"); test!(untypical_escapes, r#""\a\b\f\v""#, b"\x07\x08\x0c\x0b"); test!(literal_escape_and_double_quote, r#""\"\\""#, br#""\"#); + test!( + unicode_byte_escapes_by_number, + r#""\346\277\261\351\207\216\t\347\264\224""#, + "濱野\t純" + ); } } From 5a1cbf299f2d5c1c07143d14ee3ded95d6a44a20 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Mon, 28 Sep 2020 00:23:29 +0800 Subject: [PATCH 49/51] refactor --- git-odb/src/alternate.rs | 210 ------------------------------- git-odb/src/alternate/mod.rs | 52 ++++++++ git-odb/src/alternate/parse.rs | 32 +++++ git-odb/src/alternate/unquote.rs | 120 ++++++++++++++++++ 4 files changed, 204 insertions(+), 210 deletions(-) delete mode 100644 git-odb/src/alternate.rs create mode 100644 git-odb/src/alternate/mod.rs create mode 100644 git-odb/src/alternate/parse.rs create mode 100644 git-odb/src/alternate/unquote.rs diff --git a/git-odb/src/alternate.rs b/git-odb/src/alternate.rs deleted file mode 100644 index aaaa6fb6277..00000000000 --- a/git-odb/src/alternate.rs +++ /dev/null @@ -1,210 +0,0 @@ -use crate::compound; -use std::{fs, io, path::PathBuf}; - -#[derive(thiserror::Error, Debug)] -pub enum Error { - #[error(transparent)] - Io(#[from] io::Error), - #[error(transparent)] - Parse(#[from] parse::Error), - #[error(transparent)] - Init(#[from] compound::init::Error), - #[error("Alternates form a cycle: {} -> {}", .0.iter().map(|p| format!("'{}'", p.display())).collect::>().join(" -> "), .0.first().expect("more than one directories").display())] - Cycle(Vec), -} - -pub mod unquote { - use git_object::bstr::{BStr, BString, ByteSlice}; - use std::borrow::Cow; - use std::io::Read; - - #[derive(thiserror::Error, Debug)] - pub enum Error { - #[error("{message}: {:?}", String::from_utf8_lossy(&.input))] - InvalidInput { message: String, input: Vec }, - #[error("Invalid escaped value {byte} in input {:?}", String::from_utf8_lossy(&.input))] - UnsupportedEscapeByte { byte: u8, input: Vec }, - } - - impl Error { - fn new(message: impl ToString, input: &BStr) -> Error { - Error::InvalidInput { - message: message.to_string(), - input: input.to_vec(), - } - } - } - - pub fn ansi_c(input: &BStr) -> Result, Error> { - if !input.starts_with(b"\"") { - return Ok(input.into()); - } - if input.len() < 2 { - return Err(Error::new("Input must be surrounded by double quotes", input)); - } - let original = input.as_bstr(); - let mut input = &input[1..]; - let mut out = BString::default(); - fn consume_one_past(input: &mut &BStr, position: usize) -> Result { - *input = input - .get(position + 1..) - .ok_or_else(|| Error::new("Unexpected end of input", input))? - .as_bstr(); - let next = input[0]; - *input = input.get(1..).unwrap_or_default().as_bstr(); - Ok(next) - } - loop { - match input.find_byteset(b"\"\\") { - Some(position) => { - out.extend_from_slice(&input[..position]); - match input[position] { - b'"' => break, - b'\\' => { - let next = consume_one_past(&mut input, position)?; - match next { - b'n' => out.push(b'\n'), - b'r' => out.push(b'\r'), - b't' => out.push(b'\t'), - b'a' => out.push(7), - b'b' => out.push(8), - b'v' => out.push(0xb), - b'f' => out.push(0xc), - b'"' => out.push(b'"'), - b'\\' => out.push(b'\\'), - b'0' | b'1' | b'2' | b'3' => { - let mut buf = [next; 3]; - &input - .get(..2) - .ok_or_else(|| { - Error::new( - "Unexpected end of input when fetching two more octal bytes", - input, - ) - })? - .read(&mut buf[1..]) - .expect("impossible to fail as numbers match"); - let byte = btoi::btou_radix(&buf, 8).map_err(|e| Error::new(e, original))?; - out.push(byte); - input = &input[2..]; - } - _ => { - return Err(Error::UnsupportedEscapeByte { - byte: next, - input: original.to_vec(), - }) - } - } - } - _ => unreachable!("cannot find character that we didn't search for"), - } - } - None => { - out.extend_from_slice(input); - break; - } - } - } - Ok(out.into()) - } - - #[cfg(test)] - mod tests { - use super::*; - use git_object::bstr::ByteSlice; - - macro_rules! test { - ($name:ident, $input:literal, $expected:literal) => { - #[test] - fn $name() { - assert_eq!( - ansi_c($input.as_bytes().as_bstr()).expect("valid input"), - std::borrow::Cow::Borrowed($expected.as_bytes().as_bstr()) - ); - } - }; - } - - test!(unquoted_remains_unchanged, "hello", "hello"); - test!(empty_surrounded_by_quotes, "\"\"", ""); - test!(surrounded_only_by_quotes, "\"hello\"", "hello"); - test!(typical_escapes, r#""\n\r\t""#, b"\n\r\t"); - test!(untypical_escapes, r#""\a\b\f\v""#, b"\x07\x08\x0c\x0b"); - test!(literal_escape_and_double_quote, r#""\"\\""#, br#""\"#); - test!( - unicode_byte_escapes_by_number, - r#""\346\277\261\351\207\216\t\347\264\224""#, - "濱野\t純" - ); - } -} - -pub mod parse { - use crate::alternate::unquote; - use git_object::bstr::ByteSlice; - use std::{borrow::Cow, path::PathBuf}; - - #[derive(thiserror::Error, Debug)] - pub enum Error { - #[error("Could not obtain an object path for the alternate directory '{}'", String::from_utf8_lossy(&.0))] - PathConversion(Vec), - #[error("Could not unquote alternate path")] - Unquote(#[from] unquote::Error), - } - - pub(crate) fn content(input: &[u8]) -> Result, Error> { - let mut out = Vec::new(); - for line in input.split(|b| *b == b'\n') { - let line = line.as_bstr(); - if line.is_empty() || line.starts_with(b"#") { - continue; - } - out.push( - if line.starts_with(b"\"") { - unquote::ansi_c(line)? - } else { - Cow::Borrowed(line) - } - .to_path() - .map(ToOwned::to_owned) - .map_err(|_| Error::PathConversion(line.to_vec()))?, - ) - } - Ok(out) - } -} - -pub fn resolve(objects_directory: impl Into) -> Result, Error> { - let relative_base = objects_directory.into(); - let mut dirs = vec![(0, relative_base.clone())]; - let mut out = Vec::new(); - let mut seen = vec![relative_base.canonicalize()?]; - while let Some((depth, dir)) = dirs.pop() { - match fs::read(dir.join("info").join("alternates")) { - Ok(input) => { - for path in parse::content(&input)?.into_iter() { - let path = relative_base.join(path); - let path_canonicalized = path.canonicalize()?; - if seen.contains(&path_canonicalized) { - continue; - } - seen.push(path_canonicalized); - dirs.push((depth + 1, path)); - } - } - Err(err) if err.kind() == io::ErrorKind::NotFound => { - // Only resolve for repositories with at least one link, otherwise the line below causes infinite recursion - if depth != 0 { - // The tail of a chain doesn't have alternates, and thus is the real deal - out.push(compound::Db::at(dir)?); - } - } - Err(err) => return Err(err.into()), - }; - } - - if out.is_empty() && seen.len() > 1 { - return Err(Error::Cycle(seen)); - } - Ok(out) -} diff --git a/git-odb/src/alternate/mod.rs b/git-odb/src/alternate/mod.rs new file mode 100644 index 00000000000..38b3fd5d2f1 --- /dev/null +++ b/git-odb/src/alternate/mod.rs @@ -0,0 +1,52 @@ +use crate::compound; +use std::{fs, io, path::PathBuf}; + +pub mod parse; +pub mod unquote; + +#[derive(thiserror::Error, Debug)] +pub enum Error { + #[error(transparent)] + Io(#[from] io::Error), + #[error(transparent)] + Parse(#[from] parse::Error), + #[error(transparent)] + Init(#[from] compound::init::Error), + #[error("Alternates form a cycle: {} -> {}", .0.iter().map(|p| format!("'{}'", p.display())).collect::>().join(" -> "), .0.first().expect("more than one directories").display())] + Cycle(Vec), +} + +pub fn resolve(objects_directory: impl Into) -> Result, Error> { + let relative_base = objects_directory.into(); + let mut dirs = vec![(0, relative_base.clone())]; + let mut out = Vec::new(); + let mut seen = vec![relative_base.canonicalize()?]; + while let Some((depth, dir)) = dirs.pop() { + match fs::read(dir.join("info").join("alternates")) { + Ok(input) => { + for path in parse::content(&input)?.into_iter() { + let path = relative_base.join(path); + let path_canonicalized = path.canonicalize()?; + if seen.contains(&path_canonicalized) { + continue; + } + seen.push(path_canonicalized); + dirs.push((depth + 1, path)); + } + } + Err(err) if err.kind() == io::ErrorKind::NotFound => { + // Only resolve for repositories with at least one link, otherwise the line below causes infinite recursion + if depth != 0 { + // The tail of a chain doesn't have alternates, and thus is the real deal + out.push(compound::Db::at(dir)?); + } + } + Err(err) => return Err(err.into()), + }; + } + + if out.is_empty() && seen.len() > 1 { + return Err(Error::Cycle(seen)); + } + Ok(out) +} diff --git a/git-odb/src/alternate/parse.rs b/git-odb/src/alternate/parse.rs new file mode 100644 index 00000000000..0cbe0e70380 --- /dev/null +++ b/git-odb/src/alternate/parse.rs @@ -0,0 +1,32 @@ +use crate::alternate::unquote; +use git_object::bstr::ByteSlice; +use std::{borrow::Cow, path::PathBuf}; + +#[derive(thiserror::Error, Debug)] +pub enum Error { + #[error("Could not obtain an object path for the alternate directory '{}'", String::from_utf8_lossy(&.0))] + PathConversion(Vec), + #[error("Could not unquote alternate path")] + Unquote(#[from] unquote::Error), +} + +pub(crate) fn content(input: &[u8]) -> Result, Error> { + let mut out = Vec::new(); + for line in input.split(|b| *b == b'\n') { + let line = line.as_bstr(); + if line.is_empty() || line.starts_with(b"#") { + continue; + } + out.push( + if line.starts_with(b"\"") { + unquote::ansi_c(line)? + } else { + Cow::Borrowed(line) + } + .to_path() + .map(ToOwned::to_owned) + .map_err(|_| Error::PathConversion(line.to_vec()))?, + ) + } + Ok(out) +} diff --git a/git-odb/src/alternate/unquote.rs b/git-odb/src/alternate/unquote.rs new file mode 100644 index 00000000000..8d547579c0b --- /dev/null +++ b/git-odb/src/alternate/unquote.rs @@ -0,0 +1,120 @@ +use git_object::bstr::{BStr, BString, ByteSlice}; +use std::borrow::Cow; +use std::io::Read; + +#[derive(thiserror::Error, Debug)] +pub enum Error { + #[error("{message}: {:?}", String::from_utf8_lossy(&.input))] + InvalidInput { message: String, input: Vec }, + #[error("Invalid escaped value {byte} in input {:?}", String::from_utf8_lossy(&.input))] + UnsupportedEscapeByte { byte: u8, input: Vec }, +} + +impl Error { + fn new(message: impl ToString, input: &BStr) -> Error { + Error::InvalidInput { + message: message.to_string(), + input: input.to_vec(), + } + } +} + +pub fn ansi_c(input: &BStr) -> Result, Error> { + if !input.starts_with(b"\"") { + return Ok(input.into()); + } + if input.len() < 2 { + return Err(Error::new("Input must be surrounded by double quotes", input)); + } + let original = input.as_bstr(); + let mut input = &input[1..]; + let mut out = BString::default(); + fn consume_one_past(input: &mut &BStr, position: usize) -> Result { + *input = input + .get(position + 1..) + .ok_or_else(|| Error::new("Unexpected end of input", input))? + .as_bstr(); + let next = input[0]; + *input = input.get(1..).unwrap_or_default().as_bstr(); + Ok(next) + } + loop { + match input.find_byteset(b"\"\\") { + Some(position) => { + out.extend_from_slice(&input[..position]); + match input[position] { + b'"' => break, + b'\\' => { + let next = consume_one_past(&mut input, position)?; + match next { + b'n' => out.push(b'\n'), + b'r' => out.push(b'\r'), + b't' => out.push(b'\t'), + b'a' => out.push(7), + b'b' => out.push(8), + b'v' => out.push(0xb), + b'f' => out.push(0xc), + b'"' => out.push(b'"'), + b'\\' => out.push(b'\\'), + b'0' | b'1' | b'2' | b'3' => { + let mut buf = [next; 3]; + &input + .get(..2) + .ok_or_else(|| { + Error::new("Unexpected end of input when fetching two more octal bytes", input) + })? + .read(&mut buf[1..]) + .expect("impossible to fail as numbers match"); + let byte = btoi::btou_radix(&buf, 8).map_err(|e| Error::new(e, original))?; + out.push(byte); + input = &input[2..]; + } + _ => { + return Err(Error::UnsupportedEscapeByte { + byte: next, + input: original.to_vec(), + }) + } + } + } + _ => unreachable!("cannot find character that we didn't search for"), + } + } + None => { + out.extend_from_slice(input); + break; + } + } + } + Ok(out.into()) +} + +#[cfg(test)] +mod tests { + use super::*; + use git_object::bstr::ByteSlice; + + macro_rules! test { + ($name:ident, $input:literal, $expected:literal) => { + #[test] + fn $name() { + assert_eq!( + ansi_c($input.as_bytes().as_bstr()).expect("valid input"), + std::borrow::Cow::Borrowed($expected.as_bytes().as_bstr()) + ); + } + }; + } + + test!(unquoted_remains_unchanged, "hello", "hello"); + test!(empty_surrounded_by_quotes, "\"\"", ""); + test!(surrounded_only_by_quotes, "\"hello\"", "hello"); + test!(typical_escapes, r#""\n\r\t""#, b"\n\r\t"); + test!(untypical_escapes, r#""\a\b\f\v""#, b"\x07\x08\x0c\x0b"); + test!(literal_escape_and_double_quote, r#""\"\\""#, br#""\"#); + test!( + unicode_byte_escapes_by_number, + r#""\346\277\261\351\207\216\t\347\264\224""#, + "濱野\t純" + ); +} From e355b4ad133075152312816816af5ce72cf79cff Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Mon, 28 Sep 2020 00:25:50 +0800 Subject: [PATCH 50/51] thanks clippy --- README.md | 6 +++--- git-odb/src/alternate/unquote.rs | 4 ++-- tasks.md | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 6f1d354ab41..8f25b7605bd 100644 --- a/README.md +++ b/README.md @@ -91,9 +91,9 @@ become available. * **sink** * [x] write objects and obtain id * **alternates** - * [ ] _database that act as link to other known git ODBs on disk_ - * [ ] safe with cycles and recursive configurations - * [ ] multi-line with comments and quotes + * _database that act as link to other known git ODBs on disk_ + * [x] safe with cycles and recursive configurations + * [x] multi-line with comments and quotes * **multi-odb** * [ ] _an ODB for object lookup from multiple lower level ODB at once_ * **promisor** diff --git a/git-odb/src/alternate/unquote.rs b/git-odb/src/alternate/unquote.rs index 8d547579c0b..a8a3d523c8c 100644 --- a/git-odb/src/alternate/unquote.rs +++ b/git-odb/src/alternate/unquote.rs @@ -58,12 +58,12 @@ pub fn ansi_c(input: &BStr) -> Result, Error> { b'\\' => out.push(b'\\'), b'0' | b'1' | b'2' | b'3' => { let mut buf = [next; 3]; - &input + input .get(..2) .ok_or_else(|| { Error::new("Unexpected end of input when fetching two more octal bytes", input) })? - .read(&mut buf[1..]) + .read_exact(&mut buf[1..]) .expect("impossible to fail as numbers match"); let byte = btoi::btou_radix(&buf, 8).map_err(|e| Error::new(e, original))?; out.push(byte); diff --git a/tasks.md b/tasks.md index a28c28de30c..53b4b74d232 100644 --- a/tasks.md +++ b/tasks.md @@ -13,7 +13,7 @@ * [x] basic test * [x] multiple alternates in a single file * [x] comments - * [ ] quotes + * [x] quotes * [x] support for relative directories * [x] lookup uses alternates * [x] loose upgrade: jwalk powered iteration behind a feature flag From 44e0f05cdce77456a81bbc4f3cdaf25997af834a Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Mon, 28 Sep 2020 19:45:40 +0800 Subject: [PATCH 51/51] dependency update --- Cargo.lock | 32 ++++++++++++++++---------------- tasks.md | 2 +- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3be6a8179d1..d193c9be43a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -93,9 +93,9 @@ dependencies = [ [[package]] name = "async-io" -version = "1.1.3" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9951f92a2b4f7793f8fc06a80bdb89b62c618c993497d4606474fb8c34941b5" +checksum = "33be191d05a54ec120e4667375e2ad49fe506b846463df384460ab801c7ae5dc" dependencies = [ "concurrent-queue", "fastrand", @@ -1071,9 +1071,9 @@ dependencies = [ [[package]] name = "nom" -version = "6.0.0-alpha1" +version = "6.0.0-alpha2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25020779544bf717b917323b42f23c9dbef1bd2b97b576df6484a0c8709705c6" +checksum = "5bd3285fb63b4f8b6f48411c2dd483e2fef0188cd00a1397c106b6f7b35077b9" dependencies = [ "memchr", "version_check", @@ -1233,18 +1233,18 @@ checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" [[package]] name = "pin-project" -version = "0.4.23" +version = "0.4.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca4433fff2ae79342e497d9f8ee990d174071408f28f726d6d83af93e58e48aa" +checksum = "f48fad7cfbff853437be7cf54d7b993af21f53be7f0988cbfe4a51535aa77205" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "0.4.23" +version = "0.4.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c0e815c3ee9a031fdf5af21c10aa17c573c9c6a566328d99e3936c34e36461f" +checksum = "24c6d293bdd3ca5a1697997854c6cf7855e43fb6a0ba1c47af57a5bcafd158ae" dependencies = [ "proc-macro2", "quote", @@ -1253,9 +1253,9 @@ dependencies = [ [[package]] name = "pin-project-lite" -version = "0.1.7" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "282adbf10f2698a7a77f8e983a74b2d18176c19a7fd32a45446139ae7b02b715" +checksum = "71f349a4f0e70676ffb2dbafe16d0c992382d02f0a952e3ddf584fc289dac6b3" [[package]] name = "pin-utils" @@ -1332,9 +1332,9 @@ checksum = "99c605b9a0adc77b7211c6b1f722dcb613d68d66859a44f3d485a6da332b0598" [[package]] name = "proc-macro2" -version = "1.0.21" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36e28516df94f3dd551a587da5357459d9b36d945a7c37c3557928c1c2ff2a2c" +checksum = "51ef7cd2518ead700af67bf9d1a658d90b6037d77110fd9c0445429d0ba1c6c9" dependencies = [ "unicode-xid", ] @@ -1723,9 +1723,9 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" [[package]] name = "syn" -version = "1.0.41" +version = "1.0.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6690e3e9f692504b941dc6c3b188fd28df054f7fb8469ab40680df52fdcc842b" +checksum = "9c51d92969d209b54a98397e1b91c8ae82d8c87a7bb87df0b29aa2ad81454228" dependencies = [ "proc-macro2", "quote", @@ -1808,9 +1808,9 @@ dependencies = [ [[package]] name = "time" -version = "0.2.21" +version = "0.2.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c2e31fb28e2a9f01f5ed6901b066c1ba2333c04b64dc61254142bafcb3feb2c" +checksum = "55b7151c9065e80917fbf285d9a5d1432f60db41d170ccafc749a136b41a93af" dependencies = [ "const_fn", "libc", diff --git a/tasks.md b/tasks.md index 53b4b74d232..68a3a980bed 100644 --- a/tasks.md +++ b/tasks.md @@ -51,7 +51,7 @@ To be picked in any order…. * [ ] finish transitioning to futures-lite to get rid of futures-util dependency to reduce compile times * **criner** * [x] upgrade to prodash 9.0 - * [ ] switch to `isahc` + * [ ] switch to `isahc` or `ureq` (blocking, but could use unblock for that) seems to allow async-reading of bodies, allowing to get rid of reqwest and tokio. Redirect is configurable. [josh-aug-12]: https://github.com/Byron/gitoxide/issues/1#issuecomment-672566602