Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ jobs:
shell: bash
- run: cargo test --locked
- run: cargo test --features https,ssh
- run: cargo test --features unstable-sha256
- run: cargo run -p systest
- run: cargo run -p systest --features unstable-sha256
- run: cargo test -p git2-curl
Expand Down
12 changes: 12 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,18 @@ url = "2.5.4"

[features]
unstable = []
# Experimental SHA256 OID support,
# reflecting upstream libgit2's GIT_EXPERIMENTAL_SHA256.
#
# This is an ABI-breaking change.
# Future releases with this feature may introduce breakages without notice
# Use at your own risk.
#
# Library authors:
# DO NOT enable this feature by default in your dependencies.
# Due to Cargo's additive features,
# downstream users cannot deactivate it once enabled.
unstable-sha256 = ["libgit2-sys/unstable-sha256"]
default = []
ssh = ["libgit2-sys/ssh", "cred"]
https = ["libgit2-sys/https", "openssl-sys", "openssl-probe", "cred"]
Expand Down
8 changes: 7 additions & 1 deletion examples/diff.rs
Original file line number Diff line number Diff line change
Expand Up @@ -319,7 +319,13 @@ fn tree_to_treeish<'a>(

fn resolve_blob<'a>(repo: &'a Repository, arg: Option<&String>) -> Result<Option<Blob<'a>>, Error> {
let arg = match arg {
Some(s) => Oid::from_str(s)?,
Some(s) => {
#[cfg(not(feature = "unstable-sha256"))]
let oid = Oid::from_str(s)?;
#[cfg(feature = "unstable-sha256")]
let oid = Oid::from_str(s, repo.object_format())?;
oid
}
None => return Ok(None),
};
repo.find_blob(arg).map(|b| Some(b))
Expand Down
20 changes: 20 additions & 0 deletions examples/init.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#![deny(warnings)]

use clap::Parser;
use git2::ObjectFormat;
use git2::{Error, Repository, RepositoryInitMode, RepositoryInitOptions};
use std::path::{Path, PathBuf};

Expand All @@ -40,6 +41,9 @@ struct Args {
#[structopt(name = "perms", long = "shared")]
/// permissions to create the repository with
flag_shared: Option<String>,
#[structopt(name = "object-format", long, value_parser = parse_object_format)]
/// object format to use (sha1 or sha256, requires unstable-sha256 feature)
flag_object_format: Option<ObjectFormat>,
}

