Skip to content

Commit 165f25f

Browse files
committed
Merge remote-tracking branch 'origin/verify-commit-data'
* origin/verify-commit-data: git-ext: check commit's metadata fields meta: update to tempfile 3.4 Signed-off-by: Fintan Halpenny <[email protected]>
2 parents a887e54 + 5c1ae05 commit 165f25f

File tree

5 files changed

+134
-14
lines changed

5 files changed

+134
-14
lines changed

link-git/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ parking_lot = "0.12"
2626
pin-project = "1.0.7"
2727
regex = "1.5.4"
2828
rustc-hash = "1.1.0"
29-
tempfile = "3.3"
29+
tempfile = "3.4"
3030
thiserror = "1.0.30"
3131
tracing = "0.1"
3232
versions = "3.0.2"

link-git/t/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ anyhow = "1"
1919
bstr = "0.2"
2020
futures = "0.3"
2121
futures_ringbuf = "0.3"
22-
tempfile = "3.3"
22+
tempfile = "3.4"
2323

2424
[dev-dependencies.git2]
2525
version = "0.16.1"

radicle-git-ext/src/commit.rs

Lines changed: 58 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -40,20 +40,22 @@ pub struct Commit {
4040
}
4141

4242
impl Commit {
43-
pub fn new<I, T>(
43+
pub fn new<P, I, T>(
4444
tree: Oid,
45-
parents: Vec<Oid>,
45+
parents: P,
4646
author: Author,
4747
committer: Author,
4848
headers: Headers,
4949
message: String,
5050
trailers: I,
5151
) -> Self
5252
where
53+
P: IntoIterator<Item = Oid>,
5354
I: IntoIterator<Item = T>,
5455
OwnedTrailer: From<T>,
5556
{
5657
let trailers = trailers.into_iter().map(OwnedTrailer::from).collect();
58+
let parents = parents.into_iter().collect();
5759
Self {
5860
tree,
5961
parents,
@@ -75,9 +77,10 @@ impl Commit {
7577

7678
/// Write the given [`Commit`] to the `repo`. The resulting `Oid`
7779
/// is the identifier for this commit.
78-
pub fn write(&self, repo: &git2::Repository) -> Result<Oid, git2::Error> {
79-
let odb = repo.odb()?;
80-
odb.write(ObjectType::Commit, self.to_string().as_bytes())
80+
pub fn write(&self, repo: &git2::Repository) -> Result<Oid, error::Write> {
81+
let odb = repo.odb().map_err(error::Write::Odb)?;
82+
self.verify_for_write(&odb)?;
83+
Ok(odb.write(ObjectType::Commit, self.to_string().as_bytes())?)
8184
}
8285

8386
/// The tree [`Oid`] this commit points to.
@@ -132,6 +135,35 @@ impl Commit {
132135
pub fn trailers(&self) -> impl Iterator<Item = &OwnedTrailer> {
133136
self.trailers.iter()
134137
}
138+
139+
fn verify_for_write(&self, odb: &git2::Odb) -> Result<(), error::Write> {
140+
for parent in &self.parents {
141+
verify_object(odb, parent, ObjectType::Commit)?;
142+
}
143+
verify_object(odb, &self.tree, ObjectType::Tree)?;
144+
145+
Ok(())
146+
}
147+
}
148+
149+
fn verify_object(odb: &git2::Odb, oid: &Oid, expected: ObjectType) -> Result<(), error::Write> {
150+
use git2::{Error, ErrorClass, ErrorCode};
151+
152+
let (_, _, kind) = odb
153+
.reader(*oid)
154+
.map_err(|err| error::Write::OdbRead { oid: *oid, err })?;
155+
if kind != expected {
156+
Err(error::Write::NotCommit {
157+
oid: *oid,
158+
err: Error::new(
159+
ErrorCode::NotFound,
160+
ErrorClass::Object,
161+
format!("Object '{oid}' is not expected object type {expected}"),
162+
),
163+
})
164+
} else {
165+
Ok(())
166+
}
135167
}
136168

137169
pub mod error {
@@ -141,6 +173,26 @@ pub mod error {
141173

142174
use crate::author;
143175

176+
#[derive(Debug, Error)]
177+
pub enum Write {
178+
#[error(transparent)]
179+
Git(#[from] git2::Error),
180+
#[error("the parent '{oid}' provided is not a commit object")]
181+
NotCommit {
182+
oid: git2::Oid,
183+
#[source]
184+
err: git2::Error,
185+
},
186+
#[error("failed to access git odb")]
187+
Odb(#[source] git2::Error),
188+
#[error("failed to read '{oid}' from git odb")]
189+
OdbRead {
190+
oid: git2::Oid,
191+
#[source]
192+
err: git2::Error,
193+
},
194+
}
195+
144196
#[derive(Debug, Error)]
145197
pub enum Read {
146198
#[error(transparent)]
@@ -269,7 +321,7 @@ impl ToString for Commit {
269321

270322
writeln!(buf, "tree {}", self.tree).ok();
271323

272-
for parent in &self.parents {
324+
for parent in self.parents() {
273325
writeln!(buf, "parent {parent}").ok();
274326
}
275327

radicle-git-ext/t/src/commit.rs

Lines changed: 73 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
1-
use std::str::FromStr as _;
1+
use std::{io, str::FromStr as _};
22

3-
use radicle_git_ext::commit::Commit;
3+
use radicle_git_ext::{
4+
author::{self, Author},
5+
commit::{headers::Headers, trailers::OwnedTrailer, Commit},
6+
};
7+
use test_helpers::tempdir::WithTmpDir;
48

59
const NO_TRAILER: &str = "\
610
tree 50d6ef440728217febf9e35716d8b0296608d7f8
@@ -148,9 +152,6 @@ fn test_conversion() {
148152
assert_eq!(Commit::from_str(UNSIGNED).unwrap().to_string(), UNSIGNED);
149153
}
150154

151-
use std::io;
152-
use test_helpers::tempdir::WithTmpDir;
153-
154155
#[test]
155156
fn valid_commits() {
156157
let radicle_git = format!(
@@ -174,3 +175,70 @@ fn valid_commits() {
174175
assert!(commit.is_ok(), "Oid: {oid}, Error: {commit:?}")
175176
}
176177
}
178+
179+
#[test]
180+
fn write_valid_commit() {
181+
let repo = WithTmpDir::new(|path| {
182+
git2::Repository::init(path).map_err(|err| io::Error::new(io::ErrorKind::Other, err))
183+
})
184+
.unwrap();
185+
let author = Author {
186+
name: "Terry".to_owned(),
187+
email: "[email protected]".to_owned(),
188+
time: author::Time::new(0, 0),
189+
};
190+
let blob = repo.blob(b"The Colour of Magic").unwrap();
191+
let mut tb = repo.treebuilder(None).unwrap();
192+
tb.insert("The Colour of Magic", blob, git2::FileMode::Blob.into())
193+
.unwrap();
194+
let tree = tb.write().unwrap();
195+
let commit = repo
196+
.commit(
197+
None,
198+
&git2::Signature::try_from(&author).unwrap(),
199+
&git2::Signature::try_from(&author).unwrap(),
200+
"New beginnings",
201+
&repo.find_tree(tree).unwrap(),
202+
&[],
203+
)
204+
.unwrap();
205+
206+
let headers = Headers::new();
207+
let message = "Write Discworld".to_owned();
208+
209+
let invalid = Commit::new(
210+
tree,
211+
vec![blob],
212+
author.clone(),
213+
author.clone(),
214+
headers.clone(),
215+
message.clone(),
216+
None::<OwnedTrailer>,
217+
)
218+
.write(&repo);
219+
assert!(invalid.is_err());
220+
221+
let invalid = Commit::new(
222+
blob,
223+
vec![commit],
224+
author.clone(),
225+
author.clone(),
226+
headers.clone(),
227+
message.clone(),
228+
None::<OwnedTrailer>,
229+
)
230+
.write(&repo);
231+
assert!(invalid.is_err());
232+
233+
let valid = Commit::new(
234+
tree,
235+
vec![commit],
236+
author.clone(),
237+
author,
238+
headers,
239+
message,
240+
None::<OwnedTrailer>,
241+
)
242+
.write(&repo);
243+
assert!(valid.is_ok())
244+
}

test/test-helpers/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ log = ">= 0.4"
1717
pretty_assertions = "1.1"
1818
serde = "1"
1919
serde_json = "1"
20-
tempfile = "3.3"
20+
tempfile = "3.4"
2121
tracing = "0.1"
2222
proptest = "1"
2323

0 commit comments

Comments
 (0)