History

Merge branch 'main' into filter-refs-by-spec

Sebastian Thiel committed 4 years ago

Commit 1f6e5ab2 parents 82ee79e 8c4608840 files · +796 −413Browse files

Unified · Split

40 files
  1. Cargo.lock +8 −6
  2. etc/check-package-size.sh +1 −1
  3. git-commitgraph/Cargo.toml +1 −1
  4. git-date/Cargo.toml +4 −1
  5. git-date/src/lib.rs +3 −2
  6. git-date/src/parse.rs +131 −8
  7. git-date/tests/fixtures/generate_git_date_baseline.sh +46 −0
  8. git-date/tests/fixtures/generated-archives/generate_git_date_baseline.tar.xz +3 −0
  9. git-date/tests/time/parse.rs +117 −1
  10. git-diff/Cargo.toml +1 −1
  11. git-discover/Cargo.toml +1 −1
  12. git-features/Cargo.toml +1 −1
  13. git-hash/CHANGELOG.md +59 −20
  14. git-hash/Cargo.toml +2 −2
  15. git-hash/src/kind.rs +122 −0
  16. git-hash/src/lib.rs +13 −164
  17. git-hash/src/object_id.rs +53 −19
  18. git-hash/src/oid.rs +4 −9
  19. git-hash/src/prefix.rs +24 −30
  20. git-hash/tests/hash.rs +4 −0
  21. git-hash/tests/integration.rs +0 −2
  22. git-hash/tests/object_id/mod.rs +43 −0
  23. git-hash/tests/oid/mod.rs +1 −125
  24. git-hash/tests/prefix/mod.rs +123 −0
  25. git-index/Cargo.toml +1 −1
  26. git-object/Cargo.toml +1 −1
  27. git-odb/Cargo.toml +1 −1
  28. git-pack/Cargo.toml +1 −1
  29. git-protocol/Cargo.toml +1 −1
  30. git-ref/Cargo.toml +1 −1
  31. git-refspec/Cargo.toml +1 −1
  32. git-repository/Cargo.toml +1 −1
  33. git-repository/src/repository/identity.rs +3 −2
  34. git-revision/Cargo.toml +1 −1
  35. git-revision/src/spec/parse/function.rs +11 −3
  36. git-revision/src/spec/parse/mod.rs +4 −1
  37. git-revision/tests/spec/parse/anchor/at_symbol.rs +1 −1
  38. git-traverse/Cargo.toml +1 −1
  39. git-worktree/Cargo.toml +1 −1
  40. tests/tools/Cargo.toml +1 −1