fn run(args: &Args) -> Result<(), Error> {
Expand All @@ -48,6 +52,7 @@ fn run(args: &Args) -> Result<(), Error> {
&& args.flag_template.is_none()
&& args.flag_shared.is_none()
&& args.flag_separate_git_dir.is_none()
&& args.flag_object_format.is_none()
{
Repository::init(&path)?
} else {
Expand All @@ -68,6 +73,12 @@ fn run(args: &Args) -> Result<(), Error> {
if let Some(ref s) = args.flag_shared {
opts.mode(parse_shared(s)?);
}

#[cfg(feature = "unstable-sha256")]
if let Some(format) = args.flag_object_format {
opts.object_format(format);
}

Repository::init_opts(&path, &opts)?
};

Expand Down Expand Up @@ -136,6 +147,15 @@ fn parse_shared(shared: &str) -> Result<RepositoryInitMode, Error> {
}
}

fn parse_object_format(format: &str) -> Result<ObjectFormat, Error> {
match format {
"sha1" => Ok(ObjectFormat::Sha1),
#[cfg(feature = "unstable-sha256")]
"sha256" => Ok(ObjectFormat::Sha256),
_ => Err(Error::from_str("object format must be 'sha1' or 'sha256'")),
}
}

fn main() {
let args = Args::parse();
match run(&args) {
Expand Down
4 changes: 4 additions & 0 deletions libgit2-sys/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2304,6 +2304,8 @@ extern "C" {
pub fn git_repository_index(out: *mut *mut git_index, repo: *mut git_repository) -> c_int;
pub fn git_repository_set_index(repo: *mut git_repository, index: *mut git_index) -> c_int;

pub fn git_repository_oid_type(repo: *mut git_repository) -> git_oid_t;

pub fn git_repository_message(buf: *mut git_buf, repo: *mut git_repository) -> c_int;

pub fn git_repository_message_remove(repo: *mut git_repository) -> c_int;
Expand Down Expand Up @@ -2435,7 +2437,9 @@ extern "C" {
pub fn git_oid_cmp(a: *const git_oid, b: *const git_oid) -> c_int;
pub fn git_oid_equal(a: *const git_oid, b: *const git_oid) -> c_int;
pub fn git_oid_streq(id: *const git_oid, str: *const c_char) -> c_int;
#[deprecated = "use `git_oid_is_zero`"]
pub fn git_oid_iszero(id: *const git_oid) -> c_int;
pub fn git_oid_is_zero(id: *const git_oid) -> c_int;

// error
pub fn git_error_last() -> *const git_error;
Expand Down
4 changes: 1 addition & 3 deletions src/blob.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,9 +95,7 @@ impl<'repo> BlobWriter<'repo> {
pub fn commit(mut self) -> Result<Oid, Error> {
// After commit we already doesn't need cleanup on drop
self.need_cleanup = false;
let mut raw = raw::git_oid {
id: [0; raw::GIT_OID_MAX_SIZE],
};
let mut raw = crate::util::zeroed_raw_oid();
unsafe {
try_call!(raw::git_blob_create_fromstream_commit(&mut raw, self.raw));
Ok(Binding::from_raw(&raw as *const _))
Expand Down
8 changes: 2 additions & 6 deletions src/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -723,9 +723,7 @@ impl TreeUpdateBuilder {
self.paths.push(path);
self.updates.push(raw::git_tree_update {
action: raw::GIT_TREE_UPDATE_REMOVE,
id: raw::git_oid {
id: [0; raw::GIT_OID_MAX_SIZE],
},
id: crate::util::zeroed_raw_oid(),
filemode: raw::GIT_FILEMODE_UNREADABLE,
path: path_ptr,
});
Expand Down Expand Up @@ -754,9 +752,7 @@ impl TreeUpdateBuilder {
///
/// The baseline tree must exist in the specified repository.
pub fn create_updated(&mut self, repo: &Repository, baseline: &Tree<'_>) -> Result<Oid, Error> {
let mut ret = raw::git_oid {
id: [0; raw::GIT_OID_MAX_SIZE],
};
let mut ret = crate::util::zeroed_raw_oid();
unsafe {
try_call!(raw::git_tree_create_updated(
&mut ret,
Expand Down
58 changes: 51 additions & 7 deletions src/commit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -260,9 +260,7 @@ impl<'repo> Commit<'repo> {
message: Option<&str>,
tree: Option<&Tree<'repo>>,
) -> Result<Oid, Error> {
let mut raw = raw::git_oid {
id: [0; raw::GIT_OID_MAX_SIZE],
};
let mut raw = crate::util::zeroed_raw_oid();
let update_ref = crate::opt_cstr(update_ref)?;
let encoding = crate::opt_cstr(message_encoding)?;
let message = crate::opt_cstr(message)?;
Expand Down Expand Up @@ -439,10 +437,15 @@ mod tests {
assert_eq!(commit.parents().count(), 0);

let tree_header_bytes = commit.header_field_bytes("tree").unwrap();
assert_eq!(
crate::Oid::from_str(tree_header_bytes.as_str().unwrap()).unwrap(),
commit.tree_id()
);
let tree_oid = {
let str = tree_header_bytes.as_str().unwrap();
#[cfg(not(feature = "unstable-sha256"))]
let oid = crate::Oid::from_str(str).unwrap();
#[cfg(feature = "unstable-sha256")]
let oid = crate::Oid::from_str(str, repo.object_format()).unwrap();
oid
};
assert_eq!(tree_oid, commit.tree_id());
assert_eq!(commit.author().name(), Some("name"));
assert_eq!(commit.author().email(), Some("email"));
assert_eq!(commit.committer().name(), Some("name"));
Expand All @@ -469,4 +472,45 @@ mod tests {
.ok()
.unwrap();
}

#[test]
#[cfg(feature = "unstable-sha256")]
fn smoke_sha256() {
let (_td, repo) = crate::test::repo_init_sha256();
let head = repo.head().unwrap();
let target = head.target().unwrap();
let commit = repo.find_commit(target).unwrap();

// Verify SHA256 OID (32 bytes)
assert_eq!(commit.id().as_bytes().len(), 32);
assert_eq!(commit.tree_id().as_bytes().len(), 32);

assert_eq!(commit.message(), Some("initial\n\nbody"));
assert_eq!(commit.body(), Some("body"));
assert_eq!(commit.id(), target);
commit.summary().unwrap();
commit.tree().unwrap();
assert_eq!(commit.parents().count(), 0);

let tree_header_bytes = commit.header_field_bytes("tree").unwrap();
let tree_oid = {
let str = tree_header_bytes.as_str().unwrap();
let oid = crate::Oid::from_str(str, repo.object_format()).unwrap();
oid
};
assert_eq!(tree_oid, commit.tree_id());

// Create child commit with parent
let sig = repo.signature().unwrap();
let tree = repo.find_tree(commit.tree_id()).unwrap();
let id = repo
.commit(Some("HEAD"), &sig, &sig, "bar", &tree, &[&commit])
.unwrap();
let head = repo.find_commit(id).unwrap();

// Verify child commit ID is also SHA256
assert_eq!(head.id().as_bytes().len(), 32);
assert_eq!(head.parent_count(), 1);
assert_eq!(head.parent_id(0).unwrap(), commit.id());
}
}
60 changes: 51 additions & 9 deletions src/diff.rs
Original file line number Diff line number Diff line change
Expand Up @@ -289,9 +289,7 @@ impl<'repo> Diff<'repo> {

/// Create a patch ID from a diff.
pub fn patchid(&self, opts: Option<&mut DiffPatchidOptions>) -> Result<Oid, Error> {
let mut raw = raw::git_oid {
id: [0; raw::GIT_OID_MAX_SIZE],
};
let mut raw = crate::util::zeroed_raw_oid();
unsafe {
try_call!(raw::git_diff_patchid(
&mut raw,
Expand All @@ -312,16 +310,25 @@ impl Diff<'static> {
/// two trees, however there may be subtle differences. For example,
/// a patch file likely contains abbreviated object IDs, so the
/// object IDs parsed by this function will also be abbreviated.
pub fn from_buffer(buffer: &[u8]) -> Result<Diff<'static>, Error> {
pub fn from_buffer(
buffer: &[u8],
#[cfg(feature = "unstable-sha256")] format: crate::ObjectFormat,
) -> Result<Diff<'static>, Error> {
crate::init();
let mut diff: *mut raw::git_diff = std::ptr::null_mut();
let data = buffer.as_ptr() as *const c_char;
let len = buffer.len();
unsafe {
// NOTE: Doesn't depend on repo, so lifetime can be 'static
try_call!(raw::git_diff_from_buffer(
&mut diff,
buffer.as_ptr() as *const c_char,
buffer.len()
));
#[cfg(not(feature = "unstable-sha256"))]
try_call!(raw::git_diff_from_buffer(&mut diff, data, len));
#[cfg(feature = "unstable-sha256")]
{
let mut opts: raw::git_diff_parse_options = std::mem::zeroed();
opts.version = raw::GIT_DIFF_PARSE_OPTIONS_VERSION;
opts.oid_type = format.raw();
try_call!(raw::git_diff_from_buffer(&mut diff, data, len, &mut opts));
}
Ok(Diff::from_raw(diff))
}
}
Expand Down Expand Up @@ -1554,6 +1561,8 @@ impl DiffPatchidOptions {

#[cfg(test)]
mod tests {
#[cfg(feature = "unstable-sha256")]
use crate::Diff;
use crate::{DiffLineType, DiffOptions, Oid, Signature, Time};
use std::borrow::Borrow;
use std::fs::File;
Expand Down Expand Up @@ -1860,4 +1869,37 @@ mod tests {

assert_eq!(result.unwrap_err().code(), crate::ErrorCode::User);
}

#[test]
#[cfg(feature = "unstable-sha256")]
fn diff_sha256() {
let (_td, repo) = crate::test::repo_init_sha256();
let diff = repo.diff_tree_to_workdir(None, None).unwrap();
assert_eq!(diff.deltas().len(), 0);
let stats = diff.stats().unwrap();
assert_eq!(stats.insertions(), 0);
assert_eq!(stats.deletions(), 0);
assert_eq!(stats.files_changed(), 0);
let patchid = diff.patchid(None).unwrap();

// Verify SHA256 OID (32 bytes)
assert_eq!(patchid.as_bytes().len(), 32);
}

#[test]
#[cfg(feature = "unstable-sha256")]
fn diff_from_buffer_sha256() {
// Minimal patch with SHA256 OID (64 chars)
let patch = b"diff --git a/file.txt b/file.txt
index 0000000000000000000000000000000000000000000000000000000000000000..1111111111111111111111111111111111111111111111111111111111111111 100644
--- a/file.txt
+++ b/file.txt
@@ -1 +1 @@
-old
+new
";

let diff = Diff::from_buffer(patch, crate::ObjectFormat::Sha256).unwrap();
assert_eq!(diff.deltas().len(), 1);
}
}
Loading
Loading