Cargo.lock+8−6
@@ -1234,7 +1234,9 @@ dependencies = [ · 1233 unmodified lines
1234 "document-features",1234 "document-features",
1235 "git-testtools",1235 "git-testtools",
1236 "itoa 1.0.3",1236 "itoa 1.0.3",
1237 "once_cell",
1237 "serde",1238 "serde",
1239 "thiserror",
1238 "time",1240 "time",
1239]1241]
1240 1242
@@ -1307,13 +1309,13 @@ dependencies = [ · 66 unmodified lines
1307 1309
1308[[package]]1310[[package]]
1309name = "git-hash"1311name = "git-hash"
1310version = "0.9.8"1312version = "0.9.9"
1311dependencies = [1313dependencies = [
1312 "document-features",1314 "document-features",
1313 "git-testtools",1315 "git-testtools",
1314 "hex",1316 "hex",
1315 "quick-error",
1316 "serde",1317 "serde",
1318 "thiserror",
1317]1319]
1318 1320
1319[[package]]1321[[package]]
@@ -3026,18 +3028,18 @@ checksum = "b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb" · 1706 unmodified lines
3026 3028
3027[[package]]3029[[package]]
3028name = "thiserror"3030name = "thiserror"
3029version = "1.0.32"3031version = "1.0.33"
3030source = "registry+https://github.com/rust-lang/crates.io-index"3032source = "registry+https://github.com/rust-lang/crates.io-index"
3031checksum = "f5f6586b7f764adc0231f4c79be7b920e766bb2f3e51b3661cdb263828f19994"3033checksum = "3d0a539a918745651435ac7db7a18761589a94cd7e94cd56999f828bf73c8a57"
3032dependencies = [3034dependencies = [
3033 "thiserror-impl",3035 "thiserror-impl",
3034]3036]
3035 3037
3036[[package]]3038[[package]]
3037name = "thiserror-impl"3039name = "thiserror-impl"
3038version = "1.0.32"3040version = "1.0.33"
3039source = "registry+https://github.com/rust-lang/crates.io-index"3041source = "registry+https://github.com/rust-lang/crates.io-index"
3040checksum = "12bafc5b54507e0149cdf1b145a5d80ab80a90bcd9275df43d4fff68460f6c21"3042checksum = "c251e90f708e16c49a16f4917dc2131e75222b72edfa9cb7f7c58ae56aae0c09"
3041dependencies = [3043dependencies = [
3042 "proc-macro2",3044 "proc-macro2",
3043 "quote",3045 "quote",
etc/check-package-size.sh+1−1
@@ -42,7 +42,7 @@ echo "in root: gitoxide CLI" · 41 unmodified lines
42(enter git-traverse && indent cargo diet -n --package-size-limit 10KB)42(enter git-traverse && indent cargo diet -n --package-size-limit 10KB)
43(enter git-url && indent cargo diet -n --package-size-limit 20KB)43(enter git-url && indent cargo diet -n --package-size-limit 20KB)
44(enter git-validate && indent cargo diet -n --package-size-limit 5KB)44(enter git-validate && indent cargo diet -n --package-size-limit 5KB)
45(enter git-date && indent cargo diet -n --package-size-limit 10KB)45(enter git-date && indent cargo diet -n --package-size-limit 15KB)
46(enter git-filter && indent cargo diet -n --package-size-limit 5KB)46(enter git-filter && indent cargo diet -n --package-size-limit 5KB)
47(enter git-lfs && indent cargo diet -n --package-size-limit 5KB)47(enter git-lfs && indent cargo diet -n --package-size-limit 5KB)
48(enter git-note && indent cargo diet -n --package-size-limit 5KB)48(enter git-note && indent cargo diet -n --package-size-limit 5KB)
git-commitgraph/Cargo.toml+1−1
@@ -18,7 +18,7 @@ serde1 = ["serde", "git-hash/serde1", "bstr/serde1"] · 17 unmodified lines
18 18
19[dependencies]19[dependencies]
20git-features = { version = "^0.22.3", path = "../git-features", features = ["rustsha1"] }20git-features = { version = "^0.22.3", path = "../git-features", features = ["rustsha1"] }
21git-hash = { version = "^0.9.8", path = "../git-hash" }21git-hash = { version = "^0.9.9", path = "../git-hash" }
22git-chunk = { version = "^0.3.1", path = "../git-chunk" }22git-chunk = { version = "^0.3.1", path = "../git-chunk" }
23 23
24bstr = { version = "0.2.13", default-features = false, features = ["std"] }24bstr = { version = "0.2.13", default-features = false, features = ["std"] }
git-date/Cargo.toml+4−1
@@ -6,6 +6,7 @@ license = "MIT/Apache-2.0" · 5 unmodified lines
6description = "A WIP crate of the gitoxide project parsing dates the way git does"6description = "A WIP crate of the gitoxide project parsing dates the way git does"
7authors = ["Sebastian Thiel <sebastian.thiel@icloud.com>"]7authors = ["Sebastian Thiel <sebastian.thiel@icloud.com>"]
8edition = "2018"8edition = "2018"
9include = ["src/**/*", "CHANGELOG.md"]
9 10
10[lib]11[lib]
11doctest = false12doctest = false
@@ -18,12 +19,14 @@ serde1 = ["serde", "bstr/serde1"] · 6 unmodified lines
18bstr = { version = "0.2.13", default-features = false, features = ["std"]}19bstr = { version = "0.2.13", default-features = false, features = ["std"]}
19serde = { version = "1.0.114", optional = true, default-features = false, features = ["derive"]}20serde = { version = "1.0.114", optional = true, default-features = false, features = ["derive"]}
20itoa = "1.0.1"21itoa = "1.0.1"
21time = { version = "0.3.2", default-features = false, features = ["local-offset", "formatting", "macros"] }22time = { version = "0.3.2", default-features = false, features = ["local-offset", "formatting", "macros", "parsing"] }
23thiserror = "1.0.32"
22 24
23document-features = { version = "0.2.0", optional = true }25document-features = { version = "0.2.0", optional = true }
24 26
25[dev-dependencies]27[dev-dependencies]
26git-testtools = { path = "../tests/tools"}28git-testtools = { path = "../tests/tools"}
29once_cell = "1.12.0"
27 30
28[package.metadata.docs.rs]31[package.metadata.docs.rs]
29all-features = true32all-features = true
git-date/src/lib.rs+3−2
@@ -13,8 +13,9 @@ · 12 unmodified lines
13///13///
14pub mod time;14pub mod time;
15 15
16mod parse;16///
17pub use parse::parse;17pub mod parse;
18pub use parse::function::parse;
18 19
19/// A timestamp with timezone.20/// A timestamp with timezone.
20#[derive(PartialEq, Eq, Debug, Hash, Ord, PartialOrd, Clone, Copy)]21#[derive(PartialEq, Eq, Debug, Hash, Ord, PartialOrd, Clone, Copy)]
git-date/src/parse.rs+131−8
@@ -1,11 +1,134 @@
1use crate::Time;1#[derive(thiserror::Error, Debug)]
2
3#[allow(missing_docs)]2#[allow(missing_docs)]
4pub fn parse(input: &str) -> Option<Time> {3pub enum Error {
5 // TODO: actual implementation, this is just to not constantly fail4 #[error("Cannot represent times before UNIX epoch at timestamp {timestamp}")]
6 if input == "1979-02-26 18:30:00" {5 TooEarly { timestamp: i64 },
7 Some(Time::new(42, 1800))6 #[error("Date string can not be parsed")]
8 } else {7 InvalidDateString,
9 None8 #[error("Dates past 2038 can not be represented.")]
9 InvalidDate(#[from] std::num::TryFromIntError),
10 #[error("Current time is missing.")]
11 MissingCurrentTime,
12}
13
14pub(crate) mod function {
15 use crate::parse::{relative, Error};
16 use crate::time::format::{DEFAULT, ISO8601, ISO8601_STRICT, RFC2822, SHORT};
17 use crate::time::Sign;
18 use crate::Time;
19 use std::convert::TryInto;
20 use std::str::FromStr;
21 use std::time::SystemTime;
22 use time::{Date, OffsetDateTime};
23
24 #[allow(missing_docs)]
25 pub fn parse(input: &str, now: Option<SystemTime>) -> Result<Time, Error> {
26 // TODO: actual implementation, this is just to not constantly fail
27 if input == "1979-02-26 18:30:00" {
28 return Ok(Time::new(42, 1800));
29 }
30
31 Ok(if let Ok(val) = Date::parse(input, SHORT) {
32 let val = val.with_hms(0, 0, 0).expect("date is in range").assume_utc();
33 Time::new(val.unix_timestamp().try_into()?, val.offset().whole_seconds())
34 } else if let Ok(val) = OffsetDateTime::parse(input, RFC2822) {
35 Time::new(val.unix_timestamp().try_into()?, val.offset().whole_seconds())
36 } else if let Ok(val) = OffsetDateTime::parse(input, ISO8601) {
37 Time::new(val.unix_timestamp().try_into()?, val.offset().whole_seconds())
38 } else if let Ok(val) = OffsetDateTime::parse(input, ISO8601_STRICT) {
39 Time::new(val.unix_timestamp().try_into()?, val.offset().whole_seconds())
40 } else if let Ok(val) = OffsetDateTime::parse(input, DEFAULT) {
41 Time::new(val.unix_timestamp().try_into()?, val.offset().whole_seconds())
42 } else if let Ok(val) = u32::from_str(input) {
43 // Format::Unix
44 Time::new(val, 0)
45 } else if let Some(val) = parse_raw(input) {
46 // Format::Raw
47 val
48 } else if let Some(time) = relative::parse(input, now).transpose()? {
49 Time::new(timestamp(time)?, time.offset().whole_seconds())
50 } else {
51 return Err(Error::InvalidDateString);
52 })
53 }
54
55 fn timestamp(date: OffsetDateTime) -> Result<u32, Error> {
56 let timestamp = date.unix_timestamp();
57 if timestamp < 0 {
58 Err(Error::TooEarly { timestamp })
59 } else {
60 Ok(timestamp.try_into()?)
61 }
62 }
63
64 fn parse_raw(input: &str) -> Option<Time> {
65 let mut split = input.split_whitespace();
66 let seconds_since_unix_epoch: u32 = split.next()?.parse().ok()?;
67 let offset = split.next()?;
68 if offset.len() != 5 {
69 return None;
70 }
71 let sign = if &offset[..1] == "-" { Sign::Plus } else { Sign::Minus };
72 let hours: i32 = offset[1..3].parse().ok()?;
73 let minutes: i32 = offset[3..5].parse().ok()?;
74 let offset_in_seconds = hours * 3600 + minutes * 60;
75 let time = Time {
76 seconds_since_unix_epoch,
77 offset_in_seconds,
78 sign,
79 };
80 Some(time)
81 }
82}
83
84mod relative {
85 use crate::parse::Error;
86 use std::convert::TryInto;
87 use std::str::FromStr;
88 use std::time::SystemTime;
89 use time::{Duration, OffsetDateTime};
90
91 fn parse_inner(input: &str) -> Option<Duration> {
92 let mut split = input.split_whitespace();
93 let multiplier = i64::from_str(split.next()?).ok()?;
94 let period = split.next()?;
95 if split.next()? != "ago" {
96 return None;
97 }
98 duration(period, multiplier)
99 }
100
101 pub(crate) fn parse(input: &str, now: Option<SystemTime>) -> Option<Result<OffsetDateTime, Error>> {
102 parse_inner(input).map(|offset| {
103 let offset = std::time::Duration::from_secs(offset.whole_seconds().try_into().expect("positive value"));
104 now.ok_or(Error::MissingCurrentTime).map(|now| {
105 now.checked_sub(offset)
106 .expect("BUG: values can't be large enough to cause underflow")
107 .into()
108 })
109 })
110 }
111
112 fn duration(period: &str, multiplier: i64) -> Option<Duration> {
113 let period = period.strip_suffix('s').unwrap_or(period);
114 Some(match period {
115 "second" => Duration::seconds(multiplier),
116 "minute" => Duration::minutes(multiplier),
117 "hour" => Duration::hours(multiplier),
118 "day" => Duration::days(multiplier),
119 "week" => Duration::weeks(multiplier),
120 // TODO months & years
121 _ => return None,
122 })
123 }
124
125 #[cfg(test)]
126 mod tests {
127 use super::*;
128
129 #[test]
130 fn two_weeks_ago() {
131 assert_eq!(parse_inner("2 weeks ago"), Some(Duration::weeks(2)));
132 }
10 }133 }
11}134}
git-date/tests/fixtures/generate_git_date_baseline.sh+46−0
@@ -0,0 +1,46 @@
1#!/bin/bash
2set -eu -o pipefail
3
4git init;
5
6function baseline() {
7 local test_date=$1 # first argument is the date to test
8
9 git -c section.key="$test_date" config --type=expiry-date section.key && status=0 || status=$?
10 {
11 echo "$test_date"
12 echo "$status"
13 if [ $status == 0 ]
14 then
15 git -c section.key="$test_date" config --type=expiry-date section.key
16 else
17 echo "-1"
18 fi
19 } >> baseline.git
20}
21
22# success
23
24# date formats following to https://git-scm.com/docs/git-log#Documentation/git-log.txt---dateltformatgt
25
26# short
27# ODO
28#baseline '2022-08-22'
29# rfc2822
30baseline 'Thu, 18 Aug 2022 12:45:06 +0800'
31# iso8601
32baseline '2022-08-17 22:04:58 +0200'
33# iso8601_strict
34baseline '2022-08-17T21:43:13+08:00'
35# default
36baseline 'Thu Sep 04 2022 10:45:06 -0400'
37# unix
38baseline '123456789'
39# raw
40baseline '1660874655 +0800'
41
42# failing
43
44# empty_input
45baseline ""
46
git-date/tests/fixtures/generated-archives/generate_git_date_baseline.tar.xz+3−0
@@ -0,0 +1,3 @@
1version https://git-lfs.github.com/spec/v1
2oid sha256:7495815a04eea1a5b657d944b85f2dac5bb73bbda43a409ba70a2094c23261cb
3size 9188
git-date/tests/time/parse.rs+117−1
@@ -1,10 +1,51 @@
1use bstr::{BString, ByteSlice};
1use git_date::time::Sign;2use git_date::time::Sign;
2use git_date::Time;3use git_date::Time;
4use once_cell::sync::Lazy;
5use std::collections::HashMap;
6use std::str::FromStr;
7use std::time::SystemTime;
8
9type Result<T = ()> = std::result::Result<T, Box<dyn std::error::Error>>;
10
11static BASELINE: Lazy<HashMap<BString, (usize, BString)>> = Lazy::new(|| {
12 let base = git_testtools::scripted_fixture_repo_read_only("generate_git_date_baseline.sh").unwrap();
13
14 (|| -> Result<_> {
15 let mut map = HashMap::new();
16 let baseline = std::fs::read(base.join("baseline.git"))?;
17 let mut lines = baseline.lines();
18 while let Some(date_str) = lines.next() {
19 let exit_code = lines.next().expect("three lines per baseline").to_str()?.parse()?;
20 let output = lines.next().expect("three lines per baseline").into();
21 map.insert(date_str.into(), (exit_code, output));
22 }
23 Ok(map)
24 })()
25 .unwrap()
26});
27
28#[test]
29fn baseline() {
30 for (pattern, (exit_code, output)) in BASELINE.iter() {
31 let res = git_date::parse(pattern.to_str().expect("valid pattern"), Some(SystemTime::now()));
32 assert_eq!(
33 res.is_ok(),
34 *exit_code == 0,
35 "{pattern:?} disagrees with baseline: {res:?}"
36 );
37 if *exit_code == 0 {
38 let actual = res.unwrap().seconds_since_unix_epoch;
39 let expected = u32::from_str(output.to_str().expect("valid utf")).expect("valid epoch value");
40 assert_eq!(actual, expected, "{pattern:?} disagrees with baseline: {actual:?}")
41 }
42 }
43}
3 44
4#[test]45#[test]
5fn special_time_is_ok_for_now() {46fn special_time_is_ok_for_now() {
6 assert_eq!(47 assert_eq!(
7 git_date::parse("1979-02-26 18:30:00").unwrap(),48 git_date::parse("1979-02-26 18:30:00", Some(SystemTime::now())).unwrap(),
8 Time {49 Time {
9 seconds_since_unix_epoch: 42,50 seconds_since_unix_epoch: 42,
10 offset_in_seconds: 1800,51 offset_in_seconds: 1800,
@@ -12,3 +53,78 @@ fn special_time_is_ok_for_now() { · 1 unmodified line
12 }53 }
13 );54 );
14}55}
56
57#[test]
58fn short() {
59 assert_eq!(
60 git_date::parse("1979-02-26", Some(SystemTime::now())).expect("parsed date"),
61 Time {
62 seconds_since_unix_epoch: 288835200,
63 offset_in_seconds: 0,
64 sign: Sign::Plus,
65 },
66 "could not parse with SHORT format"
67 );
68}
69
70#[test]
71fn rfc2822() {
72 assert_eq!(
73 git_date::parse("Thu, 18 Aug 2022 12:45:06 +0800", None).expect("parsed rfc2822 string"),
74 Time {
75 seconds_since_unix_epoch: 1660797906,
76 offset_in_seconds: 28800,
77 sign: Sign::Plus,
78 },
79 "could not parse with RFC2822 format"
80 );
81}
82
83#[test]
84fn invalid_dates_can_be_produced_without_current_time() {
85 assert!(matches!(
86 git_date::parse("foobar", None).unwrap_err(),
87 git_date::parse::Error::InvalidDateString
88 ));
89}
90
91mod relative {
92 use git_date::parse::Error;
93 use git_date::time::Sign;
94 use std::time::SystemTime;
95 use time::{Duration, OffsetDateTime};
96
97 #[test]
98 #[should_panic] // TODO: fix
99 fn large_offsets_can_panic() {
100 git_date::parse("999999999999999 weeks ago", Some(std::time::UNIX_EPOCH)).ok();
101 }
102
103 #[test]
104 #[should_panic] // TODO: fix
105 fn large_offsets_can_panic_elsewhere() {
106 git_date::parse("9999999999 weeks ago", Some(std::time::UNIX_EPOCH)).ok();
107 }
108
109 #[test]
110 fn offset_leading_to_before_unix_epoch_cannot_be_represented() {
111 let err = git_date::parse("1 second ago", Some(std::time::UNIX_EPOCH)).unwrap_err();
112 assert!(matches!(err, Error::TooEarly{timestamp} if timestamp == -1));
113 }
114
115 #[test]
116 fn various() {
117 let now = Some(SystemTime::now());
118 let two_weeks_ago = git_date::parse("2 weeks ago", now).expect("valid time");
119 assert_eq!(Sign::Plus, two_weeks_ago.sign);
120 assert_eq!(0, two_weeks_ago.offset_in_seconds);
121 let expected = OffsetDateTime::from(now.unwrap()).saturating_sub(Duration::weeks(2));
122 // account for the loss of precision when creating `Time` with seconds
123 let expected = expected.replace_nanosecond(0).unwrap();
124 assert_eq!(
125 OffsetDateTime::from_unix_timestamp(two_weeks_ago.seconds_since_unix_epoch as i64).expect("valid datetime"),
126 expected,
127 "relative times differ"
128 );
129 }
130}
git-diff/Cargo.toml+1−1
@@ -12,7 +12,7 @@ include = ["src/**/*"] · 11 unmodified lines
12doctest = false12doctest = false
13 13
14[dependencies]14[dependencies]
15git-hash = { version = "^0.9.8", path = "../git-hash" }15git-hash = { version = "^0.9.9", path = "../git-hash" }
16git-object = { version = "^0.20.3", path = "../git-object" }16git-object = { version = "^0.20.3", path = "../git-object" }
17thiserror = "1.0.32"17thiserror = "1.0.32"
18 18
git-discover/Cargo.toml+1−1
@@ -15,7 +15,7 @@ doctest = false · 14 unmodified lines
15git-sec = { version = "^0.3.1", path = "../git-sec", features = ["thiserror"] }15git-sec = { version = "^0.3.1", path = "../git-sec", features = ["thiserror"] }
16git-path = { version = "^0.4.2", path = "../git-path" }16git-path = { version = "^0.4.2", path = "../git-path" }
17git-ref = { version = "^0.15.2", path = "../git-ref" }17git-ref = { version = "^0.15.2", path = "../git-ref" }
18git-hash = { version = "^0.9.8", path = "../git-hash" }18git-hash = { version = "^0.9.9", path = "../git-hash" }
19 19
20bstr = { version = "0.2.13", default-features = false, features = ["std", "unicode"] }20bstr = { version = "0.2.13", default-features = false, features = ["std", "unicode"] }
21thiserror = "1.0.26"21thiserror = "1.0.26"
git-features/Cargo.toml+1−1
@@ -92,7 +92,7 @@ required-features = ["io-pipe"] · 91 unmodified lines
92[dependencies]92[dependencies]
93#! ### Optional Dependencies93#! ### Optional Dependencies
94 94
95git-hash = { version = "^0.9.8", path = "../git-hash" }95git-hash = { version = "^0.9.9", path = "../git-hash" }
96 96
97 97
98 98
git-hash/CHANGELOG.md+59−20
@@ -5,8 +5,46 @@ All notable changes to this project will be documented in this file. · 4 unmodified lines
5The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),5The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).6and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7 7
8## 0.9.9 (2022-09-02)
9
10### Bug Fixes
11
12 - <csr-id-aaed7eaf4887d5e499437d45c8284bc8941da2ac/> don't assume hex-only characters in `ObjectId::from_hex(…)`.
13
14
15### Refactor
16
17 - <csr-id-7926f47ebc34e11c769acfd3441ab391fc1b9b36/> replace `quickerror` with `thiserror`
18
19### Commit Statistics
20
21<csr-read-only-do-not-edit/>
22
23 - 6 commits contributed to the release.
24 - 9 days passed between releases.
25 - 2 commits were understood as [conventional](https://www.conventionalcommits.org).
26 - 1 unique issue was worked on: [#522](https://github.com/Byron/gitoxide/issues/522)
27
28### Commit Details
29
30<csr-read-only-do-not-edit/>
31
32<details><summary>view details</summary>
33
34 * **[#522](https://github.com/Byron/gitoxide/issues/522)**
35 - don't assume hex-only characters in `ObjectId::from_hex(…)`. ([`aaed7ea`](https://github.com/Byron/gitoxide/commit/aaed7eaf4887d5e499437d45c8284bc8941da2ac))
36 - refactor ([`f3bcddf`](https://github.com/Byron/gitoxide/commit/f3bcddff931e50472e7a3e8c2c60f3cd565bfa56))
37 - refactor ([`0f0de2b`](https://github.com/Byron/gitoxide/commit/0f0de2ba7837e2044e22a16b6d5e20b3137e9691))
38 * **Uncategorized**
39 - fix docs ([`71cb9ea`](https://github.com/Byron/gitoxide/commit/71cb9eaefb792656b6380fdcc760c2234f9b9fa7))
40 - replace `quickerror` with `thiserror` ([`7926f47`](https://github.com/Byron/gitoxide/commit/7926f47ebc34e11c769acfd3441ab391fc1b9b36))
41 - some more tests ([`400c6cb`](https://github.com/Byron/gitoxide/commit/400c6cb14fe24b5e947f0c7f3a105deecd3b2f84))
42</details>
43
8## 0.9.8 (2022-08-24)44## 0.9.8 (2022-08-24)
9 45
46<csr-id-f7f136dbe4f86e7dee1d54835c420ec07c96cd78/>
47
10### Chore48### Chore
11 49
12 - <csr-id-f7f136dbe4f86e7dee1d54835c420ec07c96cd78/> uniformize deny attributes50 - <csr-id-f7f136dbe4f86e7dee1d54835c420ec07c96cd78/> uniformize deny attributes
@@ -20,9 +58,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 · 7 unmodified lines
20 58
21<csr-read-only-do-not-edit/>59<csr-read-only-do-not-edit/>
22 60
23 - 4 commits contributed to the release over the course of 3 calendar days.61 - 5 commits contributed to the release over the course of 3 calendar days.
24 - 8 days passed between releases.62 - 8 days passed between releases.
25 - 3 commits where understood as [conventional](https://www.conventionalcommits.org).63 - 3 commits were understood as [conventional](https://www.conventionalcommits.org).
26 - 0 issues like '(#ID)' where seen in commit messages64 - 0 issues like '(#ID)' where seen in commit messages
27 65
28### Commit Details66### Commit Details
@@ -32,6 +70,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 · 3 unmodified lines
32<details><summary>view details</summary>70<details><summary>view details</summary>
33 71
34 * **Uncategorized**72 * **Uncategorized**
73 - Release git-date v0.0.5, git-hash v0.9.8, git-features v0.22.2, git-actor v0.11.3, git-glob v0.3.2, git-quote v0.2.1, git-attributes v0.3.2, git-tempfile v2.0.4, git-lock v2.1.1, git-validate v0.5.5, git-object v0.20.2, git-ref v0.15.2, git-sec v0.3.1, git-config v0.7.0, git-credentials v0.4.0, git-diff v0.17.2, git-discover v0.4.1, git-bitmap v0.1.2, git-index v0.4.2, git-mailmap v0.3.2, git-chunk v0.3.1, git-traverse v0.16.2, git-pack v0.21.2, git-odb v0.31.2, git-packetline v0.12.7, git-url v0.7.2, git-transport v0.19.2, git-protocol v0.19.0, git-revision v0.4.2, git-refspec v0.1.0, git-worktree v0.4.2, git-repository v0.22.0, safety bump 4 crates ([`4974eca`](https://github.com/Byron/gitoxide/commit/4974eca96d525d1ee4f8cad79bb713af7a18bf9d))
35 - Merge branch 'example-new-repo' ([`946dd3a`](https://github.com/Byron/gitoxide/commit/946dd3a80522ef437e09528a93aa1433f01b0ee8))74 - Merge branch 'example-new-repo' ([`946dd3a`](https://github.com/Byron/gitoxide/commit/946dd3a80522ef437e09528a93aa1433f01b0ee8))
36 - use docsrs feature in code to show what is feature-gated automatically on docs.rs ([`b1c40b0`](https://github.com/Byron/gitoxide/commit/b1c40b0364ef092cd52d03b34f491b254816b18d))75 - use docsrs feature in code to show what is feature-gated automatically on docs.rs ([`b1c40b0`](https://github.com/Byron/gitoxide/commit/b1c40b0364ef092cd52d03b34f491b254816b18d))
37 - uniformize deny attributes ([`f7f136d`](https://github.com/Byron/gitoxide/commit/f7f136dbe4f86e7dee1d54835c420ec07c96cd78))76 - uniformize deny attributes ([`f7f136d`](https://github.com/Byron/gitoxide/commit/f7f136dbe4f86e7dee1d54835c420ec07c96cd78))
@@ -53,7 +92,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 · 15 unmodified lines
53 92
54 - 9 commits contributed to the release over the course of 19 calendar days.93 - 9 commits contributed to the release over the course of 19 calendar days.
55 - 24 days passed between releases.94 - 24 days passed between releases.
56 - 1 commit where understood as [conventional](https://www.conventionalcommits.org).95 - 1 commit was understood as [conventional](https://www.conventionalcommits.org).
57 - 1 unique issue was worked on: [#427](https://github.com/Byron/gitoxide/issues/427)96 - 1 unique issue was worked on: [#427](https://github.com/Byron/gitoxide/issues/427)
58 97
59### Thanks Clippy98### Thanks Clippy
@@ -91,7 +130,7 @@ This is a maintenance release with no functional changes. · 31 unmodified lines
91 130
92 - 3 commits contributed to the release over the course of 12 calendar days.131 - 3 commits contributed to the release over the course of 12 calendar days.
93 - 39 days passed between releases.132 - 39 days passed between releases.
94 - 0 commits where understood as [conventional](https://www.conventionalcommits.org).133 - 0 commits were understood as [conventional](https://www.conventionalcommits.org).
95 - 0 issues like '(#ID)' where seen in commit messages134 - 0 issues like '(#ID)' where seen in commit messages
96 135
97### Commit Details136### Commit Details
@@ -120,7 +159,7 @@ This is a maintenance release with no functional changes. · 22 unmodified lines
120 159
121 - 3 commits contributed to the release over the course of 5 calendar days.160 - 3 commits contributed to the release over the course of 5 calendar days.
122 - 25 days passed between releases.161 - 25 days passed between releases.
123 - 1 commit where understood as [conventional](https://www.conventionalcommits.org).162 - 1 commit was understood as [conventional](https://www.conventionalcommits.org).
124 - 1 unique issue was worked on: [#427](https://github.com/Byron/gitoxide/issues/427)163 - 1 unique issue was worked on: [#427](https://github.com/Byron/gitoxide/issues/427)
125 164
126### Commit Details165### Commit Details
@@ -151,7 +190,7 @@ This is a maintenance release with no functional changes. · 24 unmodified lines
151 190
152 - 10 commits contributed to the release over the course of 46 calendar days.191 - 10 commits contributed to the release over the course of 46 calendar days.
153 - 46 days passed between releases.192 - 46 days passed between releases.
154 - 2 commits where understood as [conventional](https://www.conventionalcommits.org).193 - 2 commits were understood as [conventional](https://www.conventionalcommits.org).
155 - 2 unique issues were worked on: [#301](https://github.com/Byron/gitoxide/issues/301), [#413](https://github.com/Byron/gitoxide/issues/413)194 - 2 unique issues were worked on: [#301](https://github.com/Byron/gitoxide/issues/301), [#413](https://github.com/Byron/gitoxide/issues/413)
156 195
157### Commit Details196### Commit Details
@@ -194,7 +233,7 @@ This is a maintenance release with no functional changes. · 36 unmodified lines
194 233
195 - 17 commits contributed to the release over the course of 54 calendar days.234 - 17 commits contributed to the release over the course of 54 calendar days.
196 - 59 days passed between releases.235 - 59 days passed between releases.
197 - 2 commits where understood as [conventional](https://www.conventionalcommits.org).236 - 2 commits were understood as [conventional](https://www.conventionalcommits.org).
198 - 4 unique issues were worked on: [#298](https://github.com/Byron/gitoxide/issues/298), [#301](https://github.com/Byron/gitoxide/issues/301), [#329](https://github.com/Byron/gitoxide/issues/329), [#331](https://github.com/Byron/gitoxide/issues/331)237 - 4 unique issues were worked on: [#298](https://github.com/Byron/gitoxide/issues/298), [#301](https://github.com/Byron/gitoxide/issues/301), [#329](https://github.com/Byron/gitoxide/issues/329), [#331](https://github.com/Byron/gitoxide/issues/331)
199 238
200### Commit Details239### Commit Details
@@ -243,7 +282,7 @@ A automated maintenance release without impact to the public API. · 42 unmodified lines
243 282
244 - 4 commits contributed to the release over the course of 8 calendar days.283 - 4 commits contributed to the release over the course of 8 calendar days.
245 - 8 days passed between releases.284 - 8 days passed between releases.
246 - 1 commit where understood as [conventional](https://www.conventionalcommits.org).285 - 1 commit was understood as [conventional](https://www.conventionalcommits.org).
247 - 1 unique issue was worked on: [#298](https://github.com/Byron/gitoxide/issues/298)286 - 1 unique issue was worked on: [#298](https://github.com/Byron/gitoxide/issues/298)
248 287
249### Commit Details288### Commit Details
@@ -315,7 +354,7 @@ A automated maintenance release without impact to the public API. · 65 unmodified lines
315 354
316 - 3 commits contributed to the release over the course of 3 calendar days.355 - 3 commits contributed to the release over the course of 3 calendar days.
317 - 4 days passed between releases.356 - 4 days passed between releases.
318 - 1 commit where understood as [conventional](https://www.conventionalcommits.org).357 - 1 commit was understood as [conventional](https://www.conventionalcommits.org).
319 - 0 issues like '(#ID)' where seen in commit messages358 - 0 issues like '(#ID)' where seen in commit messages
320 359
321### Commit Details360### Commit Details
@@ -381,7 +420,7 @@ A automated maintenance release without impact to the public API. · 59 unmodified lines
381 420
382 - 25 commits contributed to the release over the course of 30 calendar days.421 - 25 commits contributed to the release over the course of 30 calendar days.
383 - 92 days passed between releases.422 - 92 days passed between releases.
384 - 16 commits where understood as [conventional](https://www.conventionalcommits.org).423 - 16 commits were understood as [conventional](https://www.conventionalcommits.org).
385 - 3 unique issues were worked on: [#279](https://github.com/Byron/gitoxide/issues/279), [#287](https://github.com/Byron/gitoxide/issues/287), [#293](https://github.com/Byron/gitoxide/issues/293)424 - 3 unique issues were worked on: [#279](https://github.com/Byron/gitoxide/issues/279), [#287](https://github.com/Byron/gitoxide/issues/287), [#293](https://github.com/Byron/gitoxide/issues/293)
386 425
387### Commit Details426### Commit Details
@@ -433,7 +472,7 @@ A maintenance release due to reset the entire crate graph to new minor releases. · 45 unmodified lines
433 472
434 - 5 commits contributed to the release over the course of 3 calendar days.473 - 5 commits contributed to the release over the course of 3 calendar days.
435 - 3 days passed between releases.474 - 3 days passed between releases.
436 - 1 commit where understood as [conventional](https://www.conventionalcommits.org).475 - 1 commit was understood as [conventional](https://www.conventionalcommits.org).
437 - 1 unique issue was worked on: [#222](https://github.com/Byron/gitoxide/issues/222)476 - 1 unique issue was worked on: [#222](https://github.com/Byron/gitoxide/issues/222)
438 477
439## v0.7.0 (2021-10-15)478## v0.7.0 (2021-10-15)
@@ -452,7 +491,7 @@ A maintenance release due to reset the entire crate graph to new minor releases. · 12 unmodified lines
452 491
453 - 16 commits contributed to the release over the course of 11 calendar days.492 - 16 commits contributed to the release over the course of 11 calendar days.
454 - 38 days passed between releases.493 - 38 days passed between releases.
455 - 2 commits where understood as [conventional](https://www.conventionalcommits.org).494 - 2 commits were understood as [conventional](https://www.conventionalcommits.org).
456 - 1 unique issue was worked on: [#198](https://github.com/Byron/gitoxide/issues/198)495 - 1 unique issue was worked on: [#198](https://github.com/Byron/gitoxide/issues/198)
457 496
458### Commit Details497### Commit Details
@@ -494,7 +533,7 @@ A maintenance release due to reset the entire crate graph to new minor releases. · 35 unmodified lines
494 533
495 - 2 commits contributed to the release over the course of 1 calendar day.534 - 2 commits contributed to the release over the course of 1 calendar day.
496 - 20 days passed between releases.535 - 20 days passed between releases.
497 - 0 commits where understood as [conventional](https://www.conventionalcommits.org).536 - 0 commits were understood as [conventional](https://www.conventionalcommits.org).
498 - 0 issues like '(#ID)' where seen in commit messages537 - 0 issues like '(#ID)' where seen in commit messages
499 538
500### Commit Details539### Commit Details
@@ -516,7 +555,7 @@ A maintenance release due to reset the entire crate graph to new minor releases. · 15 unmodified lines
516 555
517 - 2 commits contributed to the release over the course of 1 calendar day.556 - 2 commits contributed to the release over the course of 1 calendar day.
518 - 6 days passed between releases.557 - 6 days passed between releases.
519 - 0 commits where understood as [conventional](https://www.conventionalcommits.org).558 - 0 commits were understood as [conventional](https://www.conventionalcommits.org).
520 - 0 issues like '(#ID)' where seen in commit messages559 - 0 issues like '(#ID)' where seen in commit messages
521 560
522### Commit Details561### Commit Details
@@ -538,7 +577,7 @@ A maintenance release due to reset the entire crate graph to new minor releases. · 15 unmodified lines
538 577
539 - 10 commits contributed to the release over the course of 74 calendar days.578 - 10 commits contributed to the release over the course of 74 calendar days.
540 - 102 days passed between releases.579 - 102 days passed between releases.
541 - 0 commits where understood as [conventional](https://www.conventionalcommits.org).580 - 0 commits were understood as [conventional](https://www.conventionalcommits.org).
542 - 0 issues like '(#ID)' where seen in commit messages581 - 0 issues like '(#ID)' where seen in commit messages
543 582
544### Commit Details583### Commit Details
@@ -574,7 +613,7 @@ A maintenance release due to reset the entire crate graph to new minor releases. · 29 unmodified lines
574 613
575 - 5 commits contributed to the release over the course of 16 calendar days.614 - 5 commits contributed to the release over the course of 16 calendar days.
576 - 21 days passed between releases.615 - 21 days passed between releases.
577 - 0 commits where understood as [conventional](https://www.conventionalcommits.org).616 - 0 commits were understood as [conventional](https://www.conventionalcommits.org).
578 - 0 issues like '(#ID)' where seen in commit messages617 - 0 issues like '(#ID)' where seen in commit messages
579 618
580### Commit Details619### Commit Details
@@ -599,7 +638,7 @@ A maintenance release due to reset the entire crate graph to new minor releases. · 18 unmodified lines
599 638
600 - 20 commits contributed to the release over the course of 1 calendar day.639 - 20 commits contributed to the release over the course of 1 calendar day.
601 - 86 days passed between releases.640 - 86 days passed between releases.
602 - 0 commits where understood as [conventional](https://www.conventionalcommits.org).641 - 0 commits were understood as [conventional](https://www.conventionalcommits.org).
603 - 1 unique issue was worked on: [#63](https://github.com/Byron/gitoxide/issues/63)642 - 1 unique issue was worked on: [#63](https://github.com/Byron/gitoxide/issues/63)
604 643
605### Commit Details644### Commit Details
@@ -640,7 +679,7 @@ A maintenance release due to reset the entire crate graph to new minor releases. · 34 unmodified lines
640 679
641 - 3 commits contributed to the release over the course of 26 calendar days.680 - 3 commits contributed to the release over the course of 26 calendar days.
642 - 27 days passed between releases.681 - 27 days passed between releases.
643 - 0 commits where understood as [conventional](https://www.conventionalcommits.org).682 - 0 commits were understood as [conventional](https://www.conventionalcommits.org).
644 - 0 issues like '(#ID)' where seen in commit messages683 - 0 issues like '(#ID)' where seen in commit messages
645 684
646### Commit Details685### Commit Details
@@ -662,7 +701,7 @@ A maintenance release due to reset the entire crate graph to new minor releases. · 15 unmodified lines
662<csr-read-only-do-not-edit/>701<csr-read-only-do-not-edit/>
663 702
664 - 2 commits contributed to the release.703 - 2 commits contributed to the release.
665 - 0 commits where understood as [conventional](https://www.conventionalcommits.org).704 - 0 commits were understood as [conventional](https://www.conventionalcommits.org).
666 - 0 issues like '(#ID)' where seen in commit messages705 - 0 issues like '(#ID)' where seen in commit messages
667 706
668### Commit Details707### Commit Details
@@ -683,7 +722,7 @@ A maintenance release due to reset the entire crate graph to new minor releases. · 14 unmodified lines
683<csr-read-only-do-not-edit/>722<csr-read-only-do-not-edit/>
684 723
685 - 1 commit contributed to the release.724 - 1 commit contributed to the release.
686 - 0 commits where understood as [conventional](https://www.conventionalcommits.org).725 - 0 commits were understood as [conventional](https://www.conventionalcommits.org).
687 - 0 issues like '(#ID)' where seen in commit messages726 - 0 issues like '(#ID)' where seen in commit messages
688 727
689### Commit Details728### Commit Details
git-hash/Cargo.toml+2−2
@@ -1,6 +1,6 @@
1[package]1[package]
2name = "git-hash"2name = "git-hash"
3version = "0.9.8"3version = "0.9.9"
4description = "Borrowed and owned git hash digests used to identify git objects"4description = "Borrowed and owned git hash digests used to identify git objects"
5authors = ["Sebastian Thiel <sebastian.thiel@icloud.com>"]5authors = ["Sebastian Thiel <sebastian.thiel@icloud.com>"]
6repository = "https://github.com/Byron/gitoxide"6repository = "https://github.com/Byron/gitoxide"
@@ -17,7 +17,7 @@ test = false · 10 unmodified lines
17serde1 = ["serde"]17serde1 = ["serde"]
18 18
19[dependencies]19[dependencies]
20quick-error = "2.0.0"20thiserror = "1.0.33"
21hex = "0.4.2"21hex = "0.4.2"
22serde = { version = "1.0.114", optional = true, default-features = false, features = ["derive"] }22serde = { version = "1.0.114", optional = true, default-features = false, features = ["derive"] }
23 23
git-hash/src/kind.rs+122−0
@@ -0,0 +1,122 @@
1use crate::{oid, Kind, ObjectId};
2use std::convert::TryFrom;
3use std::str::FromStr;
4
5impl Default for Kind {
6 fn default() -> Self {
7 Kind::Sha1
8 }
9}
10
11impl TryFrom<u8> for Kind {
12 type Error = u8;
13
14 fn try_from(value: u8) -> Result<Self, Self::Error> {
15 Ok(match value {
16 1 => Kind::Sha1,
17 unknown => return Err(unknown),
18 })
19 }
20}
21
22impl FromStr for Kind {
23 type Err = String;
24
25 fn from_str(s: &str) -> Result<Self, Self::Err> {
26 Ok(match s {
27 "sha1" | "SHA1" => Kind::Sha1,
28 other => return Err(other.into()),
29 })
30 }
31}
32
33impl std::fmt::Display for Kind {
34 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
35 match self {
36 Kind::Sha1 => f.write_str("SHA1"),
37 }
38 }
39}
40
41impl Kind {
42 /// Returns the shortest hash we support
43 #[inline]
44 pub const fn shortest() -> Self {
45 Self::Sha1
46 }
47
48 /// Returns the longest hash we support
49 #[inline]
50 pub const fn longest() -> Self {
51 Self::Sha1
52 }
53
54 /// Returns a buffer suitable to hold the longest possible hash in hex.
55 #[inline]
56 pub const fn hex_buf() -> [u8; Kind::longest().len_in_hex()] {
57 [0u8; Kind::longest().len_in_hex()]
58 }
59
60 /// Returns a buffer suitable to hold the longest possible hash as raw bytes.
61 #[inline]
62 pub const fn buf() -> [u8; Kind::longest().len_in_bytes()] {
63 [0u8; Kind::longest().len_in_bytes()]
64 }
65
66 /// Returns the amount of ascii-characters needed to encode this has in hex
67 #[inline]
68 pub const fn len_in_hex(&self) -> usize {
69 match self {
70 Kind::Sha1 => 40,
71 }
72 }
73 /// Returns the amount of bytes taken up by the hash of the current kind
74 #[inline]
75 pub const fn len_in_bytes(&self) -> usize {
76 match self {
77 Kind::Sha1 => 20,
78 }
79 }
80
81 /// Returns the kind of hash that would fit the given `hex_len`, or `None` if there is no fitting hash.
82 /// Note that 0 as `hex_len` fits always yields Sha1.
83 #[inline]
84 pub const fn from_hex_len(hex_len: usize) -> Option<Self> {
85 Some(match hex_len {
86 0..=40 => Kind::Sha1,
87 _ => return None,
88 })
89 }
90
91 /// Converts a size in bytes as obtained by `Kind::len_in_bytes()` into the corresponding hash kind, if possible.
92 ///
93 /// **Panics** if the hash length doesn't match a known hash.
94 ///
95 /// NOTE that this method isn't public as it shouldn't be encouraged to assume all hashes have the same length.
96 /// However, if there should be such a thing, our `oid` implementation will have to become an enum and it's pretty breaking
97 /// to the way it's currently being used as auto-dereffing doesn't work anymore. Let's hope it won't happen.
98 // TODO: make 'const' once Rust 1.57 is more readily available in projects using 'gitoxide'.
99 #[inline]
100 pub(crate) fn from_len_in_bytes(bytes: usize) -> Self {
101 match bytes {
102 20 => Kind::Sha1,
103 _ => panic!("BUG: must be called only with valid hash lengths produced by len_in_bytes()"),
104 }
105 }
106
107 /// Create a null-id of our hash kind.
108 #[inline]
109 pub fn null_ref(&self) -> &'static oid {
110 match self {
111 Kind::Sha1 => oid::null_sha1(),
112 }
113 }
114
115 /// Create a null-id of our hash kind.
116 #[inline]
117 pub const fn null(&self) -> ObjectId {
118 match self {
119 Kind::Sha1 => ObjectId::null_sha1(),
120 }
121 }
122}
git-hash/src/lib.rs+13−164
@@ -9,57 +9,23 @@ · 8 unmodified lines
9#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))]9#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))]
10#![deny(missing_docs, rust_2018_idioms, unsafe_code)]10#![deny(missing_docs, rust_2018_idioms, unsafe_code)]
11 11
12#[path = "oid.rs"]
12mod borrowed;13mod borrowed;
13
14use std::{convert::TryFrom, str::FromStr};
15
16pub use borrowed::oid;14pub use borrowed::oid;
17 15
18mod owned;16mod object_id;
19pub use owned::{prefix, ObjectId, Prefix};17pub use object_id::{decode, ObjectId};
20
21#[allow(missing_docs)]
22pub mod decode {
23 use std::str::FromStr;
24
25 use quick_error::quick_error;
26
27 use crate::owned::ObjectId;
28
29 quick_error! {
30 /// An error returned by [`ObjectId::from_40_bytes_in_hex()`]
31 #[derive(Debug)]
32 #[allow(missing_docs)]
33 pub enum Error {
34 InvalidHexEncodingLength(length: usize) {
35 display("A hash sized {} hexadecimal characters is invalid", length)
36 }
37 }
38 }
39
40 /// Hash decoding
41 impl ObjectId {
42 /// Create an instance from a `buffer` of 40 bytes encoded with hexadecimal notation.
43 ///
44 /// Such a buffer can be obtained using [`oid::write_hex_to(buffer)`][super::oid::write_hex_to()]
45 pub fn from_hex(buffer: &[u8]) -> Result<ObjectId, Error> {
46 use hex::FromHex;
47 match buffer.len() {
48 40 => Ok(ObjectId::Sha1(
49 <[u8; 20]>::from_hex(buffer).expect("our length check is correct thus we can decode hex"),
50 )),
51 len => Err(Error::InvalidHexEncodingLength(len)),
52 }
53 }
54 }
55 18
56 impl FromStr for ObjectId {19///
57 type Err = Error;20pub mod prefix;
58 21
59 fn from_str(s: &str) -> Result<Self, Self::Err> {22/// An partial owned hash possibly identifying an object uniquely,
60 Self::from_hex(s.as_bytes())23/// whose non-prefix bytes are zeroed.
61 }24#[derive(PartialEq, Eq, Hash, Ord, PartialOrd, Clone, Copy, Debug)]
62 }25#[cfg_attr(feature = "serde1", derive(serde::Serialize, serde::Deserialize))]
26pub struct Prefix {
27 bytes: ObjectId,
28 hex_len: usize,
63}29}
64 30
65/// The size of a SHA1 hash digest in bytes31/// The size of a SHA1 hash digest in bytes
@@ -73,121 +39,4 @@ pub enum Kind { · 7 unmodified lines
73 Sha1 = 1,39 Sha1 = 1,
74}40}
75 41
76impl Default for Kind {42mod kind;
77 fn default() -> Self {
78 Kind::Sha1
79 }
80}
81
82impl TryFrom<u8> for Kind {
83 type Error = u8;
84
85 fn try_from(value: u8) -> Result<Self, Self::Error> {
86 Ok(match value {
87 1 => Kind::Sha1,
88 unknown => return Err(unknown),
89 })
90 }
91}
92
93impl FromStr for Kind {
94 type Err = String;
95
96 fn from_str(s: &str) -> Result<Self, Self::Err> {
97 Ok(match s {
98 "sha1" | "SHA1" => Kind::Sha1,
99 other => return Err(other.into()),
100 })
101 }
102}
103
104impl std::fmt::Display for Kind {
105 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
106 match self {
107 Kind::Sha1 => f.write_str("SHA1"),
108 }
109 }
110}
111
112impl Kind {
113 /// Returns the shortest hash we support
114 #[inline]
115 pub const fn shortest() -> Self {
116 Self::Sha1
117 }
118
119 /// Returns the longest hash we support
120 #[inline]
121 pub const fn longest() -> Self {
122 Self::Sha1
123 }
124
125 /// Returns a buffer suitable to hold the longest possible hash in hex.
126 #[inline]
127 pub const fn hex_buf() -> [u8; Kind::longest().len_in_hex()] {
128 [0u8; Kind::longest().len_in_hex()]
129 }
130
131 /// Returns a buffer suitable to hold the longest possible hash as raw bytes.
132 #[inline]
133 pub const fn buf() -> [u8; Kind::longest().len_in_bytes()] {
134 [0u8; Kind::longest().len_in_bytes()]
135 }
136
137 /// Returns the amount of ascii-characters needed to encode this has in hex
138 #[inline]
139 pub const fn len_in_hex(&self) -> usize {
140 match self {
141 Kind::Sha1 => 40,
142 }
143 }
144 /// Returns the amount of bytes taken up by the hash of the current kind
145 #[inline]
146 pub const fn len_in_bytes(&self) -> usize {
147 match self {
148 Kind::Sha1 => 20,
149 }
150 }
151
152 /// Returns the kind of hash that would fit the given `hex_len`, or `None` if there is no fitting hash.
153 /// Note that 0 as `hex_len` fits always yields Sha1.
154 #[inline]
155 pub const fn from_hex_len(hex_len: usize) -> Option<Self> {
156 Some(match hex_len {
157 0..=40 => Kind::Sha1,
158 _ => return None,
159 })
160 }
161
162 /// Converts a size in bytes as obtained by `Kind::len_in_bytes()` into the corresponding hash kind, if possible.
163 ///
164 /// **Panics** if the hash length doesn't match a known hash.
165 ///
166 /// NOTE that this method isn't public as it shouldn't be encouraged to assume all hashes have the same length.
167 /// However, if there should be such a thing, our `oid` implementation will have to become an enum and it's pretty breaking
168 /// to the way it's currently being used as auto-dereffing doesn't work anymore. Let's hope it won't happen.
169 // TODO: make 'const' once Rust 1.57 is more readily available in projects using 'gitoxide'.
170 #[inline]
171 pub(crate) fn from_len_in_bytes(bytes: usize) -> Self {
172 match bytes {
173 20 => Kind::Sha1,
174 _ => panic!("BUG: must be called only with valid hash lengths produced by len_in_bytes()"),
175 }
176 }
177
178 /// Create a null-id of our hash kind.
179 #[inline]
180 pub fn null_ref(&self) -> &'static oid {
181 match self {
182 Kind::Sha1 => oid::null_sha1(),
183 }
184 }
185
186 /// Create a null-id of our hash kind.
187 #[inline]
188 pub const fn null(&self) -> ObjectId {
189 match self {
190 Kind::Sha1 => ObjectId::null_sha1(),
191 }
192 }
193}
git-hash/src/object_id.rsrenamed from git-hash/src/owned.rs+53−19
@@ -2,18 +2,6 @@ use std::{borrow::Borrow, convert::TryInto, fmt, ops::Deref}; · 1 unmodified line
2 2
3use crate::{borrowed::oid, Kind, SIZE_OF_SHA1_DIGEST};3use crate::{borrowed::oid, Kind, SIZE_OF_SHA1_DIGEST};
4 4
5/// An partial owned hash possibly identifying an object uniquely,
6/// whose non-prefix bytes are zeroed.
7#[derive(PartialEq, Eq, Hash, Ord, PartialOrd, Clone, Copy, Debug)]
8#[cfg_attr(feature = "serde1", derive(serde::Serialize, serde::Deserialize))]
9pub struct Prefix {
10 bytes: ObjectId,
11 hex_len: usize,
12}
13
14///
15pub mod prefix;
16
17/// An owned hash identifying objects, most commonly Sha15/// An owned hash identifying objects, most commonly Sha1
18#[derive(PartialEq, Eq, Hash, Ord, PartialOrd, Clone, Copy)]6#[derive(PartialEq, Eq, Hash, Ord, PartialOrd, Clone, Copy)]
19#[cfg_attr(feature = "serde1", derive(serde::Serialize, serde::Deserialize))]7#[cfg_attr(feature = "serde1", derive(serde::Serialize, serde::Deserialize))]
@@ -22,15 +10,49 @@ pub enum ObjectId { · 2 unmodified lines
22 Sha1([u8; SIZE_OF_SHA1_DIGEST]),10 Sha1([u8; SIZE_OF_SHA1_DIGEST]),
23}11}
24 12
25impl std::fmt::Debug for ObjectId {13#[allow(missing_docs)]
26 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {14pub mod decode {
27 match self {15 use std::str::FromStr;
28 ObjectId::Sha1(_hash) => f.write_str("Sha1(")?,16
17 use crate::object_id::ObjectId;
18
19 /// An error returned by [`ObjectId::from_40_bytes_in_hex()`][crate::ObjectId::from_40_bytes_in_hex()]
20 #[derive(Debug, thiserror::Error)]
21 #[allow(missing_docs)]
22 pub enum Error {
23 #[error("A hash sized {0} hexadecimal characters is invalid")]
24 InvalidHexEncodingLength(usize),
25 #[error("Invalid character {c} at position {index}")]
26 Invalid { c: char, index: usize },
27 }
28
29 /// Hash decoding
30 impl ObjectId {
31 /// Create an instance from a `buffer` of 40 bytes encoded with hexadecimal notation.
32 ///
33 /// Such a buffer can be obtained using [`oid::write_hex_to(buffer)`][super::oid::write_hex_to()]
34 pub fn from_hex(buffer: &[u8]) -> Result<ObjectId, Error> {
35 use hex::FromHex;
36 match buffer.len() {
37 40 => Ok(ObjectId::Sha1(<[u8; 20]>::from_hex(buffer).map_err(
38 |err| match err {
39 hex::FromHexError::InvalidHexCharacter { c, index } => Error::Invalid { c, index },
40 hex::FromHexError::OddLength | hex::FromHexError::InvalidStringLength => {
41 unreachable!("BUG: This is already checked")
42 }
43 },
44 )?)),
45 len => Err(Error::InvalidHexEncodingLength(len)),
46 }
29 }47 }
30 for b in self.as_bytes() {48 }
31 write!(f, "{:02x}", b)?;49
50 impl FromStr for ObjectId {
51 type Err = Error;
52
53 fn from_str(s: &str) -> Result<Self, Self::Err> {
54 Self::from_hex(s.as_bytes())
32 }55 }
33 f.write_str(")")
34 }56 }
35}57}
36 58
@@ -110,6 +132,18 @@ impl ObjectId { · 73 unmodified lines
110 }132 }
111}133}
112 134
135impl std::fmt::Debug for ObjectId {
136 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
137 match self {
138 ObjectId::Sha1(_hash) => f.write_str("Sha1(")?,
139 }
140 for b in self.as_bytes() {
141 write!(f, "{:02x}", b)?;
142 }
143 f.write_str(")")
144 }
145}
146
113impl From<[u8; SIZE_OF_SHA1_DIGEST]> for ObjectId {147impl From<[u8; SIZE_OF_SHA1_DIGEST]> for ObjectId {
114 fn from(v: [u8; 20]) -> Self {148 fn from(v: [u8; 20]) -> Self {
115 Self::new_sha1(v)149 Self::new_sha1(v)
git-hash/src/oid.rsrenamed from git-hash/src/borrowed.rs+4−9
@@ -51,15 +51,10 @@ impl fmt::Debug for oid { · 50 unmodified lines
51 }51 }
52}52}
53 53
54use quick_error::quick_error;54#[derive(Debug, thiserror::Error)]
55 55pub enum Error {
56quick_error! {56 #[error("Cannot instantiate git hash from a digest of length {0}")]
57 #[derive(Debug)]57 InvalidByteSliceLength(usize),
58 pub enum Error {
59 InvalidByteSliceLength(length: usize) {
60 display("Cannot instantiate git hash from a digest of length {}", length)
61 }
62 }
63}58}
64 59
65/// Conversion60/// Conversion
git-hash/src/prefix.rsrenamed from git-hash/src/owned/prefix.rs+24−30
@@ -1,41 +1,35 @@
1use std::{cmp::Ordering, convert::TryFrom};1use std::{cmp::Ordering, convert::TryFrom};
2 2
3use quick_error::quick_error;
4
5use crate::{oid, ObjectId, Prefix};3use crate::{oid, ObjectId, Prefix};
6 4
7quick_error! {5/// The error returned by [Prefix::try_from_id()].
8 /// The error returned by [Prefix::try_from_id()][super::Prefix::try_from_id()].6#[derive(Debug, thiserror::Error)]
9 #[derive(Debug)]7#[allow(missing_docs)]
10 #[allow(missing_docs)]8pub enum Error {
11 pub enum Error {9 #[error(
12 TooShort { hex_len: usize } {10 "The minimum hex length of a short object id is {}, got {hex_len}",
13 display("The minimum hex length of a short object id is {}, got {}", Prefix::MIN_HEX_LEN, hex_len)11 Prefix::MIN_HEX_LEN
14 }12 )]
15 TooLong { object_kind: crate::Kind, hex_len: usize } {13 TooShort { hex_len: usize },
16 display("An object of kind {} cannot be larger than {} in hex, but {} was requested", object_kind, object_kind.len_in_hex(), hex_len)14 #[error("An object of kind {} cannot be larger than {object_kind} in hex, but {hex_len} was requested")]
17 }15 TooLong { object_kind: crate::Kind, hex_len: usize },
18 }
19}16}
20 17
21///18///
22pub mod from_hex {19pub mod from_hex {
23 use quick_error::quick_error;20 /// The error returned by [Prefix::from_hex][super::Prefix::from_hex()].
24 quick_error! {21 #[derive(Debug, Eq, PartialEq, thiserror::Error)]
25 /// The error returned by [Prefix::from_hex][super::Prefix::from_hex()].22 #[allow(missing_docs)]
26 #[derive(Debug, Eq, PartialEq)]23 pub enum Error {
27 #[allow(missing_docs)]24 #[error(
28 pub enum Error {25 "The minimum hex length of a short object id is {}, got {hex_len}",
29 TooShort { hex_len: usize } {26 super::Prefix::MIN_HEX_LEN
30 display("The minimum hex length of a short object id is {}, got {}", super::Prefix::MIN_HEX_LEN, hex_len)27 )]
31 }28 TooShort { hex_len: usize },
32 TooLong { hex_len: usize } {29 #[error("An id cannot be larger than {} chars in hex, but {hex_len} was requested", crate::Kind::longest().len_in_hex())]
33 display("An id cannot be larger than {} chars in hex, but {} was requested", crate::Kind::longest().len_in_hex(), hex_len)30 TooLong { hex_len: usize },
34 }31 #[error("Invalid character {c} at position {index}")]
35 Invalid { c: char, index: usize } {32 Invalid { c: char, index: usize },
36 display("Invalid character {} at position {}", c, index)
37 }
38 }
39 }33 }
40}34}
41 35
git-hash/tests/hash.rs+4−0
@@ -0,0 +1,4 @@
1mod kind;
2mod object_id;
3mod oid;
4mod prefix;
git-hash/tests/integration.rs+0−2
@@ -1,2 +0,0 @@
1mod kind;
2mod oid;
git-hash/tests/object_id/mod.rs+43−0
@@ -0,0 +1,43 @@
1mod from_hex {
2
3 mod valid {
4 use git_hash::ObjectId;
5
6 #[test]
7 fn twenty_hex_chars_lowercase() {
8 assert!(ObjectId::from_hex(b"1234567890abcdefaaaaaaaaaaaaaaaaaaaaaaaa").is_ok());
9 }
10
11 #[test]
12 fn twenty_hex_chars_uppercase() {
13 assert!(ObjectId::from_hex(b"1234567890ABCDEFAAAAAAAAAAAAAAAAAAAAAAAA").is_ok());
14 }
15 }
16
17 mod invalid {
18 use git_hash::{decode, ObjectId};
19
20 #[test]
21 fn non_hex_characters() {
22 assert!(matches!(
23 ObjectId::from_hex(b"zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz").unwrap_err(),
24 decode::Error::Invalid { index: 0, c: 'z' }
25 ));
26 }
27
28 #[test]
29 fn too_short() {
30 assert!(matches!(
31 ObjectId::from_hex(b"abcd").unwrap_err(),
32 decode::Error::InvalidHexEncodingLength(4)
33 ));
34 }
35 #[test]
36 fn too_long() {
37 assert!(matches!(
38 ObjectId::from_hex(b"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaf").unwrap_err(),
39 decode::Error::InvalidHexEncodingLength(41)
40 ));
41 }
42 }
43}
git-hash/tests/oid/mod.rs+1−125
@@ -1,128 +1,4 @@
1mod prefix {1mod to_hex_with_len {
2 mod cmp_oid {
3 use std::cmp::Ordering;
4
5 use git_testtools::hex_to_id;
6
7 #[test]
8 fn it_detects_inequality() {
9 let prefix = git_hash::Prefix::new(hex_to_id("b920bbb055e1efb9080592a409d3975738b6efb3"), 7).unwrap();
10 assert_eq!(
11 prefix.cmp_oid(&hex_to_id("a920bbb055e1efb9080592a409d3975738b6efb3")),
12 Ordering::Greater
13 );
14 assert_eq!(
15 prefix.cmp_oid(&hex_to_id("b920bbf055e1efb9080592a409d3975738b6efb3")),
16 Ordering::Less
17 );
18 assert_eq!(prefix.to_string(), "b920bbb");
19 }
20
21 #[test]
22 fn it_detects_equality() {
23 let id = hex_to_id("a920bbb055e1efb9080592a409d3975738b6efb3");
24 let prefix = git_hash::Prefix::new(id, 6).unwrap();
25 assert_eq!(prefix.cmp_oid(&id), Ordering::Equal);
26 assert_eq!(
27 prefix.cmp_oid(&hex_to_id("a920bbffffffffffffffffffffffffffffffffff")),
28 Ordering::Equal
29 );
30 assert_eq!(prefix.to_string(), "a920bb");
31 }
32 }
33 mod new {
34 use std::cmp::Ordering;
35
36 use git_hash::{Kind, ObjectId};
37 use git_testtools::hex_to_id;
38
39 #[test]
40 fn various_valid_inputs() {
41 let oid_hex = "abcdefabcdefabcdefabcdefabcdefabcdefabcd";
42 let oid = hex_to_id(oid_hex);
43
44 for hex_len in 4..oid.kind().len_in_hex() {
45 let mut expected = String::from(&oid_hex[..hex_len]);
46 let num_of_zeros = oid.kind().len_in_hex() - hex_len;
47 expected.extend(std::iter::repeat('0').take(num_of_zeros));
48 let prefix = git_hash::Prefix::new(oid, hex_len).unwrap();
49 assert_eq!(prefix.as_oid().to_hex().to_string(), expected, "{}", hex_len);
50 assert_eq!(prefix.hex_len(), hex_len);
51 assert_eq!(prefix.cmp_oid(&oid), Ordering::Equal);
52 }
53 }
54
55 #[test]
56 fn errors_if_hex_len_is_longer_than_oid_len_in_hex() {
57 let kind = Kind::Sha1;
58 assert!(matches!(
59 git_hash::Prefix::new(ObjectId::null(kind), kind.len_in_hex() + 1),
60 Err(git_hash::prefix::Error::TooLong { .. })
61 ));
62 }
63
64 #[test]
65 fn errors_if_hex_len_is_too_short() {
66 let kind = Kind::Sha1;
67 assert!(matches!(
68 git_hash::Prefix::new(ObjectId::null(kind), 3),
69 Err(git_hash::prefix::Error::TooShort { .. })
70 ));
71 }
72 }
73
74 mod try_from {
75 use std::{cmp::Ordering, convert::TryFrom};
76
77 use git_hash::{prefix::from_hex::Error, Prefix};
78 use git_testtools::hex_to_id;
79
80 #[test]
81 fn id_8_chars() {
82 let oid_hex = "abcdefabcdefabcdefabcdefabcdefabcdefabcd";
83 let input = "abcdef";
84
85 let expected = hex_to_id(oid_hex);
86 let actual = Prefix::try_from(input).expect("No errors");
87 assert_eq!(actual.cmp_oid(&expected), Ordering::Equal);
88 }
89
90 #[test]
91 fn id_9_chars() {
92 let oid_hex = "abcdefabcdefabcdefabcdefabcdefabcdefabcd";
93 let input = "abcdefa";
94
95 let expected = hex_to_id(oid_hex);
96 let actual = Prefix::try_from(input).expect("No errors");
97 assert_eq!(actual.cmp_oid(&expected), Ordering::Equal);
98 }
99 #[test]
100 fn id_to_short() {
101 let input = "ab";
102 let expected = Error::TooShort { hex_len: 2 };
103 let actual = Prefix::try_from(input).unwrap_err();
104 assert_eq!(actual, expected);
105 }
106
107 #[test]
108 fn id_to_long() {
109 let input = "abcdefabcdefabcdefabcdefabcdefabcdefabcd123123123123123123";
110 let expected = Error::TooLong { hex_len: 58 };
111 let actual = Prefix::try_from(input).unwrap_err();
112 assert_eq!(actual, expected);
113 }
114
115 #[test]
116 fn invalid_chars() {
117 let input = "abcdfOsd";
118 let expected = Error::Invalid { c: 'O', index: 5 };
119 let actual = Prefix::try_from(input).unwrap_err();
120 assert_eq!(actual, expected);
121 }
122 }
123}
124
125mod short_hex {
126 #[test]2 #[test]
127 fn display_entire_range_sha1() {3 fn display_entire_range_sha1() {
128 let id_hex = "0123456789abcdef123456789abcdef123456789";4 let id_hex = "0123456789abcdef123456789abcdef123456789";
git-hash/tests/prefix/mod.rs+123−0
@@ -0,0 +1,123 @@
1mod cmp_oid {
2 use std::cmp::Ordering;
3
4 use git_testtools::hex_to_id;
5
6 #[test]
7 fn it_detects_inequality() {
8 let prefix = git_hash::Prefix::new(hex_to_id("b920bbb055e1efb9080592a409d3975738b6efb3"), 7).unwrap();
9 assert_eq!(
10 prefix.cmp_oid(&hex_to_id("a920bbb055e1efb9080592a409d3975738b6efb3")),
11 Ordering::Greater
12 );
13 assert_eq!(
14 prefix.cmp_oid(&hex_to_id("b920bbf055e1efb9080592a409d3975738b6efb3")),
15 Ordering::Less
16 );
17 assert_eq!(prefix.to_string(), "b920bbb");
18 }
19
20 #[test]
21 fn it_detects_equality() {
22 let id = hex_to_id("a920bbb055e1efb9080592a409d3975738b6efb3");
23 let prefix = git_hash::Prefix::new(id, 6).unwrap();
24 assert_eq!(prefix.cmp_oid(&id), Ordering::Equal);
25 assert_eq!(
26 prefix.cmp_oid(&hex_to_id("a920bbffffffffffffffffffffffffffffffffff")),
27 Ordering::Equal
28 );
29 assert_eq!(prefix.to_string(), "a920bb");
30 }
31}
32
33mod new {
34 use std::cmp::Ordering;
35
36 use git_hash::{Kind, ObjectId};
37 use git_testtools::hex_to_id;
38
39 #[test]
40 fn various_valid_inputs() {
41 let oid_hex = "abcdefabcdefabcdefabcdefabcdefabcdefabcd";
42 let oid = hex_to_id(oid_hex);
43
44 for hex_len in 4..oid.kind().len_in_hex() {
45 let mut expected = String::from(&oid_hex[..hex_len]);
46 let num_of_zeros = oid.kind().len_in_hex() - hex_len;
47 expected.extend(std::iter::repeat('0').take(num_of_zeros));
48 let prefix = git_hash::Prefix::new(oid, hex_len).unwrap();
49 assert_eq!(prefix.as_oid().to_hex().to_string(), expected, "{}", hex_len);
50 assert_eq!(prefix.hex_len(), hex_len);
51 assert_eq!(prefix.cmp_oid(&oid), Ordering::Equal);
52 }
53 }
54
55 #[test]
56 fn errors_if_hex_len_is_longer_than_oid_len_in_hex() {
57 let kind = Kind::Sha1;
58 assert!(matches!(
59 git_hash::Prefix::new(ObjectId::null(kind), kind.len_in_hex() + 1),
60 Err(git_hash::prefix::Error::TooLong { .. })
61 ));
62 }
63
64 #[test]
65 fn errors_if_hex_len_is_too_short() {
66 let kind = Kind::Sha1;
67 assert!(matches!(
68 git_hash::Prefix::new(ObjectId::null(kind), 3),
69 Err(git_hash::prefix::Error::TooShort { .. })
70 ));
71 }
72}
73
74mod try_from {
75 use std::{cmp::Ordering, convert::TryFrom};
76
77 use git_hash::prefix::from_hex::Error;
78 use git_hash::Prefix;
79 use git_testtools::hex_to_id;
80
81 #[test]
82 fn id_8_chars() {
83 let oid_hex = "abcdefabcdefabcdefabcdefabcdefabcdefabcd";
84 let input = "abcdef";
85
86 let expected = hex_to_id(oid_hex);
87 let actual = Prefix::try_from(input).expect("No errors");
88 assert_eq!(actual.cmp_oid(&expected), Ordering::Equal);
89 }
90
91 #[test]
92 fn id_9_chars() {
93 let oid_hex = "abcdefabcdefabcdefabcdefabcdefabcdefabcd";
94 let input = "abcdefa";
95
96 let expected = hex_to_id(oid_hex);
97 let actual = Prefix::try_from(input).expect("No errors");
98 assert_eq!(actual.cmp_oid(&expected), Ordering::Equal);
99 }
100 #[test]
101 fn id_to_short() {
102 let input = "ab";
103 let expected = Error::TooShort { hex_len: 2 };
104 let actual = Prefix::try_from(input).unwrap_err();
105 assert_eq!(actual, expected);
106 }
107
108 #[test]
109 fn id_to_long() {
110 let input = "abcdefabcdefabcdefabcdefabcdefabcdefabcd123123123123123123";
111 let expected = Error::TooLong { hex_len: 58 };
112 let actual = Prefix::try_from(input).unwrap_err();
113 assert_eq!(actual, expected);
114 }
115
116 #[test]
117 fn invalid_chars() {
118 let input = "abcdfOsd";
119 let expected = Error::Invalid { c: 'O', index: 5 };
120 let actual = Prefix::try_from(input).unwrap_err();
121 assert_eq!(actual, expected);
122 }
123}
git-index/Cargo.toml+1−1
@@ -31,7 +31,7 @@ internal-testing-to-avoid-being-run-by-cargo-test-all = [] · 30 unmodified lines
31 31
32[dependencies]32[dependencies]
33git-features = { version = "^0.22.3", path = "../git-features", features = ["rustsha1", "progress"] }33git-features = { version = "^0.22.3", path = "../git-features", features = ["rustsha1", "progress"] }
34git-hash = { version = "^0.9.8", path = "../git-hash" }34git-hash = { version = "^0.9.9", path = "../git-hash" }
35git-bitmap = { version = "^0.1.2", path = "../git-bitmap" }35git-bitmap = { version = "^0.1.2", path = "../git-bitmap" }
36git-object = { version = "^0.20.2", path = "../git-object" }36git-object = { version = "^0.20.2", path = "../git-object" }
37 37
git-object/Cargo.toml+1−1
@@ -22,7 +22,7 @@ verbose-object-parsing-errors = ["nom/std"] · 21 unmodified lines
22 22
23[dependencies]23[dependencies]
24git-features = { version = "^0.22.2", path = "../git-features", features = ["rustsha1"] }24git-features = { version = "^0.22.2", path = "../git-features", features = ["rustsha1"] }
25git-hash = { version = "^0.9.8", path = "../git-hash" }25git-hash = { version = "^0.9.9", path = "../git-hash" }
26git-validate = { version = "^0.5.5", path = "../git-validate" }26git-validate = { version = "^0.5.5", path = "../git-validate" }
27git-actor = { version = "^0.11.3", path = "../git-actor" }27git-actor = { version = "^0.11.3", path = "../git-actor" }
28 28
git-odb/Cargo.toml+1−1
@@ -28,8 +28,8 @@ required-features = [] · 27 unmodified lines
28 28
29[dependencies]29[dependencies]
30git-features = { version = "^0.22.3", path = "../git-features", features = ["rustsha1", "walkdir", "zlib", "crc32" ] }30git-features = { version = "^0.22.3", path = "../git-features", features = ["rustsha1", "walkdir", "zlib", "crc32" ] }
31git-hash = { version = "^0.9.9", path = "../git-hash" }
31git-path = { version = "^0.4.2", path = "../git-path" }32git-path = { version = "^0.4.2", path = "../git-path" }
32git-hash = { version = "^0.9.8", path = "../git-hash" }
33git-quote = { version = "^0.2.1", path = "../git-quote" }33git-quote = { version = "^0.2.1", path = "../git-quote" }
34git-object = { version = "^0.20.3", path = "../git-object" }34git-object = { version = "^0.20.3", path = "../git-object" }
35git-pack = { version = "^0.22.0", path = "../git-pack" }35git-pack = { version = "^0.22.0", path = "../git-pack" }
git-pack/Cargo.toml+1−1
@@ -38,8 +38,8 @@ required-features = ["internal-testing-to-avoid-being-run-by-cargo-test-all"] · 37 unmodified lines
38 38
39[dependencies]39[dependencies]
40git-features = { version = "^0.22.3", path = "../git-features", features = ["crc32", "rustsha1", "progress", "zlib"] }40git-features = { version = "^0.22.3", path = "../git-features", features = ["crc32", "rustsha1", "progress", "zlib"] }
41git-hash = { version = "^0.9.8", path = "../git-hash" }
42git-path = { version = "^0.4.2", path = "../git-path" }41git-path = { version = "^0.4.2", path = "../git-path" }
42git-hash = { version = "^0.9.9", path = "../git-hash" }
43git-chunk = { version = "^0.3.1", path = "../git-chunk" }43git-chunk = { version = "^0.3.1", path = "../git-chunk" }
44git-object = { version = "^0.20.3", path = "../git-object" }44git-object = { version = "^0.20.3", path = "../git-object" }
45git-traverse = { version = "^0.16.3", path = "../git-traverse" }45git-traverse = { version = "^0.16.3", path = "../git-traverse" }
git-protocol/Cargo.toml+1−1
@@ -41,7 +41,7 @@ required-features = ["async-client"] · 40 unmodified lines
41[dependencies]41[dependencies]
42git-features = { version = "^0.22.3", path = "../git-features", features = ["progress"] }42git-features = { version = "^0.22.3", path = "../git-features", features = ["progress"] }
43git-transport = { version = "^0.19.3", path = "../git-transport" }43git-transport = { version = "^0.19.3", path = "../git-transport" }
44git-hash = { version = "^0.9.8", path = "../git-hash" }44git-hash = { version = "^0.9.9", path = "../git-hash" }
45git-credentials = { version = "^0.4.0", path = "../git-credentials" }45git-credentials = { version = "^0.4.0", path = "../git-credentials" }
46 46
47thiserror = "1.0.32"47thiserror = "1.0.32"
git-ref/Cargo.toml+1−1
@@ -25,7 +25,7 @@ required-features = ["internal-testing-git-features-parallel"] · 24 unmodified lines
25[dependencies]25[dependencies]
26git-features = { version = "^0.22.3", path = "../git-features", features = ["walkdir"]}26git-features = { version = "^0.22.3", path = "../git-features", features = ["walkdir"]}
27git-path = { version = "^0.4.2", path = "../git-path" }27git-path = { version = "^0.4.2", path = "../git-path" }
28git-hash = { version = "^0.9.8", path = "../git-hash" }28git-hash = { version = "^0.9.9", path = "../git-hash" }
29git-object = { version = "^0.20.3", path = "../git-object" }29git-object = { version = "^0.20.3", path = "../git-object" }
30git-validate = { version = "^0.5.5", path = "../git-validate" }30git-validate = { version = "^0.5.5", path = "../git-validate" }
31git-actor = { version = "^0.11.3", path = "../git-actor" }31git-actor = { version = "^0.11.3", path = "../git-actor" }
git-refspec/Cargo.toml+1−1
@@ -14,7 +14,7 @@ doctest = false · 13 unmodified lines
14[dependencies]14[dependencies]
15git-revision = { version = "^0.4.4", path = "../git-revision" }15git-revision = { version = "^0.4.4", path = "../git-revision" }
16git-validate = { version = "^0.5.5", path = "../git-validate" }16git-validate = { version = "^0.5.5", path = "../git-validate" }
17git-hash = { version = "^0.9.8", path = "../git-hash" }17git-hash = { version = "^0.9.9", path = "../git-hash" }
18 18
19bstr = { version = "0.2.13", default-features = false, features = ["std"]}19bstr = { version = "0.2.13", default-features = false, features = ["std"]}
20thiserror = "1.0.26"20thiserror = "1.0.26"
git-repository/Cargo.toml+1−1
@@ -87,7 +87,7 @@ git-refspec = { version = "^0.1.1", path = "../git-refspec" } · 86 unmodified lines
87 87
88git-config = { version = "^0.7.1", path = "../git-config" }88git-config = { version = "^0.7.1", path = "../git-config" }
89git-odb = { version = "^0.32.0", path = "../git-odb" }89git-odb = { version = "^0.32.0", path = "../git-odb" }
90git-hash = { version = "^0.9.8", path = "../git-hash" }90git-hash = { version = "^0.9.9", path = "../git-hash" }
91git-object = { version = "^0.20.3", path = "../git-object" }91git-object = { version = "^0.20.3", path = "../git-object" }
92git-actor = { version = "^0.11.4", path = "../git-actor" }92git-actor = { version = "^0.11.4", path = "../git-actor" }
93git-pack = { version = "^0.22.0", path = "../git-pack", features = ["object-cache-dynamic"] }93git-pack = { version = "^0.22.0", path = "../git-pack", features = ["object-cache-dynamic"] }
git-repository/src/repository/identity.rs+3−2
@@ -1,4 +1,5 @@
1use std::borrow::Cow;1use std::borrow::Cow;
2use std::time::SystemTime;
2 3
3use crate::{bstr::BString, permission};4use crate::{bstr::BString, permission};
4 5
@@ -125,13 +126,13 @@ impl Personas { · 120 unmodified lines
125 committer_email = committer_email.or_else(|| env_var("GIT_COMMITTER_EMAIL"));126 committer_email = committer_email.or_else(|| env_var("GIT_COMMITTER_EMAIL"));
126 committer_date = std::env::var("GIT_COMMITTER_DATE")127 committer_date = std::env::var("GIT_COMMITTER_DATE")
127 .ok()128 .ok()
128 .and_then(|date| git_date::parse(&date));129 .and_then(|date| git_date::parse(&date, Some(SystemTime::now())).ok());
129 130
130 author_name = author_name.or_else(|| env_var("GIT_AUTHOR_NAME"));131 author_name = author_name.or_else(|| env_var("GIT_AUTHOR_NAME"));
131 author_email = author_email.or_else(|| env_var("GIT_AUTHOR_EMAIL"));132 author_email = author_email.or_else(|| env_var("GIT_AUTHOR_EMAIL"));
132 author_date = std::env::var("GIT_AUTHOR_DATE")133 author_date = std::env::var("GIT_AUTHOR_DATE")
133 .ok()134 .ok()
134 .and_then(|date| git_date::parse(&date));135 .and_then(|date| git_date::parse(&date, Some(SystemTime::now())).ok());
135 136
136 user_email = user_email.or_else(|| env_var("EMAIL")); // NOTE: we don't have permission for this specific one…137 user_email = user_email.or_else(|| env_var("EMAIL")); // NOTE: we don't have permission for this specific one…
137 }138 }
git-revision/Cargo.toml+1−1
@@ -16,7 +16,7 @@ doctest = false · 15 unmodified lines
16serde1 = [ "serde", "git-hash/serde1", "git-object/serde1" ]16serde1 = [ "serde", "git-hash/serde1", "git-object/serde1" ]
17 17
18[dependencies]18[dependencies]
19git-hash = { version = "^0.9.8", path = "../git-hash" }19git-hash = { version = "^0.9.9", path = "../git-hash" }
20git-object = { version = "^0.20.2", path = "../git-object" }20git-object = { version = "^0.20.2", path = "../git-object" }
21git-date = { version = "^0.1.0", path = "../git-date" }21git-date = { version = "^0.1.0", path = "../git-date" }
22 22
git-revision/src/spec/parse/function.rs+11−3
@@ -1,3 +1,4 @@
1use std::time::SystemTime;
1use std::{convert::TryInto, str::FromStr};2use std::{convert::TryInto, str::FromStr};
2 3
3use bstr::{BStr, BString, ByteSlice, ByteVec};4use bstr::{BStr, BString, ByteSlice, ByteVec};
@@ -434,9 +435,16 @@ where · 430 unmodified lines
434 } else if has_ref_or_implied_name {435 } else if has_ref_or_implied_name {
435 let time = nav436 let time = nav
436 .to_str()437 .to_str()
437 .ok()438 .map_err(|_| Error::Time {
438 .and_then(git_date::parse)439 input: nav.into(),
439 .ok_or_else(|| Error::Time { input: nav.into() })?;440 source: None,
441 })
442 .and_then(|date| {
443 git_date::parse(date, Some(SystemTime::now())).map_err(|err| Error::Time {
444 input: nav.into(),
445 source: err.into(),
446 })
447 })?;
440 delegate448 delegate
441 .reflog(delegate::ReflogLookup::Date(time))449 .reflog(delegate::ReflogLookup::Date(time))
442 .ok_or(Error::Delegate)?;450 .ok_or(Error::Delegate)?;
git-revision/src/spec/parse/mod.rs+4−1
@@ -17,7 +17,10 @@ pub enum Error { · 16 unmodified lines
17 #[error("Cannot peel to {:?} - unknown target.", .input)]17 #[error("Cannot peel to {:?} - unknown target.", .input)]
18 InvalidObject { input: BString },18 InvalidObject { input: BString },
19 #[error("Could not parse time {:?} for revlog lookup.", .input)]19 #[error("Could not parse time {:?} for revlog lookup.", .input)]
20 Time { input: BString },20 Time {
21 input: BString,
22 source: Option<git_date::parse::Error>,
23 },
21 #[error("Sibling branches like 'upstream' or 'push' require a branch name with remote configuration, got {:?}", .name)]24 #[error("Sibling branches like 'upstream' or 'push' require a branch name with remote configuration, got {:?}", .name)]
22 SiblingBranchNeedsBranchName { name: BString },25 SiblingBranchNeedsBranchName { name: BString },
23 #[error("Reflog entries require a ref name, got {:?}", .name)]26 #[error("Reflog entries require a ref name, got {:?}", .name)]
git-revision/tests/spec/parse/anchor/at_symbol.rs+1−1
@@ -43,7 +43,7 @@ fn reflog_by_date_for_current_branch() { · 42 unmodified lines
43#[test]43#[test]
44fn reflog_by_date_with_date_parse_failure() {44fn reflog_by_date_with_date_parse_failure() {
45 let err = try_parse("@{foo}").unwrap_err();45 let err = try_parse("@{foo}").unwrap_err();
46 assert!(matches!(err, spec::parse::Error::Time {input} if input == "foo"));46 assert!(matches!(err, spec::parse::Error::Time {input, source} if input == "foo" && source.is_some()));
47}47}
48 48
49#[test]49#[test]
git-traverse/Cargo.toml+1−1
@@ -12,7 +12,7 @@ include = ["src/**/*"] · 11 unmodified lines
12doctest = false12doctest = false
13 13
14[dependencies]14[dependencies]
15git-hash = { version = "^0.9.8", path = "../git-hash" }15git-hash = { version = "^0.9.9", path = "../git-hash" }
16git-object = { version = "^0.20.3", path = "../git-object" }16git-object = { version = "^0.20.3", path = "../git-object" }
17thiserror = "1.0.32"17thiserror = "1.0.32"
18hash_hasher = "2.0.3"18hash_hasher = "2.0.3"
git-worktree/Cargo.toml+1−1
@@ -30,7 +30,7 @@ internal-testing-to-avoid-being-run-by-cargo-test-all = [] · 29 unmodified lines
30 30
31[dependencies]31[dependencies]
32git-index = { version = "^0.4.3", path = "../git-index" }32git-index = { version = "^0.4.3", path = "../git-index" }
33git-hash = { version = "^0.9.8", path = "../git-hash" }33git-hash = { version = "^0.9.9", path = "../git-hash" }
34git-object = { version = "^0.20.2", path = "../git-object" }34git-object = { version = "^0.20.2", path = "../git-object" }
35git-glob = { version = "^0.3.2", path = "../git-glob" }35git-glob = { version = "^0.3.2", path = "../git-glob" }
36git-path = { version = "^0.4.2", path = "../git-path" }36git-path = { version = "^0.4.2", path = "../git-path" }
tests/tools/Cargo.toml+1−1
@@ -14,7 +14,7 @@ path = "src/main.rs" · 13 unmodified lines
14doctest = false14doctest = false
15 15
16[dependencies]16[dependencies]
17git-hash = { version = "^0.9.8", path = "../../git-hash" }17git-hash = { version = "^0.9.9", path = "../../git-hash" }
18git-lock = { version = "^2.0.0", path = "../../git-lock" }18git-lock = { version = "^2.0.0", path = "../../git-lock" }
19git-discover = { version = "^0.4.2", path = "../../git-discover" }19git-discover = { version = "^0.4.2", path = "../../git-discover" }
20git-attributes = { version = "^0.3.3", path = "../../git-attributes" }20git-attributes = { version = "^0.3.3", path = "../../git-attributes" }