summaryrefslogtreecommitdiffstats
path: root/extra/git2/src
diff options
context:
space:
mode:
Diffstat (limited to 'extra/git2/src')
-rw-r--r--extra/git2/src/apply.rs208
-rw-r--r--extra/git2/src/attr.rs175
-rw-r--r--extra/git2/src/blame.rs379
-rw-r--r--extra/git2/src/blob.rs208
-rw-r--r--extra/git2/src/branch.rs197
-rw-r--r--extra/git2/src/buf.rs71
-rw-r--r--extra/git2/src/build.rs861
-rw-r--r--extra/git2/src/call.rs246
-rw-r--r--extra/git2/src/cert.rs191
-rw-r--r--extra/git2/src/cherrypick.rs72
-rw-r--r--extra/git2/src/commit.rs473
-rw-r--r--extra/git2/src/config.rs777
-rw-r--r--extra/git2/src/cred.rs717
-rw-r--r--extra/git2/src/describe.rs201
-rw-r--r--extra/git2/src/diff.rs1863
-rw-r--r--extra/git2/src/email.rs183
-rw-r--r--extra/git2/src/error.rs399
-rw-r--r--extra/git2/src/index.rs929
-rw-r--r--extra/git2/src/indexer.rs255
-rw-r--r--extra/git2/src/lib.rs1668
-rw-r--r--extra/git2/src/mailmap.rs134
-rw-r--r--extra/git2/src/mempack.rs49
-rw-r--r--extra/git2/src/merge.rs194
-rw-r--r--extra/git2/src/message.rs349
-rw-r--r--extra/git2/src/note.rs147
-rw-r--r--extra/git2/src/object.rs248
-rw-r--r--extra/git2/src/odb.rs729
-rw-r--r--extra/git2/src/oid.rs259
-rw-r--r--extra/git2/src/oid_array.rs52
-rw-r--r--extra/git2/src/opts.rs206
-rw-r--r--extra/git2/src/packbuilder.rs413
-rw-r--r--extra/git2/src/panic.rs33
-rw-r--r--extra/git2/src/patch.rs235
-rw-r--r--extra/git2/src/pathspec.rs368
-rw-r--r--extra/git2/src/proxy_options.rs56
-rw-r--r--extra/git2/src/push_update.rs55
-rw-r--r--extra/git2/src/rebase.rs441
-rw-r--r--extra/git2/src/reference.rs586
-rw-r--r--extra/git2/src/reflog.rs196
-rw-r--r--extra/git2/src/refspec.rs122
-rw-r--r--extra/git2/src/remote.rs1123
-rw-r--r--extra/git2/src/remote_callbacks.rs518
-rw-r--r--extra/git2/src/repo.rs4226
-rw-r--r--extra/git2/src/revert.rs69
-rw-r--r--extra/git2/src/revspec.rs34
-rw-r--r--extra/git2/src/revwalk.rs316
-rw-r--r--extra/git2/src/signature.rs189
-rw-r--r--extra/git2/src/stash.rs348
-rw-r--r--extra/git2/src/status.rs435
-rw-r--r--extra/git2/src/string_array.rs136
-rw-r--r--extra/git2/src/submodule.rs471
-rw-r--r--extra/git2/src/tag.rs234
-rw-r--r--extra/git2/src/tagforeach.rs69
-rw-r--r--extra/git2/src/test.rs89
-rw-r--r--extra/git2/src/time.rs127
-rw-r--r--extra/git2/src/tracing.rs85
-rw-r--r--extra/git2/src/transaction.rs285
-rw-r--r--extra/git2/src/transport.rs429
-rw-r--r--extra/git2/src/tree.rs570
-rw-r--r--extra/git2/src/treebuilder.rs234
-rw-r--r--extra/git2/src/util.rs342
-rw-r--r--extra/git2/src/version.rs95
-rw-r--r--extra/git2/src/worktree.rs331
63 files changed, 0 insertions, 25700 deletions
diff --git a/extra/git2/src/apply.rs b/extra/git2/src/apply.rs
deleted file mode 100644
index 34dc811a0..000000000
--- a/extra/git2/src/apply.rs
+++ /dev/null
@@ -1,208 +0,0 @@
-//! git_apply support
-//! see original: <https://github.com/libgit2/libgit2/blob/master/include/git2/apply.h>
-
-use crate::{panic, raw, util::Binding, DiffDelta, DiffHunk};
-use libc::c_int;
-use std::{ffi::c_void, mem};
-
-/// Possible application locations for git_apply
-/// see <https://libgit2.org/libgit2/#HEAD/type/git_apply_options>
-#[derive(Copy, Clone, Debug)]
-pub enum ApplyLocation {
- /// Apply the patch to the workdir
- WorkDir,
- /// Apply the patch to the index
- Index,
- /// Apply the patch to both the working directory and the index
- Both,
-}
-
-impl Binding for ApplyLocation {
- type Raw = raw::git_apply_location_t;
- unsafe fn from_raw(raw: raw::git_apply_location_t) -> Self {
- match raw {
- raw::GIT_APPLY_LOCATION_WORKDIR => Self::WorkDir,
- raw::GIT_APPLY_LOCATION_INDEX => Self::Index,
- raw::GIT_APPLY_LOCATION_BOTH => Self::Both,
- _ => panic!("Unknown git diff binary kind"),
- }
- }
- fn raw(&self) -> raw::git_apply_location_t {
- match *self {
- Self::WorkDir => raw::GIT_APPLY_LOCATION_WORKDIR,
- Self::Index => raw::GIT_APPLY_LOCATION_INDEX,
- Self::Both => raw::GIT_APPLY_LOCATION_BOTH,
- }
- }
-}
-
-/// Options to specify when applying a diff
-pub struct ApplyOptions<'cb> {
- raw: raw::git_apply_options,
- hunk_cb: Option<Box<HunkCB<'cb>>>,
- delta_cb: Option<Box<DeltaCB<'cb>>>,
-}
-
-type HunkCB<'a> = dyn FnMut(Option<DiffHunk<'_>>) -> bool + 'a;
-type DeltaCB<'a> = dyn FnMut(Option<DiffDelta<'_>>) -> bool + 'a;
-
-extern "C" fn delta_cb_c(delta: *const raw::git_diff_delta, data: *mut c_void) -> c_int {
- panic::wrap(|| unsafe {
- let delta = Binding::from_raw_opt(delta as *mut _);
-
- let payload = &mut *(data as *mut ApplyOptions<'_>);
- let callback = match payload.delta_cb {
- Some(ref mut c) => c,
- None => return -1,
- };
-
- let apply = callback(delta);
- if apply {
- 0
- } else {
- 1
- }
- })
- .unwrap_or(-1)
-}
-
-extern "C" fn hunk_cb_c(hunk: *const raw::git_diff_hunk, data: *mut c_void) -> c_int {
- panic::wrap(|| unsafe {
- let hunk = Binding::from_raw_opt(hunk);
-
- let payload = &mut *(data as *mut ApplyOptions<'_>);
- let callback = match payload.hunk_cb {
- Some(ref mut c) => c,
- None => return -1,
- };
-
- let apply = callback(hunk);
- if apply {
- 0
- } else {
- 1
- }
- })
- .unwrap_or(-1)
-}
-
-impl<'cb> ApplyOptions<'cb> {
- /// Creates a new set of empty options (zeroed).
- pub fn new() -> Self {
- let mut opts = Self {
- raw: unsafe { mem::zeroed() },
- hunk_cb: None,
- delta_cb: None,
- };
- assert_eq!(
- unsafe { raw::git_apply_options_init(&mut opts.raw, raw::GIT_APPLY_OPTIONS_VERSION) },
- 0
- );
- opts
- }
-
- fn flag(&mut self, opt: raw::git_apply_flags_t, val: bool) -> &mut Self {
- let opt = opt as u32;
- if val {
- self.raw.flags |= opt;
- } else {
- self.raw.flags &= !opt;
- }
- self
- }
-
- /// Don't actually make changes, just test that the patch applies.
- pub fn check(&mut self, check: bool) -> &mut Self {
- self.flag(raw::GIT_APPLY_CHECK, check)
- }
-
- /// When applying a patch, callback that will be made per hunk.
- pub fn hunk_callback<F>(&mut self, cb: F) -> &mut Self
- where
- F: FnMut(Option<DiffHunk<'_>>) -> bool + 'cb,
- {
- self.hunk_cb = Some(Box::new(cb) as Box<HunkCB<'cb>>);
-
- self.raw.hunk_cb = Some(hunk_cb_c);
- self.raw.payload = self as *mut _ as *mut _;
-
- self
- }
-
- /// When applying a patch, callback that will be made per delta (file).
- pub fn delta_callback<F>(&mut self, cb: F) -> &mut Self
- where
- F: FnMut(Option<DiffDelta<'_>>) -> bool + 'cb,
- {
- self.delta_cb = Some(Box::new(cb) as Box<DeltaCB<'cb>>);
-
- self.raw.delta_cb = Some(delta_cb_c);
- self.raw.payload = self as *mut _ as *mut _;
-
- self
- }
-
- /// Pointer to a raw git_stash_apply_options
- pub unsafe fn raw(&mut self) -> *const raw::git_apply_options {
- &self.raw as *const _
- }
-}
-
-#[cfg(test)]
-mod tests {
- use super::*;
- use std::{fs::File, io::Write, path::Path};
-
- #[test]
- fn smoke_test() {
- let (_td, repo) = crate::test::repo_init();
- let diff = t!(repo.diff_tree_to_workdir(None, None));
- let mut count_hunks = 0;
- let mut count_delta = 0;
- {
- let mut opts = ApplyOptions::new();
- opts.hunk_callback(|_hunk| {
- count_hunks += 1;
- true
- });
- opts.delta_callback(|_delta| {
- count_delta += 1;
- true
- });
- t!(repo.apply(&diff, ApplyLocation::Both, Some(&mut opts)));
- }
- assert_eq!(count_hunks, 0);
- assert_eq!(count_delta, 0);
- }
-
- #[test]
- fn apply_hunks_and_delta() {
- let file_path = Path::new("foo.txt");
- let (td, repo) = crate::test::repo_init();
- // create new file
- t!(t!(File::create(&td.path().join(file_path))).write_all(b"bar"));
- // stage the new file
- t!(t!(repo.index()).add_path(file_path));
- // now change workdir version
- t!(t!(File::create(&td.path().join(file_path))).write_all(b"foo\nbar"));
-
- let diff = t!(repo.diff_index_to_workdir(None, None));
- assert_eq!(diff.deltas().len(), 1);
- let mut count_hunks = 0;
- let mut count_delta = 0;
- {
- let mut opts = ApplyOptions::new();
- opts.hunk_callback(|_hunk| {
- count_hunks += 1;
- true
- });
- opts.delta_callback(|_delta| {
- count_delta += 1;
- true
- });
- t!(repo.apply(&diff, ApplyLocation::Index, Some(&mut opts)));
- }
- assert_eq!(count_delta, 1);
- assert_eq!(count_hunks, 1);
- }
-}
diff --git a/extra/git2/src/attr.rs b/extra/git2/src/attr.rs
deleted file mode 100644
index 33b1d2d4a..000000000
--- a/extra/git2/src/attr.rs
+++ /dev/null
@@ -1,175 +0,0 @@
-use crate::raw;
-use std::ptr;
-use std::str;
-
-/// All possible states of an attribute.
-///
-/// This enum is used to interpret the value returned by
-/// [`Repository::get_attr`](crate::Repository::get_attr) and
-/// [`Repository::get_attr_bytes`](crate::Repository::get_attr_bytes).
-#[derive(Debug, Clone, Copy, Eq)]
-pub enum AttrValue<'string> {
- /// The attribute is set to true.
- True,
- /// The attribute is unset (set to false).
- False,
- /// The attribute is set to a [valid UTF-8 string](prim@str).
- String(&'string str),
- /// The attribute is set to a string that might not be [valid UTF-8](prim@str).
- Bytes(&'string [u8]),
- /// The attribute is not specified.
- Unspecified,
-}
-
-macro_rules! from_value {
- ($value:expr => $string:expr) => {
- match unsafe { raw::git_attr_value($value.map_or(ptr::null(), |v| v.as_ptr().cast())) } {
- raw::GIT_ATTR_VALUE_TRUE => Self::True,
- raw::GIT_ATTR_VALUE_FALSE => Self::False,
- raw::GIT_ATTR_VALUE_STRING => $string,
- raw::GIT_ATTR_VALUE_UNSPECIFIED => Self::Unspecified,
- _ => unreachable!(),
- }
- };
-}
-
-impl<'string> AttrValue<'string> {
- /// Returns the state of an attribute by inspecting its [value](crate::Repository::get_attr)
- /// by a [string](prim@str).
- ///
- /// This function always returns [`AttrValue::String`] and never returns [`AttrValue::Bytes`]
- /// when the attribute is set to a string.
- pub fn from_string(value: Option<&'string str>) -> Self {
- from_value!(value => Self::String(value.unwrap()))
- }
-
- /// Returns the state of an attribute by inspecting its [value](crate::Repository::get_attr_bytes)
- /// by a [byte](u8) [slice].
- ///
- /// This function will perform UTF-8 validation when the attribute is set to a string, returns
- /// [`AttrValue::String`] if it's valid UTF-8 and [`AttrValue::Bytes`] otherwise.
- pub fn from_bytes(value: Option<&'string [u8]>) -> Self {
- let mut value = Self::always_bytes(value);
- if let Self::Bytes(bytes) = value {
- if let Ok(string) = str::from_utf8(bytes) {
- value = Self::String(string);
- }
- }
- value
- }
-
- /// Returns the state of an attribute just like [`AttrValue::from_bytes`], but skips UTF-8
- /// validation and always returns [`AttrValue::Bytes`] when it's set to a string.
- pub fn always_bytes(value: Option<&'string [u8]>) -> Self {
- from_value!(value => Self::Bytes(value.unwrap()))
- }
-}
-
-/// Compare two [`AttrValue`]s.
-///
-/// Note that this implementation does not differentiate between [`AttrValue::String`] and
-/// [`AttrValue::Bytes`].
-impl PartialEq for AttrValue<'_> {
- fn eq(&self, other: &AttrValue<'_>) -> bool {
- match (self, other) {
- (Self::True, AttrValue::True)
- | (Self::False, AttrValue::False)
- | (Self::Unspecified, AttrValue::Unspecified) => true,
- (AttrValue::String(string), AttrValue::Bytes(bytes))
- | (AttrValue::Bytes(bytes), AttrValue::String(string)) => string.as_bytes() == *bytes,
- (AttrValue::String(left), AttrValue::String(right)) => left == right,
- (AttrValue::Bytes(left), AttrValue::Bytes(right)) => left == right,
- _ => false,
- }
- }
-}
-
-#[cfg(test)]
-mod tests {
- use super::AttrValue;
-
- macro_rules! test_attr_value {
- ($function:ident, $variant:ident) => {
- const ATTR_TRUE: &str = "[internal]__TRUE__";
- const ATTR_FALSE: &str = "[internal]__FALSE__";
- const ATTR_UNSET: &str = "[internal]__UNSET__";
- let as_bytes = AsRef::<[u8]>::as_ref;
- // Use `matches!` here since the `PartialEq` implementation does not differentiate
- // between `String` and `Bytes`.
- assert!(matches!(
- AttrValue::$function(Some(ATTR_TRUE.as_ref())),
- AttrValue::$variant(s) if as_bytes(s) == ATTR_TRUE.as_bytes()
- ));
- assert!(matches!(
- AttrValue::$function(Some(ATTR_FALSE.as_ref())),
- AttrValue::$variant(s) if as_bytes(s) == ATTR_FALSE.as_bytes()
- ));
- assert!(matches!(
- AttrValue::$function(Some(ATTR_UNSET.as_ref())),
- AttrValue::$variant(s) if as_bytes(s) == ATTR_UNSET.as_bytes()
- ));
- assert!(matches!(
- AttrValue::$function(Some("foo".as_ref())),
- AttrValue::$variant(s) if as_bytes(s) == b"foo"
- ));
- assert!(matches!(
- AttrValue::$function(Some("bar".as_ref())),
- AttrValue::$variant(s) if as_bytes(s) == b"bar"
- ));
- assert_eq!(AttrValue::$function(None), AttrValue::Unspecified);
- };
- }
-
- #[test]
- fn attr_value_from_string() {
- test_attr_value!(from_string, String);
- }
-
- #[test]
- fn attr_value_from_bytes() {
- test_attr_value!(from_bytes, String);
- assert!(matches!(
- AttrValue::from_bytes(Some(&[0xff])),
- AttrValue::Bytes(&[0xff])
- ));
- assert!(matches!(
- AttrValue::from_bytes(Some(b"\xffoobar")),
- AttrValue::Bytes(b"\xffoobar")
- ));
- }
-
- #[test]
- fn attr_value_always_bytes() {
- test_attr_value!(always_bytes, Bytes);
- assert!(matches!(
- AttrValue::always_bytes(Some(&[0xff; 2])),
- AttrValue::Bytes(&[0xff, 0xff])
- ));
- assert!(matches!(
- AttrValue::always_bytes(Some(b"\xffoo")),
- AttrValue::Bytes(b"\xffoo")
- ));
- }
-
- #[test]
- fn attr_value_partial_eq() {
- assert_eq!(AttrValue::True, AttrValue::True);
- assert_eq!(AttrValue::False, AttrValue::False);
- assert_eq!(AttrValue::String("foo"), AttrValue::String("foo"));
- assert_eq!(AttrValue::Bytes(b"foo"), AttrValue::Bytes(b"foo"));
- assert_eq!(AttrValue::String("bar"), AttrValue::Bytes(b"bar"));
- assert_eq!(AttrValue::Bytes(b"bar"), AttrValue::String("bar"));
- assert_eq!(AttrValue::Unspecified, AttrValue::Unspecified);
- assert_ne!(AttrValue::True, AttrValue::False);
- assert_ne!(AttrValue::False, AttrValue::Unspecified);
- assert_ne!(AttrValue::Unspecified, AttrValue::True);
- assert_ne!(AttrValue::True, AttrValue::String("true"));
- assert_ne!(AttrValue::Unspecified, AttrValue::Bytes(b"unspecified"));
- assert_ne!(AttrValue::Bytes(b"false"), AttrValue::False);
- assert_ne!(AttrValue::String("unspecified"), AttrValue::Unspecified);
- assert_ne!(AttrValue::String("foo"), AttrValue::String("bar"));
- assert_ne!(AttrValue::Bytes(b"foo"), AttrValue::Bytes(b"bar"));
- assert_ne!(AttrValue::String("foo"), AttrValue::Bytes(b"bar"));
- assert_ne!(AttrValue::Bytes(b"foo"), AttrValue::String("bar"));
- }
-}
diff --git a/extra/git2/src/blame.rs b/extra/git2/src/blame.rs
deleted file mode 100644
index 4bf41fed1..000000000
--- a/extra/git2/src/blame.rs
+++ /dev/null
@@ -1,379 +0,0 @@
-use crate::util::{self, Binding};
-use crate::{raw, signature, Error, Oid, Repository, Signature};
-use libc::c_char;
-use std::iter::FusedIterator;
-use std::mem;
-use std::ops::Range;
-use std::path::Path;
-use std::{marker, ptr};
-
-/// Opaque structure to hold blame results.
-pub struct Blame<'repo> {
- raw: *mut raw::git_blame,
- _marker: marker::PhantomData<&'repo Repository>,
-}
-
-/// Structure that represents a blame hunk.
-pub struct BlameHunk<'blame> {
- raw: *mut raw::git_blame_hunk,
- _marker: marker::PhantomData<&'blame raw::git_blame>,
-}
-
-/// Blame options
-pub struct BlameOptions {
- raw: raw::git_blame_options,
-}
-
-/// An iterator over the hunks in a blame.
-pub struct BlameIter<'blame> {
- range: Range<usize>,
- blame: &'blame Blame<'blame>,
-}
-
-impl<'repo> Blame<'repo> {
- /// Get blame data for a file that has been modified in memory.
- ///
- /// Lines that differ between the buffer and the committed version are
- /// marked as having a zero OID for their final_commit_id.
- pub fn blame_buffer(&self, buffer: &[u8]) -> Result<Blame<'_>, Error> {
- let mut raw = ptr::null_mut();
-
- unsafe {
- try_call!(raw::git_blame_buffer(
- &mut raw,
- self.raw,
- buffer.as_ptr() as *const c_char,
- buffer.len()
- ));
- Ok(Binding::from_raw(raw))
- }
- }
-
- /// Gets the number of hunks that exist in the blame structure.
- pub fn len(&self) -> usize {
- unsafe { raw::git_blame_get_hunk_count(self.raw) as usize }
- }
-
- /// Return `true` is there is no hunk in the blame structure.
- pub fn is_empty(&self) -> bool {
- self.len() == 0
- }
-
- /// Gets the blame hunk at the given index.
- pub fn get_index(&self, index: usize) -> Option<BlameHunk<'_>> {
- unsafe {
- let ptr = raw::git_blame_get_hunk_byindex(self.raw(), index as u32);
- if ptr.is_null() {
- None
- } else {
- Some(BlameHunk::from_raw_const(ptr))
- }
- }
- }
-
- /// Gets the hunk that relates to the given line number in the newest
- /// commit.
- pub fn get_line(&self, lineno: usize) -> Option<BlameHunk<'_>> {
- unsafe {
- let ptr = raw::git_blame_get_hunk_byline(self.raw(), lineno);
- if ptr.is_null() {
- None
- } else {
- Some(BlameHunk::from_raw_const(ptr))
- }
- }
- }
-
- /// Returns an iterator over the hunks in this blame.
- pub fn iter(&self) -> BlameIter<'_> {
- BlameIter {
- range: 0..self.len(),
- blame: self,
- }
- }
-}
-
-impl<'blame> BlameHunk<'blame> {
- unsafe fn from_raw_const(raw: *const raw::git_blame_hunk) -> BlameHunk<'blame> {
- BlameHunk {
- raw: raw as *mut raw::git_blame_hunk,
- _marker: marker::PhantomData,
- }
- }
-
- /// Returns OID of the commit where this line was last changed
- pub fn final_commit_id(&self) -> Oid {
- unsafe { Oid::from_raw(&(*self.raw).final_commit_id) }
- }
-
- /// Returns signature of the commit.
- pub fn final_signature(&self) -> Signature<'_> {
- unsafe { signature::from_raw_const(self, (*self.raw).final_signature) }
- }
-
- /// Returns line number where this hunk begins.
- ///
- /// Note that the start line is counting from 1.
- pub fn final_start_line(&self) -> usize {
- unsafe { (*self.raw).final_start_line_number }
- }
-
- /// Returns the OID of the commit where this hunk was found.
- ///
- /// This will usually be the same as `final_commit_id`,
- /// except when `BlameOptions::track_copies_any_commit_copies` has been
- /// turned on
- pub fn orig_commit_id(&self) -> Oid {
- unsafe { Oid::from_raw(&(*self.raw).orig_commit_id) }
- }
-
- /// Returns signature of the commit.
- pub fn orig_signature(&self) -> Signature<'_> {
- unsafe { signature::from_raw_const(self, (*self.raw).orig_signature) }
- }
-
- /// Returns line number where this hunk begins.
- ///
- /// Note that the start line is counting from 1.
- pub fn orig_start_line(&self) -> usize {
- unsafe { (*self.raw).orig_start_line_number }
- }
-
- /// Returns path to the file where this hunk originated.
- ///
- /// Note: `None` could be returned for non-unicode paths on Windows.
- pub fn path(&self) -> Option<&Path> {
- unsafe {
- if let Some(bytes) = crate::opt_bytes(self, (*self.raw).orig_path) {
- Some(util::bytes2path(bytes))
- } else {
- None
- }
- }
- }
-
- /// Tests whether this hunk has been tracked to a boundary commit
- /// (the root, or the commit specified in git_blame_options.oldest_commit).
- pub fn is_boundary(&self) -> bool {
- unsafe { (*self.raw).boundary == 1 }
- }
-
- /// Returns number of lines in this hunk.
- pub fn lines_in_hunk(&self) -> usize {
- unsafe { (*self.raw).lines_in_hunk as usize }
- }
-}
-
-impl Default for BlameOptions {
- fn default() -> Self {
- Self::new()
- }
-}
-
-impl BlameOptions {
- /// Initialize options
- pub fn new() -> BlameOptions {
- unsafe {
- let mut raw: raw::git_blame_options = mem::zeroed();
- assert_eq!(
- raw::git_blame_init_options(&mut raw, raw::GIT_BLAME_OPTIONS_VERSION),
- 0
- );
-
- Binding::from_raw(&raw as *const _ as *mut _)
- }
- }
-
- fn flag(&mut self, opt: u32, val: bool) -> &mut BlameOptions {
- if val {
- self.raw.flags |= opt;
- } else {
- self.raw.flags &= !opt;
- }
- self
- }
-
- /// Track lines that have moved within a file.
- pub fn track_copies_same_file(&mut self, opt: bool) -> &mut BlameOptions {
- self.flag(raw::GIT_BLAME_TRACK_COPIES_SAME_FILE, opt)
- }
-
- /// Track lines that have moved across files in the same commit.
- pub fn track_copies_same_commit_moves(&mut self, opt: bool) -> &mut BlameOptions {
- self.flag(raw::GIT_BLAME_TRACK_COPIES_SAME_COMMIT_MOVES, opt)
- }
-
- /// Track lines that have been copied from another file that exists
- /// in the same commit.
- pub fn track_copies_same_commit_copies(&mut self, opt: bool) -> &mut BlameOptions {
- self.flag(raw::GIT_BLAME_TRACK_COPIES_SAME_COMMIT_COPIES, opt)
- }
-
- /// Track lines that have been copied from another file that exists
- /// in any commit.
- pub fn track_copies_any_commit_copies(&mut self, opt: bool) -> &mut BlameOptions {
- self.flag(raw::GIT_BLAME_TRACK_COPIES_ANY_COMMIT_COPIES, opt)
- }
-
- /// Restrict the search of commits to those reachable following only
- /// the first parents.
- pub fn first_parent(&mut self, opt: bool) -> &mut BlameOptions {
- self.flag(raw::GIT_BLAME_FIRST_PARENT, opt)
- }
-
- /// Use mailmap file to map author and committer names and email addresses
- /// to canonical real names and email addresses. The mailmap will be read
- /// from the working directory, or HEAD in a bare repository.
- pub fn use_mailmap(&mut self, opt: bool) -> &mut BlameOptions {
- self.flag(raw::GIT_BLAME_USE_MAILMAP, opt)
- }
-
- /// Ignore whitespace differences.
- pub fn ignore_whitespace(&mut self, opt: bool) -> &mut BlameOptions {
- self.flag(raw::GIT_BLAME_IGNORE_WHITESPACE, opt)
- }
-
- /// Setter for the id of the newest commit to consider.
- pub fn newest_commit(&mut self, id: Oid) -> &mut BlameOptions {
- unsafe {
- self.raw.newest_commit = *id.raw();
- }
- self
- }
-
- /// Setter for the id of the oldest commit to consider.
- pub fn oldest_commit(&mut self, id: Oid) -> &mut BlameOptions {
- unsafe {
- self.raw.oldest_commit = *id.raw();
- }
- self
- }
-
- /// The first line in the file to blame.
- pub fn min_line(&mut self, lineno: usize) -> &mut BlameOptions {
- self.raw.min_line = lineno;
- self
- }
-
- /// The last line in the file to blame.
- pub fn max_line(&mut self, lineno: usize) -> &mut BlameOptions {
- self.raw.max_line = lineno;
- self
- }
-}
-
-impl<'repo> Binding for Blame<'repo> {
- type Raw = *mut raw::git_blame;
-
- unsafe fn from_raw(raw: *mut raw::git_blame) -> Blame<'repo> {
- Blame {
- raw,
- _marker: marker::PhantomData,
- }
- }
-
- fn raw(&self) -> *mut raw::git_blame {
- self.raw
- }
-}
-
-impl<'repo> Drop for Blame<'repo> {
- fn drop(&mut self) {
- unsafe { raw::git_blame_free(self.raw) }
- }
-}
-
-impl<'blame> Binding for BlameHunk<'blame> {
- type Raw = *mut raw::git_blame_hunk;
-
- unsafe fn from_raw(raw: *mut raw::git_blame_hunk) -> BlameHunk<'blame> {
- BlameHunk {
- raw,
- _marker: marker::PhantomData,
- }
- }
-
- fn raw(&self) -> *mut raw::git_blame_hunk {
- self.raw
- }
-}
-
-impl Binding for BlameOptions {
- type Raw = *mut raw::git_blame_options;
-
- unsafe fn from_raw(opts: *mut raw::git_blame_options) -> BlameOptions {
- BlameOptions { raw: *opts }
- }
-
- fn raw(&self) -> *mut raw::git_blame_options {
- &self.raw as *const _ as *mut _
- }
-}
-
-impl<'blame> Iterator for BlameIter<'blame> {
- type Item = BlameHunk<'blame>;
- fn next(&mut self) -> Option<BlameHunk<'blame>> {
- self.range.next().and_then(|i| self.blame.get_index(i))
- }
-
- fn size_hint(&self) -> (usize, Option<usize>) {
- self.range.size_hint()
- }
-}
-
-impl<'blame> DoubleEndedIterator for BlameIter<'blame> {
- fn next_back(&mut self) -> Option<BlameHunk<'blame>> {
- self.range.next_back().and_then(|i| self.blame.get_index(i))
- }
-}
-
-impl<'blame> FusedIterator for BlameIter<'blame> {}
-
-impl<'blame> ExactSizeIterator for BlameIter<'blame> {}
-
-#[cfg(test)]
-mod tests {
- use std::fs::{self, File};
- use std::path::Path;
-
- #[test]
- fn smoke() {
- let (_td, repo) = crate::test::repo_init();
- let mut index = repo.index().unwrap();
-
- let root = repo.workdir().unwrap();
- fs::create_dir(&root.join("foo")).unwrap();
- File::create(&root.join("foo/bar")).unwrap();
- index.add_path(Path::new("foo/bar")).unwrap();
-
- let id = index.write_tree().unwrap();
- let tree = repo.find_tree(id).unwrap();
- let sig = repo.signature().unwrap();
- let id = repo.refname_to_id("HEAD").unwrap();
- let parent = repo.find_commit(id).unwrap();
- let commit = repo
- .commit(Some("HEAD"), &sig, &sig, "commit", &tree, &[&parent])
- .unwrap();
-
- let blame = repo.blame_file(Path::new("foo/bar"), None).unwrap();
-
- assert_eq!(blame.len(), 1);
- assert_eq!(blame.iter().count(), 1);
-
- let hunk = blame.get_index(0).unwrap();
- assert_eq!(hunk.final_commit_id(), commit);
- assert_eq!(hunk.final_signature().name(), sig.name());
- assert_eq!(hunk.final_signature().email(), sig.email());
- assert_eq!(hunk.final_start_line(), 1);
- assert_eq!(hunk.path(), Some(Path::new("foo/bar")));
- assert_eq!(hunk.lines_in_hunk(), 0);
- assert!(!hunk.is_boundary());
-
- let blame_buffer = blame.blame_buffer("\n".as_bytes()).unwrap();
- let line = blame_buffer.get_line(1).unwrap();
-
- assert_eq!(blame_buffer.len(), 2);
- assert_eq!(blame_buffer.iter().count(), 2);
- assert!(line.final_commit_id().is_zero());
- }
-}
diff --git a/extra/git2/src/blob.rs b/extra/git2/src/blob.rs
deleted file mode 100644
index 5c4a6ce6b..000000000
--- a/extra/git2/src/blob.rs
+++ /dev/null
@@ -1,208 +0,0 @@
-use std::io;
-use std::marker;
-use std::mem;
-use std::slice;
-
-use crate::util::Binding;
-use crate::{raw, Error, Object, Oid};
-
-/// A structure to represent a git [blob][1]
-///
-/// [1]: http://git-scm.com/book/en/Git-Internals-Git-Objects
-pub struct Blob<'repo> {
- raw: *mut raw::git_blob,
- _marker: marker::PhantomData<Object<'repo>>,
-}
-
-impl<'repo> Blob<'repo> {
- /// Get the id (SHA1) of a repository blob
- pub fn id(&self) -> Oid {
- unsafe { Binding::from_raw(raw::git_blob_id(&*self.raw)) }
- }
-
- /// Determine if the blob content is most certainly binary or not.
- pub fn is_binary(&self) -> bool {
- unsafe { raw::git_blob_is_binary(&*self.raw) == 1 }
- }
-
- /// Get the content of this blob.
- pub fn content(&self) -> &[u8] {
- unsafe {
- let data = raw::git_blob_rawcontent(&*self.raw) as *const u8;
- let len = raw::git_blob_rawsize(&*self.raw) as usize;
- slice::from_raw_parts(data, len)
- }
- }
-
- /// Get the size in bytes of the contents of this blob.
- pub fn size(&self) -> usize {
- unsafe { raw::git_blob_rawsize(&*self.raw) as usize }
- }
-
- /// Casts this Blob to be usable as an `Object`
- pub fn as_object(&self) -> &Object<'repo> {
- unsafe { &*(self as *const _ as *const Object<'repo>) }
- }
-
- /// Consumes Blob to be returned as an `Object`
- pub fn into_object(self) -> Object<'repo> {
- assert_eq!(mem::size_of_val(&self), mem::size_of::<Object<'_>>());
- unsafe { mem::transmute(self) }
- }
-}
-
-impl<'repo> Binding for Blob<'repo> {
- type Raw = *mut raw::git_blob;
-
- unsafe fn from_raw(raw: *mut raw::git_blob) -> Blob<'repo> {
- Blob {
- raw,
- _marker: marker::PhantomData,
- }
- }
- fn raw(&self) -> *mut raw::git_blob {
- self.raw
- }
-}
-
-impl<'repo> std::fmt::Debug for Blob<'repo> {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
- f.debug_struct("Blob").field("id", &self.id()).finish()
- }
-}
-
-impl<'repo> Clone for Blob<'repo> {
- fn clone(&self) -> Self {
- self.as_object().clone().into_blob().ok().unwrap()
- }
-}
-
-impl<'repo> Drop for Blob<'repo> {
- fn drop(&mut self) {
- unsafe { raw::git_blob_free(self.raw) }
- }
-}
-
-/// A structure to represent a git writestream for blobs
-pub struct BlobWriter<'repo> {
- raw: *mut raw::git_writestream,
- need_cleanup: bool,
- _marker: marker::PhantomData<Object<'repo>>,
-}
-
-impl<'repo> BlobWriter<'repo> {
- /// Finalize blob writing stream and write the blob to the object db
- 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_RAWSZ],
- };
- unsafe {
- try_call!(raw::git_blob_create_fromstream_commit(&mut raw, self.raw));
- Ok(Binding::from_raw(&raw as *const _))
- }
- }
-}
-
-impl<'repo> Binding for BlobWriter<'repo> {
- type Raw = *mut raw::git_writestream;
-
- unsafe fn from_raw(raw: *mut raw::git_writestream) -> BlobWriter<'repo> {
- BlobWriter {
- raw,
- need_cleanup: true,
- _marker: marker::PhantomData,
- }
- }
- fn raw(&self) -> *mut raw::git_writestream {
- self.raw
- }
-}
-
-impl<'repo> Drop for BlobWriter<'repo> {
- fn drop(&mut self) {
- // We need cleanup in case the stream has not been committed
- if self.need_cleanup {
- unsafe {
- if let Some(f) = (*self.raw).free {
- f(self.raw)
- }
- }
- }
- }
-}
-
-impl<'repo> io::Write for BlobWriter<'repo> {
- fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
- unsafe {
- if let Some(f) = (*self.raw).write {
- let res = f(self.raw, buf.as_ptr() as *const _, buf.len());
- if res < 0 {
- Err(io::Error::new(io::ErrorKind::Other, "Write error"))
- } else {
- Ok(buf.len())
- }
- } else {
- Err(io::Error::new(io::ErrorKind::Other, "no write callback"))
- }
- }
- }
- fn flush(&mut self) -> io::Result<()> {
- Ok(())
- }
-}
-
-#[cfg(test)]
-mod tests {
- use crate::Repository;
- use std::fs::File;
- use std::io::prelude::*;
- use std::path::Path;
- use tempfile::TempDir;
-
- #[test]
- fn buffer() {
- let td = TempDir::new().unwrap();
- let repo = Repository::init(td.path()).unwrap();
- let id = repo.blob(&[5, 4, 6]).unwrap();
- let blob = repo.find_blob(id).unwrap();
-
- assert_eq!(blob.id(), id);
- assert_eq!(blob.size(), 3);
- assert_eq!(blob.content(), [5, 4, 6]);
- assert!(blob.is_binary());
-
- repo.find_object(id, None).unwrap().as_blob().unwrap();
- repo.find_object(id, None)
- .unwrap()
- .into_blob()
- .ok()
- .unwrap();
- }
-
- #[test]
- fn path() {
- let td = TempDir::new().unwrap();
- let path = td.path().join("foo");
- File::create(&path).unwrap().write_all(&[7, 8, 9]).unwrap();
- let repo = Repository::init(td.path()).unwrap();
- let id = repo.blob_path(&path).unwrap();
- let blob = repo.find_blob(id).unwrap();
- assert_eq!(blob.content(), [7, 8, 9]);
- blob.into_object();
- }
-
- #[test]
- fn stream() {
- let td = TempDir::new().unwrap();
- let repo = Repository::init(td.path()).unwrap();
- let mut ws = repo.blob_writer(Some(Path::new("foo"))).unwrap();
- let wl = ws.write(&[10, 11, 12]).unwrap();
- assert_eq!(wl, 3);
- let id = ws.commit().unwrap();
- let blob = repo.find_blob(id).unwrap();
- assert_eq!(blob.content(), [10, 11, 12]);
- blob.into_object();
- }
-}
diff --git a/extra/git2/src/branch.rs b/extra/git2/src/branch.rs
deleted file mode 100644
index e1eba99c2..000000000
--- a/extra/git2/src/branch.rs
+++ /dev/null
@@ -1,197 +0,0 @@
-use std::ffi::CString;
-use std::marker;
-use std::ptr;
-use std::str;
-
-use crate::util::Binding;
-use crate::{raw, BranchType, Error, Reference, References};
-
-/// A structure to represent a git [branch][1]
-///
-/// A branch is currently just a wrapper to an underlying `Reference`. The
-/// reference can be accessed through the `get` and `into_reference` methods.
-///
-/// [1]: http://git-scm.com/book/en/Git-Branching-What-a-Branch-Is
-pub struct Branch<'repo> {
- inner: Reference<'repo>,
-}
-
-/// An iterator over the branches inside of a repository.
-pub struct Branches<'repo> {
- raw: *mut raw::git_branch_iterator,
- _marker: marker::PhantomData<References<'repo>>,
-}
-
-impl<'repo> Branch<'repo> {
- /// Creates Branch type from a Reference
- pub fn wrap(reference: Reference<'_>) -> Branch<'_> {
- Branch { inner: reference }
- }
-
- /// Ensure the branch name is well-formed.
- pub fn name_is_valid(name: &str) -> Result<bool, Error> {
- crate::init();
- let name = CString::new(name)?;
- let mut valid: libc::c_int = 0;
- unsafe {
- try_call!(raw::git_branch_name_is_valid(&mut valid, name.as_ptr()));
- }
- Ok(valid == 1)
- }
-
- /// Gain access to the reference that is this branch
- pub fn get(&self) -> &Reference<'repo> {
- &self.inner
- }
-
- /// Gain mutable access to the reference that is this branch
- pub fn get_mut(&mut self) -> &mut Reference<'repo> {
- &mut self.inner
- }
-
- /// Take ownership of the underlying reference.
- pub fn into_reference(self) -> Reference<'repo> {
- self.inner
- }
-
- /// Delete an existing branch reference.
- pub fn delete(&mut self) -> Result<(), Error> {
- unsafe {
- try_call!(raw::git_branch_delete(self.get().raw()));
- }
- Ok(())
- }
-
- /// Determine if the current local branch is pointed at by HEAD.
- pub fn is_head(&self) -> bool {
- unsafe { raw::git_branch_is_head(&*self.get().raw()) == 1 }
- }
-
- /// Move/rename an existing local branch reference.
- pub fn rename(&mut self, new_branch_name: &str, force: bool) -> Result<Branch<'repo>, Error> {
- let mut ret = ptr::null_mut();
- let new_branch_name = CString::new(new_branch_name)?;
- unsafe {
- try_call!(raw::git_branch_move(
- &mut ret,
- self.get().raw(),
- new_branch_name,
- force
- ));
- Ok(Branch::wrap(Binding::from_raw(ret)))
- }
- }
-
- /// Return the name of the given local or remote branch.
- ///
- /// May return `Ok(None)` if the name is not valid utf-8.
- pub fn name(&self) -> Result<Option<&str>, Error> {
- self.name_bytes().map(|s| str::from_utf8(s).ok())
- }
-
- /// Return the name of the given local or remote branch.
- pub fn name_bytes(&self) -> Result<&[u8], Error> {
- let mut ret = ptr::null();
- unsafe {
- try_call!(raw::git_branch_name(&mut ret, &*self.get().raw()));
- Ok(crate::opt_bytes(self, ret).unwrap())
- }
- }
-
- /// Return the reference supporting the remote tracking branch, given a
- /// local branch reference.
- pub fn upstream(&self) -> Result<Branch<'repo>, Error> {
- let mut ret = ptr::null_mut();
- unsafe {
- try_call!(raw::git_branch_upstream(&mut ret, &*self.get().raw()));
- Ok(Branch::wrap(Binding::from_raw(ret)))
- }
- }
-
- /// Set the upstream configuration for a given local branch.
- ///
- /// If `None` is specified, then the upstream branch is unset. The name
- /// provided is the name of the branch to set as upstream.
- pub fn set_upstream(&mut self, upstream_name: Option<&str>) -> Result<(), Error> {
- let upstream_name = crate::opt_cstr(upstream_name)?;
- unsafe {
- try_call!(raw::git_branch_set_upstream(
- self.get().raw(),
- upstream_name
- ));
- Ok(())
- }
- }
-}
-
-impl<'repo> Branches<'repo> {
- /// Creates a new iterator from the raw pointer given.
- ///
- /// This function is unsafe as it is not guaranteed that `raw` is a valid
- /// pointer.
- pub unsafe fn from_raw(raw: *mut raw::git_branch_iterator) -> Branches<'repo> {
- Branches {
- raw,
- _marker: marker::PhantomData,
- }
- }
-}
-
-impl<'repo> Iterator for Branches<'repo> {
- type Item = Result<(Branch<'repo>, BranchType), Error>;
- fn next(&mut self) -> Option<Result<(Branch<'repo>, BranchType), Error>> {
- let mut ret = ptr::null_mut();
- let mut typ = raw::GIT_BRANCH_LOCAL;
- unsafe {
- try_call_iter!(raw::git_branch_next(&mut ret, &mut typ, self.raw));
- let typ = match typ {
- raw::GIT_BRANCH_LOCAL => BranchType::Local,
- raw::GIT_BRANCH_REMOTE => BranchType::Remote,
- n => panic!("unexected branch type: {}", n),
- };
- Some(Ok((Branch::wrap(Binding::from_raw(ret)), typ)))
- }
- }
-}
-
-impl<'repo> Drop for Branches<'repo> {
- fn drop(&mut self) {
- unsafe { raw::git_branch_iterator_free(self.raw) }
- }
-}
-
-#[cfg(test)]
-mod tests {
- use crate::{Branch, BranchType};
-
- #[test]
- fn smoke() {
- let (_td, repo) = crate::test::repo_init();
- let head = repo.head().unwrap();
- let target = head.target().unwrap();
- let commit = repo.find_commit(target).unwrap();
-
- let mut b1 = repo.branch("foo", &commit, false).unwrap();
- assert!(!b1.is_head());
- repo.branch("foo2", &commit, false).unwrap();
-
- assert_eq!(repo.branches(None).unwrap().count(), 3);
- repo.find_branch("foo", BranchType::Local).unwrap();
- let mut b1 = b1.rename("bar", false).unwrap();
- assert_eq!(b1.name().unwrap(), Some("bar"));
- assert!(b1.upstream().is_err());
- b1.set_upstream(Some("main")).unwrap();
- b1.upstream().unwrap();
- b1.set_upstream(None).unwrap();
-
- b1.delete().unwrap();
- }
-
- #[test]
- fn name_is_valid() {
- assert!(Branch::name_is_valid("foo").unwrap());
- assert!(!Branch::name_is_valid("").unwrap());
- assert!(!Branch::name_is_valid("with spaces").unwrap());
- assert!(!Branch::name_is_valid("~tilde").unwrap());
- }
-}
diff --git a/extra/git2/src/buf.rs b/extra/git2/src/buf.rs
deleted file mode 100644
index fd2bcbf96..000000000
--- a/extra/git2/src/buf.rs
+++ /dev/null
@@ -1,71 +0,0 @@
-use std::ops::{Deref, DerefMut};
-use std::ptr;
-use std::slice;
-use std::str;
-
-use crate::raw;
-use crate::util::Binding;
-
-/// A structure to wrap an intermediate buffer used by libgit2.
-///
-/// A buffer can be thought of a `Vec<u8>`, but the `Vec` type is not used to
-/// avoid copying data back and forth.
-pub struct Buf {
- raw: raw::git_buf,
-}
-
-impl Default for Buf {
- fn default() -> Self {
- Self::new()
- }
-}
-
-impl Buf {
- /// Creates a new empty buffer.
- pub fn new() -> Buf {
- crate::init();
- unsafe {
- Binding::from_raw(&mut raw::git_buf {
- ptr: ptr::null_mut(),
- size: 0,
- reserved: 0,
- } as *mut _)
- }
- }
-
- /// Attempt to view this buffer as a string slice.
- ///
- /// Returns `None` if the buffer is not valid utf-8.
- pub fn as_str(&self) -> Option<&str> {
- str::from_utf8(&**self).ok()
- }
-}
-
-impl Deref for Buf {
- type Target = [u8];
- fn deref(&self) -> &[u8] {
- unsafe { slice::from_raw_parts(self.raw.ptr as *const u8, self.raw.size as usize) }
- }
-}
-
-impl DerefMut for Buf {
- fn deref_mut(&mut self) -> &mut [u8] {
- unsafe { slice::from_raw_parts_mut(self.raw.ptr as *mut u8, self.raw.size as usize) }
- }
-}
-
-impl Binding for Buf {
- type Raw = *mut raw::git_buf;
- unsafe fn from_raw(raw: *mut raw::git_buf) -> Buf {
- Buf { raw: *raw }
- }
- fn raw(&self) -> *mut raw::git_buf {
- &self.raw as *const _ as *mut _
- }
-}
-
-impl Drop for Buf {
- fn drop(&mut self) {
- unsafe { raw::git_buf_dispose(&mut self.raw) }
- }
-}
diff --git a/extra/git2/src/build.rs b/extra/git2/src/build.rs
deleted file mode 100644
index d3c95f655..000000000
--- a/extra/git2/src/build.rs
+++ /dev/null
@@ -1,861 +0,0 @@
-//! Builder-pattern objects for configuration various git operations.
-
-use libc::{c_char, c_int, c_uint, c_void, size_t};
-use std::ffi::{CStr, CString};
-use std::mem;
-use std::path::Path;
-use std::ptr;
-
-use crate::util::{self, Binding};
-use crate::{panic, raw, Error, FetchOptions, IntoCString, Oid, Repository, Tree};
-use crate::{CheckoutNotificationType, DiffFile, FileMode, Remote};
-
-/// A builder struct which is used to build configuration for cloning a new git
-/// repository.
-///
-/// # Example
-///
-/// Cloning using SSH:
-///
-/// ```no_run
-/// use git2::{Cred, Error, RemoteCallbacks};
-/// use std::env;
-/// use std::path::Path;
-///
-/// // Prepare callbacks.
-/// let mut callbacks = RemoteCallbacks::new();
-/// callbacks.credentials(|_url, username_from_url, _allowed_types| {
-/// Cred::ssh_key(
-/// username_from_url.unwrap(),
-/// None,
-/// Path::new(&format!("{}/.ssh/id_rsa", env::var("HOME").unwrap())),
-/// None,
-/// )
-/// });
-///
-/// // Prepare fetch options.
-/// let mut fo = git2::FetchOptions::new();
-/// fo.remote_callbacks(callbacks);
-///
-/// // Prepare builder.
-/// let mut builder = git2::build::RepoBuilder::new();
-/// builder.fetch_options(fo);
-///
-/// // Clone the project.
-/// builder.clone(
-/// "git@github.com:rust-lang/git2-rs.git",
-/// Path::new("/tmp/git2-rs"),
-/// );
-/// ```
-pub struct RepoBuilder<'cb> {
- bare: bool,
- branch: Option<CString>,
- local: bool,
- hardlinks: bool,
- checkout: Option<CheckoutBuilder<'cb>>,
- fetch_opts: Option<FetchOptions<'cb>>,
- clone_local: Option<CloneLocal>,
- remote_create: Option<Box<RemoteCreate<'cb>>>,
-}
-
-/// Type of callback passed to `RepoBuilder::remote_create`.
-///
-/// The second and third arguments are the remote's name and the remote's URL.
-pub type RemoteCreate<'cb> =
- dyn for<'a> FnMut(&'a Repository, &str, &str) -> Result<Remote<'a>, Error> + 'cb;
-
-/// A builder struct for git tree updates.
-///
-/// Paths passed to `remove` and `upsert` can be multi-component paths, i.e. they
-/// may contain slashes.
-///
-/// This is a higher-level tree update facility. There is also [`TreeBuilder`]
-/// which is lower-level (and operates only on one level of the tree at a time).
-///
-/// [`TreeBuilder`]: crate::TreeBuilder
-pub struct TreeUpdateBuilder {
- updates: Vec<raw::git_tree_update>,
- paths: Vec<CString>,
-}
-
-/// A builder struct for configuring checkouts of a repository.
-pub struct CheckoutBuilder<'cb> {
- their_label: Option<CString>,
- our_label: Option<CString>,
- ancestor_label: Option<CString>,
- target_dir: Option<CString>,
- paths: Vec<CString>,
- path_ptrs: Vec<*const c_char>,
- file_perm: Option<i32>,
- dir_perm: Option<i32>,
- disable_filters: bool,
- checkout_opts: u32,
- progress: Option<Box<Progress<'cb>>>,
- notify: Option<Box<Notify<'cb>>>,
- notify_flags: CheckoutNotificationType,
-}
-
-/// Checkout progress notification callback.
-///
-/// The first argument is the path for the notification, the next is the number
-/// of completed steps so far, and the final is the total number of steps.
-pub type Progress<'a> = dyn FnMut(Option<&Path>, usize, usize) + 'a;
-
-/// Checkout notifications callback.
-///
-/// The first argument is the notification type, the next is the path for the
-/// the notification, followed by the baseline diff, target diff, and workdir diff.
-///
-/// The callback must return a bool specifying whether the checkout should
-/// continue.
-pub type Notify<'a> = dyn FnMut(
- CheckoutNotificationType,
- Option<&Path>,
- Option<DiffFile<'_>>,
- Option<DiffFile<'_>>,
- Option<DiffFile<'_>>,
- ) -> bool
- + 'a;
-
-impl<'cb> Default for RepoBuilder<'cb> {
- fn default() -> Self {
- Self::new()
- }
-}
-
-/// Options that can be passed to `RepoBuilder::clone_local`.
-#[derive(Clone, Copy)]
-pub enum CloneLocal {
- /// Auto-detect (default)
- ///
- /// Here libgit2 will bypass the git-aware transport for local paths, but
- /// use a normal fetch for `file://` URLs.
- Auto = raw::GIT_CLONE_LOCAL_AUTO as isize,
-
- /// Bypass the git-aware transport even for `file://` URLs.
- Local = raw::GIT_CLONE_LOCAL as isize,
-
- /// Never bypass the git-aware transport
- None = raw::GIT_CLONE_NO_LOCAL as isize,
-
- /// Bypass the git-aware transport, but don't try to use hardlinks.
- NoLinks = raw::GIT_CLONE_LOCAL_NO_LINKS as isize,
-
- #[doc(hidden)]
- __Nonexhaustive = 0xff,
-}
-
-impl<'cb> RepoBuilder<'cb> {
- /// Creates a new repository builder with all of the default configuration.
- ///
- /// When ready, the `clone()` method can be used to clone a new repository
- /// using this configuration.
- pub fn new() -> RepoBuilder<'cb> {
- crate::init();
- RepoBuilder {
- bare: false,
- branch: None,
- local: true,
- clone_local: None,
- hardlinks: true,
- checkout: None,
- fetch_opts: None,
- remote_create: None,
- }
- }
-
- /// Indicate whether the repository will be cloned as a bare repository or
- /// not.
- pub fn bare(&mut self, bare: bool) -> &mut RepoBuilder<'cb> {
- self.bare = bare;
- self
- }
-
- /// Specify the name of the branch to check out after the clone.
- ///
- /// If not specified, the remote's default branch will be used.
- pub fn branch(&mut self, branch: &str) -> &mut RepoBuilder<'cb> {
- self.branch = Some(CString::new(branch).unwrap());
- self
- }
-
- /// Configures options for bypassing the git-aware transport on clone.
- ///
- /// Bypassing it means that instead of a fetch libgit2 will copy the object
- /// database directory instead of figuring out what it needs, which is
- /// faster. If possible, it will hardlink the files to save space.
- pub fn clone_local(&mut self, clone_local: CloneLocal) -> &mut RepoBuilder<'cb> {
- self.clone_local = Some(clone_local);
- self
- }
-
- /// Set the flag for bypassing the git aware transport mechanism for local
- /// paths.
- ///
- /// If `true`, the git-aware transport will be bypassed for local paths. If
- /// `false`, the git-aware transport will not be bypassed.
- #[deprecated(note = "use `clone_local` instead")]
- #[doc(hidden)]
- pub fn local(&mut self, local: bool) -> &mut RepoBuilder<'cb> {
- self.local = local;
- self
- }
-
- /// Set the flag for whether hardlinks are used when using a local git-aware
- /// transport mechanism.
- #[deprecated(note = "use `clone_local` instead")]
- #[doc(hidden)]
- pub fn hardlinks(&mut self, links: bool) -> &mut RepoBuilder<'cb> {
- self.hardlinks = links;
- self
- }
-
- /// Configure the checkout which will be performed by consuming a checkout
- /// builder.
- pub fn with_checkout(&mut self, checkout: CheckoutBuilder<'cb>) -> &mut RepoBuilder<'cb> {
- self.checkout = Some(checkout);
- self
- }
-
- /// Options which control the fetch, including callbacks.
- ///
- /// The callbacks are used for reporting fetch progress, and for acquiring
- /// credentials in the event they are needed.
- pub fn fetch_options(&mut self, fetch_opts: FetchOptions<'cb>) -> &mut RepoBuilder<'cb> {
- self.fetch_opts = Some(fetch_opts);
- self
- }
-
- /// Configures a callback used to create the git remote, prior to its being
- /// used to perform the clone operation.
- pub fn remote_create<F>(&mut self, f: F) -> &mut RepoBuilder<'cb>
- where
- F: for<'a> FnMut(&'a Repository, &str, &str) -> Result<Remote<'a>, Error> + 'cb,
- {
- self.remote_create = Some(Box::new(f));
- self
- }
-
- /// Clone a remote repository.
- ///
- /// This will use the options configured so far to clone the specified URL
- /// into the specified local path.
- pub fn clone(&mut self, url: &str, into: &Path) -> Result<Repository, Error> {
- let mut opts: raw::git_clone_options = unsafe { mem::zeroed() };
- unsafe {
- try_call!(raw::git_clone_init_options(
- &mut opts,
- raw::GIT_CLONE_OPTIONS_VERSION
- ));
- }
- opts.bare = self.bare as c_int;
- opts.checkout_branch = self
- .branch
- .as_ref()
- .map(|s| s.as_ptr())
- .unwrap_or(ptr::null());
-
- if let Some(ref local) = self.clone_local {
- opts.local = *local as raw::git_clone_local_t;
- } else {
- opts.local = match (self.local, self.hardlinks) {
- (true, false) => raw::GIT_CLONE_LOCAL_NO_LINKS,
- (false, _) => raw::GIT_CLONE_NO_LOCAL,
- (true, _) => raw::GIT_CLONE_LOCAL_AUTO,
- };
- }
-
- if let Some(ref mut cbs) = self.fetch_opts {
- opts.fetch_opts = cbs.raw();
- }
-
- if let Some(ref mut c) = self.checkout {
- unsafe {
- c.configure(&mut opts.checkout_opts);
- }
- }
-
- if let Some(ref mut callback) = self.remote_create {
- opts.remote_cb = Some(remote_create_cb);
- opts.remote_cb_payload = callback as *mut _ as *mut _;
- }
-
- let url = CString::new(url)?;
- // Normal file path OK (does not need Windows conversion).
- let into = into.into_c_string()?;
- let mut raw = ptr::null_mut();
- unsafe {
- try_call!(raw::git_clone(&mut raw, url, into, &opts));
- Ok(Binding::from_raw(raw))
- }
- }
-}
-
-extern "C" fn remote_create_cb(
- out: *mut *mut raw::git_remote,
- repo: *mut raw::git_repository,
- name: *const c_char,
- url: *const c_char,
- payload: *mut c_void,
-) -> c_int {
- unsafe {
- let repo = Repository::from_raw(repo);
- let code = panic::wrap(|| {
- let name = CStr::from_ptr(name).to_str().unwrap();
- let url = CStr::from_ptr(url).to_str().unwrap();
- let f = payload as *mut Box<RemoteCreate<'_>>;
- match (*f)(&repo, name, url) {
- Ok(remote) => {
- *out = crate::remote::remote_into_raw(remote);
- 0
- }
- Err(e) => e.raw_code(),
- }
- });
- mem::forget(repo);
- code.unwrap_or(-1)
- }
-}
-
-impl<'cb> Default for CheckoutBuilder<'cb> {
- fn default() -> Self {
- Self::new()
- }
-}
-
-impl<'cb> CheckoutBuilder<'cb> {
- /// Creates a new builder for checkouts with all of its default
- /// configuration.
- pub fn new() -> CheckoutBuilder<'cb> {
- crate::init();
- CheckoutBuilder {
- disable_filters: false,
- dir_perm: None,
- file_perm: None,
- path_ptrs: Vec::new(),
- paths: Vec::new(),
- target_dir: None,
- ancestor_label: None,
- our_label: None,
- their_label: None,
- checkout_opts: raw::GIT_CHECKOUT_SAFE as u32,
- progress: None,
- notify: None,
- notify_flags: CheckoutNotificationType::empty(),
- }
- }
-
- /// Indicate that this checkout should perform a dry run by checking for
- /// conflicts but not make any actual changes.
- pub fn dry_run(&mut self) -> &mut CheckoutBuilder<'cb> {
- self.checkout_opts &= !((1 << 4) - 1);
- self.checkout_opts |= raw::GIT_CHECKOUT_NONE as u32;
- self
- }
-
- /// Take any action necessary to get the working directory to match the
- /// target including potentially discarding modified files.
- pub fn force(&mut self) -> &mut CheckoutBuilder<'cb> {
- self.checkout_opts &= !((1 << 4) - 1);
- self.checkout_opts |= raw::GIT_CHECKOUT_FORCE as u32;
- self
- }
-
- /// Indicate that the checkout should be performed safely, allowing new
- /// files to be created but not overwriting existing files or changes.
- ///
- /// This is the default.
- pub fn safe(&mut self) -> &mut CheckoutBuilder<'cb> {
- self.checkout_opts &= !((1 << 4) - 1);
- self.checkout_opts |= raw::GIT_CHECKOUT_SAFE as u32;
- self
- }
-
- fn flag(&mut self, bit: raw::git_checkout_strategy_t, on: bool) -> &mut CheckoutBuilder<'cb> {
- if on {
- self.checkout_opts |= bit as u32;
- } else {
- self.checkout_opts &= !(bit as u32);
- }
- self
- }
-
- /// In safe mode, create files that don't exist.
- ///
- /// Defaults to false.
- pub fn recreate_missing(&mut self, allow: bool) -> &mut CheckoutBuilder<'cb> {
- self.flag(raw::GIT_CHECKOUT_RECREATE_MISSING, allow)
- }
-
- /// In safe mode, apply safe file updates even when there are conflicts
- /// instead of canceling the checkout.
- ///
- /// Defaults to false.
- pub fn allow_conflicts(&mut self, allow: bool) -> &mut CheckoutBuilder<'cb> {
- self.flag(raw::GIT_CHECKOUT_ALLOW_CONFLICTS, allow)
- }
-
- /// Remove untracked files from the working dir.
- ///
- /// Defaults to false.
- pub fn remove_untracked(&mut self, remove: bool) -> &mut CheckoutBuilder<'cb> {
- self.flag(raw::GIT_CHECKOUT_REMOVE_UNTRACKED, remove)
- }
-
- /// Remove ignored files from the working dir.
- ///
- /// Defaults to false.
- pub fn remove_ignored(&mut self, remove: bool) -> &mut CheckoutBuilder<'cb> {
- self.flag(raw::GIT_CHECKOUT_REMOVE_IGNORED, remove)
- }
-
- /// Only update the contents of files that already exist.
- ///
- /// If set, files will not be created or deleted.
- ///
- /// Defaults to false.
- pub fn update_only(&mut self, update: bool) -> &mut CheckoutBuilder<'cb> {
- self.flag(raw::GIT_CHECKOUT_UPDATE_ONLY, update)
- }
-
- /// Prevents checkout from writing the updated files' information to the
- /// index.
- ///
- /// Defaults to true.
- pub fn update_index(&mut self, update: bool) -> &mut CheckoutBuilder<'cb> {
- self.flag(raw::GIT_CHECKOUT_DONT_UPDATE_INDEX, !update)
- }
-
- /// Indicate whether the index and git attributes should be refreshed from
- /// disk before any operations.
- ///
- /// Defaults to true,
- pub fn refresh(&mut self, refresh: bool) -> &mut CheckoutBuilder<'cb> {
- self.flag(raw::GIT_CHECKOUT_NO_REFRESH, !refresh)
- }
-
- /// Skip files with unmerged index entries.
- ///
- /// Defaults to false.
- pub fn skip_unmerged(&mut self, skip: bool) -> &mut CheckoutBuilder<'cb> {
- self.flag(raw::GIT_CHECKOUT_SKIP_UNMERGED, skip)
- }
-
- /// Indicate whether the checkout should proceed on conflicts by using the
- /// stage 2 version of the file ("ours").
- ///
- /// Defaults to false.
- pub fn use_ours(&mut self, ours: bool) -> &mut CheckoutBuilder<'cb> {
- self.flag(raw::GIT_CHECKOUT_USE_OURS, ours)
- }
-
- /// Indicate whether the checkout should proceed on conflicts by using the
- /// stage 3 version of the file ("theirs").
- ///
- /// Defaults to false.
- pub fn use_theirs(&mut self, theirs: bool) -> &mut CheckoutBuilder<'cb> {
- self.flag(raw::GIT_CHECKOUT_USE_THEIRS, theirs)
- }
-
- /// Indicate whether ignored files should be overwritten during the checkout.
- ///
- /// Defaults to true.
- pub fn overwrite_ignored(&mut self, overwrite: bool) -> &mut CheckoutBuilder<'cb> {
- self.flag(raw::GIT_CHECKOUT_DONT_OVERWRITE_IGNORED, !overwrite)
- }
-
- /// Indicate whether a normal merge file should be written for conflicts.
- ///
- /// Defaults to false.
- pub fn conflict_style_merge(&mut self, on: bool) -> &mut CheckoutBuilder<'cb> {
- self.flag(raw::GIT_CHECKOUT_CONFLICT_STYLE_MERGE, on)
- }
-
- /// Specify for which notification types to invoke the notification
- /// callback.
- ///
- /// Defaults to none.
- pub fn notify_on(
- &mut self,
- notification_types: CheckoutNotificationType,
- ) -> &mut CheckoutBuilder<'cb> {
- self.notify_flags = notification_types;
- self
- }
-
- /// Indicates whether to include common ancestor data in diff3 format files
- /// for conflicts.
- ///
- /// Defaults to false.
- pub fn conflict_style_diff3(&mut self, on: bool) -> &mut CheckoutBuilder<'cb> {
- self.flag(raw::GIT_CHECKOUT_CONFLICT_STYLE_DIFF3, on)
- }
-
- /// Indicate whether to apply filters like CRLF conversion.
- pub fn disable_filters(&mut self, disable: bool) -> &mut CheckoutBuilder<'cb> {
- self.disable_filters = disable;
- self
- }
-
- /// Set the mode with which new directories are created.
- ///
- /// Default is 0755
- pub fn dir_perm(&mut self, perm: i32) -> &mut CheckoutBuilder<'cb> {
- self.dir_perm = Some(perm);
- self
- }
-
- /// Set the mode with which new files are created.
- ///
- /// The default is 0644 or 0755 as dictated by the blob.
- pub fn file_perm(&mut self, perm: i32) -> &mut CheckoutBuilder<'cb> {
- self.file_perm = Some(perm);
- self
- }
-
- /// Add a path to be checked out.
- ///
- /// If no paths are specified, then all files are checked out. Otherwise
- /// only these specified paths are checked out.
- pub fn path<T: IntoCString>(&mut self, path: T) -> &mut CheckoutBuilder<'cb> {
- let path = util::cstring_to_repo_path(path).unwrap();
- self.path_ptrs.push(path.as_ptr());
- self.paths.push(path);
- self
- }
-
- /// Set the directory to check out to
- pub fn target_dir(&mut self, dst: &Path) -> &mut CheckoutBuilder<'cb> {
- // Normal file path OK (does not need Windows conversion).
- self.target_dir = Some(dst.into_c_string().unwrap());
- self
- }
-
- /// The name of the common ancestor side of conflicts
- pub fn ancestor_label(&mut self, label: &str) -> &mut CheckoutBuilder<'cb> {
- self.ancestor_label = Some(CString::new(label).unwrap());
- self
- }
-
- /// The name of the common our side of conflicts
- pub fn our_label(&mut self, label: &str) -> &mut CheckoutBuilder<'cb> {
- self.our_label = Some(CString::new(label).unwrap());
- self
- }
-
- /// The name of the common their side of conflicts
- pub fn their_label(&mut self, label: &str) -> &mut CheckoutBuilder<'cb> {
- self.their_label = Some(CString::new(label).unwrap());
- self
- }
-
- /// Set a callback to receive notifications of checkout progress.
- pub fn progress<F>(&mut self, cb: F) -> &mut CheckoutBuilder<'cb>
- where
- F: FnMut(Option<&Path>, usize, usize) + 'cb,
- {
- self.progress = Some(Box::new(cb) as Box<Progress<'cb>>);
- self
- }
-
- /// Set a callback to receive checkout notifications.
- ///
- /// Callbacks are invoked prior to modifying any files on disk.
- /// Returning `false` from the callback will cancel the checkout.
- pub fn notify<F>(&mut self, cb: F) -> &mut CheckoutBuilder<'cb>
- where
- F: FnMut(
- CheckoutNotificationType,
- Option<&Path>,
- Option<DiffFile<'_>>,
- Option<DiffFile<'_>>,
- Option<DiffFile<'_>>,
- ) -> bool
- + 'cb,
- {
- self.notify = Some(Box::new(cb) as Box<Notify<'cb>>);
- self
- }
-
- /// Configure a raw checkout options based on this configuration.
- ///
- /// This method is unsafe as there is no guarantee that this structure will
- /// outlive the provided checkout options.
- pub unsafe fn configure(&mut self, opts: &mut raw::git_checkout_options) {
- opts.version = raw::GIT_CHECKOUT_OPTIONS_VERSION;
- opts.disable_filters = self.disable_filters as c_int;
- opts.dir_mode = self.dir_perm.unwrap_or(0) as c_uint;
- opts.file_mode = self.file_perm.unwrap_or(0) as c_uint;
-
- if !self.path_ptrs.is_empty() {
- opts.paths.strings = self.path_ptrs.as_ptr() as *mut _;
- opts.paths.count = self.path_ptrs.len() as size_t;
- }
-
- if let Some(ref c) = self.target_dir {
- opts.target_directory = c.as_ptr();
- }
- if let Some(ref c) = self.ancestor_label {
- opts.ancestor_label = c.as_ptr();
- }
- if let Some(ref c) = self.our_label {
- opts.our_label = c.as_ptr();
- }
- if let Some(ref c) = self.their_label {
- opts.their_label = c.as_ptr();
- }
- if self.progress.is_some() {
- opts.progress_cb = Some(progress_cb);
- opts.progress_payload = self as *mut _ as *mut _;
- }
- if self.notify.is_some() {
- opts.notify_cb = Some(notify_cb);
- opts.notify_payload = self as *mut _ as *mut _;
- opts.notify_flags = self.notify_flags.bits() as c_uint;
- }
- opts.checkout_strategy = self.checkout_opts as c_uint;
- }
-}
-
-extern "C" fn progress_cb(
- path: *const c_char,
- completed: size_t,
- total: size_t,
- data: *mut c_void,
-) {
- panic::wrap(|| unsafe {
- let payload = &mut *(data as *mut CheckoutBuilder<'_>);
- let callback = match payload.progress {
- Some(ref mut c) => c,
- None => return,
- };
- let path = if path.is_null() {
- None
- } else {
- Some(util::bytes2path(CStr::from_ptr(path).to_bytes()))
- };
- callback(path, completed as usize, total as usize)
- });
-}
-
-extern "C" fn notify_cb(
- why: raw::git_checkout_notify_t,
- path: *const c_char,
- baseline: *const raw::git_diff_file,
- target: *const raw::git_diff_file,
- workdir: *const raw::git_diff_file,
- data: *mut c_void,
-) -> c_int {
- // pack callback etc
- panic::wrap(|| unsafe {
- let payload = &mut *(data as *mut CheckoutBuilder<'_>);
- let callback = match payload.notify {
- Some(ref mut c) => c,
- None => return 0,
- };
- let path = if path.is_null() {
- None
- } else {
- Some(util::bytes2path(CStr::from_ptr(path).to_bytes()))
- };
-
- let baseline = if baseline.is_null() {
- None
- } else {
- Some(DiffFile::from_raw(baseline))
- };
-
- let target = if target.is_null() {
- None
- } else {
- Some(DiffFile::from_raw(target))
- };
-
- let workdir = if workdir.is_null() {
- None
- } else {
- Some(DiffFile::from_raw(workdir))
- };
-
- let why = CheckoutNotificationType::from_bits_truncate(why as u32);
- let keep_going = callback(why, path, baseline, target, workdir);
- if keep_going {
- 0
- } else {
- 1
- }
- })
- .unwrap_or(2)
-}
-
-unsafe impl Send for TreeUpdateBuilder {}
-
-impl Default for TreeUpdateBuilder {
- fn default() -> Self {
- Self::new()
- }
-}
-
-impl TreeUpdateBuilder {
- /// Create a new empty series of updates.
- pub fn new() -> Self {
- Self {
- updates: Vec::new(),
- paths: Vec::new(),
- }
- }
-
- /// Add an update removing the specified `path` from a tree.
- pub fn remove<T: IntoCString>(&mut self, path: T) -> &mut Self {
- let path = util::cstring_to_repo_path(path).unwrap();
- let path_ptr = path.as_ptr();
- 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_RAWSZ],
- },
- filemode: raw::GIT_FILEMODE_UNREADABLE,
- path: path_ptr,
- });
- self
- }
-
- /// Add an update setting the specified `path` to a specific Oid, whether it currently exists
- /// or not.
- ///
- /// Note that libgit2 does not support an upsert of a previously removed path, or an upsert
- /// that changes the type of an object (such as from tree to blob or vice versa).
- pub fn upsert<T: IntoCString>(&mut self, path: T, id: Oid, filemode: FileMode) -> &mut Self {
- let path = util::cstring_to_repo_path(path).unwrap();
- let path_ptr = path.as_ptr();
- self.paths.push(path);
- self.updates.push(raw::git_tree_update {
- action: raw::GIT_TREE_UPDATE_UPSERT,
- id: unsafe { *id.raw() },
- filemode: u32::from(filemode) as raw::git_filemode_t,
- path: path_ptr,
- });
- self
- }
-
- /// Create a new tree from the specified baseline and this series of updates.
- ///
- /// 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_RAWSZ],
- };
- unsafe {
- try_call!(raw::git_tree_create_updated(
- &mut ret,
- repo.raw(),
- baseline.raw(),
- self.updates.len(),
- self.updates.as_ptr()
- ));
- Ok(Binding::from_raw(&ret as *const _))
- }
- }
-}
-
-#[cfg(test)]
-mod tests {
- use super::{CheckoutBuilder, RepoBuilder, TreeUpdateBuilder};
- use crate::{CheckoutNotificationType, FileMode, Repository};
- use std::fs;
- use std::path::Path;
- use tempfile::TempDir;
-
- #[test]
- fn smoke() {
- let r = RepoBuilder::new().clone("/path/to/nowhere", Path::new("foo"));
- assert!(r.is_err());
- }
-
- #[test]
- fn smoke2() {
- let td = TempDir::new().unwrap();
- Repository::init_bare(&td.path().join("bare")).unwrap();
- let url = if cfg!(unix) {
- format!("file://{}/bare", td.path().display())
- } else {
- format!(
- "file:///{}/bare",
- td.path().display().to_string().replace("\\", "/")
- )
- };
-
- let dst = td.path().join("foo");
- RepoBuilder::new().clone(&url, &dst).unwrap();
- fs::remove_dir_all(&dst).unwrap();
- assert!(RepoBuilder::new().branch("foo").clone(&url, &dst).is_err());
- }
-
- #[test]
- fn smoke_tree_create_updated() {
- let (_tempdir, repo) = crate::test::repo_init();
- let (_, tree_id) = crate::test::commit(&repo);
- let tree = t!(repo.find_tree(tree_id));
- assert!(tree.get_name("bar").is_none());
- let foo_id = tree.get_name("foo").unwrap().id();
- let tree2_id = t!(TreeUpdateBuilder::new()
- .remove("foo")
- .upsert("bar/baz", foo_id, FileMode::Blob)
- .create_updated(&repo, &tree));
- let tree2 = t!(repo.find_tree(tree2_id));
- assert!(tree2.get_name("foo").is_none());
- let baz_id = tree2.get_path(Path::new("bar/baz")).unwrap().id();
- assert_eq!(foo_id, baz_id);
- }
-
- /// Issue regression test #365
- #[test]
- fn notify_callback() {
- let td = TempDir::new().unwrap();
- let cd = TempDir::new().unwrap();
-
- {
- let mut opts = crate::RepositoryInitOptions::new();
- opts.initial_head("main");
- let repo = Repository::init_opts(&td.path(), &opts).unwrap();
-
- let mut config = repo.config().unwrap();
- config.set_str("user.name", "name").unwrap();
- config.set_str("user.email", "email").unwrap();
-
- let mut index = repo.index().unwrap();
- let p = Path::new(td.path()).join("file");
- println!("using path {:?}", p);
- fs::File::create(&p).unwrap();
- index.add_path(&Path::new("file")).unwrap();
- let id = index.write_tree().unwrap();
-
- let tree = repo.find_tree(id).unwrap();
- let sig = repo.signature().unwrap();
- repo.commit(Some("HEAD"), &sig, &sig, "initial", &tree, &[])
- .unwrap();
- }
-
- let repo = Repository::open_bare(&td.path().join(".git")).unwrap();
- let tree = repo
- .revparse_single(&"main")
- .unwrap()
- .peel_to_tree()
- .unwrap();
- let mut index = repo.index().unwrap();
- index.read_tree(&tree).unwrap();
-
- let mut checkout_opts = CheckoutBuilder::new();
- checkout_opts.target_dir(&cd.path());
- checkout_opts.notify_on(CheckoutNotificationType::all());
- checkout_opts.notify(|_notif, _path, baseline, target, workdir| {
- assert!(baseline.is_none());
- assert_eq!(target.unwrap().path(), Some(Path::new("file")));
- assert!(workdir.is_none());
- true
- });
- repo.checkout_index(Some(&mut index), Some(&mut checkout_opts))
- .unwrap();
- }
-}
diff --git a/extra/git2/src/call.rs b/extra/git2/src/call.rs
deleted file mode 100644
index d9fd23468..000000000
--- a/extra/git2/src/call.rs
+++ /dev/null
@@ -1,246 +0,0 @@
-#![macro_use]
-use libc;
-
-use crate::Error;
-
-macro_rules! call {
- (raw::$p:ident ($($e:expr),*)) => (
- raw::$p($(crate::call::convert(&$e)),*)
- )
-}
-
-macro_rules! try_call {
- (raw::$p:ident ($($e:expr),*)) => ({
- match crate::call::c_try(raw::$p($(crate::call::convert(&$e)),*)) {
- Ok(o) => o,
- Err(e) => { crate::panic::check(); return Err(e) }
- }
- })
-}
-
-macro_rules! try_call_iter {
- ($($f:tt)*) => {
- match call!($($f)*) {
- 0 => {}
- raw::GIT_ITEROVER => return None,
- e => return Some(Err(crate::call::last_error(e)))
- }
- }
-}
-
-#[doc(hidden)]
-pub trait Convert<T> {
- fn convert(&self) -> T;
-}
-
-pub fn convert<T, U: Convert<T>>(u: &U) -> T {
- u.convert()
-}
-
-pub fn c_try(ret: libc::c_int) -> Result<libc::c_int, Error> {
- match ret {
- n if n < 0 => Err(last_error(n)),
- n => Ok(n),
- }
-}
-
-pub fn last_error(code: libc::c_int) -> Error {
- // nowadays this unwrap is safe as `Error::last_error` always returns
- // `Some`.
- Error::last_error(code).unwrap()
-}
-
-mod impls {
- use std::ffi::CString;
- use std::ptr;
-
- use libc;
-
- use crate::call::Convert;
- use crate::{raw, BranchType, ConfigLevel, Direction, ObjectType, ResetType};
- use crate::{
- AutotagOption, DiffFormat, FetchPrune, FileFavor, SubmoduleIgnore, SubmoduleUpdate,
- };
-
- impl<T: Copy> Convert<T> for T {
- fn convert(&self) -> T {
- *self
- }
- }
-
- impl Convert<libc::c_int> for bool {
- fn convert(&self) -> libc::c_int {
- *self as libc::c_int
- }
- }
- impl<'a, T> Convert<*const T> for &'a T {
- fn convert(&self) -> *const T {
- *self as *const T
- }
- }
- impl<'a, T> Convert<*mut T> for &'a mut T {
- fn convert(&self) -> *mut T {
- &**self as *const T as *mut T
- }
- }
- impl<T> Convert<*const T> for *mut T {
- fn convert(&self) -> *const T {
- *self as *const T
- }
- }
-
- impl Convert<*const libc::c_char> for CString {
- fn convert(&self) -> *const libc::c_char {
- self.as_ptr()
- }
- }
-
- impl<T, U: Convert<*const T>> Convert<*const T> for Option<U> {
- fn convert(&self) -> *const T {
- self.as_ref().map(|s| s.convert()).unwrap_or(ptr::null())
- }
- }
-
- impl<T, U: Convert<*mut T>> Convert<*mut T> for Option<U> {
- fn convert(&self) -> *mut T {
- self.as_ref()
- .map(|s| s.convert())
- .unwrap_or(ptr::null_mut())
- }
- }
-
- impl Convert<raw::git_reset_t> for ResetType {
- fn convert(&self) -> raw::git_reset_t {
- match *self {
- ResetType::Soft => raw::GIT_RESET_SOFT,
- ResetType::Hard => raw::GIT_RESET_HARD,
- ResetType::Mixed => raw::GIT_RESET_MIXED,
- }
- }
- }
-
- impl Convert<raw::git_direction> for Direction {
- fn convert(&self) -> raw::git_direction {
- match *self {
- Direction::Push => raw::GIT_DIRECTION_PUSH,
- Direction::Fetch => raw::GIT_DIRECTION_FETCH,
- }
- }
- }
-
- impl Convert<raw::git_object_t> for ObjectType {
- fn convert(&self) -> raw::git_object_t {
- match *self {
- ObjectType::Any => raw::GIT_OBJECT_ANY,
- ObjectType::Commit => raw::GIT_OBJECT_COMMIT,
- ObjectType::Tree => raw::GIT_OBJECT_TREE,
- ObjectType::Blob => raw::GIT_OBJECT_BLOB,
- ObjectType::Tag => raw::GIT_OBJECT_TAG,
- }
- }
- }
-
- impl Convert<raw::git_object_t> for Option<ObjectType> {
- fn convert(&self) -> raw::git_object_t {
- self.unwrap_or(ObjectType::Any).convert()
- }
- }
-
- impl Convert<raw::git_branch_t> for BranchType {
- fn convert(&self) -> raw::git_branch_t {
- match *self {
- BranchType::Remote => raw::GIT_BRANCH_REMOTE,
- BranchType::Local => raw::GIT_BRANCH_LOCAL,
- }
- }
- }
-
- impl Convert<raw::git_branch_t> for Option<BranchType> {
- fn convert(&self) -> raw::git_branch_t {
- self.map(|s| s.convert()).unwrap_or(raw::GIT_BRANCH_ALL)
- }
- }
-
- impl Convert<raw::git_config_level_t> for ConfigLevel {
- fn convert(&self) -> raw::git_config_level_t {
- match *self {
- ConfigLevel::ProgramData => raw::GIT_CONFIG_LEVEL_PROGRAMDATA,
- ConfigLevel::System => raw::GIT_CONFIG_LEVEL_SYSTEM,
- ConfigLevel::XDG => raw::GIT_CONFIG_LEVEL_XDG,
- ConfigLevel::Global => raw::GIT_CONFIG_LEVEL_GLOBAL,
- ConfigLevel::Local => raw::GIT_CONFIG_LEVEL_LOCAL,
- ConfigLevel::App => raw::GIT_CONFIG_LEVEL_APP,
- ConfigLevel::Highest => raw::GIT_CONFIG_HIGHEST_LEVEL,
- }
- }
- }
-
- impl Convert<raw::git_diff_format_t> for DiffFormat {
- fn convert(&self) -> raw::git_diff_format_t {
- match *self {
- DiffFormat::Patch => raw::GIT_DIFF_FORMAT_PATCH,
- DiffFormat::PatchHeader => raw::GIT_DIFF_FORMAT_PATCH_HEADER,
- DiffFormat::Raw => raw::GIT_DIFF_FORMAT_RAW,
- DiffFormat::NameOnly => raw::GIT_DIFF_FORMAT_NAME_ONLY,
- DiffFormat::NameStatus => raw::GIT_DIFF_FORMAT_NAME_STATUS,
- DiffFormat::PatchId => raw::GIT_DIFF_FORMAT_PATCH_ID,
- }
- }
- }
-
- impl Convert<raw::git_merge_file_favor_t> for FileFavor {
- fn convert(&self) -> raw::git_merge_file_favor_t {
- match *self {
- FileFavor::Normal => raw::GIT_MERGE_FILE_FAVOR_NORMAL,
- FileFavor::Ours => raw::GIT_MERGE_FILE_FAVOR_OURS,
- FileFavor::Theirs => raw::GIT_MERGE_FILE_FAVOR_THEIRS,
- FileFavor::Union => raw::GIT_MERGE_FILE_FAVOR_UNION,
- }
- }
- }
-
- impl Convert<raw::git_submodule_ignore_t> for SubmoduleIgnore {
- fn convert(&self) -> raw::git_submodule_ignore_t {
- match *self {
- SubmoduleIgnore::Unspecified => raw::GIT_SUBMODULE_IGNORE_UNSPECIFIED,
- SubmoduleIgnore::None => raw::GIT_SUBMODULE_IGNORE_NONE,
- SubmoduleIgnore::Untracked => raw::GIT_SUBMODULE_IGNORE_UNTRACKED,
- SubmoduleIgnore::Dirty => raw::GIT_SUBMODULE_IGNORE_DIRTY,
- SubmoduleIgnore::All => raw::GIT_SUBMODULE_IGNORE_ALL,
- }
- }
- }
-
- impl Convert<raw::git_submodule_update_t> for SubmoduleUpdate {
- fn convert(&self) -> raw::git_submodule_update_t {
- match *self {
- SubmoduleUpdate::Checkout => raw::GIT_SUBMODULE_UPDATE_CHECKOUT,
- SubmoduleUpdate::Rebase => raw::GIT_SUBMODULE_UPDATE_REBASE,
- SubmoduleUpdate::Merge => raw::GIT_SUBMODULE_UPDATE_MERGE,
- SubmoduleUpdate::None => raw::GIT_SUBMODULE_UPDATE_NONE,
- SubmoduleUpdate::Default => raw::GIT_SUBMODULE_UPDATE_DEFAULT,
- }
- }
- }
-
- impl Convert<raw::git_remote_autotag_option_t> for AutotagOption {
- fn convert(&self) -> raw::git_remote_autotag_option_t {
- match *self {
- AutotagOption::Unspecified => raw::GIT_REMOTE_DOWNLOAD_TAGS_UNSPECIFIED,
- AutotagOption::None => raw::GIT_REMOTE_DOWNLOAD_TAGS_NONE,
- AutotagOption::Auto => raw::GIT_REMOTE_DOWNLOAD_TAGS_AUTO,
- AutotagOption::All => raw::GIT_REMOTE_DOWNLOAD_TAGS_ALL,
- }
- }
- }
-
- impl Convert<raw::git_fetch_prune_t> for FetchPrune {
- fn convert(&self) -> raw::git_fetch_prune_t {
- match *self {
- FetchPrune::Unspecified => raw::GIT_FETCH_PRUNE_UNSPECIFIED,
- FetchPrune::On => raw::GIT_FETCH_PRUNE,
- FetchPrune::Off => raw::GIT_FETCH_NO_PRUNE,
- }
- }
- }
-}
diff --git a/extra/git2/src/cert.rs b/extra/git2/src/cert.rs
deleted file mode 100644
index b232cc3ce..000000000
--- a/extra/git2/src/cert.rs
+++ /dev/null
@@ -1,191 +0,0 @@
-//! Certificate types which are passed to `CertificateCheck` in
-//! `RemoteCallbacks`.
-
-use std::marker;
-use std::mem;
-use std::slice;
-
-use crate::raw;
-use crate::util::Binding;
-
-/// A certificate for a remote connection, viewable as one of `CertHostkey` or
-/// `CertX509` currently.
-pub struct Cert<'a> {
- raw: *mut raw::git_cert,
- _marker: marker::PhantomData<&'a raw::git_cert>,
-}
-
-/// Hostkey information taken from libssh2
-pub struct CertHostkey<'a> {
- raw: *mut raw::git_cert_hostkey,
- _marker: marker::PhantomData<&'a raw::git_cert>,
-}
-
-/// X.509 certificate information
-pub struct CertX509<'a> {
- raw: *mut raw::git_cert_x509,
- _marker: marker::PhantomData<&'a raw::git_cert>,
-}
-
-/// The SSH host key type.
-#[derive(Copy, Clone, Debug)]
-#[non_exhaustive]
-pub enum SshHostKeyType {
- /// Unknown key type
- Unknown = raw::GIT_CERT_SSH_RAW_TYPE_UNKNOWN as isize,
- /// RSA key type
- Rsa = raw::GIT_CERT_SSH_RAW_TYPE_RSA as isize,
- /// DSS key type
- Dss = raw::GIT_CERT_SSH_RAW_TYPE_DSS as isize,
- /// ECDSA 256 key type
- Ecdsa256 = raw::GIT_CERT_SSH_RAW_TYPE_KEY_ECDSA_256 as isize,
- /// ECDSA 384 key type
- Ecdsa384 = raw::GIT_CERT_SSH_RAW_TYPE_KEY_ECDSA_384 as isize,
- /// ECDSA 521 key type
- Ecdsa521 = raw::GIT_CERT_SSH_RAW_TYPE_KEY_ECDSA_521 as isize,
- /// ED25519 key type
- Ed255219 = raw::GIT_CERT_SSH_RAW_TYPE_KEY_ED25519 as isize,
-}
-
-impl SshHostKeyType {
- /// The name of the key type as encoded in the known_hosts file.
- pub fn name(&self) -> &'static str {
- match self {
- SshHostKeyType::Unknown => "unknown",
- SshHostKeyType::Rsa => "ssh-rsa",
- SshHostKeyType::Dss => "ssh-dss",
- SshHostKeyType::Ecdsa256 => "ecdsa-sha2-nistp256",
- SshHostKeyType::Ecdsa384 => "ecdsa-sha2-nistp384",
- SshHostKeyType::Ecdsa521 => "ecdsa-sha2-nistp521",
- SshHostKeyType::Ed255219 => "ssh-ed25519",
- }
- }
-
- /// A short name of the key type, the colloquial form used as a human-readable description.
- pub fn short_name(&self) -> &'static str {
- match self {
- SshHostKeyType::Unknown => "Unknown",
- SshHostKeyType::Rsa => "RSA",
- SshHostKeyType::Dss => "DSA",
- SshHostKeyType::Ecdsa256 => "ECDSA",
- SshHostKeyType::Ecdsa384 => "ECDSA",
- SshHostKeyType::Ecdsa521 => "ECDSA",
- SshHostKeyType::Ed255219 => "ED25519",
- }
- }
-}
-
-impl<'a> Cert<'a> {
- /// Attempt to view this certificate as an SSH hostkey.
- ///
- /// Returns `None` if this is not actually an SSH hostkey.
- pub fn as_hostkey(&self) -> Option<&CertHostkey<'a>> {
- self.cast(raw::GIT_CERT_HOSTKEY_LIBSSH2)
- }
-
- /// Attempt to view this certificate as an X.509 certificate.
- ///
- /// Returns `None` if this is not actually an X.509 certificate.
- pub fn as_x509(&self) -> Option<&CertX509<'a>> {
- self.cast(raw::GIT_CERT_X509)
- }
-
- fn cast<T>(&self, kind: raw::git_cert_t) -> Option<&T> {
- assert_eq!(mem::size_of::<Cert<'a>>(), mem::size_of::<T>());
- unsafe {
- if kind == (*self.raw).cert_type {
- Some(&*(self as *const Cert<'a> as *const T))
- } else {
- None
- }
- }
- }
-}
-
-impl<'a> CertHostkey<'a> {
- /// Returns the md5 hash of the hostkey, if available.
- pub fn hash_md5(&self) -> Option<&[u8; 16]> {
- unsafe {
- if (*self.raw).kind as u32 & raw::GIT_CERT_SSH_MD5 as u32 == 0 {
- None
- } else {
- Some(&(*self.raw).hash_md5)
- }
- }
- }
-
- /// Returns the SHA-1 hash of the hostkey, if available.
- pub fn hash_sha1(&self) -> Option<&[u8; 20]> {
- unsafe {
- if (*self.raw).kind as u32 & raw::GIT_CERT_SSH_SHA1 as u32 == 0 {
- None
- } else {
- Some(&(*self.raw).hash_sha1)
- }
- }
- }
-
- /// Returns the SHA-256 hash of the hostkey, if available.
- pub fn hash_sha256(&self) -> Option<&[u8; 32]> {
- unsafe {
- if (*self.raw).kind as u32 & raw::GIT_CERT_SSH_SHA256 as u32 == 0 {
- None
- } else {
- Some(&(*self.raw).hash_sha256)
- }
- }
- }
-
- /// Returns the raw host key.
- pub fn hostkey(&self) -> Option<&[u8]> {
- unsafe {
- if (*self.raw).kind & raw::GIT_CERT_SSH_RAW == 0 {
- return None;
- }
- Some(slice::from_raw_parts(
- (*self.raw).hostkey as *const u8,
- (*self.raw).hostkey_len as usize,
- ))
- }
- }
-
- /// Returns the type of the host key.
- pub fn hostkey_type(&self) -> Option<SshHostKeyType> {
- unsafe {
- if (*self.raw).kind & raw::GIT_CERT_SSH_RAW == 0 {
- return None;
- }
- let t = match (*self.raw).raw_type {
- raw::GIT_CERT_SSH_RAW_TYPE_UNKNOWN => SshHostKeyType::Unknown,
- raw::GIT_CERT_SSH_RAW_TYPE_RSA => SshHostKeyType::Rsa,
- raw::GIT_CERT_SSH_RAW_TYPE_DSS => SshHostKeyType::Dss,
- raw::GIT_CERT_SSH_RAW_TYPE_KEY_ECDSA_256 => SshHostKeyType::Ecdsa256,
- raw::GIT_CERT_SSH_RAW_TYPE_KEY_ECDSA_384 => SshHostKeyType::Ecdsa384,
- raw::GIT_CERT_SSH_RAW_TYPE_KEY_ECDSA_521 => SshHostKeyType::Ecdsa521,
- raw::GIT_CERT_SSH_RAW_TYPE_KEY_ED25519 => SshHostKeyType::Ed255219,
- t => panic!("unexpected host key type {:?}", t),
- };
- Some(t)
- }
- }
-}
-
-impl<'a> CertX509<'a> {
- /// Return the X.509 certificate data as a byte slice
- pub fn data(&self) -> &[u8] {
- unsafe { slice::from_raw_parts((*self.raw).data as *const u8, (*self.raw).len as usize) }
- }
-}
-
-impl<'a> Binding for Cert<'a> {
- type Raw = *mut raw::git_cert;
- unsafe fn from_raw(raw: *mut raw::git_cert) -> Cert<'a> {
- Cert {
- raw,
- _marker: marker::PhantomData,
- }
- }
- fn raw(&self) -> *mut raw::git_cert {
- self.raw
- }
-}
diff --git a/extra/git2/src/cherrypick.rs b/extra/git2/src/cherrypick.rs
deleted file mode 100644
index 659b73089..000000000
--- a/extra/git2/src/cherrypick.rs
+++ /dev/null
@@ -1,72 +0,0 @@
-use std::mem;
-
-use crate::build::CheckoutBuilder;
-use crate::merge::MergeOptions;
-use crate::raw;
-use std::ptr;
-
-/// Options to specify when cherry picking
-pub struct CherrypickOptions<'cb> {
- mainline: u32,
- checkout_builder: Option<CheckoutBuilder<'cb>>,
- merge_opts: Option<MergeOptions>,
-}
-
-impl<'cb> CherrypickOptions<'cb> {
- /// Creates a default set of cherrypick options
- pub fn new() -> CherrypickOptions<'cb> {
- CherrypickOptions {
- mainline: 0,
- checkout_builder: None,
- merge_opts: None,
- }
- }
-
- /// Set the mainline value
- ///
- /// For merge commits, the "mainline" is treated as the parent.
- pub fn mainline(&mut self, mainline: u32) -> &mut Self {
- self.mainline = mainline;
- self
- }
-
- /// Set the checkout builder
- pub fn checkout_builder(&mut self, cb: CheckoutBuilder<'cb>) -> &mut Self {
- self.checkout_builder = Some(cb);
- self
- }
-
- /// Set the merge options
- pub fn merge_opts(&mut self, merge_opts: MergeOptions) -> &mut Self {
- self.merge_opts = Some(merge_opts);
- self
- }
-
- /// Obtain the raw struct
- pub fn raw(&mut self) -> raw::git_cherrypick_options {
- unsafe {
- let mut checkout_opts: raw::git_checkout_options = mem::zeroed();
- raw::git_checkout_init_options(&mut checkout_opts, raw::GIT_CHECKOUT_OPTIONS_VERSION);
- if let Some(ref mut cb) = self.checkout_builder {
- cb.configure(&mut checkout_opts);
- }
-
- let mut merge_opts: raw::git_merge_options = mem::zeroed();
- raw::git_merge_init_options(&mut merge_opts, raw::GIT_MERGE_OPTIONS_VERSION);
- if let Some(ref opts) = self.merge_opts {
- ptr::copy(opts.raw(), &mut merge_opts, 1);
- }
-
- let mut cherrypick_opts: raw::git_cherrypick_options = mem::zeroed();
- raw::git_cherrypick_init_options(
- &mut cherrypick_opts,
- raw::GIT_CHERRYPICK_OPTIONS_VERSION,
- );
- cherrypick_opts.mainline = self.mainline;
- cherrypick_opts.checkout_opts = checkout_opts;
- cherrypick_opts.merge_opts = merge_opts;
-
- cherrypick_opts
- }
- }
-}
diff --git a/extra/git2/src/commit.rs b/extra/git2/src/commit.rs
deleted file mode 100644
index 4887e927e..000000000
--- a/extra/git2/src/commit.rs
+++ /dev/null
@@ -1,473 +0,0 @@
-use libc;
-use std::iter::FusedIterator;
-use std::marker;
-use std::mem;
-use std::ops::Range;
-use std::ptr;
-use std::str;
-
-use crate::util::Binding;
-use crate::{raw, signature, Buf, Error, IntoCString, Mailmap, Object, Oid, Signature, Time, Tree};
-
-/// A structure to represent a git [commit][1]
-///
-/// [1]: http://git-scm.com/book/en/Git-Internals-Git-Objects
-pub struct Commit<'repo> {
- raw: *mut raw::git_commit,
- _marker: marker::PhantomData<Object<'repo>>,
-}
-
-/// An iterator over the parent commits of a commit.
-///
-/// Aborts iteration when a commit cannot be found
-pub struct Parents<'commit, 'repo> {
- range: Range<usize>,
- commit: &'commit Commit<'repo>,
-}
-
-/// An iterator over the parent commits' ids of a commit.
-///
-/// Aborts iteration when a commit cannot be found
-pub struct ParentIds<'commit> {
- range: Range<usize>,
- commit: &'commit Commit<'commit>,
-}
-
-impl<'repo> Commit<'repo> {
- /// Get the id (SHA1) of a repository commit
- pub fn id(&self) -> Oid {
- unsafe { Binding::from_raw(raw::git_commit_id(&*self.raw)) }
- }
-
- /// Get the id of the tree pointed to by this commit.
- ///
- /// No attempts are made to fetch an object from the ODB.
- pub fn tree_id(&self) -> Oid {
- unsafe { Binding::from_raw(raw::git_commit_tree_id(&*self.raw)) }
- }
-
- /// Get the tree pointed to by a commit.
- pub fn tree(&self) -> Result<Tree<'repo>, Error> {
- let mut ret = ptr::null_mut();
- unsafe {
- try_call!(raw::git_commit_tree(&mut ret, &*self.raw));
- Ok(Binding::from_raw(ret))
- }
- }
-
- /// Get access to the underlying raw pointer.
- pub fn raw(&self) -> *mut raw::git_commit {
- self.raw
- }
-
- /// Get the full message of a commit.
- ///
- /// The returned message will be slightly prettified by removing any
- /// potential leading newlines.
- ///
- /// `None` will be returned if the message is not valid utf-8
- pub fn message(&self) -> Option<&str> {
- str::from_utf8(self.message_bytes()).ok()
- }
-
- /// Get the full message of a commit as a byte slice.
- ///
- /// The returned message will be slightly prettified by removing any
- /// potential leading newlines.
- pub fn message_bytes(&self) -> &[u8] {
- unsafe { crate::opt_bytes(self, raw::git_commit_message(&*self.raw)).unwrap() }
- }
-
- /// Get the encoding for the message of a commit, as a string representing a
- /// standard encoding name.
- ///
- /// `None` will be returned if the encoding is not known
- pub fn message_encoding(&self) -> Option<&str> {
- let bytes = unsafe { crate::opt_bytes(self, raw::git_commit_message_encoding(&*self.raw)) };
- bytes.and_then(|b| str::from_utf8(b).ok())
- }
-
- /// Get the full raw message of a commit.
- ///
- /// `None` will be returned if the message is not valid utf-8
- pub fn message_raw(&self) -> Option<&str> {
- str::from_utf8(self.message_raw_bytes()).ok()
- }
-
- /// Get the full raw message of a commit.
- pub fn message_raw_bytes(&self) -> &[u8] {
- unsafe { crate::opt_bytes(self, raw::git_commit_message_raw(&*self.raw)).unwrap() }
- }
-
- /// Get the full raw text of the commit header.
- ///
- /// `None` will be returned if the message is not valid utf-8
- pub fn raw_header(&self) -> Option<&str> {
- str::from_utf8(self.raw_header_bytes()).ok()
- }
-
- /// Get an arbitrary header field.
- pub fn header_field_bytes<T: IntoCString>(&self, field: T) -> Result<Buf, Error> {
- let buf = Buf::new();
- let raw_field = field.into_c_string()?;
- unsafe {
- try_call!(raw::git_commit_header_field(
- buf.raw(),
- &*self.raw,
- raw_field
- ));
- }
- Ok(buf)
- }
-
- /// Get the full raw text of the commit header.
- pub fn raw_header_bytes(&self) -> &[u8] {
- unsafe { crate::opt_bytes(self, raw::git_commit_raw_header(&*self.raw)).unwrap() }
- }
-
- /// Get the short "summary" of the git commit message.
- ///
- /// The returned message is the summary of the commit, comprising the first
- /// paragraph of the message with whitespace trimmed and squashed.
- ///
- /// `None` may be returned if an error occurs or if the summary is not valid
- /// utf-8.
- pub fn summary(&self) -> Option<&str> {
- self.summary_bytes().and_then(|s| str::from_utf8(s).ok())
- }
-
- /// Get the short "summary" of the git commit message.
- ///
- /// The returned message is the summary of the commit, comprising the first
- /// paragraph of the message with whitespace trimmed and squashed.
- ///
- /// `None` may be returned if an error occurs
- pub fn summary_bytes(&self) -> Option<&[u8]> {
- unsafe { crate::opt_bytes(self, raw::git_commit_summary(self.raw)) }
- }
-
- /// Get the long "body" of the git commit message.
- ///
- /// The returned message is the body of the commit, comprising everything
- /// but the first paragraph of the message. Leading and trailing whitespaces
- /// are trimmed.
- ///
- /// `None` may be returned if an error occurs or if the summary is not valid
- /// utf-8.
- pub fn body(&self) -> Option<&str> {
- self.body_bytes().and_then(|s| str::from_utf8(s).ok())
- }
-
- /// Get the long "body" of the git commit message.
- ///
- /// The returned message is the body of the commit, comprising everything
- /// but the first paragraph of the message. Leading and trailing whitespaces
- /// are trimmed.
- ///
- /// `None` may be returned if an error occurs.
- pub fn body_bytes(&self) -> Option<&[u8]> {
- unsafe { crate::opt_bytes(self, raw::git_commit_body(self.raw)) }
- }
-
- /// Get the commit time (i.e. committer time) of a commit.
- ///
- /// The first element of the tuple is the time, in seconds, since the epoch.
- /// The second element is the offset, in minutes, of the time zone of the
- /// committer's preferred time zone.
- pub fn time(&self) -> Time {
- unsafe {
- Time::new(
- raw::git_commit_time(&*self.raw) as i64,
- raw::git_commit_time_offset(&*self.raw) as i32,
- )
- }
- }
-
- /// Creates a new iterator over the parents of this commit.
- pub fn parents<'a>(&'a self) -> Parents<'a, 'repo> {
- Parents {
- range: 0..self.parent_count(),
- commit: self,
- }
- }
-
- /// Creates a new iterator over the parents of this commit.
- pub fn parent_ids(&self) -> ParentIds<'_> {
- ParentIds {
- range: 0..self.parent_count(),
- commit: self,
- }
- }
-
- /// Get the author of this commit.
- pub fn author(&self) -> Signature<'_> {
- unsafe {
- let ptr = raw::git_commit_author(&*self.raw);
- signature::from_raw_const(self, ptr)
- }
- }
-
- /// Get the author of this commit, using the mailmap to map names and email
- /// addresses to canonical real names and email addresses.
- pub fn author_with_mailmap(&self, mailmap: &Mailmap) -> Result<Signature<'static>, Error> {
- let mut ret = ptr::null_mut();
- unsafe {
- try_call!(raw::git_commit_author_with_mailmap(
- &mut ret,
- &*self.raw,
- &*mailmap.raw()
- ));
- Ok(Binding::from_raw(ret))
- }
- }
-
- /// Get the committer of this commit.
- pub fn committer(&self) -> Signature<'_> {
- unsafe {
- let ptr = raw::git_commit_committer(&*self.raw);
- signature::from_raw_const(self, ptr)
- }
- }
-
- /// Get the committer of this commit, using the mailmap to map names and email
- /// addresses to canonical real names and email addresses.
- pub fn committer_with_mailmap(&self, mailmap: &Mailmap) -> Result<Signature<'static>, Error> {
- let mut ret = ptr::null_mut();
- unsafe {
- try_call!(raw::git_commit_committer_with_mailmap(
- &mut ret,
- &*self.raw,
- &*mailmap.raw()
- ));
- Ok(Binding::from_raw(ret))
- }
- }
-
- /// Amend this existing commit with all non-`None` values
- ///
- /// This creates a new commit that is exactly the same as the old commit,
- /// except that any non-`None` values will be updated. The new commit has
- /// the same parents as the old commit.
- ///
- /// For information about `update_ref`, see [`Repository::commit`].
- ///
- /// [`Repository::commit`]: struct.Repository.html#method.commit
- pub fn amend(
- &self,
- update_ref: Option<&str>,
- author: Option<&Signature<'_>>,
- committer: Option<&Signature<'_>>,
- message_encoding: Option<&str>,
- message: Option<&str>,
- tree: Option<&Tree<'repo>>,
- ) -> Result<Oid, Error> {
- let mut raw = raw::git_oid {
- id: [0; raw::GIT_OID_RAWSZ],
- };
- let update_ref = crate::opt_cstr(update_ref)?;
- let encoding = crate::opt_cstr(message_encoding)?;
- let message = crate::opt_cstr(message)?;
- unsafe {
- try_call!(raw::git_commit_amend(
- &mut raw,
- self.raw(),
- update_ref,
- author.map(|s| s.raw()),
- committer.map(|s| s.raw()),
- encoding,
- message,
- tree.map(|t| t.raw())
- ));
- Ok(Binding::from_raw(&raw as *const _))
- }
- }
-
- /// Get the number of parents of this commit.
- ///
- /// Use the `parents` iterator to return an iterator over all parents.
- pub fn parent_count(&self) -> usize {
- unsafe { raw::git_commit_parentcount(&*self.raw) as usize }
- }
-
- /// Get the specified parent of the commit.
- ///
- /// Use the `parents` iterator to return an iterator over all parents.
- pub fn parent(&self, i: usize) -> Result<Commit<'repo>, Error> {
- unsafe {
- let mut raw = ptr::null_mut();
- try_call!(raw::git_commit_parent(
- &mut raw,
- &*self.raw,
- i as libc::c_uint
- ));
- Ok(Binding::from_raw(raw))
- }
- }
-
- /// Get the specified parent id of the commit.
- ///
- /// This is different from `parent`, which will attempt to load the
- /// parent commit from the ODB.
- ///
- /// Use the `parent_ids` iterator to return an iterator over all parents.
- pub fn parent_id(&self, i: usize) -> Result<Oid, Error> {
- unsafe {
- let id = raw::git_commit_parent_id(self.raw, i as libc::c_uint);
- if id.is_null() {
- Err(Error::from_str("parent index out of bounds"))
- } else {
- Ok(Binding::from_raw(id))
- }
- }
- }
-
- /// Casts this Commit to be usable as an `Object`
- pub fn as_object(&self) -> &Object<'repo> {
- unsafe { &*(self as *const _ as *const Object<'repo>) }
- }
-
- /// Consumes Commit to be returned as an `Object`
- pub fn into_object(self) -> Object<'repo> {
- assert_eq!(mem::size_of_val(&self), mem::size_of::<Object<'_>>());
- unsafe { mem::transmute(self) }
- }
-}
-
-impl<'repo> Binding for Commit<'repo> {
- type Raw = *mut raw::git_commit;
- unsafe fn from_raw(raw: *mut raw::git_commit) -> Commit<'repo> {
- Commit {
- raw,
- _marker: marker::PhantomData,
- }
- }
- fn raw(&self) -> *mut raw::git_commit {
- self.raw
- }
-}
-
-impl<'repo> std::fmt::Debug for Commit<'repo> {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
- let mut ds = f.debug_struct("Commit");
- ds.field("id", &self.id());
- if let Some(summary) = self.summary() {
- ds.field("summary", &summary);
- }
- ds.finish()
- }
-}
-
-/// Aborts iteration when a commit cannot be found
-impl<'repo, 'commit> Iterator for Parents<'commit, 'repo> {
- type Item = Commit<'repo>;
- fn next(&mut self) -> Option<Commit<'repo>> {
- self.range.next().and_then(|i| self.commit.parent(i).ok())
- }
- fn size_hint(&self) -> (usize, Option<usize>) {
- self.range.size_hint()
- }
-}
-
-/// Aborts iteration when a commit cannot be found
-impl<'repo, 'commit> DoubleEndedIterator for Parents<'commit, 'repo> {
- fn next_back(&mut self) -> Option<Commit<'repo>> {
- self.range
- .next_back()
- .and_then(|i| self.commit.parent(i).ok())
- }
-}
-
-impl<'repo, 'commit> FusedIterator for Parents<'commit, 'repo> {}
-
-impl<'repo, 'commit> ExactSizeIterator for Parents<'commit, 'repo> {}
-
-/// Aborts iteration when a commit cannot be found
-impl<'commit> Iterator for ParentIds<'commit> {
- type Item = Oid;
- fn next(&mut self) -> Option<Oid> {
- self.range
- .next()
- .and_then(|i| self.commit.parent_id(i).ok())
- }
- fn size_hint(&self) -> (usize, Option<usize>) {
- self.range.size_hint()
- }
-}
-
-/// Aborts iteration when a commit cannot be found
-impl<'commit> DoubleEndedIterator for ParentIds<'commit> {
- fn next_back(&mut self) -> Option<Oid> {
- self.range
- .next_back()
- .and_then(|i| self.commit.parent_id(i).ok())
- }
-}
-
-impl<'commit> FusedIterator for ParentIds<'commit> {}
-
-impl<'commit> ExactSizeIterator for ParentIds<'commit> {}
-
-impl<'repo> Clone for Commit<'repo> {
- fn clone(&self) -> Self {
- self.as_object().clone().into_commit().ok().unwrap()
- }
-}
-
-impl<'repo> Drop for Commit<'repo> {
- fn drop(&mut self) {
- unsafe { raw::git_commit_free(self.raw) }
- }
-}
-
-#[cfg(test)]
-mod tests {
- #[test]
- fn smoke() {
- let (_td, repo) = crate::test::repo_init();
- let head = repo.head().unwrap();
- let target = head.target().unwrap();
- let commit = repo.find_commit(target).unwrap();
- assert_eq!(commit.message(), Some("initial\n\nbody"));
- assert_eq!(commit.body(), Some("body"));
- assert_eq!(commit.id(), target);
- commit.message_raw().unwrap();
- commit.raw_header().unwrap();
- commit.message_encoding();
- commit.summary().unwrap();
- commit.body().unwrap();
- commit.tree_id();
- commit.tree().unwrap();
- 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()
- );
- assert_eq!(commit.author().name(), Some("name"));
- assert_eq!(commit.author().email(), Some("email"));
- assert_eq!(commit.committer().name(), Some("name"));
- assert_eq!(commit.committer().email(), Some("email"));
-
- 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();
-
- let new_head = head
- .amend(Some("HEAD"), None, None, None, Some("new message"), None)
- .unwrap();
- let new_head = repo.find_commit(new_head).unwrap();
- assert_eq!(new_head.message(), Some("new message"));
- new_head.into_object();
-
- repo.find_object(target, None).unwrap().as_commit().unwrap();
- repo.find_object(target, None)
- .unwrap()
- .into_commit()
- .ok()
- .unwrap();
- }
-}
diff --git a/extra/git2/src/config.rs b/extra/git2/src/config.rs
deleted file mode 100644
index ae5c4ff63..000000000
--- a/extra/git2/src/config.rs
+++ /dev/null
@@ -1,777 +0,0 @@
-use libc;
-use std::ffi::CString;
-use std::marker;
-use std::path::{Path, PathBuf};
-use std::ptr;
-use std::str;
-
-use crate::util::{self, Binding};
-use crate::{raw, Buf, ConfigLevel, Error, IntoCString};
-
-/// A structure representing a git configuration key/value store
-pub struct Config {
- raw: *mut raw::git_config,
-}
-
-/// A struct representing a certain entry owned by a `Config` instance.
-///
-/// An entry has a name, a value, and a level it applies to.
-pub struct ConfigEntry<'cfg> {
- raw: *mut raw::git_config_entry,
- _marker: marker::PhantomData<&'cfg Config>,
- owned: bool,
-}
-
-/// An iterator over the `ConfigEntry` values of a `Config` structure.
-///
-/// Due to lifetime restrictions, `ConfigEntries` does not implement the
-/// standard [`Iterator`] trait. It provides a [`next`] function which only
-/// allows access to one entry at a time. [`for_each`] is available as a
-/// convenience function.
-///
-/// [`next`]: ConfigEntries::next
-/// [`for_each`]: ConfigEntries::for_each
-///
-/// # Example
-///
-/// ```
-/// // Example of how to collect all entries.
-/// use git2::Config;
-///
-/// let config = Config::new()?;
-/// let iter = config.entries(None)?;
-/// let mut entries = Vec::new();
-/// iter
-/// .for_each(|entry| {
-/// let name = entry.name().unwrap().to_string();
-/// let value = entry.value().unwrap_or("").to_string();
-/// entries.push((name, value))
-/// })?;
-/// for entry in &entries {
-/// println!("{} = {}", entry.0, entry.1);
-/// }
-/// # Ok::<(), git2::Error>(())
-///
-/// ```
-pub struct ConfigEntries<'cfg> {
- raw: *mut raw::git_config_iterator,
- current: Option<ConfigEntry<'cfg>>,
- _marker: marker::PhantomData<&'cfg Config>,
-}
-
-impl Config {
- /// Allocate a new configuration object
- ///
- /// This object is empty, so you have to add a file to it before you can do
- /// anything with it.
- pub fn new() -> Result<Config, Error> {
- crate::init();
- let mut raw = ptr::null_mut();
- unsafe {
- try_call!(raw::git_config_new(&mut raw));
- Ok(Binding::from_raw(raw))
- }
- }
-
- /// Create a new config instance containing a single on-disk file
- pub fn open(path: &Path) -> Result<Config, Error> {
- crate::init();
- let mut raw = ptr::null_mut();
- // Normal file path OK (does not need Windows conversion).
- let path = path.into_c_string()?;
- unsafe {
- try_call!(raw::git_config_open_ondisk(&mut raw, path));
- Ok(Binding::from_raw(raw))
- }
- }
-
- /// Open the global, XDG and system configuration files
- ///
- /// Utility wrapper that finds the global, XDG and system configuration
- /// files and opens them into a single prioritized config object that can
- /// be used when accessing default config data outside a repository.
- pub fn open_default() -> Result<Config, Error> {
- crate::init();
- let mut raw = ptr::null_mut();
- unsafe {
- try_call!(raw::git_config_open_default(&mut raw));
- Ok(Binding::from_raw(raw))
- }
- }
-
- /// Locate the path to the global configuration file
- ///
- /// The user or global configuration file is usually located in
- /// `$HOME/.gitconfig`.
- ///
- /// This method will try to guess the full path to that file, if the file
- /// exists. The returned path may be used on any method call to load
- /// the global configuration file.
- ///
- /// This method will not guess the path to the XDG compatible config file
- /// (`.config/git/config`).
- pub fn find_global() -> Result<PathBuf, Error> {
- crate::init();
- let buf = Buf::new();
- unsafe {
- try_call!(raw::git_config_find_global(buf.raw()));
- }
- Ok(util::bytes2path(&buf).to_path_buf())
- }
-
- /// Locate the path to the system configuration file
- ///
- /// If /etc/gitconfig doesn't exist, it will look for `%PROGRAMFILES%`
- pub fn find_system() -> Result<PathBuf, Error> {
- crate::init();
- let buf = Buf::new();
- unsafe {
- try_call!(raw::git_config_find_system(buf.raw()));
- }
- Ok(util::bytes2path(&buf).to_path_buf())
- }
-
- /// Locate the path to the global XDG compatible configuration file
- ///
- /// The XDG compatible configuration file is usually located in
- /// `$HOME/.config/git/config`.
- pub fn find_xdg() -> Result<PathBuf, Error> {
- crate::init();
- let buf = Buf::new();
- unsafe {
- try_call!(raw::git_config_find_xdg(buf.raw()));
- }
- Ok(util::bytes2path(&buf).to_path_buf())
- }
-
- /// Add an on-disk config file instance to an existing config
- ///
- /// The on-disk file pointed at by path will be opened and parsed; it's
- /// expected to be a native Git config file following the default Git config
- /// syntax (see man git-config).
- ///
- /// Further queries on this config object will access each of the config
- /// file instances in order (instances with a higher priority level will be
- /// accessed first).
- pub fn add_file(&mut self, path: &Path, level: ConfigLevel, force: bool) -> Result<(), Error> {
- // Normal file path OK (does not need Windows conversion).
- let path = path.into_c_string()?;
- unsafe {
- try_call!(raw::git_config_add_file_ondisk(
- self.raw,
- path,
- level,
- ptr::null(),
- force
- ));
- Ok(())
- }
- }
-
- /// Delete a config variable from the config file with the highest level
- /// (usually the local one).
- pub fn remove(&mut self, name: &str) -> Result<(), Error> {
- let name = CString::new(name)?;
- unsafe {
- try_call!(raw::git_config_delete_entry(self.raw, name));
- Ok(())
- }
- }
-
- /// Remove multivar config variables in the config file with the highest level (usually the
- /// local one).
- ///
- /// The regular expression is applied case-sensitively on the value.
- pub fn remove_multivar(&mut self, name: &str, regexp: &str) -> Result<(), Error> {
- let name = CString::new(name)?;
- let regexp = CString::new(regexp)?;
- unsafe {
- try_call!(raw::git_config_delete_multivar(self.raw, name, regexp));
- }
- Ok(())
- }
-
- /// Get the value of a boolean config variable.
- ///
- /// All config files will be looked into, in the order of their defined
- /// level. A higher level means a higher priority. The first occurrence of
- /// the variable will be returned here.
- pub fn get_bool(&self, name: &str) -> Result<bool, Error> {
- let mut out = 0 as libc::c_int;
- let name = CString::new(name)?;
- unsafe {
- try_call!(raw::git_config_get_bool(&mut out, &*self.raw, name));
- }
- Ok(out != 0)
- }
-
- /// Get the value of an integer config variable.
- ///
- /// All config files will be looked into, in the order of their defined
- /// level. A higher level means a higher priority. The first occurrence of
- /// the variable will be returned here.
- pub fn get_i32(&self, name: &str) -> Result<i32, Error> {
- let mut out = 0i32;
- let name = CString::new(name)?;
- unsafe {
- try_call!(raw::git_config_get_int32(&mut out, &*self.raw, name));
- }
- Ok(out)
- }
-
- /// Get the value of an integer config variable.
- ///
- /// All config files will be looked into, in the order of their defined
- /// level. A higher level means a higher priority. The first occurrence of
- /// the variable will be returned here.
- pub fn get_i64(&self, name: &str) -> Result<i64, Error> {
- let mut out = 0i64;
- let name = CString::new(name)?;
- unsafe {
- try_call!(raw::git_config_get_int64(&mut out, &*self.raw, name));
- }
- Ok(out)
- }
-
- /// Get the value of a string config variable.
- ///
- /// This is the same as `get_bytes` except that it may return `Err` if
- /// the bytes are not valid utf-8.
- ///
- /// This method will return an error if this `Config` is not a snapshot.
- pub fn get_str(&self, name: &str) -> Result<&str, Error> {
- str::from_utf8(self.get_bytes(name)?)
- .map_err(|_| Error::from_str("configuration value is not valid utf8"))
- }
-
- /// Get the value of a string config variable as a byte slice.
- ///
- /// This method will return an error if this `Config` is not a snapshot.
- pub fn get_bytes(&self, name: &str) -> Result<&[u8], Error> {
- let mut ret = ptr::null();
- let name = CString::new(name)?;
- unsafe {
- try_call!(raw::git_config_get_string(&mut ret, &*self.raw, name));
- Ok(crate::opt_bytes(self, ret).unwrap())
- }
- }
-
- /// Get the value of a string config variable as an owned string.
- ///
- /// All config files will be looked into, in the order of their
- /// defined level. A higher level means a higher priority. The
- /// first occurrence of the variable will be returned here.
- ///
- /// An error will be returned if the config value is not valid utf-8.
- pub fn get_string(&self, name: &str) -> Result<String, Error> {
- let ret = Buf::new();
- let name = CString::new(name)?;
- unsafe {
- try_call!(raw::git_config_get_string_buf(ret.raw(), self.raw, name));
- }
- str::from_utf8(&ret)
- .map(|s| s.to_string())
- .map_err(|_| Error::from_str("configuration value is not valid utf8"))
- }
-
- /// Get the value of a path config variable as an owned `PathBuf`.
- ///
- /// A leading '~' will be expanded to the global search path (which
- /// defaults to the user's home directory but can be overridden via
- /// [`raw::git_libgit2_opts`].
- ///
- /// All config files will be looked into, in the order of their
- /// defined level. A higher level means a higher priority. The
- /// first occurrence of the variable will be returned here.
- pub fn get_path(&self, name: &str) -> Result<PathBuf, Error> {
- let ret = Buf::new();
- let name = CString::new(name)?;
- unsafe {
- try_call!(raw::git_config_get_path(ret.raw(), self.raw, name));
- }
- Ok(crate::util::bytes2path(&ret).to_path_buf())
- }
-
- /// Get the ConfigEntry for a config variable.
- pub fn get_entry(&self, name: &str) -> Result<ConfigEntry<'_>, Error> {
- let mut ret = ptr::null_mut();
- let name = CString::new(name)?;
- unsafe {
- try_call!(raw::git_config_get_entry(&mut ret, self.raw, name));
- Ok(Binding::from_raw(ret))
- }
- }
-
- /// Iterate over all the config variables
- ///
- /// If `glob` is `Some`, then the iterator will only iterate over all
- /// variables whose name matches the pattern.
- ///
- /// The regular expression is applied case-sensitively on the normalized form of
- /// the variable name: the section and variable parts are lower-cased. The
- /// subsection is left unchanged.
- ///
- /// Due to lifetime restrictions, the returned value does not implement
- /// the standard [`Iterator`] trait. See [`ConfigEntries`] for more.
- ///
- /// # Example
- ///
- /// ```
- /// use git2::Config;
- ///
- /// let cfg = Config::new().unwrap();
- ///
- /// let mut entries = cfg.entries(None).unwrap();
- /// while let Some(entry) = entries.next() {
- /// let entry = entry.unwrap();
- /// println!("{} => {}", entry.name().unwrap(), entry.value().unwrap());
- /// }
- /// ```
- pub fn entries(&self, glob: Option<&str>) -> Result<ConfigEntries<'_>, Error> {
- let mut ret = ptr::null_mut();
- unsafe {
- match glob {
- Some(s) => {
- let s = CString::new(s)?;
- try_call!(raw::git_config_iterator_glob_new(&mut ret, &*self.raw, s));
- }
- None => {
- try_call!(raw::git_config_iterator_new(&mut ret, &*self.raw));
- }
- }
- Ok(Binding::from_raw(ret))
- }
- }
-
- /// Iterate over the values of a multivar
- ///
- /// If `regexp` is `Some`, then the iterator will only iterate over all
- /// values which match the pattern.
- ///
- /// The regular expression is applied case-sensitively on the normalized form of
- /// the variable name: the section and variable parts are lower-cased. The
- /// subsection is left unchanged.
- ///
- /// Due to lifetime restrictions, the returned value does not implement
- /// the standard [`Iterator`] trait. See [`ConfigEntries`] for more.
- pub fn multivar(&self, name: &str, regexp: Option<&str>) -> Result<ConfigEntries<'_>, Error> {
- let mut ret = ptr::null_mut();
- let name = CString::new(name)?;
- let regexp = regexp.map(CString::new).transpose()?;
- unsafe {
- try_call!(raw::git_config_multivar_iterator_new(
- &mut ret, &*self.raw, name, regexp
- ));
- Ok(Binding::from_raw(ret))
- }
- }
-
- /// Open the global/XDG configuration file according to git's rules
- ///
- /// Git allows you to store your global configuration at `$HOME/.config` or
- /// `$XDG_CONFIG_HOME/git/config`. For backwards compatibility, the XDG file
- /// shouldn't be used unless the use has created it explicitly. With this
- /// function you'll open the correct one to write to.
- pub fn open_global(&mut self) -> Result<Config, Error> {
- let mut raw = ptr::null_mut();
- unsafe {
- try_call!(raw::git_config_open_global(&mut raw, self.raw));
- Ok(Binding::from_raw(raw))
- }
- }
-
- /// Build a single-level focused config object from a multi-level one.
- ///
- /// The returned config object can be used to perform get/set/delete
- /// operations on a single specific level.
- pub fn open_level(&self, level: ConfigLevel) -> Result<Config, Error> {
- let mut raw = ptr::null_mut();
- unsafe {
- try_call!(raw::git_config_open_level(&mut raw, &*self.raw, level));
- Ok(Binding::from_raw(raw))
- }
- }
-
- /// Set the value of a boolean config variable in the config file with the
- /// highest level (usually the local one).
- pub fn set_bool(&mut self, name: &str, value: bool) -> Result<(), Error> {
- let name = CString::new(name)?;
- unsafe {
- try_call!(raw::git_config_set_bool(self.raw, name, value));
- }
- Ok(())
- }
-
- /// Set the value of an integer config variable in the config file with the
- /// highest level (usually the local one).
- pub fn set_i32(&mut self, name: &str, value: i32) -> Result<(), Error> {
- let name = CString::new(name)?;
- unsafe {
- try_call!(raw::git_config_set_int32(self.raw, name, value));
- }
- Ok(())
- }
-
- /// Set the value of an integer config variable in the config file with the
- /// highest level (usually the local one).
- pub fn set_i64(&mut self, name: &str, value: i64) -> Result<(), Error> {
- let name = CString::new(name)?;
- unsafe {
- try_call!(raw::git_config_set_int64(self.raw, name, value));
- }
- Ok(())
- }
-
- /// Set the value of an multivar config variable in the config file with the
- /// highest level (usually the local one).
- ///
- /// The regular expression is applied case-sensitively on the value.
- pub fn set_multivar(&mut self, name: &str, regexp: &str, value: &str) -> Result<(), Error> {
- let name = CString::new(name)?;
- let regexp = CString::new(regexp)?;
- let value = CString::new(value)?;
- unsafe {
- try_call!(raw::git_config_set_multivar(self.raw, name, regexp, value));
- }
- Ok(())
- }
-
- /// Set the value of a string config variable in the config file with the
- /// highest level (usually the local one).
- pub fn set_str(&mut self, name: &str, value: &str) -> Result<(), Error> {
- let name = CString::new(name)?;
- let value = CString::new(value)?;
- unsafe {
- try_call!(raw::git_config_set_string(self.raw, name, value));
- }
- Ok(())
- }
-
- /// Create a snapshot of the configuration
- ///
- /// Create a snapshot of the current state of a configuration, which allows
- /// you to look into a consistent view of the configuration for looking up
- /// complex values (e.g. a remote, submodule).
- pub fn snapshot(&mut self) -> Result<Config, Error> {
- let mut ret = ptr::null_mut();
- unsafe {
- try_call!(raw::git_config_snapshot(&mut ret, self.raw));
- Ok(Binding::from_raw(ret))
- }
- }
-
- /// Parse a string as a bool.
- ///
- /// Interprets "true", "yes", "on", 1, or any non-zero number as true.
- /// Interprets "false", "no", "off", 0, or an empty string as false.
- pub fn parse_bool<S: IntoCString>(s: S) -> Result<bool, Error> {
- let s = s.into_c_string()?;
- let mut out = 0;
- crate::init();
- unsafe {
- try_call!(raw::git_config_parse_bool(&mut out, s));
- }
- Ok(out != 0)
- }
-
- /// Parse a string as an i32; handles suffixes like k, M, or G, and
- /// multiplies by the appropriate power of 1024.
- pub fn parse_i32<S: IntoCString>(s: S) -> Result<i32, Error> {
- let s = s.into_c_string()?;
- let mut out = 0;
- crate::init();
- unsafe {
- try_call!(raw::git_config_parse_int32(&mut out, s));
- }
- Ok(out)
- }
-
- /// Parse a string as an i64; handles suffixes like k, M, or G, and
- /// multiplies by the appropriate power of 1024.
- pub fn parse_i64<S: IntoCString>(s: S) -> Result<i64, Error> {
- let s = s.into_c_string()?;
- let mut out = 0;
- crate::init();
- unsafe {
- try_call!(raw::git_config_parse_int64(&mut out, s));
- }
- Ok(out)
- }
-}
-
-impl Binding for Config {
- type Raw = *mut raw::git_config;
- unsafe fn from_raw(raw: *mut raw::git_config) -> Config {
- Config { raw }
- }
- fn raw(&self) -> *mut raw::git_config {
- self.raw
- }
-}
-
-impl Drop for Config {
- fn drop(&mut self) {
- unsafe { raw::git_config_free(self.raw) }
- }
-}
-
-impl<'cfg> ConfigEntry<'cfg> {
- /// Gets the name of this entry.
- ///
- /// May return `None` if the name is not valid utf-8
- pub fn name(&self) -> Option<&str> {
- str::from_utf8(self.name_bytes()).ok()
- }
-
- /// Gets the name of this entry as a byte slice.
- pub fn name_bytes(&self) -> &[u8] {
- unsafe { crate::opt_bytes(self, (*self.raw).name).unwrap() }
- }
-
- /// Gets the value of this entry.
- ///
- /// May return `None` if the value is not valid utf-8
- ///
- /// # Panics
- ///
- /// Panics when no value is defined.
- pub fn value(&self) -> Option<&str> {
- str::from_utf8(self.value_bytes()).ok()
- }
-
- /// Gets the value of this entry as a byte slice.
- ///
- /// # Panics
- ///
- /// Panics when no value is defined.
- pub fn value_bytes(&self) -> &[u8] {
- unsafe { crate::opt_bytes(self, (*self.raw).value).unwrap() }
- }
-
- /// Returns `true` when a value is defined otherwise `false`.
- ///
- /// No value defined is a short-hand to represent a Boolean `true`.
- pub fn has_value(&self) -> bool {
- unsafe { !(*self.raw).value.is_null() }
- }
-
- /// Gets the configuration level of this entry.
- pub fn level(&self) -> ConfigLevel {
- unsafe { ConfigLevel::from_raw((*self.raw).level) }
- }
-
- /// Depth of includes where this variable was found
- pub fn include_depth(&self) -> u32 {
- unsafe { (*self.raw).include_depth as u32 }
- }
-}
-
-impl<'cfg> Binding for ConfigEntry<'cfg> {
- type Raw = *mut raw::git_config_entry;
-
- unsafe fn from_raw(raw: *mut raw::git_config_entry) -> ConfigEntry<'cfg> {
- ConfigEntry {
- raw,
- _marker: marker::PhantomData,
- owned: true,
- }
- }
- fn raw(&self) -> *mut raw::git_config_entry {
- self.raw
- }
-}
-
-impl<'cfg> Binding for ConfigEntries<'cfg> {
- type Raw = *mut raw::git_config_iterator;
-
- unsafe fn from_raw(raw: *mut raw::git_config_iterator) -> ConfigEntries<'cfg> {
- ConfigEntries {
- raw,
- current: None,
- _marker: marker::PhantomData,
- }
- }
- fn raw(&self) -> *mut raw::git_config_iterator {
- self.raw
- }
-}
-
-impl<'cfg> ConfigEntries<'cfg> {
- /// Advances the iterator and returns the next value.
- ///
- /// Returns `None` when iteration is finished.
- pub fn next(&mut self) -> Option<Result<&ConfigEntry<'cfg>, Error>> {
- let mut raw = ptr::null_mut();
- drop(self.current.take());
- unsafe {
- try_call_iter!(raw::git_config_next(&mut raw, self.raw));
- let entry = ConfigEntry {
- owned: false,
- raw,
- _marker: marker::PhantomData,
- };
- self.current = Some(entry);
- Some(Ok(self.current.as_ref().unwrap()))
- }
- }
-
- /// Calls the given closure for each remaining entry in the iterator.
- pub fn for_each<F: FnMut(&ConfigEntry<'cfg>)>(mut self, mut f: F) -> Result<(), Error> {
- while let Some(entry) = self.next() {
- let entry = entry?;
- f(entry);
- }
- Ok(())
- }
-}
-
-impl<'cfg> Drop for ConfigEntries<'cfg> {
- fn drop(&mut self) {
- unsafe { raw::git_config_iterator_free(self.raw) }
- }
-}
-
-impl<'cfg> Drop for ConfigEntry<'cfg> {
- fn drop(&mut self) {
- if self.owned {
- unsafe { raw::git_config_entry_free(self.raw) }
- }
- }
-}
-
-#[cfg(test)]
-mod tests {
- use std::fs::File;
- use tempfile::TempDir;
-
- use crate::Config;
-
- #[test]
- fn smoke() {
- let _cfg = Config::new().unwrap();
- let _ = Config::find_global();
- let _ = Config::find_system();
- let _ = Config::find_xdg();
- }
-
- #[test]
- fn persisted() {
- let td = TempDir::new().unwrap();
- let path = td.path().join("foo");
- File::create(&path).unwrap();
-
- let mut cfg = Config::open(&path).unwrap();
- assert!(cfg.get_bool("foo.bar").is_err());
- cfg.set_bool("foo.k1", true).unwrap();
- cfg.set_i32("foo.k2", 1).unwrap();
- cfg.set_i64("foo.k3", 2).unwrap();
- cfg.set_str("foo.k4", "bar").unwrap();
- cfg.snapshot().unwrap();
- drop(cfg);
-
- let cfg = Config::open(&path).unwrap().snapshot().unwrap();
- assert_eq!(cfg.get_bool("foo.k1").unwrap(), true);
- assert_eq!(cfg.get_i32("foo.k2").unwrap(), 1);
- assert_eq!(cfg.get_i64("foo.k3").unwrap(), 2);
- assert_eq!(cfg.get_str("foo.k4").unwrap(), "bar");
-
- let mut entries = cfg.entries(None).unwrap();
- while let Some(entry) = entries.next() {
- let entry = entry.unwrap();
- entry.name();
- entry.value();
- entry.level();
- }
- }
-
- #[test]
- fn multivar() {
- let td = TempDir::new().unwrap();
- let path = td.path().join("foo");
- File::create(&path).unwrap();
-
- let mut cfg = Config::open(&path).unwrap();
- cfg.set_multivar("foo.bar", "^$", "baz").unwrap();
- cfg.set_multivar("foo.bar", "^$", "qux").unwrap();
- cfg.set_multivar("foo.bar", "^$", "quux").unwrap();
- cfg.set_multivar("foo.baz", "^$", "oki").unwrap();
-
- // `entries` filters by name
- let mut entries: Vec<String> = Vec::new();
- cfg.entries(Some("foo.bar"))
- .unwrap()
- .for_each(|entry| entries.push(entry.value().unwrap().to_string()))
- .unwrap();
- entries.sort();
- assert_eq!(entries, ["baz", "quux", "qux"]);
-
- // which is the same as `multivar` without a regex
- let mut multivals = Vec::new();
- cfg.multivar("foo.bar", None)
- .unwrap()
- .for_each(|entry| multivals.push(entry.value().unwrap().to_string()))
- .unwrap();
- multivals.sort();
- assert_eq!(multivals, entries);
-
- // yet _with_ a regex, `multivar` filters by value
- let mut quxish = Vec::new();
- cfg.multivar("foo.bar", Some("qu.*x"))
- .unwrap()
- .for_each(|entry| quxish.push(entry.value().unwrap().to_string()))
- .unwrap();
- quxish.sort();
- assert_eq!(quxish, ["quux", "qux"]);
-
- cfg.remove_multivar("foo.bar", ".*").unwrap();
-
- let count = |entries: super::ConfigEntries<'_>| -> usize {
- let mut c = 0;
- entries.for_each(|_| c += 1).unwrap();
- c
- };
-
- assert_eq!(count(cfg.entries(Some("foo.bar")).unwrap()), 0);
- assert_eq!(count(cfg.multivar("foo.bar", None).unwrap()), 0);
- }
-
- #[test]
- fn parse() {
- assert_eq!(Config::parse_bool("").unwrap(), false);
- assert_eq!(Config::parse_bool("false").unwrap(), false);
- assert_eq!(Config::parse_bool("no").unwrap(), false);
- assert_eq!(Config::parse_bool("off").unwrap(), false);
- assert_eq!(Config::parse_bool("0").unwrap(), false);
-
- assert_eq!(Config::parse_bool("true").unwrap(), true);
- assert_eq!(Config::parse_bool("yes").unwrap(), true);
- assert_eq!(Config::parse_bool("on").unwrap(), true);
- assert_eq!(Config::parse_bool("1").unwrap(), true);
- assert_eq!(Config::parse_bool("42").unwrap(), true);
-
- assert!(Config::parse_bool(" ").is_err());
- assert!(Config::parse_bool("some-string").is_err());
- assert!(Config::parse_bool("-").is_err());
-
- assert_eq!(Config::parse_i32("0").unwrap(), 0);
- assert_eq!(Config::parse_i32("1").unwrap(), 1);
- assert_eq!(Config::parse_i32("100").unwrap(), 100);
- assert_eq!(Config::parse_i32("-1").unwrap(), -1);
- assert_eq!(Config::parse_i32("-100").unwrap(), -100);
- assert_eq!(Config::parse_i32("1k").unwrap(), 1024);
- assert_eq!(Config::parse_i32("4k").unwrap(), 4096);
- assert_eq!(Config::parse_i32("1M").unwrap(), 1048576);
- assert_eq!(Config::parse_i32("1G").unwrap(), 1024 * 1024 * 1024);
-
- assert_eq!(Config::parse_i64("0").unwrap(), 0);
- assert_eq!(Config::parse_i64("1").unwrap(), 1);
- assert_eq!(Config::parse_i64("100").unwrap(), 100);
- assert_eq!(Config::parse_i64("-1").unwrap(), -1);
- assert_eq!(Config::parse_i64("-100").unwrap(), -100);
- assert_eq!(Config::parse_i64("1k").unwrap(), 1024);
- assert_eq!(Config::parse_i64("4k").unwrap(), 4096);
- assert_eq!(Config::parse_i64("1M").unwrap(), 1048576);
- assert_eq!(Config::parse_i64("1G").unwrap(), 1024 * 1024 * 1024);
- assert_eq!(Config::parse_i64("100G").unwrap(), 100 * 1024 * 1024 * 1024);
- }
-}
diff --git a/extra/git2/src/cred.rs b/extra/git2/src/cred.rs
deleted file mode 100644
index 49afb4239..000000000
--- a/extra/git2/src/cred.rs
+++ /dev/null
@@ -1,717 +0,0 @@
-use log::{debug, trace};
-use std::ffi::CString;
-use std::io::Write;
-use std::mem;
-use std::path::Path;
-use std::process::{Command, Stdio};
-use std::ptr;
-use url;
-
-use crate::util::Binding;
-use crate::{raw, Config, Error, IntoCString};
-
-/// A structure to represent git credentials in libgit2.
-pub struct Cred {
- raw: *mut raw::git_cred,
-}
-
-/// Management of the gitcredentials(7) interface.
-pub struct CredentialHelper {
- /// A public field representing the currently discovered username from
- /// configuration.
- pub username: Option<String>,
- protocol: Option<String>,
- host: Option<String>,
- port: Option<u16>,
- path: Option<String>,
- url: String,
- commands: Vec<String>,
-}
-
-impl Cred {
- /// Create a "default" credential usable for Negotiate mechanisms like NTLM
- /// or Kerberos authentication.
- pub fn default() -> Result<Cred, Error> {
- crate::init();
- let mut out = ptr::null_mut();
- unsafe {
- try_call!(raw::git_cred_default_new(&mut out));
- Ok(Binding::from_raw(out))
- }
- }
-
- /// Create a new ssh key credential object used for querying an ssh-agent.
- ///
- /// The username specified is the username to authenticate.
- pub fn ssh_key_from_agent(username: &str) -> Result<Cred, Error> {
- crate::init();
- let mut out = ptr::null_mut();
- let username = CString::new(username)?;
- unsafe {
- try_call!(raw::git_cred_ssh_key_from_agent(&mut out, username));
- Ok(Binding::from_raw(out))
- }
- }
-
- /// Create a new passphrase-protected ssh key credential object.
- pub fn ssh_key(
- username: &str,
- publickey: Option<&Path>,
- privatekey: &Path,
- passphrase: Option<&str>,
- ) -> Result<Cred, Error> {
- crate::init();
- let username = CString::new(username)?;
- let publickey = crate::opt_cstr(publickey)?;
- let privatekey = privatekey.into_c_string()?;
- let passphrase = crate::opt_cstr(passphrase)?;
- let mut out = ptr::null_mut();
- unsafe {
- try_call!(raw::git_cred_ssh_key_new(
- &mut out, username, publickey, privatekey, passphrase
- ));
- Ok(Binding::from_raw(out))
- }
- }
-
- /// Create a new ssh key credential object reading the keys from memory.
- pub fn ssh_key_from_memory(
- username: &str,
- publickey: Option<&str>,
- privatekey: &str,
- passphrase: Option<&str>,
- ) -> Result<Cred, Error> {
- crate::init();
- let username = CString::new(username)?;
- let publickey = crate::opt_cstr(publickey)?;
- let privatekey = CString::new(privatekey)?;
- let passphrase = crate::opt_cstr(passphrase)?;
- let mut out = ptr::null_mut();
- unsafe {
- try_call!(raw::git_cred_ssh_key_memory_new(
- &mut out, username, publickey, privatekey, passphrase
- ));
- Ok(Binding::from_raw(out))
- }
- }
-
- /// Create a new plain-text username and password credential object.
- pub fn userpass_plaintext(username: &str, password: &str) -> Result<Cred, Error> {
- crate::init();
- let username = CString::new(username)?;
- let password = CString::new(password)?;
- let mut out = ptr::null_mut();
- unsafe {
- try_call!(raw::git_cred_userpass_plaintext_new(
- &mut out, username, password
- ));
- Ok(Binding::from_raw(out))
- }
- }
-
- /// Attempt to read `credential.helper` according to gitcredentials(7) [1]
- ///
- /// This function will attempt to parse the user's `credential.helper`
- /// configuration, invoke the necessary processes, and read off what the
- /// username/password should be for a particular URL.
- ///
- /// The returned credential type will be a username/password credential if
- /// successful.
- ///
- /// [1]: https://www.kernel.org/pub/software/scm/git/docs/gitcredentials.html
- pub fn credential_helper(
- config: &Config,
- url: &str,
- username: Option<&str>,
- ) -> Result<Cred, Error> {
- match CredentialHelper::new(url)
- .config(config)
- .username(username)
- .execute()
- {
- Some((username, password)) => Cred::userpass_plaintext(&username, &password),
- None => Err(Error::from_str(
- "failed to acquire username/password \
- from local configuration",
- )),
- }
- }
-
- /// Create a credential to specify a username.
- ///
- /// This is used with ssh authentication to query for the username if none is
- /// specified in the URL.
- pub fn username(username: &str) -> Result<Cred, Error> {
- crate::init();
- let username = CString::new(username)?;
- let mut out = ptr::null_mut();
- unsafe {
- try_call!(raw::git_cred_username_new(&mut out, username));
- Ok(Binding::from_raw(out))
- }
- }
-
- /// Check whether a credential object contains username information.
- pub fn has_username(&self) -> bool {
- unsafe { raw::git_cred_has_username(self.raw) == 1 }
- }
-
- /// Return the type of credentials that this object represents.
- pub fn credtype(&self) -> raw::git_credtype_t {
- unsafe { (*self.raw).credtype }
- }
-
- /// Unwrap access to the underlying raw pointer, canceling the destructor
- pub unsafe fn unwrap(mut self) -> *mut raw::git_cred {
- mem::replace(&mut self.raw, ptr::null_mut())
- }
-}
-
-impl Binding for Cred {
- type Raw = *mut raw::git_cred;
-
- unsafe fn from_raw(raw: *mut raw::git_cred) -> Cred {
- Cred { raw }
- }
- fn raw(&self) -> *mut raw::git_cred {
- self.raw
- }
-}
-
-impl Drop for Cred {
- fn drop(&mut self) {
- if !self.raw.is_null() {
- unsafe {
- if let Some(f) = (*self.raw).free {
- f(self.raw)
- }
- }
- }
- }
-}
-
-impl CredentialHelper {
- /// Create a new credential helper object which will be used to probe git's
- /// local credential configuration.
- ///
- /// The URL specified is the namespace on which this will query credentials.
- /// Invalid URLs are currently ignored.
- pub fn new(url: &str) -> CredentialHelper {
- let mut ret = CredentialHelper {
- protocol: None,
- host: None,
- port: None,
- path: None,
- username: None,
- url: url.to_string(),
- commands: Vec::new(),
- };
-
- // Parse out the (protocol, host) if one is available
- if let Ok(url) = url::Url::parse(url) {
- if let Some(url::Host::Domain(s)) = url.host() {
- ret.host = Some(s.to_string());
- }
- ret.port = url.port();
- ret.protocol = Some(url.scheme().to_string());
- }
- ret
- }
-
- /// Set the username that this credential helper will query with.
- ///
- /// By default the username is `None`.
- pub fn username(&mut self, username: Option<&str>) -> &mut CredentialHelper {
- self.username = username.map(|s| s.to_string());
- self
- }
-
- /// Query the specified configuration object to discover commands to
- /// execute, usernames to query, etc.
- pub fn config(&mut self, config: &Config) -> &mut CredentialHelper {
- // Figure out the configured username/helper program.
- //
- // see http://git-scm.com/docs/gitcredentials.html#_configuration_options
- if self.username.is_none() {
- self.config_username(config);
- }
- self.config_helper(config);
- self.config_use_http_path(config);
- self
- }
-
- // Configure the queried username from `config`
- fn config_username(&mut self, config: &Config) {
- let key = self.exact_key("username");
- self.username = config
- .get_string(&key)
- .ok()
- .or_else(|| {
- self.url_key("username")
- .and_then(|s| config.get_string(&s).ok())
- })
- .or_else(|| config.get_string("credential.username").ok())
- }
-
- // Discover all `helper` directives from `config`
- fn config_helper(&mut self, config: &Config) {
- let exact = config.get_string(&self.exact_key("helper"));
- self.add_command(exact.as_ref().ok().map(|s| &s[..]));
- if let Some(key) = self.url_key("helper") {
- let url = config.get_string(&key);
- self.add_command(url.as_ref().ok().map(|s| &s[..]));
- }
- let global = config.get_string("credential.helper");
- self.add_command(global.as_ref().ok().map(|s| &s[..]));
- }
-
- // Discover `useHttpPath` from `config`
- fn config_use_http_path(&mut self, config: &Config) {
- let mut use_http_path = false;
- if let Some(value) = config.get_bool(&self.exact_key("useHttpPath")).ok() {
- use_http_path = value;
- } else if let Some(value) = self
- .url_key("useHttpPath")
- .and_then(|key| config.get_bool(&key).ok())
- {
- use_http_path = value;
- } else if let Some(value) = config.get_bool("credential.useHttpPath").ok() {
- use_http_path = value;
- }
-
- if use_http_path {
- if let Ok(url) = url::Url::parse(&self.url) {
- let path = url.path();
- // Url::parse always includes a leading slash for rooted URLs, while git does not.
- self.path = Some(path.strip_prefix('/').unwrap_or(path).to_string());
- }
- }
- }
-
- // Add a `helper` configured command to the list of commands to execute.
- //
- // see https://www.kernel.org/pub/software/scm/git/docs/technical
- // /api-credentials.html#_credential_helpers
- fn add_command(&mut self, cmd: Option<&str>) {
- let cmd = match cmd {
- Some("") | None => return,
- Some(s) => s,
- };
-
- if cmd.starts_with('!') {
- self.commands.push(cmd[1..].to_string());
- } else if cmd.contains("/") || cmd.contains("\\") {
- self.commands.push(cmd.to_string());
- } else {
- self.commands.push(format!("git credential-{}", cmd));
- }
- }
-
- fn exact_key(&self, name: &str) -> String {
- format!("credential.{}.{}", self.url, name)
- }
-
- fn url_key(&self, name: &str) -> Option<String> {
- match (&self.host, &self.protocol) {
- (&Some(ref host), &Some(ref protocol)) => {
- Some(format!("credential.{}://{}.{}", protocol, host, name))
- }
- _ => None,
- }
- }
-
- /// Execute this helper, attempting to discover a username/password pair.
- ///
- /// All I/O errors are ignored, (to match git behavior), and this function
- /// only succeeds if both a username and a password were found
- pub fn execute(&self) -> Option<(String, String)> {
- let mut username = self.username.clone();
- let mut password = None;
- for cmd in &self.commands {
- let (u, p) = self.execute_cmd(cmd, &username);
- if u.is_some() && username.is_none() {
- username = u;
- }
- if p.is_some() && password.is_none() {
- password = p;
- }
- if username.is_some() && password.is_some() {
- break;
- }
- }
-
- match (username, password) {
- (Some(u), Some(p)) => Some((u, p)),
- _ => None,
- }
- }
-
- // Execute the given `cmd`, providing the appropriate variables on stdin and
- // then afterwards parsing the output into the username/password on stdout.
- fn execute_cmd(
- &self,
- cmd: &str,
- username: &Option<String>,
- ) -> (Option<String>, Option<String>) {
- macro_rules! my_try( ($e:expr) => (
- match $e {
- Ok(e) => e,
- Err(e) => {
- debug!("{} failed with {}", stringify!($e), e);
- return (None, None)
- }
- }
- ) );
-
- // It looks like the `cmd` specification is typically bourne-shell-like
- // syntax, so try that first. If that fails, though, we may be on a
- // Windows machine for example where `sh` isn't actually available by
- // default. Most credential helper configurations though are pretty
- // simple (aka one or two space-separated strings) so also try to invoke
- // the process directly.
- //
- // If that fails then it's up to the user to put `sh` in path and make
- // sure it works.
- let mut c = Command::new("sh");
- c.arg("-c")
- .arg(&format!("{} get", cmd))
- .stdin(Stdio::piped())
- .stdout(Stdio::piped())
- .stderr(Stdio::piped());
- debug!("executing credential helper {:?}", c);
- let mut p = match c.spawn() {
- Ok(p) => p,
- Err(e) => {
- debug!("`sh` failed to spawn: {}", e);
- let mut parts = cmd.split_whitespace();
- let mut c = Command::new(parts.next().unwrap());
- for arg in parts {
- c.arg(arg);
- }
- c.arg("get")
- .stdin(Stdio::piped())
- .stdout(Stdio::piped())
- .stderr(Stdio::piped());
- debug!("executing credential helper {:?}", c);
- match c.spawn() {
- Ok(p) => p,
- Err(e) => {
- debug!("fallback of {:?} failed with {}", cmd, e);
- return (None, None);
- }
- }
- }
- };
-
- // Ignore write errors as the command may not actually be listening for
- // stdin
- {
- let stdin = p.stdin.as_mut().unwrap();
- if let Some(ref p) = self.protocol {
- let _ = writeln!(stdin, "protocol={}", p);
- }
- if let Some(ref p) = self.host {
- if let Some(ref p2) = self.port {
- let _ = writeln!(stdin, "host={}:{}", p, p2);
- } else {
- let _ = writeln!(stdin, "host={}", p);
- }
- }
- if let Some(ref p) = self.path {
- let _ = writeln!(stdin, "path={}", p);
- }
- if let Some(ref p) = *username {
- let _ = writeln!(stdin, "username={}", p);
- }
- }
- let output = my_try!(p.wait_with_output());
- if !output.status.success() {
- debug!(
- "credential helper failed: {}\nstdout ---\n{}\nstderr ---\n{}",
- output.status,
- String::from_utf8_lossy(&output.stdout),
- String::from_utf8_lossy(&output.stderr)
- );
- return (None, None);
- }
- trace!(
- "credential helper stderr ---\n{}",
- String::from_utf8_lossy(&output.stderr)
- );
- self.parse_output(output.stdout)
- }
-
- // Parse the output of a command into the username/password found
- fn parse_output(&self, output: Vec<u8>) -> (Option<String>, Option<String>) {
- // Parse the output of the command, looking for username/password
- let mut username = None;
- let mut password = None;
- for line in output.split(|t| *t == b'\n') {
- let mut parts = line.splitn(2, |t| *t == b'=');
- let key = parts.next().unwrap();
- let value = match parts.next() {
- Some(s) => s,
- None => {
- trace!("ignoring output line: {}", String::from_utf8_lossy(line));
- continue;
- }
- };
- let value = match String::from_utf8(value.to_vec()) {
- Ok(s) => s,
- Err(..) => continue,
- };
- match key {
- b"username" => username = Some(value),
- b"password" => password = Some(value),
- _ => {}
- }
- }
- (username, password)
- }
-}
-
-#[cfg(test)]
-mod test {
- use std::env;
- use std::fs::File;
- use std::io::prelude::*;
- use std::path::Path;
- use tempfile::TempDir;
-
- use crate::{Config, ConfigLevel, Cred, CredentialHelper};
-
- macro_rules! test_cfg( ($($k:expr => $v:expr),*) => ({
- let td = TempDir::new().unwrap();
- let mut cfg = Config::new().unwrap();
- cfg.add_file(&td.path().join("cfg"), ConfigLevel::Highest, false).unwrap();
- $(cfg.set_str($k, $v).unwrap();)*
- cfg
- }) );
-
- #[test]
- fn smoke() {
- Cred::default().unwrap();
- }
-
- #[test]
- fn credential_helper1() {
- let cfg = test_cfg! {
- "credential.helper" => "!f() { echo username=a; echo password=b; }; f"
- };
- let (u, p) = CredentialHelper::new("https://example.com/foo/bar")
- .config(&cfg)
- .execute()
- .unwrap();
- assert_eq!(u, "a");
- assert_eq!(p, "b");
- }
-
- #[test]
- fn credential_helper2() {
- let cfg = test_cfg! {};
- assert!(CredentialHelper::new("https://example.com/foo/bar")
- .config(&cfg)
- .execute()
- .is_none());
- }
-
- #[test]
- fn credential_helper3() {
- let cfg = test_cfg! {
- "credential.https://example.com.helper" =>
- "!f() { echo username=c; }; f",
- "credential.helper" => "!f() { echo username=a; echo password=b; }; f"
- };
- let (u, p) = CredentialHelper::new("https://example.com/foo/bar")
- .config(&cfg)
- .execute()
- .unwrap();
- assert_eq!(u, "c");
- assert_eq!(p, "b");
- }
-
- #[test]
- fn credential_helper4() {
- if cfg!(windows) {
- return;
- } // shell scripts don't work on Windows
-
- let td = TempDir::new().unwrap();
- let path = td.path().join("script");
- File::create(&path)
- .unwrap()
- .write(
- br"\
-#!/bin/sh
-echo username=c
-",
- )
- .unwrap();
- chmod(&path);
- let cfg = test_cfg! {
- "credential.https://example.com.helper" =>
- &path.display().to_string()[..],
- "credential.helper" => "!f() { echo username=a; echo password=b; }; f"
- };
- let (u, p) = CredentialHelper::new("https://example.com/foo/bar")
- .config(&cfg)
- .execute()
- .unwrap();
- assert_eq!(u, "c");
- assert_eq!(p, "b");
- }
-
- #[test]
- fn credential_helper5() {
- if !Path::new("/usr/bin/git").exists() {
- return;
- } //this test does not work if git is not installed
- if cfg!(windows) {
- return;
- } // shell scripts don't work on Windows
- let td = TempDir::new().unwrap();
- let path = td.path().join("git-credential-script");
- File::create(&path)
- .unwrap()
- .write(
- br"\
-#!/bin/sh
-echo username=c
-",
- )
- .unwrap();
- chmod(&path);
-
- let paths = env::var("PATH").unwrap();
- let paths =
- env::split_paths(&paths).chain(path.parent().map(|p| p.to_path_buf()).into_iter());
- env::set_var("PATH", &env::join_paths(paths).unwrap());
-
- let cfg = test_cfg! {
- "credential.https://example.com.helper" => "script",
- "credential.helper" => "!f() { echo username=a; echo password=b; }; f"
- };
- let (u, p) = CredentialHelper::new("https://example.com/foo/bar")
- .config(&cfg)
- .execute()
- .unwrap();
- assert_eq!(u, "c");
- assert_eq!(p, "b");
- }
-
- #[test]
- fn credential_helper6() {
- let cfg = test_cfg! {
- "credential.helper" => ""
- };
- assert!(CredentialHelper::new("https://example.com/foo/bar")
- .config(&cfg)
- .execute()
- .is_none());
- }
-
- #[test]
- fn credential_helper7() {
- if cfg!(windows) {
- return;
- } // shell scripts don't work on Windows
- let td = TempDir::new().unwrap();
- let path = td.path().join("script");
- File::create(&path)
- .unwrap()
- .write(
- br"\
-#!/bin/sh
-echo username=$1
-echo password=$2
-",
- )
- .unwrap();
- chmod(&path);
- let cfg = test_cfg! {
- "credential.helper" => &format!("{} a b", path.display())
- };
- let (u, p) = CredentialHelper::new("https://example.com/foo/bar")
- .config(&cfg)
- .execute()
- .unwrap();
- assert_eq!(u, "a");
- assert_eq!(p, "b");
- }
-
- #[test]
- fn credential_helper8() {
- let cfg = test_cfg! {
- "credential.useHttpPath" => "true"
- };
- let mut helper = CredentialHelper::new("https://example.com/foo/bar");
- helper.config(&cfg);
- assert_eq!(helper.path.as_deref(), Some("foo/bar"));
- }
-
- #[test]
- fn credential_helper9() {
- let cfg = test_cfg! {
- "credential.helper" => "!f() { while read line; do eval $line; done; if [ \"$host\" = example.com:3000 ]; then echo username=a; echo password=b; fi; }; f"
- };
- let (u, p) = CredentialHelper::new("https://example.com:3000/foo/bar")
- .config(&cfg)
- .execute()
- .unwrap();
- assert_eq!(u, "a");
- assert_eq!(p, "b");
- }
-
- #[test]
- #[cfg(feature = "ssh")]
- fn ssh_key_from_memory() {
- let cred = Cred::ssh_key_from_memory(
- "test",
- Some("ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDByAO8uj+kXicj6C2ODMspgmUoVyl5eaw8vR6a1yEnFuJFzevabNlN6Ut+CPT3TRnYk5BW73pyXBtnSL2X95BOnbjMDXc4YIkgs3YYHWnxbqsD4Pj/RoGqhf+gwhOBtL0poh8tT8WqXZYxdJQKLQC7oBqf3ykCEYulE4oeRUmNh4IzEE+skD/zDkaJ+S1HRD8D8YCiTO01qQnSmoDFdmIZTi8MS8Cw+O/Qhym1271ThMlhD6PubSYJXfE6rVbE7A9RzH73A6MmKBlzK8VTb4SlNSrr/DOk+L0uq+wPkv+pm+D9WtxoqQ9yl6FaK1cPawa3+7yRNle3m+72KCtyMkQv"),
- r#"
- -----BEGIN RSA PRIVATE KEY-----
- Proc-Type: 4,ENCRYPTED
- DEK-Info: AES-128-CBC,818C7722D3B01F2161C2ACF6A5BBAAE8
-
- 3Cht4QB3PcoQ0I55j1B3m2ZzIC/mrh+K5nQeA1Vy2GBTMyM7yqGHqTOv7qLhJscd
- H+cB0Pm6yCr3lYuNrcKWOCUto+91P7ikyARruHVwyIxKdNx15uNulOzQJHQWNbA4
- RQHlhjON4atVo2FyJ6n+ujK6QiBg2PR5Vbbw/AtV6zBCFW3PhzDn+qqmHjpBFqj2
- vZUUe+MkDQcaF5J45XMHahhSdo/uKCDhfbylExp/+ACWkvxdPpsvcARM6X434ucD
- aPY+4i0/JyLkdbm0GFN9/q3i53qf4kCBhojFl4AYJdGI0AzAgbdTXZ7EJHbAGZHS
- os5K0oTwDVXMI0sSE2I/qHxaZZsDP1dOKq6di6SFPUp8liYimm7rNintRX88Gl2L
- g1ko9abp/NlgD0YY/3mad+NNAISDL/YfXq2fklH3En3/7ZrOVZFKfZXwQwas5g+p
- VQPKi3+ae74iOjLyuPDSc1ePmhUNYeP+9rLSc0wiaiHqls+2blPPDxAGMEo63kbz
- YPVjdmuVX4VWnyEsfTxxJdFDYGSNh6rlrrO1RFrex7kJvpg5gTX4M/FT8TfCd7Hn
- M6adXsLMqwu5tz8FuDmAtVdq8zdSrgZeAbpJ9D3EDOmZ70xz4XBL19ImxDp+Qqs2
- kQX7kobRzeeP2URfRoGr7XZikQWyQ2UASfPcQULY8R58QoZWWsQ4w51GZHg7TDnw
- 1DRo/0OgkK7Gqf215nFmMpB4uyi58cq3WFwWQa1IqslkObpVgBQZcNZb/hKUYPGk
- g4zehfIgAfCdnQHwZvQ6Fdzhcs3SZeO+zVyuiZN3Gsi9HU0/1vpAKiuuOzcG02vF
- b6Y6hwsAA9yphF3atI+ARD4ZwXdDfzuGb3yJglMT3Fr/xuLwAvdchRo1spANKA0E
- tT5okLrK0H4wnHvf2SniVVWRhmJis0lQo9LjGGwRIdsPpVnJSDvaISIVF+fHT90r
- HvxN8zXI93x9jcPtwp7puQ1C7ehKJK10sZ71OLIZeuUgwt+5DRunqg6evPco9Go7
- UOGwcVhLY200KT+1k7zWzCS0yVQp2HRm6cxsZXAp4ClBSwIx15eIoLIrjZdJRjCq
- COp6pZx1fnvJ9ERIvl5hon+Ty+renMcFKz2HmchC7egpcqIxW9Dsv6zjhHle6pxb
- 37GaEKHF2KA3RN+dSV/K8n+C9Yent5tx5Y9a/pMcgRGtgu+G+nyFmkPKn5Zt39yX
- qDpyM0LtbRVZPs+MgiqoGIwYc/ujoCq7GL38gezsBQoHaTt79yYBqCp6UR0LMuZ5
- f/7CtWqffgySfJ/0wjGidDAumDv8CK45AURpL/Z+tbFG3M9ar/LZz/Y6EyBcLtGY
- Wwb4zs8zXIA0qHrjNTnPqHDvezziArYfgPjxCIHMZzms9Yn8+N02p39uIytqg434
- BAlCqZ7GYdDFfTpWIwX+segTK9ux0KdBqcQv+9Fwwjkq9KySnRKqNl7ZJcefFZJq
- c6PA1iinZWBjuaO1HKx3PFulrl0bcpR9Kud1ZIyfnh5rwYN8UQkkcR/wZPla04TY
- 8l5dq/LI/3G5sZXwUHKOcuQWTj7Saq7Q6gkKoMfqt0wC5bpZ1m17GHPoMz6GtX9O
- -----END RSA PRIVATE KEY-----
- "#,
- Some("test123"));
- assert!(cred.is_ok());
- }
-
- #[cfg(unix)]
- fn chmod(path: &Path) {
- use std::fs;
- use std::os::unix::prelude::*;
- let mut perms = fs::metadata(path).unwrap().permissions();
- perms.set_mode(0o755);
- fs::set_permissions(path, perms).unwrap();
- }
- #[cfg(windows)]
- fn chmod(_path: &Path) {}
-}
diff --git a/extra/git2/src/describe.rs b/extra/git2/src/describe.rs
deleted file mode 100644
index cbaa1893b..000000000
--- a/extra/git2/src/describe.rs
+++ /dev/null
@@ -1,201 +0,0 @@
-use std::ffi::CString;
-use std::marker;
-use std::mem;
-use std::ptr;
-
-use libc::{c_int, c_uint};
-
-use crate::util::Binding;
-use crate::{raw, Buf, Error, Repository};
-
-/// The result of a `describe` operation on either an `Describe` or a
-/// `Repository`.
-pub struct Describe<'repo> {
- raw: *mut raw::git_describe_result,
- _marker: marker::PhantomData<&'repo Repository>,
-}
-
-/// Options which indicate how a `Describe` is created.
-pub struct DescribeOptions {
- raw: raw::git_describe_options,
- pattern: CString,
-}
-
-/// Options which can be used to customize how a description is formatted.
-pub struct DescribeFormatOptions {
- raw: raw::git_describe_format_options,
- dirty_suffix: CString,
-}
-
-impl<'repo> Describe<'repo> {
- /// Prints this describe result, returning the result as a string.
- pub fn format(&self, opts: Option<&DescribeFormatOptions>) -> Result<String, Error> {
- let buf = Buf::new();
- let opts = opts.map(|o| &o.raw as *const _).unwrap_or(ptr::null());
- unsafe {
- try_call!(raw::git_describe_format(buf.raw(), self.raw, opts));
- }
- Ok(String::from_utf8(buf.to_vec()).unwrap())
- }
-}
-
-impl<'repo> Binding for Describe<'repo> {
- type Raw = *mut raw::git_describe_result;
-
- unsafe fn from_raw(raw: *mut raw::git_describe_result) -> Describe<'repo> {
- Describe {
- raw,
- _marker: marker::PhantomData,
- }
- }
- fn raw(&self) -> *mut raw::git_describe_result {
- self.raw
- }
-}
-
-impl<'repo> Drop for Describe<'repo> {
- fn drop(&mut self) {
- unsafe { raw::git_describe_result_free(self.raw) }
- }
-}
-
-impl Default for DescribeFormatOptions {
- fn default() -> Self {
- Self::new()
- }
-}
-
-impl DescribeFormatOptions {
- /// Creates a new blank set of formatting options for a description.
- pub fn new() -> DescribeFormatOptions {
- let mut opts = DescribeFormatOptions {
- raw: unsafe { mem::zeroed() },
- dirty_suffix: CString::new(Vec::new()).unwrap(),
- };
- opts.raw.version = 1;
- opts.raw.abbreviated_size = 7;
- opts
- }
-
- /// Sets the size of the abbreviated commit id to use.
- ///
- /// The value is the lower bound for the length of the abbreviated string,
- /// and the default is 7.
- pub fn abbreviated_size(&mut self, size: u32) -> &mut Self {
- self.raw.abbreviated_size = size as c_uint;
- self
- }
-
- /// Sets whether or not the long format is used even when a shorter name
- /// could be used.
- pub fn always_use_long_format(&mut self, long: bool) -> &mut Self {
- self.raw.always_use_long_format = long as c_int;
- self
- }
-
- /// If the workdir is dirty and this is set, this string will be appended to
- /// the description string.
- pub fn dirty_suffix(&mut self, suffix: &str) -> &mut Self {
- self.dirty_suffix = CString::new(suffix).unwrap();
- self.raw.dirty_suffix = self.dirty_suffix.as_ptr();
- self
- }
-}
-
-impl Default for DescribeOptions {
- fn default() -> Self {
- Self::new()
- }
-}
-
-impl DescribeOptions {
- /// Creates a new blank set of formatting options for a description.
- pub fn new() -> DescribeOptions {
- let mut opts = DescribeOptions {
- raw: unsafe { mem::zeroed() },
- pattern: CString::new(Vec::new()).unwrap(),
- };
- opts.raw.version = 1;
- opts.raw.max_candidates_tags = 10;
- opts
- }
-
- #[allow(missing_docs)]
- pub fn max_candidates_tags(&mut self, max: u32) -> &mut Self {
- self.raw.max_candidates_tags = max as c_uint;
- self
- }
-
- /// Sets the reference lookup strategy
- ///
- /// This behaves like the `--tags` option to git-describe.
- pub fn describe_tags(&mut self) -> &mut Self {
- self.raw.describe_strategy = raw::GIT_DESCRIBE_TAGS as c_uint;
- self
- }
-
- /// Sets the reference lookup strategy
- ///
- /// This behaves like the `--all` option to git-describe.
- pub fn describe_all(&mut self) -> &mut Self {
- self.raw.describe_strategy = raw::GIT_DESCRIBE_ALL as c_uint;
- self
- }
-
- /// Indicates when calculating the distance from the matching tag or
- /// reference whether to only walk down the first-parent ancestry.
- pub fn only_follow_first_parent(&mut self, follow: bool) -> &mut Self {
- self.raw.only_follow_first_parent = follow as c_int;
- self
- }
-
- /// If no matching tag or reference is found whether a describe option would
- /// normally fail. This option indicates, however, that it will instead fall
- /// back to showing the full id of the commit.
- pub fn show_commit_oid_as_fallback(&mut self, show: bool) -> &mut Self {
- self.raw.show_commit_oid_as_fallback = show as c_int;
- self
- }
-
- #[allow(missing_docs)]
- pub fn pattern(&mut self, pattern: &str) -> &mut Self {
- self.pattern = CString::new(pattern).unwrap();
- self.raw.pattern = self.pattern.as_ptr();
- self
- }
-}
-
-impl Binding for DescribeOptions {
- type Raw = *mut raw::git_describe_options;
-
- unsafe fn from_raw(_raw: *mut raw::git_describe_options) -> DescribeOptions {
- panic!("unimplemened")
- }
- fn raw(&self) -> *mut raw::git_describe_options {
- &self.raw as *const _ as *mut _
- }
-}
-
-#[cfg(test)]
-mod tests {
- use crate::DescribeOptions;
-
- #[test]
- fn smoke() {
- let (_td, repo) = crate::test::repo_init();
- let head = t!(repo.head()).target().unwrap();
-
- let d = t!(repo.describe(DescribeOptions::new().show_commit_oid_as_fallback(true)));
- let id = head.to_string();
- assert_eq!(t!(d.format(None)), &id[..7]);
-
- let obj = t!(repo.find_object(head, None));
- let sig = t!(repo.signature());
- t!(repo.tag("foo", &obj, &sig, "message", true));
- let d = t!(repo.describe(&DescribeOptions::new()));
- assert_eq!(t!(d.format(None)), "foo");
-
- let d = t!(obj.describe(&DescribeOptions::new()));
- assert_eq!(t!(d.format(None)), "foo");
- }
-}
diff --git a/extra/git2/src/diff.rs b/extra/git2/src/diff.rs
deleted file mode 100644
index 16595509d..000000000
--- a/extra/git2/src/diff.rs
+++ /dev/null
@@ -1,1863 +0,0 @@
-use libc::{c_char, c_int, c_void, size_t};
-use std::ffi::CString;
-use std::iter::FusedIterator;
-use std::marker;
-use std::mem;
-use std::ops::Range;
-use std::path::Path;
-use std::ptr;
-use std::slice;
-
-use crate::util::{self, Binding};
-use crate::{panic, raw, Buf, Delta, DiffFormat, Error, FileMode, Oid, Repository};
-use crate::{DiffFlags, DiffStatsFormat, IntoCString};
-
-/// The diff object that contains all individual file deltas.
-///
-/// This is an opaque structure which will be allocated by one of the diff
-/// generator functions on the `Repository` structure (e.g. `diff_tree_to_tree`
-/// or other `diff_*` functions).
-pub struct Diff<'repo> {
- raw: *mut raw::git_diff,
- _marker: marker::PhantomData<&'repo Repository>,
-}
-
-unsafe impl<'repo> Send for Diff<'repo> {}
-
-/// Description of changes to one entry.
-pub struct DiffDelta<'a> {
- raw: *mut raw::git_diff_delta,
- _marker: marker::PhantomData<&'a raw::git_diff_delta>,
-}
-
-/// Description of one side of a delta.
-///
-/// Although this is called a "file" it could represent a file, a symbolic
-/// link, a submodule commit id, or even a tree (although that only happens if
-/// you are tracking type changes or ignored/untracked directories).
-pub struct DiffFile<'a> {
- raw: *const raw::git_diff_file,
- _marker: marker::PhantomData<&'a raw::git_diff_file>,
-}
-
-/// Structure describing options about how the diff should be executed.
-pub struct DiffOptions {
- pathspec: Vec<CString>,
- pathspec_ptrs: Vec<*const c_char>,
- old_prefix: Option<CString>,
- new_prefix: Option<CString>,
- raw: raw::git_diff_options,
-}
-
-/// Control behavior of rename and copy detection
-pub struct DiffFindOptions {
- raw: raw::git_diff_find_options,
-}
-
-/// Control behavior of formatting emails
-pub struct DiffFormatEmailOptions {
- raw: raw::git_diff_format_email_options,
-}
-
-/// Control behavior of formatting emails
-pub struct DiffPatchidOptions {
- raw: raw::git_diff_patchid_options,
-}
-
-/// An iterator over the diffs in a delta
-pub struct Deltas<'diff> {
- range: Range<usize>,
- diff: &'diff Diff<'diff>,
-}
-
-/// Structure describing a line (or data span) of a diff.
-pub struct DiffLine<'a> {
- raw: *const raw::git_diff_line,
- _marker: marker::PhantomData<&'a raw::git_diff_line>,
-}
-
-/// Structure describing a hunk of a diff.
-pub struct DiffHunk<'a> {
- raw: *const raw::git_diff_hunk,
- _marker: marker::PhantomData<&'a raw::git_diff_hunk>,
-}
-
-/// Structure describing a hunk of a diff.
-pub struct DiffStats {
- raw: *mut raw::git_diff_stats,
-}
-
-/// Structure describing the binary contents of a diff.
-pub struct DiffBinary<'a> {
- raw: *const raw::git_diff_binary,
- _marker: marker::PhantomData<&'a raw::git_diff_binary>,
-}
-
-/// The contents of one of the files in a binary diff.
-pub struct DiffBinaryFile<'a> {
- raw: *const raw::git_diff_binary_file,
- _marker: marker::PhantomData<&'a raw::git_diff_binary_file>,
-}
-
-/// When producing a binary diff, the binary data returned will be
-/// either the deflated full ("literal") contents of the file, or
-/// the deflated binary delta between the two sides (whichever is
-/// smaller).
-#[derive(Copy, Clone, Debug)]
-pub enum DiffBinaryKind {
- /// There is no binary delta
- None,
- /// The binary data is the literal contents of the file
- Literal,
- /// The binary data is the delta from one side to the other
- Delta,
-}
-
-type PrintCb<'a> = dyn FnMut(DiffDelta<'_>, Option<DiffHunk<'_>>, DiffLine<'_>) -> bool + 'a;
-
-pub type FileCb<'a> = dyn FnMut(DiffDelta<'_>, f32) -> bool + 'a;
-pub type BinaryCb<'a> = dyn FnMut(DiffDelta<'_>, DiffBinary<'_>) -> bool + 'a;
-pub type HunkCb<'a> = dyn FnMut(DiffDelta<'_>, DiffHunk<'_>) -> bool + 'a;
-pub type LineCb<'a> = dyn FnMut(DiffDelta<'_>, Option<DiffHunk<'_>>, DiffLine<'_>) -> bool + 'a;
-
-pub struct DiffCallbacks<'a, 'b, 'c, 'd, 'e, 'f, 'g, 'h> {
- pub file: Option<&'a mut FileCb<'b>>,
- pub binary: Option<&'c mut BinaryCb<'d>>,
- pub hunk: Option<&'e mut HunkCb<'f>>,
- pub line: Option<&'g mut LineCb<'h>>,
-}
-
-impl<'repo> Diff<'repo> {
- /// Merge one diff into another.
- ///
- /// This merges items from the "from" list into the "self" list. The
- /// resulting diff will have all items that appear in either list.
- /// If an item appears in both lists, then it will be "merged" to appear
- /// as if the old version was from the "onto" list and the new version
- /// is from the "from" list (with the exception that if the item has a
- /// pending DELETE in the middle, then it will show as deleted).
- pub fn merge(&mut self, from: &Diff<'repo>) -> Result<(), Error> {
- unsafe {
- try_call!(raw::git_diff_merge(self.raw, &*from.raw));
- }
- Ok(())
- }
-
- /// Returns an iterator over the deltas in this diff.
- pub fn deltas(&self) -> Deltas<'_> {
- let num_deltas = unsafe { raw::git_diff_num_deltas(&*self.raw) };
- Deltas {
- range: 0..(num_deltas as usize),
- diff: self,
- }
- }
-
- /// Return the diff delta for an entry in the diff list.
- pub fn get_delta(&self, i: usize) -> Option<DiffDelta<'_>> {
- unsafe {
- let ptr = raw::git_diff_get_delta(&*self.raw, i as size_t);
- Binding::from_raw_opt(ptr as *mut _)
- }
- }
-
- /// Check if deltas are sorted case sensitively or insensitively.
- pub fn is_sorted_icase(&self) -> bool {
- unsafe { raw::git_diff_is_sorted_icase(&*self.raw) == 1 }
- }
-
- /// Iterate over a diff generating formatted text output.
- ///
- /// Returning `false` from the callback will terminate the iteration and
- /// return an error from this function.
- pub fn print<F>(&self, format: DiffFormat, mut cb: F) -> Result<(), Error>
- where
- F: FnMut(DiffDelta<'_>, Option<DiffHunk<'_>>, DiffLine<'_>) -> bool,
- {
- let mut cb: &mut PrintCb<'_> = &mut cb;
- let ptr = &mut cb as *mut _;
- let print: raw::git_diff_line_cb = Some(print_cb);
- unsafe {
- try_call!(raw::git_diff_print(self.raw, format, print, ptr as *mut _));
- Ok(())
- }
- }
-
- /// Loop over all deltas in a diff issuing callbacks.
- ///
- /// Returning `false` from any callback will terminate the iteration and
- /// return an error from this function.
- pub fn foreach(
- &self,
- file_cb: &mut FileCb<'_>,
- binary_cb: Option<&mut BinaryCb<'_>>,
- hunk_cb: Option<&mut HunkCb<'_>>,
- line_cb: Option<&mut LineCb<'_>>,
- ) -> Result<(), Error> {
- let mut cbs = DiffCallbacks {
- file: Some(file_cb),
- binary: binary_cb,
- hunk: hunk_cb,
- line: line_cb,
- };
- let ptr = &mut cbs as *mut _;
- unsafe {
- let binary_cb_c: raw::git_diff_binary_cb = if cbs.binary.is_some() {
- Some(binary_cb_c)
- } else {
- None
- };
- let hunk_cb_c: raw::git_diff_hunk_cb = if cbs.hunk.is_some() {
- Some(hunk_cb_c)
- } else {
- None
- };
- let line_cb_c: raw::git_diff_line_cb = if cbs.line.is_some() {
- Some(line_cb_c)
- } else {
- None
- };
- let file_cb: raw::git_diff_file_cb = Some(file_cb_c);
- try_call!(raw::git_diff_foreach(
- self.raw,
- file_cb,
- binary_cb_c,
- hunk_cb_c,
- line_cb_c,
- ptr as *mut _
- ));
- Ok(())
- }
- }
-
- /// Accumulate diff statistics for all patches.
- pub fn stats(&self) -> Result<DiffStats, Error> {
- let mut ret = ptr::null_mut();
- unsafe {
- try_call!(raw::git_diff_get_stats(&mut ret, self.raw));
- Ok(Binding::from_raw(ret))
- }
- }
-
- /// Transform a diff marking file renames, copies, etc.
- ///
- /// This modifies a diff in place, replacing old entries that look like
- /// renames or copies with new entries reflecting those changes. This also
- /// will, if requested, break modified files into add/remove pairs if the
- /// amount of change is above a threshold.
- pub fn find_similar(&mut self, opts: Option<&mut DiffFindOptions>) -> Result<(), Error> {
- let opts = opts.map(|opts| &opts.raw);
- unsafe {
- try_call!(raw::git_diff_find_similar(self.raw, opts));
- }
- Ok(())
- }
-
- /// Create an e-mail ready patch from a diff.
- ///
- /// Matches the format created by `git format-patch`
- #[doc(hidden)]
- #[deprecated(note = "refactored to `Email::from_diff` to match upstream")]
- pub fn format_email(
- &mut self,
- patch_no: usize,
- total_patches: usize,
- commit: &crate::Commit<'repo>,
- opts: Option<&mut DiffFormatEmailOptions>,
- ) -> Result<Buf, Error> {
- assert!(patch_no > 0);
- assert!(patch_no <= total_patches);
- let mut default = DiffFormatEmailOptions::default();
- let raw_opts = opts.map_or(&mut default.raw, |opts| &mut opts.raw);
- let summary = commit.summary_bytes().unwrap();
- let mut message = commit.message_bytes();
- assert!(message.starts_with(summary));
- message = &message[summary.len()..];
- raw_opts.patch_no = patch_no;
- raw_opts.total_patches = total_patches;
- let id = commit.id();
- raw_opts.id = id.raw();
- raw_opts.summary = summary.as_ptr() as *const _;
- raw_opts.body = message.as_ptr() as *const _;
- raw_opts.author = commit.author().raw();
- let buf = Buf::new();
- #[allow(deprecated)]
- unsafe {
- try_call!(raw::git_diff_format_email(buf.raw(), self.raw, &*raw_opts));
- }
- Ok(buf)
- }
-
- /// Create an 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_RAWSZ],
- };
- unsafe {
- try_call!(raw::git_diff_patchid(
- &mut raw,
- self.raw,
- opts.map(|o| &mut o.raw)
- ));
- Ok(Binding::from_raw(&raw as *const _))
- }
- }
-
- // TODO: num_deltas_of_type, find_similar
-}
-impl Diff<'static> {
- /// Read the contents of a git patch file into a `git_diff` object.
- ///
- /// The diff object produced is similar to the one that would be
- /// produced if you actually produced it computationally by comparing
- /// 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> {
- crate::init();
- let mut diff: *mut raw::git_diff = std::ptr::null_mut();
- 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()
- ));
- Ok(Diff::from_raw(diff))
- }
- }
-}
-
-pub extern "C" fn print_cb(
- delta: *const raw::git_diff_delta,
- hunk: *const raw::git_diff_hunk,
- line: *const raw::git_diff_line,
- data: *mut c_void,
-) -> c_int {
- unsafe {
- let delta = Binding::from_raw(delta as *mut _);
- let hunk = Binding::from_raw_opt(hunk);
- let line = Binding::from_raw(line);
-
- let r = panic::wrap(|| {
- let data = data as *mut &mut PrintCb<'_>;
- (*data)(delta, hunk, line)
- });
- if r == Some(true) {
- raw::GIT_OK
- } else {
- raw::GIT_EUSER
- }
- }
-}
-
-pub extern "C" fn file_cb_c(
- delta: *const raw::git_diff_delta,
- progress: f32,
- data: *mut c_void,
-) -> c_int {
- unsafe {
- let delta = Binding::from_raw(delta as *mut _);
-
- let r = panic::wrap(|| {
- let cbs = data as *mut DiffCallbacks<'_, '_, '_, '_, '_, '_, '_, '_>;
- match (*cbs).file {
- Some(ref mut cb) => cb(delta, progress),
- None => false,
- }
- });
- if r == Some(true) {
- raw::GIT_OK
- } else {
- raw::GIT_EUSER
- }
- }
-}
-
-pub extern "C" fn binary_cb_c(
- delta: *const raw::git_diff_delta,
- binary: *const raw::git_diff_binary,
- data: *mut c_void,
-) -> c_int {
- unsafe {
- let delta = Binding::from_raw(delta as *mut _);
- let binary = Binding::from_raw(binary);
-
- let r = panic::wrap(|| {
- let cbs = data as *mut DiffCallbacks<'_, '_, '_, '_, '_, '_, '_, '_>;
- match (*cbs).binary {
- Some(ref mut cb) => cb(delta, binary),
- None => false,
- }
- });
- if r == Some(true) {
- raw::GIT_OK
- } else {
- raw::GIT_EUSER
- }
- }
-}
-
-pub extern "C" fn hunk_cb_c(
- delta: *const raw::git_diff_delta,
- hunk: *const raw::git_diff_hunk,
- data: *mut c_void,
-) -> c_int {
- unsafe {
- let delta = Binding::from_raw(delta as *mut _);
- let hunk = Binding::from_raw(hunk);
-
- let r = panic::wrap(|| {
- let cbs = data as *mut DiffCallbacks<'_, '_, '_, '_, '_, '_, '_, '_>;
- match (*cbs).hunk {
- Some(ref mut cb) => cb(delta, hunk),
- None => false,
- }
- });
- if r == Some(true) {
- raw::GIT_OK
- } else {
- raw::GIT_EUSER
- }
- }
-}
-
-pub extern "C" fn line_cb_c(
- delta: *const raw::git_diff_delta,
- hunk: *const raw::git_diff_hunk,
- line: *const raw::git_diff_line,
- data: *mut c_void,
-) -> c_int {
- unsafe {
- let delta = Binding::from_raw(delta as *mut _);
- let hunk = Binding::from_raw_opt(hunk);
- let line = Binding::from_raw(line);
-
- let r = panic::wrap(|| {
- let cbs = data as *mut DiffCallbacks<'_, '_, '_, '_, '_, '_, '_, '_>;
- match (*cbs).line {
- Some(ref mut cb) => cb(delta, hunk, line),
- None => false,
- }
- });
- if r == Some(true) {
- raw::GIT_OK
- } else {
- raw::GIT_EUSER
- }
- }
-}
-
-impl<'repo> Binding for Diff<'repo> {
- type Raw = *mut raw::git_diff;
- unsafe fn from_raw(raw: *mut raw::git_diff) -> Diff<'repo> {
- Diff {
- raw,
- _marker: marker::PhantomData,
- }
- }
- fn raw(&self) -> *mut raw::git_diff {
- self.raw
- }
-}
-
-impl<'repo> Drop for Diff<'repo> {
- fn drop(&mut self) {
- unsafe { raw::git_diff_free(self.raw) }
- }
-}
-
-impl<'a> DiffDelta<'a> {
- /// Returns the flags on the delta.
- ///
- /// For more information, see `DiffFlags`'s documentation.
- pub fn flags(&self) -> DiffFlags {
- let flags = unsafe { (*self.raw).flags };
- let mut result = DiffFlags::empty();
-
- #[cfg(target_env = "msvc")]
- fn as_u32(flag: i32) -> u32 {
- flag as u32
- }
- #[cfg(not(target_env = "msvc"))]
- fn as_u32(flag: u32) -> u32 {
- flag
- }
-
- if (flags & as_u32(raw::GIT_DIFF_FLAG_BINARY)) != 0 {
- result |= DiffFlags::BINARY;
- }
- if (flags & as_u32(raw::GIT_DIFF_FLAG_NOT_BINARY)) != 0 {
- result |= DiffFlags::NOT_BINARY;
- }
- if (flags & as_u32(raw::GIT_DIFF_FLAG_VALID_ID)) != 0 {
- result |= DiffFlags::VALID_ID;
- }
- if (flags & as_u32(raw::GIT_DIFF_FLAG_EXISTS)) != 0 {
- result |= DiffFlags::EXISTS;
- }
- result
- }
-
- // TODO: expose when diffs are more exposed
- // pub fn similarity(&self) -> u16 {
- // unsafe { (*self.raw).similarity }
- // }
-
- /// Returns the number of files in this delta.
- pub fn nfiles(&self) -> u16 {
- unsafe { (*self.raw).nfiles }
- }
-
- /// Returns the status of this entry
- ///
- /// For more information, see `Delta`'s documentation
- pub fn status(&self) -> Delta {
- match unsafe { (*self.raw).status } {
- raw::GIT_DELTA_UNMODIFIED => Delta::Unmodified,
- raw::GIT_DELTA_ADDED => Delta::Added,
- raw::GIT_DELTA_DELETED => Delta::Deleted,
- raw::GIT_DELTA_MODIFIED => Delta::Modified,
- raw::GIT_DELTA_RENAMED => Delta::Renamed,
- raw::GIT_DELTA_COPIED => Delta::Copied,
- raw::GIT_DELTA_IGNORED => Delta::Ignored,
- raw::GIT_DELTA_UNTRACKED => Delta::Untracked,
- raw::GIT_DELTA_TYPECHANGE => Delta::Typechange,
- raw::GIT_DELTA_UNREADABLE => Delta::Unreadable,
- raw::GIT_DELTA_CONFLICTED => Delta::Conflicted,
- n => panic!("unknown diff status: {}", n),
- }
- }
-
- /// Return the file which represents the "from" side of the diff.
- ///
- /// What side this means depends on the function that was used to generate
- /// the diff and will be documented on the function itself.
- pub fn old_file(&self) -> DiffFile<'a> {
- unsafe { Binding::from_raw(&(*self.raw).old_file as *const _) }
- }
-
- /// Return the file which represents the "to" side of the diff.
- ///
- /// What side this means depends on the function that was used to generate
- /// the diff and will be documented on the function itself.
- pub fn new_file(&self) -> DiffFile<'a> {
- unsafe { Binding::from_raw(&(*self.raw).new_file as *const _) }
- }
-}
-
-impl<'a> Binding for DiffDelta<'a> {
- type Raw = *mut raw::git_diff_delta;
- unsafe fn from_raw(raw: *mut raw::git_diff_delta) -> DiffDelta<'a> {
- DiffDelta {
- raw,
- _marker: marker::PhantomData,
- }
- }
- fn raw(&self) -> *mut raw::git_diff_delta {
- self.raw
- }
-}
-
-impl<'a> std::fmt::Debug for DiffDelta<'a> {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
- f.debug_struct("DiffDelta")
- .field("nfiles", &self.nfiles())
- .field("status", &self.status())
- .field("old_file", &self.old_file())
- .field("new_file", &self.new_file())
- .finish()
- }
-}
-
-impl<'a> DiffFile<'a> {
- /// Returns the Oid of this item.
- ///
- /// If this entry represents an absent side of a diff (e.g. the `old_file`
- /// of a `Added` delta), then the oid returned will be zeroes.
- pub fn id(&self) -> Oid {
- unsafe { Binding::from_raw(&(*self.raw).id as *const _) }
- }
-
- /// Returns the path, in bytes, of the entry relative to the working
- /// directory of the repository.
- pub fn path_bytes(&self) -> Option<&'a [u8]> {
- static FOO: () = ();
- unsafe { crate::opt_bytes(&FOO, (*self.raw).path) }
- }
-
- /// Returns the path of the entry relative to the working directory of the
- /// repository.
- pub fn path(&self) -> Option<&'a Path> {
- self.path_bytes().map(util::bytes2path)
- }
-
- /// Returns the size of this entry, in bytes
- pub fn size(&self) -> u64 {
- unsafe { (*self.raw).size as u64 }
- }
-
- /// Returns `true` if file(s) are treated as binary data.
- pub fn is_binary(&self) -> bool {
- unsafe { (*self.raw).flags & raw::GIT_DIFF_FLAG_BINARY as u32 != 0 }
- }
-
- /// Returns `true` if file(s) are treated as text data.
- pub fn is_not_binary(&self) -> bool {
- unsafe { (*self.raw).flags & raw::GIT_DIFF_FLAG_NOT_BINARY as u32 != 0 }
- }
-
- /// Returns `true` if `id` value is known correct.
- pub fn is_valid_id(&self) -> bool {
- unsafe { (*self.raw).flags & raw::GIT_DIFF_FLAG_VALID_ID as u32 != 0 }
- }
-
- /// Returns `true` if file exists at this side of the delta.
- pub fn exists(&self) -> bool {
- unsafe { (*self.raw).flags & raw::GIT_DIFF_FLAG_EXISTS as u32 != 0 }
- }
-
- /// Returns file mode.
- pub fn mode(&self) -> FileMode {
- match unsafe { (*self.raw).mode.into() } {
- raw::GIT_FILEMODE_UNREADABLE => FileMode::Unreadable,
- raw::GIT_FILEMODE_TREE => FileMode::Tree,
- raw::GIT_FILEMODE_BLOB => FileMode::Blob,
- raw::GIT_FILEMODE_BLOB_GROUP_WRITABLE => FileMode::BlobGroupWritable,
- raw::GIT_FILEMODE_BLOB_EXECUTABLE => FileMode::BlobExecutable,
- raw::GIT_FILEMODE_LINK => FileMode::Link,
- raw::GIT_FILEMODE_COMMIT => FileMode::Commit,
- mode => panic!("unknown mode: {}", mode),
- }
- }
-}
-
-impl<'a> Binding for DiffFile<'a> {
- type Raw = *const raw::git_diff_file;
- unsafe fn from_raw(raw: *const raw::git_diff_file) -> DiffFile<'a> {
- DiffFile {
- raw,
- _marker: marker::PhantomData,
- }
- }
- fn raw(&self) -> *const raw::git_diff_file {
- self.raw
- }
-}
-
-impl<'a> std::fmt::Debug for DiffFile<'a> {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
- let mut ds = f.debug_struct("DiffFile");
- ds.field("id", &self.id());
- if let Some(path_bytes) = &self.path_bytes() {
- ds.field("path_bytes", path_bytes);
- }
- if let Some(path) = &self.path() {
- ds.field("path", path);
- }
- ds.field("size", &self.size()).finish()
- }
-}
-
-impl Default for DiffOptions {
- fn default() -> Self {
- Self::new()
- }
-}
-
-impl DiffOptions {
- /// Creates a new set of empty diff options.
- ///
- /// All flags and other options are defaulted to false or their otherwise
- /// zero equivalents.
- pub fn new() -> DiffOptions {
- let mut opts = DiffOptions {
- pathspec: Vec::new(),
- pathspec_ptrs: Vec::new(),
- raw: unsafe { mem::zeroed() },
- old_prefix: None,
- new_prefix: None,
- };
- assert_eq!(unsafe { raw::git_diff_init_options(&mut opts.raw, 1) }, 0);
- opts
- }
-
- fn flag(&mut self, opt: raw::git_diff_option_t, val: bool) -> &mut DiffOptions {
- let opt = opt as u32;
- if val {
- self.raw.flags |= opt;
- } else {
- self.raw.flags &= !opt;
- }
- self
- }
-
- /// Flag indicating whether the sides of the diff will be reversed.
- pub fn reverse(&mut self, reverse: bool) -> &mut DiffOptions {
- self.flag(raw::GIT_DIFF_REVERSE, reverse)
- }
-
- /// Flag indicating whether ignored files are included.
- pub fn include_ignored(&mut self, include: bool) -> &mut DiffOptions {
- self.flag(raw::GIT_DIFF_INCLUDE_IGNORED, include)
- }
-
- /// Flag indicating whether ignored directories are traversed deeply or not.
- pub fn recurse_ignored_dirs(&mut self, recurse: bool) -> &mut DiffOptions {
- self.flag(raw::GIT_DIFF_RECURSE_IGNORED_DIRS, recurse)
- }
-
- /// Flag indicating whether untracked files are in the diff
- pub fn include_untracked(&mut self, include: bool) -> &mut DiffOptions {
- self.flag(raw::GIT_DIFF_INCLUDE_UNTRACKED, include)
- }
-
- /// Flag indicating whether untracked directories are traversed deeply or
- /// not.
- pub fn recurse_untracked_dirs(&mut self, recurse: bool) -> &mut DiffOptions {
- self.flag(raw::GIT_DIFF_RECURSE_UNTRACKED_DIRS, recurse)
- }
-
- /// Flag indicating whether unmodified files are in the diff.
- pub fn include_unmodified(&mut self, include: bool) -> &mut DiffOptions {
- self.flag(raw::GIT_DIFF_INCLUDE_UNMODIFIED, include)
- }
-
- /// If enabled, then Typechange delta records are generated.
- pub fn include_typechange(&mut self, include: bool) -> &mut DiffOptions {
- self.flag(raw::GIT_DIFF_INCLUDE_TYPECHANGE, include)
- }
-
- /// Event with `include_typechange`, the tree returned generally shows a
- /// deleted blob. This flag correctly labels the tree transitions as a
- /// typechange record with the `new_file`'s mode set to tree.
- ///
- /// Note that the tree SHA will not be available.
- pub fn include_typechange_trees(&mut self, include: bool) -> &mut DiffOptions {
- self.flag(raw::GIT_DIFF_INCLUDE_TYPECHANGE_TREES, include)
- }
-
- /// Flag indicating whether file mode changes are ignored.
- pub fn ignore_filemode(&mut self, ignore: bool) -> &mut DiffOptions {
- self.flag(raw::GIT_DIFF_IGNORE_FILEMODE, ignore)
- }
-
- /// Flag indicating whether all submodules should be treated as unmodified.
- pub fn ignore_submodules(&mut self, ignore: bool) -> &mut DiffOptions {
- self.flag(raw::GIT_DIFF_IGNORE_SUBMODULES, ignore)
- }
-
- /// Flag indicating whether case insensitive filenames should be used.
- pub fn ignore_case(&mut self, ignore: bool) -> &mut DiffOptions {
- self.flag(raw::GIT_DIFF_IGNORE_CASE, ignore)
- }
-
- /// If pathspecs are specified, this flag means that they should be applied
- /// as an exact match instead of a fnmatch pattern.
- pub fn disable_pathspec_match(&mut self, disable: bool) -> &mut DiffOptions {
- self.flag(raw::GIT_DIFF_DISABLE_PATHSPEC_MATCH, disable)
- }
-
- /// Disable updating the `binary` flag in delta records. This is useful when
- /// iterating over a diff if you don't need hunk and data callbacks and want
- /// to avoid having to load a file completely.
- pub fn skip_binary_check(&mut self, skip: bool) -> &mut DiffOptions {
- self.flag(raw::GIT_DIFF_SKIP_BINARY_CHECK, skip)
- }
-
- /// When diff finds an untracked directory, to match the behavior of core
- /// Git, it scans the contents for ignored and untracked files. If all
- /// contents are ignored, then the directory is ignored; if any contents are
- /// not ignored, then the directory is untracked. This is extra work that
- /// may not matter in many cases.
- ///
- /// This flag turns off that scan and immediately labels an untracked
- /// directory as untracked (changing the behavior to not match core git).
- pub fn enable_fast_untracked_dirs(&mut self, enable: bool) -> &mut DiffOptions {
- self.flag(raw::GIT_DIFF_ENABLE_FAST_UNTRACKED_DIRS, enable)
- }
-
- /// When diff finds a file in the working directory with stat information
- /// different from the index, but the OID ends up being the same, write the
- /// correct stat information into the index. Note: without this flag, diff
- /// will always leave the index untouched.
- pub fn update_index(&mut self, update: bool) -> &mut DiffOptions {
- self.flag(raw::GIT_DIFF_UPDATE_INDEX, update)
- }
-
- /// Include unreadable files in the diff
- pub fn include_unreadable(&mut self, include: bool) -> &mut DiffOptions {
- self.flag(raw::GIT_DIFF_INCLUDE_UNREADABLE, include)
- }
-
- /// Include unreadable files in the diff as untracked files
- pub fn include_unreadable_as_untracked(&mut self, include: bool) -> &mut DiffOptions {
- self.flag(raw::GIT_DIFF_INCLUDE_UNREADABLE_AS_UNTRACKED, include)
- }
-
- /// Treat all files as text, disabling binary attributes and detection.
- pub fn force_text(&mut self, force: bool) -> &mut DiffOptions {
- self.flag(raw::GIT_DIFF_FORCE_TEXT, force)
- }
-
- /// Treat all files as binary, disabling text diffs
- pub fn force_binary(&mut self, force: bool) -> &mut DiffOptions {
- self.flag(raw::GIT_DIFF_FORCE_BINARY, force)
- }
-
- /// Ignore all whitespace
- pub fn ignore_whitespace(&mut self, ignore: bool) -> &mut DiffOptions {
- self.flag(raw::GIT_DIFF_IGNORE_WHITESPACE, ignore)
- }
-
- /// Ignore changes in the amount of whitespace
- pub fn ignore_whitespace_change(&mut self, ignore: bool) -> &mut DiffOptions {
- self.flag(raw::GIT_DIFF_IGNORE_WHITESPACE_CHANGE, ignore)
- }
-
- /// Ignore whitespace at the end of line
- pub fn ignore_whitespace_eol(&mut self, ignore: bool) -> &mut DiffOptions {
- self.flag(raw::GIT_DIFF_IGNORE_WHITESPACE_EOL, ignore)
- }
-
- /// Ignore blank lines
- pub fn ignore_blank_lines(&mut self, ignore: bool) -> &mut DiffOptions {
- self.flag(raw::GIT_DIFF_IGNORE_BLANK_LINES, ignore)
- }
-
- /// When generating patch text, include the content of untracked files.
- ///
- /// This automatically turns on `include_untracked` but it does not turn on
- /// `recurse_untracked_dirs`. Add that flag if you want the content of every
- /// single untracked file.
- pub fn show_untracked_content(&mut self, show: bool) -> &mut DiffOptions {
- self.flag(raw::GIT_DIFF_SHOW_UNTRACKED_CONTENT, show)
- }
-
- /// When generating output, include the names of unmodified files if they
- /// are included in the `Diff`. Normally these are skipped in the formats
- /// that list files (e.g. name-only, name-status, raw). Even with this these
- /// will not be included in the patch format.
- pub fn show_unmodified(&mut self, show: bool) -> &mut DiffOptions {
- self.flag(raw::GIT_DIFF_SHOW_UNMODIFIED, show)
- }
-
- /// Use the "patience diff" algorithm
- pub fn patience(&mut self, patience: bool) -> &mut DiffOptions {
- self.flag(raw::GIT_DIFF_PATIENCE, patience)
- }
-
- /// Take extra time to find the minimal diff
- pub fn minimal(&mut self, minimal: bool) -> &mut DiffOptions {
- self.flag(raw::GIT_DIFF_MINIMAL, minimal)
- }
-
- /// Include the necessary deflate/delta information so that `git-apply` can
- /// apply given diff information to binary files.
- pub fn show_binary(&mut self, show: bool) -> &mut DiffOptions {
- self.flag(raw::GIT_DIFF_SHOW_BINARY, show)
- }
-
- /// Use a heuristic that takes indentation and whitespace into account
- /// which generally can produce better diffs when dealing with ambiguous
- /// diff hunks.
- pub fn indent_heuristic(&mut self, heuristic: bool) -> &mut DiffOptions {
- self.flag(raw::GIT_DIFF_INDENT_HEURISTIC, heuristic)
- }
-
- /// Set the number of unchanged lines that define the boundary of a hunk
- /// (and to display before and after).
- ///
- /// The default value for this is 3.
- pub fn context_lines(&mut self, lines: u32) -> &mut DiffOptions {
- self.raw.context_lines = lines;
- self
- }
-
- /// Set the maximum number of unchanged lines between hunk boundaries before
- /// the hunks will be merged into one.
- ///
- /// The default value for this is 0.
- pub fn interhunk_lines(&mut self, lines: u32) -> &mut DiffOptions {
- self.raw.interhunk_lines = lines;
- self
- }
-
- /// The default value for this is `core.abbrev` or 7 if unset.
- pub fn id_abbrev(&mut self, abbrev: u16) -> &mut DiffOptions {
- self.raw.id_abbrev = abbrev;
- self
- }
-
- /// Maximum size (in bytes) above which a blob will be marked as binary
- /// automatically.
- ///
- /// A negative value will disable this entirely.
- ///
- /// The default value for this is 512MB.
- pub fn max_size(&mut self, size: i64) -> &mut DiffOptions {
- self.raw.max_size = size as raw::git_off_t;
- self
- }
-
- /// The virtual "directory" to prefix old file names with in hunk headers.
- ///
- /// The default value for this is "a".
- pub fn old_prefix<T: IntoCString>(&mut self, t: T) -> &mut DiffOptions {
- self.old_prefix = Some(t.into_c_string().unwrap());
- self
- }
-
- /// The virtual "directory" to prefix new file names with in hunk headers.
- ///
- /// The default value for this is "b".
- pub fn new_prefix<T: IntoCString>(&mut self, t: T) -> &mut DiffOptions {
- self.new_prefix = Some(t.into_c_string().unwrap());
- self
- }
-
- /// Add to the array of paths/fnmatch patterns to constrain the diff.
- pub fn pathspec<T: IntoCString>(&mut self, pathspec: T) -> &mut DiffOptions {
- let s = util::cstring_to_repo_path(pathspec).unwrap();
- self.pathspec_ptrs.push(s.as_ptr());
- self.pathspec.push(s);
- self
- }
-
- /// Acquire a pointer to the underlying raw options.
- ///
- /// This function is unsafe as the pointer is only valid so long as this
- /// structure is not moved, modified, or used elsewhere.
- pub unsafe fn raw(&mut self) -> *const raw::git_diff_options {
- self.raw.old_prefix = self
- .old_prefix
- .as_ref()
- .map(|s| s.as_ptr())
- .unwrap_or(ptr::null());
- self.raw.new_prefix = self
- .new_prefix
- .as_ref()
- .map(|s| s.as_ptr())
- .unwrap_or(ptr::null());
- self.raw.pathspec.count = self.pathspec_ptrs.len() as size_t;
- self.raw.pathspec.strings = self.pathspec_ptrs.as_ptr() as *mut _;
- &self.raw as *const _
- }
-
- // TODO: expose ignore_submodules, notify_cb/notify_payload
-}
-
-impl<'diff> Iterator for Deltas<'diff> {
- type Item = DiffDelta<'diff>;
- fn next(&mut self) -> Option<DiffDelta<'diff>> {
- self.range.next().and_then(|i| self.diff.get_delta(i))
- }
- fn size_hint(&self) -> (usize, Option<usize>) {
- self.range.size_hint()
- }
-}
-impl<'diff> DoubleEndedIterator for Deltas<'diff> {
- fn next_back(&mut self) -> Option<DiffDelta<'diff>> {
- self.range.next_back().and_then(|i| self.diff.get_delta(i))
- }
-}
-impl<'diff> FusedIterator for Deltas<'diff> {}
-
-impl<'diff> ExactSizeIterator for Deltas<'diff> {}
-
-/// Line origin constants.
-#[derive(Copy, Clone, Debug, PartialEq)]
-pub enum DiffLineType {
- /// These values will be sent to `git_diff_line_cb` along with the line
- Context,
- ///
- Addition,
- ///
- Deletion,
- /// Both files have no LF at end
- ContextEOFNL,
- /// Old has no LF at end, new does
- AddEOFNL,
- /// Old has LF at end, new does not
- DeleteEOFNL,
- /// The following values will only be sent to a `git_diff_line_cb` when
- /// the content of a diff is being formatted through `git_diff_print`.
- FileHeader,
- ///
- HunkHeader,
- /// For "Binary files x and y differ"
- Binary,
-}
-
-impl Binding for DiffLineType {
- type Raw = raw::git_diff_line_t;
- unsafe fn from_raw(raw: raw::git_diff_line_t) -> Self {
- match raw {
- raw::GIT_DIFF_LINE_CONTEXT => DiffLineType::Context,
- raw::GIT_DIFF_LINE_ADDITION => DiffLineType::Addition,
- raw::GIT_DIFF_LINE_DELETION => DiffLineType::Deletion,
- raw::GIT_DIFF_LINE_CONTEXT_EOFNL => DiffLineType::ContextEOFNL,
- raw::GIT_DIFF_LINE_ADD_EOFNL => DiffLineType::AddEOFNL,
- raw::GIT_DIFF_LINE_DEL_EOFNL => DiffLineType::DeleteEOFNL,
- raw::GIT_DIFF_LINE_FILE_HDR => DiffLineType::FileHeader,
- raw::GIT_DIFF_LINE_HUNK_HDR => DiffLineType::HunkHeader,
- raw::GIT_DIFF_LINE_BINARY => DiffLineType::Binary,
- _ => panic!("Unknown git diff line type"),
- }
- }
- fn raw(&self) -> raw::git_diff_line_t {
- match *self {
- DiffLineType::Context => raw::GIT_DIFF_LINE_CONTEXT,
- DiffLineType::Addition => raw::GIT_DIFF_LINE_ADDITION,
- DiffLineType::Deletion => raw::GIT_DIFF_LINE_DELETION,
- DiffLineType::ContextEOFNL => raw::GIT_DIFF_LINE_CONTEXT_EOFNL,
- DiffLineType::AddEOFNL => raw::GIT_DIFF_LINE_ADD_EOFNL,
- DiffLineType::DeleteEOFNL => raw::GIT_DIFF_LINE_DEL_EOFNL,
- DiffLineType::FileHeader => raw::GIT_DIFF_LINE_FILE_HDR,
- DiffLineType::HunkHeader => raw::GIT_DIFF_LINE_HUNK_HDR,
- DiffLineType::Binary => raw::GIT_DIFF_LINE_BINARY,
- }
- }
-}
-
-impl<'a> DiffLine<'a> {
- /// Line number in old file or `None` for added line
- pub fn old_lineno(&self) -> Option<u32> {
- match unsafe { (*self.raw).old_lineno } {
- n if n < 0 => None,
- n => Some(n as u32),
- }
- }
-
- /// Line number in new file or `None` for deleted line
- pub fn new_lineno(&self) -> Option<u32> {
- match unsafe { (*self.raw).new_lineno } {
- n if n < 0 => None,
- n => Some(n as u32),
- }
- }
-
- /// Number of newline characters in content
- pub fn num_lines(&self) -> u32 {
- unsafe { (*self.raw).num_lines as u32 }
- }
-
- /// Offset in the original file to the content
- pub fn content_offset(&self) -> i64 {
- unsafe { (*self.raw).content_offset as i64 }
- }
-
- /// Content of this line as bytes.
- pub fn content(&self) -> &'a [u8] {
- unsafe {
- slice::from_raw_parts(
- (*self.raw).content as *const u8,
- (*self.raw).content_len as usize,
- )
- }
- }
-
- /// origin of this `DiffLine`.
- ///
- pub fn origin_value(&self) -> DiffLineType {
- unsafe { Binding::from_raw((*self.raw).origin as raw::git_diff_line_t) }
- }
-
- /// Sigil showing the origin of this `DiffLine`.
- ///
- /// * ` ` - Line context
- /// * `+` - Line addition
- /// * `-` - Line deletion
- /// * `=` - Context (End of file)
- /// * `>` - Add (End of file)
- /// * `<` - Remove (End of file)
- /// * `F` - File header
- /// * `H` - Hunk header
- /// * `B` - Line binary
- pub fn origin(&self) -> char {
- match unsafe { (*self.raw).origin as raw::git_diff_line_t } {
- raw::GIT_DIFF_LINE_CONTEXT => ' ',
- raw::GIT_DIFF_LINE_ADDITION => '+',
- raw::GIT_DIFF_LINE_DELETION => '-',
- raw::GIT_DIFF_LINE_CONTEXT_EOFNL => '=',
- raw::GIT_DIFF_LINE_ADD_EOFNL => '>',
- raw::GIT_DIFF_LINE_DEL_EOFNL => '<',
- raw::GIT_DIFF_LINE_FILE_HDR => 'F',
- raw::GIT_DIFF_LINE_HUNK_HDR => 'H',
- raw::GIT_DIFF_LINE_BINARY => 'B',
- _ => ' ',
- }
- }
-}
-
-impl<'a> Binding for DiffLine<'a> {
- type Raw = *const raw::git_diff_line;
- unsafe fn from_raw(raw: *const raw::git_diff_line) -> DiffLine<'a> {
- DiffLine {
- raw,
- _marker: marker::PhantomData,
- }
- }
- fn raw(&self) -> *const raw::git_diff_line {
- self.raw
- }
-}
-
-impl<'a> std::fmt::Debug for DiffLine<'a> {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
- let mut ds = f.debug_struct("DiffLine");
- if let Some(old_lineno) = &self.old_lineno() {
- ds.field("old_lineno", old_lineno);
- }
- if let Some(new_lineno) = &self.new_lineno() {
- ds.field("new_lineno", new_lineno);
- }
- ds.field("num_lines", &self.num_lines())
- .field("content_offset", &self.content_offset())
- .field("content", &self.content())
- .field("origin", &self.origin())
- .finish()
- }
-}
-
-impl<'a> DiffHunk<'a> {
- /// Starting line number in old_file
- pub fn old_start(&self) -> u32 {
- unsafe { (*self.raw).old_start as u32 }
- }
-
- /// Number of lines in old_file
- pub fn old_lines(&self) -> u32 {
- unsafe { (*self.raw).old_lines as u32 }
- }
-
- /// Starting line number in new_file
- pub fn new_start(&self) -> u32 {
- unsafe { (*self.raw).new_start as u32 }
- }
-
- /// Number of lines in new_file
- pub fn new_lines(&self) -> u32 {
- unsafe { (*self.raw).new_lines as u32 }
- }
-
- /// Header text
- pub fn header(&self) -> &'a [u8] {
- unsafe {
- slice::from_raw_parts(
- (*self.raw).header.as_ptr() as *const u8,
- (*self.raw).header_len as usize,
- )
- }
- }
-}
-
-impl<'a> Binding for DiffHunk<'a> {
- type Raw = *const raw::git_diff_hunk;
- unsafe fn from_raw(raw: *const raw::git_diff_hunk) -> DiffHunk<'a> {
- DiffHunk {
- raw,
- _marker: marker::PhantomData,
- }
- }
- fn raw(&self) -> *const raw::git_diff_hunk {
- self.raw
- }
-}
-
-impl<'a> std::fmt::Debug for DiffHunk<'a> {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
- f.debug_struct("DiffHunk")
- .field("old_start", &self.old_start())
- .field("old_lines", &self.old_lines())
- .field("new_start", &self.new_start())
- .field("new_lines", &self.new_lines())
- .field("header", &self.header())
- .finish()
- }
-}
-
-impl DiffStats {
- /// Get the total number of files changed in a diff.
- pub fn files_changed(&self) -> usize {
- unsafe { raw::git_diff_stats_files_changed(&*self.raw) as usize }
- }
-
- /// Get the total number of insertions in a diff
- pub fn insertions(&self) -> usize {
- unsafe { raw::git_diff_stats_insertions(&*self.raw) as usize }
- }
-
- /// Get the total number of deletions in a diff
- pub fn deletions(&self) -> usize {
- unsafe { raw::git_diff_stats_deletions(&*self.raw) as usize }
- }
-
- /// Print diff statistics to a Buf
- pub fn to_buf(&self, format: DiffStatsFormat, width: usize) -> Result<Buf, Error> {
- let buf = Buf::new();
- unsafe {
- try_call!(raw::git_diff_stats_to_buf(
- buf.raw(),
- self.raw,
- format.bits(),
- width as size_t
- ));
- }
- Ok(buf)
- }
-}
-
-impl Binding for DiffStats {
- type Raw = *mut raw::git_diff_stats;
-
- unsafe fn from_raw(raw: *mut raw::git_diff_stats) -> DiffStats {
- DiffStats { raw }
- }
- fn raw(&self) -> *mut raw::git_diff_stats {
- self.raw
- }
-}
-
-impl Drop for DiffStats {
- fn drop(&mut self) {
- unsafe { raw::git_diff_stats_free(self.raw) }
- }
-}
-
-impl std::fmt::Debug for DiffStats {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
- f.debug_struct("DiffStats")
- .field("files_changed", &self.files_changed())
- .field("insertions", &self.insertions())
- .field("deletions", &self.deletions())
- .finish()
- }
-}
-
-impl<'a> DiffBinary<'a> {
- /// Returns whether there is data in this binary structure or not.
- ///
- /// If this is `true`, then this was produced and included binary content.
- /// If this is `false` then this was generated knowing only that a binary
- /// file changed but without providing the data, probably from a patch that
- /// said `Binary files a/file.txt and b/file.txt differ`.
- pub fn contains_data(&self) -> bool {
- unsafe { (*self.raw).contains_data == 1 }
- }
-
- /// The contents of the old file.
- pub fn old_file(&self) -> DiffBinaryFile<'a> {
- unsafe { Binding::from_raw(&(*self.raw).old_file as *const _) }
- }
-
- /// The contents of the new file.
- pub fn new_file(&self) -> DiffBinaryFile<'a> {
- unsafe { Binding::from_raw(&(*self.raw).new_file as *const _) }
- }
-}
-
-impl<'a> Binding for DiffBinary<'a> {
- type Raw = *const raw::git_diff_binary;
- unsafe fn from_raw(raw: *const raw::git_diff_binary) -> DiffBinary<'a> {
- DiffBinary {
- raw,
- _marker: marker::PhantomData,
- }
- }
- fn raw(&self) -> *const raw::git_diff_binary {
- self.raw
- }
-}
-
-impl<'a> DiffBinaryFile<'a> {
- /// The type of binary data for this file
- pub fn kind(&self) -> DiffBinaryKind {
- unsafe { Binding::from_raw((*self.raw).kind) }
- }
-
- /// The binary data, deflated
- pub fn data(&self) -> &[u8] {
- unsafe {
- slice::from_raw_parts((*self.raw).data as *const u8, (*self.raw).datalen as usize)
- }
- }
-
- /// The length of the binary data after inflation
- pub fn inflated_len(&self) -> usize {
- unsafe { (*self.raw).inflatedlen as usize }
- }
-}
-
-impl<'a> Binding for DiffBinaryFile<'a> {
- type Raw = *const raw::git_diff_binary_file;
- unsafe fn from_raw(raw: *const raw::git_diff_binary_file) -> DiffBinaryFile<'a> {
- DiffBinaryFile {
- raw,
- _marker: marker::PhantomData,
- }
- }
- fn raw(&self) -> *const raw::git_diff_binary_file {
- self.raw
- }
-}
-
-impl Binding for DiffBinaryKind {
- type Raw = raw::git_diff_binary_t;
- unsafe fn from_raw(raw: raw::git_diff_binary_t) -> DiffBinaryKind {
- match raw {
- raw::GIT_DIFF_BINARY_NONE => DiffBinaryKind::None,
- raw::GIT_DIFF_BINARY_LITERAL => DiffBinaryKind::Literal,
- raw::GIT_DIFF_BINARY_DELTA => DiffBinaryKind::Delta,
- _ => panic!("Unknown git diff binary kind"),
- }
- }
- fn raw(&self) -> raw::git_diff_binary_t {
- match *self {
- DiffBinaryKind::None => raw::GIT_DIFF_BINARY_NONE,
- DiffBinaryKind::Literal => raw::GIT_DIFF_BINARY_LITERAL,
- DiffBinaryKind::Delta => raw::GIT_DIFF_BINARY_DELTA,
- }
- }
-}
-
-impl Default for DiffFindOptions {
- fn default() -> Self {
- Self::new()
- }
-}
-
-impl DiffFindOptions {
- /// Creates a new set of empty diff find options.
- ///
- /// All flags and other options are defaulted to false or their otherwise
- /// zero equivalents.
- pub fn new() -> DiffFindOptions {
- let mut opts = DiffFindOptions {
- raw: unsafe { mem::zeroed() },
- };
- assert_eq!(
- unsafe { raw::git_diff_find_init_options(&mut opts.raw, 1) },
- 0
- );
- opts
- }
-
- fn flag(&mut self, opt: u32, val: bool) -> &mut DiffFindOptions {
- if val {
- self.raw.flags |= opt;
- } else {
- self.raw.flags &= !opt;
- }
- self
- }
-
- /// Reset all flags back to their unset state, indicating that
- /// `diff.renames` should be used instead. This is overridden once any flag
- /// is set.
- pub fn by_config(&mut self) -> &mut DiffFindOptions {
- self.flag(0xffffffff, false)
- }
-
- /// Look for renames?
- pub fn renames(&mut self, find: bool) -> &mut DiffFindOptions {
- self.flag(raw::GIT_DIFF_FIND_RENAMES, find)
- }
-
- /// Consider old side of modified for renames?
- pub fn renames_from_rewrites(&mut self, find: bool) -> &mut DiffFindOptions {
- self.flag(raw::GIT_DIFF_FIND_RENAMES_FROM_REWRITES, find)
- }
-
- /// Look for copies?
- pub fn copies(&mut self, find: bool) -> &mut DiffFindOptions {
- self.flag(raw::GIT_DIFF_FIND_COPIES, find)
- }
-
- /// Consider unmodified as copy sources?
- ///
- /// For this to work correctly, use `include_unmodified` when the initial
- /// diff is being generated.
- pub fn copies_from_unmodified(&mut self, find: bool) -> &mut DiffFindOptions {
- self.flag(raw::GIT_DIFF_FIND_COPIES_FROM_UNMODIFIED, find)
- }
-
- /// Mark significant rewrites for split.
- pub fn rewrites(&mut self, find: bool) -> &mut DiffFindOptions {
- self.flag(raw::GIT_DIFF_FIND_REWRITES, find)
- }
-
- /// Actually split large rewrites into delete/add pairs
- pub fn break_rewrites(&mut self, find: bool) -> &mut DiffFindOptions {
- self.flag(raw::GIT_DIFF_BREAK_REWRITES, find)
- }
-
- #[doc(hidden)]
- pub fn break_rewries(&mut self, find: bool) -> &mut DiffFindOptions {
- self.break_rewrites(find)
- }
-
- /// Find renames/copies for untracked items in working directory.
- ///
- /// For this to work correctly use the `include_untracked` option when the
- /// initial diff is being generated.
- pub fn for_untracked(&mut self, find: bool) -> &mut DiffFindOptions {
- self.flag(raw::GIT_DIFF_FIND_FOR_UNTRACKED, find)
- }
-
- /// Turn on all finding features.
- pub fn all(&mut self, find: bool) -> &mut DiffFindOptions {
- self.flag(raw::GIT_DIFF_FIND_ALL, find)
- }
-
- /// Measure similarity ignoring leading whitespace (default)
- pub fn ignore_leading_whitespace(&mut self, ignore: bool) -> &mut DiffFindOptions {
- self.flag(raw::GIT_DIFF_FIND_IGNORE_LEADING_WHITESPACE, ignore)
- }
-
- /// Measure similarity ignoring all whitespace
- pub fn ignore_whitespace(&mut self, ignore: bool) -> &mut DiffFindOptions {
- self.flag(raw::GIT_DIFF_FIND_IGNORE_WHITESPACE, ignore)
- }
-
- /// Measure similarity including all data
- pub fn dont_ignore_whitespace(&mut self, dont: bool) -> &mut DiffFindOptions {
- self.flag(raw::GIT_DIFF_FIND_DONT_IGNORE_WHITESPACE, dont)
- }
-
- /// Measure similarity only by comparing SHAs (fast and cheap)
- pub fn exact_match_only(&mut self, exact: bool) -> &mut DiffFindOptions {
- self.flag(raw::GIT_DIFF_FIND_EXACT_MATCH_ONLY, exact)
- }
-
- /// Do not break rewrites unless they contribute to a rename.
- ///
- /// Normally, `break_rewrites` and `rewrites` will measure the
- /// self-similarity of modified files and split the ones that have changed a
- /// lot into a delete/add pair. Then the sides of that pair will be
- /// considered candidates for rename and copy detection
- ///
- /// If you add this flag in and the split pair is not used for an actual
- /// rename or copy, then the modified record will be restored to a regular
- /// modified record instead of being split.
- pub fn break_rewrites_for_renames_only(&mut self, b: bool) -> &mut DiffFindOptions {
- self.flag(raw::GIT_DIFF_BREAK_REWRITES_FOR_RENAMES_ONLY, b)
- }
-
- /// Remove any unmodified deltas after find_similar is done.
- ///
- /// Using `copies_from_unmodified` to emulate the `--find-copies-harder`
- /// behavior requires building a diff with the `include_unmodified` flag. If
- /// you do not want unmodified records in the final result, pas this flag to
- /// have them removed.
- pub fn remove_unmodified(&mut self, remove: bool) -> &mut DiffFindOptions {
- self.flag(raw::GIT_DIFF_FIND_REMOVE_UNMODIFIED, remove)
- }
-
- /// Similarity to consider a file renamed (default 50)
- pub fn rename_threshold(&mut self, thresh: u16) -> &mut DiffFindOptions {
- self.raw.rename_threshold = thresh;
- self
- }
-
- /// Similarity of modified to be eligible rename source (default 50)
- pub fn rename_from_rewrite_threshold(&mut self, thresh: u16) -> &mut DiffFindOptions {
- self.raw.rename_from_rewrite_threshold = thresh;
- self
- }
-
- /// Similarity to consider a file copy (default 50)
- pub fn copy_threshold(&mut self, thresh: u16) -> &mut DiffFindOptions {
- self.raw.copy_threshold = thresh;
- self
- }
-
- /// Similarity to split modify into delete/add pair (default 60)
- pub fn break_rewrite_threshold(&mut self, thresh: u16) -> &mut DiffFindOptions {
- self.raw.break_rewrite_threshold = thresh;
- self
- }
-
- /// Maximum similarity sources to examine for a file (somewhat like
- /// git-diff's `-l` option or `diff.renameLimit` config)
- ///
- /// Defaults to 200
- pub fn rename_limit(&mut self, limit: usize) -> &mut DiffFindOptions {
- self.raw.rename_limit = limit as size_t;
- self
- }
-
- // TODO: expose git_diff_similarity_metric
-
- /// Acquire a pointer to the underlying raw options.
- pub unsafe fn raw(&mut self) -> *const raw::git_diff_find_options {
- &self.raw
- }
-}
-
-impl Default for DiffFormatEmailOptions {
- fn default() -> Self {
- Self::new()
- }
-}
-
-impl DiffFormatEmailOptions {
- /// Creates a new set of email options,
- /// initialized to the default values
- pub fn new() -> Self {
- let mut opts = DiffFormatEmailOptions {
- raw: unsafe { mem::zeroed() },
- };
- assert_eq!(
- unsafe { raw::git_diff_format_email_options_init(&mut opts.raw, 1) },
- 0
- );
- opts
- }
-
- fn flag(&mut self, opt: u32, val: bool) -> &mut Self {
- if val {
- self.raw.flags |= opt;
- } else {
- self.raw.flags &= !opt;
- }
- self
- }
-
- /// Exclude `[PATCH]` from the subject header
- pub fn exclude_subject_patch_header(&mut self, should_exclude: bool) -> &mut Self {
- self.flag(
- raw::GIT_DIFF_FORMAT_EMAIL_EXCLUDE_SUBJECT_PATCH_MARKER,
- should_exclude,
- )
- }
-}
-
-impl DiffPatchidOptions {
- /// Creates a new set of patchid options,
- /// initialized to the default values
- pub fn new() -> Self {
- let mut opts = DiffPatchidOptions {
- raw: unsafe { mem::zeroed() },
- };
- assert_eq!(
- unsafe {
- raw::git_diff_patchid_options_init(
- &mut opts.raw,
- raw::GIT_DIFF_PATCHID_OPTIONS_VERSION,
- )
- },
- 0
- );
- opts
- }
-}
-
-#[cfg(test)]
-mod tests {
- use crate::{DiffLineType, DiffOptions, Oid, Signature, Time};
- use std::borrow::Borrow;
- use std::fs::File;
- use std::io::Write;
- use std::path::Path;
-
- #[test]
- fn smoke() {
- let (_td, repo) = crate::test::repo_init();
- 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();
- assert_ne!(patchid, Oid::zero());
- }
-
- #[test]
- fn foreach_smoke() {
- let (_td, repo) = crate::test::repo_init();
- let diff = t!(repo.diff_tree_to_workdir(None, None));
- let mut count = 0;
- t!(diff.foreach(
- &mut |_file, _progress| {
- count = count + 1;
- true
- },
- None,
- None,
- None
- ));
- assert_eq!(count, 0);
- }
-
- #[test]
- fn foreach_file_only() {
- let path = Path::new("foo");
- let (td, repo) = crate::test::repo_init();
- t!(t!(File::create(&td.path().join(path))).write_all(b"bar"));
- let mut opts = DiffOptions::new();
- opts.include_untracked(true);
- let diff = t!(repo.diff_tree_to_workdir(None, Some(&mut opts)));
- let mut count = 0;
- let mut result = None;
- t!(diff.foreach(
- &mut |file, _progress| {
- count = count + 1;
- result = file.new_file().path().map(ToOwned::to_owned);
- true
- },
- None,
- None,
- None
- ));
- assert_eq!(result.as_ref().map(Borrow::borrow), Some(path));
- assert_eq!(count, 1);
- }
-
- #[test]
- fn foreach_file_and_hunk() {
- let path = Path::new("foo");
- let (td, repo) = crate::test::repo_init();
- t!(t!(File::create(&td.path().join(path))).write_all(b"bar"));
- let mut index = t!(repo.index());
- t!(index.add_path(path));
- let mut opts = DiffOptions::new();
- opts.include_untracked(true);
- let diff = t!(repo.diff_tree_to_index(None, Some(&index), Some(&mut opts)));
- let mut new_lines = 0;
- t!(diff.foreach(
- &mut |_file, _progress| { true },
- None,
- Some(&mut |_file, hunk| {
- new_lines = hunk.new_lines();
- true
- }),
- None
- ));
- assert_eq!(new_lines, 1);
- }
-
- #[test]
- fn foreach_all_callbacks() {
- let fib = vec![0, 1, 1, 2, 3, 5, 8];
- // Verified with a node implementation of deflate, might be worth
- // adding a deflate lib to do this inline here.
- let deflated_fib = vec![120, 156, 99, 96, 100, 100, 98, 102, 229, 0, 0, 0, 53, 0, 21];
- let foo_path = Path::new("foo");
- let bin_path = Path::new("bin");
- let (td, repo) = crate::test::repo_init();
- t!(t!(File::create(&td.path().join(foo_path))).write_all(b"bar\n"));
- t!(t!(File::create(&td.path().join(bin_path))).write_all(&fib));
- let mut index = t!(repo.index());
- t!(index.add_path(foo_path));
- t!(index.add_path(bin_path));
- let mut opts = DiffOptions::new();
- opts.include_untracked(true).show_binary(true);
- let diff = t!(repo.diff_tree_to_index(None, Some(&index), Some(&mut opts)));
- let mut bin_content = None;
- let mut new_lines = 0;
- let mut line_content = None;
- t!(diff.foreach(
- &mut |_file, _progress| { true },
- Some(&mut |_file, binary| {
- bin_content = Some(binary.new_file().data().to_owned());
- true
- }),
- Some(&mut |_file, hunk| {
- new_lines = hunk.new_lines();
- true
- }),
- Some(&mut |_file, _hunk, line| {
- line_content = String::from_utf8(line.content().into()).ok();
- true
- })
- ));
- assert_eq!(bin_content, Some(deflated_fib));
- assert_eq!(new_lines, 1);
- assert_eq!(line_content, Some("bar\n".to_string()));
- }
-
- #[test]
- fn format_email_simple() {
- let (_td, repo) = crate::test::repo_init();
- const COMMIT_MESSAGE: &str = "Modify some content";
- const EXPECTED_EMAIL_START: &str = concat!(
- "From f1234fb0588b6ed670779a34ba5c51ef962f285f Mon Sep 17 00:00:00 2001\n",
- "From: Techcable <dummy@dummy.org>\n",
- "Date: Tue, 11 Jan 1972 17:46:40 +0000\n",
- "Subject: [PATCH] Modify some content\n",
- "\n",
- "---\n",
- " file1.txt | 8 +++++---\n",
- " 1 file changed, 5 insertions(+), 3 deletions(-)\n",
- "\n",
- "diff --git a/file1.txt b/file1.txt\n",
- "index 94aaae8..af8f41d 100644\n",
- "--- a/file1.txt\n",
- "+++ b/file1.txt\n",
- "@@ -1,15 +1,17 @@\n",
- " file1.txt\n",
- " file1.txt\n",
- "+_file1.txt_\n",
- " file1.txt\n",
- " file1.txt\n",
- " file1.txt\n",
- " file1.txt\n",
- "+\n",
- "+\n",
- " file1.txt\n",
- " file1.txt\n",
- " file1.txt\n",
- " file1.txt\n",
- " file1.txt\n",
- "-file1.txt\n",
- "-file1.txt\n",
- "-file1.txt\n",
- "+_file1.txt_\n",
- "+_file1.txt_\n",
- " file1.txt\n",
- "--\n"
- );
- const ORIGINAL_FILE: &str = concat!(
- "file1.txt\n",
- "file1.txt\n",
- "file1.txt\n",
- "file1.txt\n",
- "file1.txt\n",
- "file1.txt\n",
- "file1.txt\n",
- "file1.txt\n",
- "file1.txt\n",
- "file1.txt\n",
- "file1.txt\n",
- "file1.txt\n",
- "file1.txt\n",
- "file1.txt\n",
- "file1.txt\n"
- );
- const UPDATED_FILE: &str = concat!(
- "file1.txt\n",
- "file1.txt\n",
- "_file1.txt_\n",
- "file1.txt\n",
- "file1.txt\n",
- "file1.txt\n",
- "file1.txt\n",
- "\n",
- "\n",
- "file1.txt\n",
- "file1.txt\n",
- "file1.txt\n",
- "file1.txt\n",
- "file1.txt\n",
- "_file1.txt_\n",
- "_file1.txt_\n",
- "file1.txt\n"
- );
- const FILE_MODE: i32 = 0o100644;
- let original_file = repo.blob(ORIGINAL_FILE.as_bytes()).unwrap();
- let updated_file = repo.blob(UPDATED_FILE.as_bytes()).unwrap();
- let mut original_tree = repo.treebuilder(None).unwrap();
- original_tree
- .insert("file1.txt", original_file, FILE_MODE)
- .unwrap();
- let original_tree = original_tree.write().unwrap();
- let mut updated_tree = repo.treebuilder(None).unwrap();
- updated_tree
- .insert("file1.txt", updated_file, FILE_MODE)
- .unwrap();
- let updated_tree = updated_tree.write().unwrap();
- let time = Time::new(64_000_000, 0);
- let author = Signature::new("Techcable", "dummy@dummy.org", &time).unwrap();
- let updated_commit = repo
- .commit(
- None,
- &author,
- &author,
- COMMIT_MESSAGE,
- &repo.find_tree(updated_tree).unwrap(),
- &[], // NOTE: Have no parents to ensure stable hash
- )
- .unwrap();
- let updated_commit = repo.find_commit(updated_commit).unwrap();
- let mut diff = repo
- .diff_tree_to_tree(
- Some(&repo.find_tree(original_tree).unwrap()),
- Some(&repo.find_tree(updated_tree).unwrap()),
- None,
- )
- .unwrap();
- #[allow(deprecated)]
- let actual_email = diff.format_email(1, 1, &updated_commit, None).unwrap();
- let actual_email = actual_email.as_str().unwrap();
- assert!(
- actual_email.starts_with(EXPECTED_EMAIL_START),
- "Unexpected email:\n{}",
- actual_email
- );
- let mut remaining_lines = actual_email[EXPECTED_EMAIL_START.len()..].lines();
- let version_line = remaining_lines.next();
- assert!(
- version_line.unwrap().starts_with("libgit2"),
- "Invalid version line: {:?}",
- version_line
- );
- while let Some(line) = remaining_lines.next() {
- assert_eq!(line.trim(), "")
- }
- }
-
- #[test]
- fn foreach_diff_line_origin_value() {
- let foo_path = Path::new("foo");
- let (td, repo) = crate::test::repo_init();
- t!(t!(File::create(&td.path().join(foo_path))).write_all(b"bar\n"));
- let mut index = t!(repo.index());
- t!(index.add_path(foo_path));
- let mut opts = DiffOptions::new();
- opts.include_untracked(true);
- let diff = t!(repo.diff_tree_to_index(None, Some(&index), Some(&mut opts)));
- let mut origin_values: Vec<DiffLineType> = Vec::new();
- t!(diff.foreach(
- &mut |_file, _progress| { true },
- None,
- None,
- Some(&mut |_file, _hunk, line| {
- origin_values.push(line.origin_value());
- true
- })
- ));
- assert_eq!(origin_values.len(), 1);
- assert_eq!(origin_values[0], DiffLineType::Addition);
- }
-
- #[test]
- fn foreach_exits_with_euser() {
- let foo_path = Path::new("foo");
- let bar_path = Path::new("foo");
-
- let (td, repo) = crate::test::repo_init();
- t!(t!(File::create(&td.path().join(foo_path))).write_all(b"bar\n"));
-
- let mut index = t!(repo.index());
- t!(index.add_path(foo_path));
- t!(index.add_path(bar_path));
-
- let mut opts = DiffOptions::new();
- opts.include_untracked(true);
- let diff = t!(repo.diff_tree_to_index(None, Some(&index), Some(&mut opts)));
-
- let mut calls = 0;
- let result = diff.foreach(
- &mut |_file, _progress| {
- calls += 1;
- false
- },
- None,
- None,
- None,
- );
-
- assert_eq!(result.unwrap_err().code(), crate::ErrorCode::User);
- }
-}
diff --git a/extra/git2/src/email.rs b/extra/git2/src/email.rs
deleted file mode 100644
index d3ebc0384..000000000
--- a/extra/git2/src/email.rs
+++ /dev/null
@@ -1,183 +0,0 @@
-use std::ffi::CString;
-use std::{mem, ptr};
-
-use crate::util::Binding;
-use crate::{raw, Buf, Commit, DiffFindOptions, DiffOptions, Error, IntoCString};
-use crate::{Diff, Oid, Signature};
-
-/// A structure to represent patch in mbox format for sending via email
-pub struct Email {
- buf: Buf,
-}
-
-/// Options for controlling the formatting of the generated e-mail.
-pub struct EmailCreateOptions {
- diff_options: DiffOptions,
- diff_find_options: DiffFindOptions,
- subject_prefix: Option<CString>,
- raw: raw::git_email_create_options,
-}
-
-impl Default for EmailCreateOptions {
- fn default() -> Self {
- // Defaults options created in corresponding to `GIT_EMAIL_CREATE_OPTIONS_INIT`
- let default_options = raw::git_email_create_options {
- version: raw::GIT_EMAIL_CREATE_OPTIONS_VERSION,
- flags: raw::GIT_EMAIL_CREATE_DEFAULT as u32,
- diff_opts: unsafe { mem::zeroed() },
- diff_find_opts: unsafe { mem::zeroed() },
- subject_prefix: ptr::null(),
- start_number: 1,
- reroll_number: 0,
- };
- let mut diff_options = DiffOptions::new();
- diff_options.show_binary(true).context_lines(3);
- Self {
- diff_options,
- diff_find_options: DiffFindOptions::new(),
- subject_prefix: None,
- raw: default_options,
- }
- }
-}
-
-impl EmailCreateOptions {
- /// Creates a new set of email create options
- ///
- /// By default, options include rename detection and binary
- /// diffs to match `git format-patch`.
- pub fn new() -> Self {
- Self::default()
- }
-
- fn flag(&mut self, opt: raw::git_email_create_flags_t, val: bool) -> &mut Self {
- let opt = opt as u32;
- if val {
- self.raw.flags |= opt;
- } else {
- self.raw.flags &= !opt;
- }
- self
- }
-
- /// Flag indicating whether patch numbers are included in the subject prefix.
- pub fn omit_numbers(&mut self, omit: bool) -> &mut Self {
- self.flag(raw::GIT_EMAIL_CREATE_OMIT_NUMBERS, omit)
- }
-
- /// Flag indicating whether numbers included in the subject prefix even when
- /// the patch is for a single commit (1/1).
- pub fn always_number(&mut self, always: bool) -> &mut Self {
- self.flag(raw::GIT_EMAIL_CREATE_ALWAYS_NUMBER, always)
- }
-
- /// Flag indicating whether rename or similarity detection are ignored.
- pub fn ignore_renames(&mut self, ignore: bool) -> &mut Self {
- self.flag(raw::GIT_EMAIL_CREATE_NO_RENAMES, ignore)
- }
-
- /// Get mutable access to `DiffOptions` that are used for creating diffs.
- pub fn diff_options(&mut self) -> &mut DiffOptions {
- &mut self.diff_options
- }
-
- /// Get mutable access to `DiffFindOptions` that are used for finding
- /// similarities within diffs.
- pub fn diff_find_options(&mut self) -> &mut DiffFindOptions {
- &mut self.diff_find_options
- }
-
- /// Set the subject prefix
- ///
- /// The default value for this is "PATCH". If set to an empty string ("")
- /// then only the patch numbers will be shown in the prefix.
- /// If the subject_prefix is empty and patch numbers are not being shown,
- /// the prefix will be omitted entirely.
- pub fn subject_prefix<T: IntoCString>(&mut self, t: T) -> &mut Self {
- self.subject_prefix = Some(t.into_c_string().unwrap());
- self
- }
-
- /// Set the starting patch number; this cannot be 0.
- ///
- /// The default value for this is 1.
- pub fn start_number(&mut self, number: usize) -> &mut Self {
- self.raw.start_number = number;
- self
- }
-
- /// Set the "re-roll" number.
- ///
- /// The default value for this is 0 (no re-roll).
- pub fn reroll_number(&mut self, number: usize) -> &mut Self {
- self.raw.reroll_number = number;
- self
- }
-
- /// Acquire a pointer to the underlying raw options.
- ///
- /// This function is unsafe as the pointer is only valid so long as this
- /// structure is not moved, modified, or used elsewhere.
- unsafe fn raw(&mut self) -> *const raw::git_email_create_options {
- self.raw.subject_prefix = self
- .subject_prefix
- .as_ref()
- .map(|s| s.as_ptr())
- .unwrap_or(ptr::null());
- self.raw.diff_opts = ptr::read(self.diff_options.raw());
- self.raw.diff_find_opts = ptr::read(self.diff_find_options.raw());
- &self.raw as *const _
- }
-}
-
-impl Email {
- /// Returns a byte slice with stored e-mail patch in. `Email` could be
- /// created by one of the `from_*` functions.
- pub fn as_slice(&self) -> &[u8] {
- &self.buf
- }
-
- /// Create a diff for a commit in mbox format for sending via email.
- pub fn from_diff<T: IntoCString>(
- diff: &Diff<'_>,
- patch_idx: usize,
- patch_count: usize,
- commit_id: &Oid,
- summary: T,
- body: T,
- author: &Signature<'_>,
- opts: &mut EmailCreateOptions,
- ) -> Result<Self, Error> {
- let buf = Buf::new();
- let summary = summary.into_c_string()?;
- let body = body.into_c_string()?;
- unsafe {
- try_call!(raw::git_email_create_from_diff(
- buf.raw(),
- Binding::raw(diff),
- patch_idx,
- patch_count,
- Binding::raw(commit_id),
- summary.as_ptr(),
- body.as_ptr(),
- Binding::raw(author),
- opts.raw()
- ));
- Ok(Self { buf })
- }
- }
-
- /// Create a diff for a commit in mbox format for sending via email.
- /// The commit must not be a merge commit.
- pub fn from_commit(commit: &Commit<'_>, opts: &mut EmailCreateOptions) -> Result<Self, Error> {
- let buf = Buf::new();
- unsafe {
- try_call!(raw::git_email_create_from_commit(
- buf.raw(),
- commit.raw(),
- opts.raw()
- ));
- Ok(Self { buf })
- }
- }
-}
diff --git a/extra/git2/src/error.rs b/extra/git2/src/error.rs
deleted file mode 100644
index 6f1c4d4c7..000000000
--- a/extra/git2/src/error.rs
+++ /dev/null
@@ -1,399 +0,0 @@
-use libc::c_int;
-use std::env::JoinPathsError;
-use std::error;
-use std::ffi::{CStr, NulError};
-use std::fmt;
-use std::str;
-
-use crate::{raw, ErrorClass, ErrorCode};
-
-/// A structure to represent errors coming out of libgit2.
-#[derive(Debug, PartialEq)]
-pub struct Error {
- code: c_int,
- klass: c_int,
- message: String,
-}
-
-impl Error {
- /// Creates a new error.
- ///
- /// This is mainly intended for implementers of custom transports or
- /// database backends, where it is desirable to propagate an [`Error`]
- /// through `libgit2`.
- pub fn new<S: AsRef<str>>(code: ErrorCode, class: ErrorClass, message: S) -> Self {
- let mut err = Error::from_str(message.as_ref());
- err.set_code(code);
- err.set_class(class);
- err
- }
-
- /// Returns the last error that happened with the code specified by `code`.
- ///
- /// The `code` argument typically comes from the return value of a function
- /// call. This code will later be returned from the `code` function.
- ///
- /// Historically this function returned `Some` or `None` based on the return
- /// value of `git_error_last` but nowadays it always returns `Some` so it's
- /// safe to unwrap the return value. This API will change in the next major
- /// version.
- pub fn last_error(code: c_int) -> Option<Error> {
- crate::init();
- unsafe {
- // Note that whenever libgit2 returns an error any negative value
- // indicates that an error happened. Auxiliary information is
- // *usually* in `git_error_last` but unfortunately that's not always
- // the case. Sometimes a negative error code is returned from
- // libgit2 *without* calling `git_error_set` internally to configure
- // the error.
- //
- // To handle this case and hopefully provide better error messages
- // on our end we unconditionally call `git_error_clear` when we're done
- // with an error. This is an attempt to clear it as aggressively as
- // possible when we can to ensure that error information from one
- // api invocation doesn't leak over to the next api invocation.
- //
- // Additionally if `git_error_last` returns null then we returned a
- // canned error out.
- let ptr = raw::git_error_last();
- let err = if ptr.is_null() {
- let mut error = Error::from_str("an unknown git error occurred");
- error.code = code;
- error
- } else {
- Error::from_raw(code, ptr)
- };
- raw::git_error_clear();
- Some(err)
- }
- }
-
- unsafe fn from_raw(code: c_int, ptr: *const raw::git_error) -> Error {
- let message = CStr::from_ptr((*ptr).message as *const _).to_bytes();
- let message = String::from_utf8_lossy(message).into_owned();
- Error {
- code,
- klass: (*ptr).klass,
- message,
- }
- }
-
- /// Creates a new error from the given string as the error.
- ///
- /// The error returned will have the code `GIT_ERROR` and the class
- /// `GIT_ERROR_NONE`.
- pub fn from_str(s: &str) -> Error {
- Error {
- code: raw::GIT_ERROR as c_int,
- klass: raw::GIT_ERROR_NONE as c_int,
- message: s.to_string(),
- }
- }
-
- /// Return the error code associated with this error.
- ///
- /// An error code is intended to be programmatically actionable most of the
- /// time. For example the code `GIT_EAGAIN` indicates that an error could be
- /// fixed by trying again, while the code `GIT_ERROR` is more bland and
- /// doesn't convey anything in particular.
- pub fn code(&self) -> ErrorCode {
- match self.raw_code() {
- raw::GIT_OK => super::ErrorCode::GenericError,
- raw::GIT_ERROR => super::ErrorCode::GenericError,
- raw::GIT_ENOTFOUND => super::ErrorCode::NotFound,
- raw::GIT_EEXISTS => super::ErrorCode::Exists,
- raw::GIT_EAMBIGUOUS => super::ErrorCode::Ambiguous,
- raw::GIT_EBUFS => super::ErrorCode::BufSize,
- raw::GIT_EUSER => super::ErrorCode::User,
- raw::GIT_EBAREREPO => super::ErrorCode::BareRepo,
- raw::GIT_EUNBORNBRANCH => super::ErrorCode::UnbornBranch,
- raw::GIT_EUNMERGED => super::ErrorCode::Unmerged,
- raw::GIT_ENONFASTFORWARD => super::ErrorCode::NotFastForward,
- raw::GIT_EINVALIDSPEC => super::ErrorCode::InvalidSpec,
- raw::GIT_ECONFLICT => super::ErrorCode::Conflict,
- raw::GIT_ELOCKED => super::ErrorCode::Locked,
- raw::GIT_EMODIFIED => super::ErrorCode::Modified,
- raw::GIT_PASSTHROUGH => super::ErrorCode::GenericError,
- raw::GIT_ITEROVER => super::ErrorCode::GenericError,
- raw::GIT_EAUTH => super::ErrorCode::Auth,
- raw::GIT_ECERTIFICATE => super::ErrorCode::Certificate,
- raw::GIT_EAPPLIED => super::ErrorCode::Applied,
- raw::GIT_EPEEL => super::ErrorCode::Peel,
- raw::GIT_EEOF => super::ErrorCode::Eof,
- raw::GIT_EINVALID => super::ErrorCode::Invalid,
- raw::GIT_EUNCOMMITTED => super::ErrorCode::Uncommitted,
- raw::GIT_EDIRECTORY => super::ErrorCode::Directory,
- raw::GIT_EMERGECONFLICT => super::ErrorCode::MergeConflict,
- raw::GIT_EMISMATCH => super::ErrorCode::HashsumMismatch,
- raw::GIT_EINDEXDIRTY => super::ErrorCode::IndexDirty,
- raw::GIT_EAPPLYFAIL => super::ErrorCode::ApplyFail,
- raw::GIT_EOWNER => super::ErrorCode::Owner,
- _ => super::ErrorCode::GenericError,
- }
- }
-
- /// Modify the error code associated with this error.
- ///
- /// This is mainly intended to be used by implementers of custom transports
- /// or database backends, and should be used with care.
- pub fn set_code(&mut self, code: ErrorCode) {
- self.code = match code {
- ErrorCode::GenericError => raw::GIT_ERROR,
- ErrorCode::NotFound => raw::GIT_ENOTFOUND,
- ErrorCode::Exists => raw::GIT_EEXISTS,
- ErrorCode::Ambiguous => raw::GIT_EAMBIGUOUS,
- ErrorCode::BufSize => raw::GIT_EBUFS,
- ErrorCode::User => raw::GIT_EUSER,
- ErrorCode::BareRepo => raw::GIT_EBAREREPO,
- ErrorCode::UnbornBranch => raw::GIT_EUNBORNBRANCH,
- ErrorCode::Unmerged => raw::GIT_EUNMERGED,
- ErrorCode::NotFastForward => raw::GIT_ENONFASTFORWARD,
- ErrorCode::InvalidSpec => raw::GIT_EINVALIDSPEC,
- ErrorCode::Conflict => raw::GIT_ECONFLICT,
- ErrorCode::Locked => raw::GIT_ELOCKED,
- ErrorCode::Modified => raw::GIT_EMODIFIED,
- ErrorCode::Auth => raw::GIT_EAUTH,
- ErrorCode::Certificate => raw::GIT_ECERTIFICATE,
- ErrorCode::Applied => raw::GIT_EAPPLIED,
- ErrorCode::Peel => raw::GIT_EPEEL,
- ErrorCode::Eof => raw::GIT_EEOF,
- ErrorCode::Invalid => raw::GIT_EINVALID,
- ErrorCode::Uncommitted => raw::GIT_EUNCOMMITTED,
- ErrorCode::Directory => raw::GIT_EDIRECTORY,
- ErrorCode::MergeConflict => raw::GIT_EMERGECONFLICT,
- ErrorCode::HashsumMismatch => raw::GIT_EMISMATCH,
- ErrorCode::IndexDirty => raw::GIT_EINDEXDIRTY,
- ErrorCode::ApplyFail => raw::GIT_EAPPLYFAIL,
- ErrorCode::Owner => raw::GIT_EOWNER,
- };
- }
-
- /// Return the error class associated with this error.
- ///
- /// Error classes are in general mostly just informative. For example the
- /// class will show up in the error message but otherwise an error class is
- /// typically not directly actionable.
- pub fn class(&self) -> ErrorClass {
- match self.raw_class() {
- raw::GIT_ERROR_NONE => super::ErrorClass::None,
- raw::GIT_ERROR_NOMEMORY => super::ErrorClass::NoMemory,
- raw::GIT_ERROR_OS => super::ErrorClass::Os,
- raw::GIT_ERROR_INVALID => super::ErrorClass::Invalid,
- raw::GIT_ERROR_REFERENCE => super::ErrorClass::Reference,
- raw::GIT_ERROR_ZLIB => super::ErrorClass::Zlib,
- raw::GIT_ERROR_REPOSITORY => super::ErrorClass::Repository,
- raw::GIT_ERROR_CONFIG => super::ErrorClass::Config,
- raw::GIT_ERROR_REGEX => super::ErrorClass::Regex,
- raw::GIT_ERROR_ODB => super::ErrorClass::Odb,
- raw::GIT_ERROR_INDEX => super::ErrorClass::Index,
- raw::GIT_ERROR_OBJECT => super::ErrorClass::Object,
- raw::GIT_ERROR_NET => super::ErrorClass::Net,
- raw::GIT_ERROR_TAG => super::ErrorClass::Tag,
- raw::GIT_ERROR_TREE => super::ErrorClass::Tree,
- raw::GIT_ERROR_INDEXER => super::ErrorClass::Indexer,
- raw::GIT_ERROR_SSL => super::ErrorClass::Ssl,
- raw::GIT_ERROR_SUBMODULE => super::ErrorClass::Submodule,
- raw::GIT_ERROR_THREAD => super::ErrorClass::Thread,
- raw::GIT_ERROR_STASH => super::ErrorClass::Stash,
- raw::GIT_ERROR_CHECKOUT => super::ErrorClass::Checkout,
- raw::GIT_ERROR_FETCHHEAD => super::ErrorClass::FetchHead,
- raw::GIT_ERROR_MERGE => super::ErrorClass::Merge,
- raw::GIT_ERROR_SSH => super::ErrorClass::Ssh,
- raw::GIT_ERROR_FILTER => super::ErrorClass::Filter,
- raw::GIT_ERROR_REVERT => super::ErrorClass::Revert,
- raw::GIT_ERROR_CALLBACK => super::ErrorClass::Callback,
- raw::GIT_ERROR_CHERRYPICK => super::ErrorClass::CherryPick,
- raw::GIT_ERROR_DESCRIBE => super::ErrorClass::Describe,
- raw::GIT_ERROR_REBASE => super::ErrorClass::Rebase,
- raw::GIT_ERROR_FILESYSTEM => super::ErrorClass::Filesystem,
- raw::GIT_ERROR_PATCH => super::ErrorClass::Patch,
- raw::GIT_ERROR_WORKTREE => super::ErrorClass::Worktree,
- raw::GIT_ERROR_SHA1 => super::ErrorClass::Sha1,
- raw::GIT_ERROR_HTTP => super::ErrorClass::Http,
- _ => super::ErrorClass::None,
- }
- }
-
- /// Modify the error class associated with this error.
- ///
- /// This is mainly intended to be used by implementers of custom transports
- /// or database backends, and should be used with care.
- pub fn set_class(&mut self, class: ErrorClass) {
- self.klass = match class {
- ErrorClass::None => raw::GIT_ERROR_NONE,
- ErrorClass::NoMemory => raw::GIT_ERROR_NOMEMORY,
- ErrorClass::Os => raw::GIT_ERROR_OS,
- ErrorClass::Invalid => raw::GIT_ERROR_INVALID,
- ErrorClass::Reference => raw::GIT_ERROR_REFERENCE,
- ErrorClass::Zlib => raw::GIT_ERROR_ZLIB,
- ErrorClass::Repository => raw::GIT_ERROR_REPOSITORY,
- ErrorClass::Config => raw::GIT_ERROR_CONFIG,
- ErrorClass::Regex => raw::GIT_ERROR_REGEX,
- ErrorClass::Odb => raw::GIT_ERROR_ODB,
- ErrorClass::Index => raw::GIT_ERROR_INDEX,
- ErrorClass::Object => raw::GIT_ERROR_OBJECT,
- ErrorClass::Net => raw::GIT_ERROR_NET,
- ErrorClass::Tag => raw::GIT_ERROR_TAG,
- ErrorClass::Tree => raw::GIT_ERROR_TREE,
- ErrorClass::Indexer => raw::GIT_ERROR_INDEXER,
- ErrorClass::Ssl => raw::GIT_ERROR_SSL,
- ErrorClass::Submodule => raw::GIT_ERROR_SUBMODULE,
- ErrorClass::Thread => raw::GIT_ERROR_THREAD,
- ErrorClass::Stash => raw::GIT_ERROR_STASH,
- ErrorClass::Checkout => raw::GIT_ERROR_CHECKOUT,
- ErrorClass::FetchHead => raw::GIT_ERROR_FETCHHEAD,
- ErrorClass::Merge => raw::GIT_ERROR_MERGE,
- ErrorClass::Ssh => raw::GIT_ERROR_SSH,
- ErrorClass::Filter => raw::GIT_ERROR_FILTER,
- ErrorClass::Revert => raw::GIT_ERROR_REVERT,
- ErrorClass::Callback => raw::GIT_ERROR_CALLBACK,
- ErrorClass::CherryPick => raw::GIT_ERROR_CHERRYPICK,
- ErrorClass::Describe => raw::GIT_ERROR_DESCRIBE,
- ErrorClass::Rebase => raw::GIT_ERROR_REBASE,
- ErrorClass::Filesystem => raw::GIT_ERROR_FILESYSTEM,
- ErrorClass::Patch => raw::GIT_ERROR_PATCH,
- ErrorClass::Worktree => raw::GIT_ERROR_WORKTREE,
- ErrorClass::Sha1 => raw::GIT_ERROR_SHA1,
- ErrorClass::Http => raw::GIT_ERROR_HTTP,
- } as c_int;
- }
-
- /// Return the raw error code associated with this error.
- pub fn raw_code(&self) -> raw::git_error_code {
- macro_rules! check( ($($e:ident,)*) => (
- $(if self.code == raw::$e as c_int { raw::$e }) else *
- else {
- raw::GIT_ERROR
- }
- ) );
- check!(
- GIT_OK,
- GIT_ERROR,
- GIT_ENOTFOUND,
- GIT_EEXISTS,
- GIT_EAMBIGUOUS,
- GIT_EBUFS,
- GIT_EUSER,
- GIT_EBAREREPO,
- GIT_EUNBORNBRANCH,
- GIT_EUNMERGED,
- GIT_ENONFASTFORWARD,
- GIT_EINVALIDSPEC,
- GIT_ECONFLICT,
- GIT_ELOCKED,
- GIT_EMODIFIED,
- GIT_EAUTH,
- GIT_ECERTIFICATE,
- GIT_EAPPLIED,
- GIT_EPEEL,
- GIT_EEOF,
- GIT_EINVALID,
- GIT_EUNCOMMITTED,
- GIT_PASSTHROUGH,
- GIT_ITEROVER,
- GIT_RETRY,
- GIT_EMISMATCH,
- GIT_EINDEXDIRTY,
- GIT_EAPPLYFAIL,
- GIT_EOWNER,
- )
- }
-
- /// Return the raw error class associated with this error.
- pub fn raw_class(&self) -> raw::git_error_t {
- macro_rules! check( ($($e:ident,)*) => (
- $(if self.klass == raw::$e as c_int { raw::$e }) else *
- else {
- raw::GIT_ERROR_NONE
- }
- ) );
- check!(
- GIT_ERROR_NONE,
- GIT_ERROR_NOMEMORY,
- GIT_ERROR_OS,
- GIT_ERROR_INVALID,
- GIT_ERROR_REFERENCE,
- GIT_ERROR_ZLIB,
- GIT_ERROR_REPOSITORY,
- GIT_ERROR_CONFIG,
- GIT_ERROR_REGEX,
- GIT_ERROR_ODB,
- GIT_ERROR_INDEX,
- GIT_ERROR_OBJECT,
- GIT_ERROR_NET,
- GIT_ERROR_TAG,
- GIT_ERROR_TREE,
- GIT_ERROR_INDEXER,
- GIT_ERROR_SSL,
- GIT_ERROR_SUBMODULE,
- GIT_ERROR_THREAD,
- GIT_ERROR_STASH,
- GIT_ERROR_CHECKOUT,
- GIT_ERROR_FETCHHEAD,
- GIT_ERROR_MERGE,
- GIT_ERROR_SSH,
- GIT_ERROR_FILTER,
- GIT_ERROR_REVERT,
- GIT_ERROR_CALLBACK,
- GIT_ERROR_CHERRYPICK,
- GIT_ERROR_DESCRIBE,
- GIT_ERROR_REBASE,
- GIT_ERROR_FILESYSTEM,
- GIT_ERROR_PATCH,
- GIT_ERROR_WORKTREE,
- GIT_ERROR_SHA1,
- GIT_ERROR_HTTP,
- )
- }
-
- /// Return the message associated with this error
- pub fn message(&self) -> &str {
- &self.message
- }
-}
-
-impl error::Error for Error {}
-
-impl fmt::Display for Error {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- write!(f, "{}", self.message)?;
- match self.class() {
- ErrorClass::None => {}
- other => write!(f, "; class={:?} ({})", other, self.klass)?,
- }
- match self.code() {
- ErrorCode::GenericError => {}
- other => write!(f, "; code={:?} ({})", other, self.code)?,
- }
- Ok(())
- }
-}
-
-impl From<NulError> for Error {
- fn from(_: NulError) -> Error {
- Error::from_str(
- "data contained a nul byte that could not be \
- represented as a string",
- )
- }
-}
-
-impl From<JoinPathsError> for Error {
- fn from(e: JoinPathsError) -> Error {
- Error::from_str(&e.to_string())
- }
-}
-
-#[cfg(test)]
-mod tests {
- use crate::{ErrorClass, ErrorCode};
-
- #[test]
- fn smoke() {
- let (_td, repo) = crate::test::repo_init();
-
- let err = repo.find_submodule("does_not_exist").err().unwrap();
- assert_eq!(err.code(), ErrorCode::NotFound);
- assert_eq!(err.class(), ErrorClass::Submodule);
- }
-}
diff --git a/extra/git2/src/index.rs b/extra/git2/src/index.rs
deleted file mode 100644
index 0291d3cb9..000000000
--- a/extra/git2/src/index.rs
+++ /dev/null
@@ -1,929 +0,0 @@
-use std::ffi::{CStr, CString};
-use std::marker;
-use std::ops::Range;
-use std::path::Path;
-use std::ptr;
-use std::slice;
-
-use libc::{c_char, c_int, c_uint, c_void, size_t};
-
-use crate::util::{self, path_to_repo_path, Binding};
-use crate::IntoCString;
-use crate::{panic, raw, Error, IndexAddOption, IndexTime, Oid, Repository, Tree};
-
-/// A structure to represent a git [index][1]
-///
-/// [1]: http://git-scm.com/book/en/Git-Internals-Git-Objects
-pub struct Index {
- raw: *mut raw::git_index,
-}
-
-/// An iterator over the entries in an index
-pub struct IndexEntries<'index> {
- range: Range<usize>,
- index: &'index Index,
-}
-
-/// An iterator over the conflicting entries in an index
-pub struct IndexConflicts<'index> {
- conflict_iter: *mut raw::git_index_conflict_iterator,
- _marker: marker::PhantomData<&'index Index>,
-}
-
-/// A structure to represent the information returned when a conflict is detected in an index entry
-pub struct IndexConflict {
- /// The ancestor index entry of the two conflicting index entries
- pub ancestor: Option<IndexEntry>,
- /// The index entry originating from the user's copy of the repository.
- /// Its contents conflict with 'their' index entry
- pub our: Option<IndexEntry>,
- /// The index entry originating from the external repository.
- /// Its contents conflict with 'our' index entry
- pub their: Option<IndexEntry>,
-}
-
-/// A callback function to filter index matches.
-///
-/// Used by `Index::{add_all,remove_all,update_all}`. The first argument is the
-/// path, and the second is the pathspec that matched it. Return 0 to confirm
-/// the operation on the item, > 0 to skip the item, and < 0 to abort the scan.
-pub type IndexMatchedPath<'a> = dyn FnMut(&Path, &[u8]) -> i32 + 'a;
-
-/// A structure to represent an entry or a file inside of an index.
-///
-/// All fields of an entry are public for modification and inspection. This is
-/// also how a new index entry is created.
-#[allow(missing_docs)]
-#[derive(Debug)]
-pub struct IndexEntry {
- pub ctime: IndexTime,
- pub mtime: IndexTime,
- pub dev: u32,
- pub ino: u32,
- pub mode: u32,
- pub uid: u32,
- pub gid: u32,
- pub file_size: u32,
- pub id: Oid,
- pub flags: u16,
- pub flags_extended: u16,
-
- /// The path of this index entry as a byte vector. Regardless of the
- /// current platform, the directory separator is an ASCII forward slash
- /// (`0x2F`). There are no terminating or internal NUL characters, and no
- /// trailing slashes. Most of the time, paths will be valid utf-8 — but
- /// not always. For more information on the path storage format, see
- /// [these git docs][git-index-docs]. Note that libgit2 will take care of
- /// handling the prefix compression mentioned there.
- ///
- /// [git-index-docs]: https://github.com/git/git/blob/a08a83db2bf27f015bec9a435f6d73e223c21c5e/Documentation/technical/index-format.txt#L107-L124
- ///
- /// You can turn this value into a `std::ffi::CString` with
- /// `CString::new(&entry.path[..]).unwrap()`. To turn a reference into a
- /// `&std::path::Path`, see the `bytes2path()` function in the private,
- /// internal `util` module in this crate’s source code.
- pub path: Vec<u8>,
-}
-
-impl Index {
- /// Creates a new in-memory index.
- ///
- /// This index object cannot be read/written to the filesystem, but may be
- /// used to perform in-memory index operations.
- pub fn new() -> Result<Index, Error> {
- crate::init();
- let mut raw = ptr::null_mut();
- unsafe {
- try_call!(raw::git_index_new(&mut raw));
- Ok(Binding::from_raw(raw))
- }
- }
-
- /// Create a new bare Git index object as a memory representation of the Git
- /// index file in 'index_path', without a repository to back it.
- ///
- /// Since there is no ODB or working directory behind this index, any Index
- /// methods which rely on these (e.g. add_path) will fail.
- ///
- /// If you need an index attached to a repository, use the `index()` method
- /// on `Repository`.
- pub fn open(index_path: &Path) -> Result<Index, Error> {
- crate::init();
- let mut raw = ptr::null_mut();
- // Normal file path OK (does not need Windows conversion).
- let index_path = index_path.into_c_string()?;
- unsafe {
- try_call!(raw::git_index_open(&mut raw, index_path));
- Ok(Binding::from_raw(raw))
- }
- }
-
- /// Get index on-disk version.
- ///
- /// Valid return values are 2, 3, or 4. If 3 is returned, an index
- /// with version 2 may be written instead, if the extension data in
- /// version 3 is not necessary.
- pub fn version(&self) -> u32 {
- unsafe { raw::git_index_version(self.raw) }
- }
-
- /// Set index on-disk version.
- ///
- /// Valid values are 2, 3, or 4. If 2 is given, git_index_write may
- /// write an index with version 3 instead, if necessary to accurately
- /// represent the index.
- pub fn set_version(&mut self, version: u32) -> Result<(), Error> {
- unsafe {
- try_call!(raw::git_index_set_version(self.raw, version));
- }
- Ok(())
- }
-
- /// Add or update an index entry from an in-memory struct
- ///
- /// If a previous index entry exists that has the same path and stage as the
- /// given 'source_entry', it will be replaced. Otherwise, the 'source_entry'
- /// will be added.
- pub fn add(&mut self, entry: &IndexEntry) -> Result<(), Error> {
- let path = CString::new(&entry.path[..])?;
-
- // libgit2 encodes the length of the path in the lower bits of the
- // `flags` entry, so mask those out and recalculate here to ensure we
- // don't corrupt anything.
- let mut flags = entry.flags & !raw::GIT_INDEX_ENTRY_NAMEMASK;
-
- if entry.path.len() < raw::GIT_INDEX_ENTRY_NAMEMASK as usize {
- flags |= entry.path.len() as u16;
- } else {
- flags |= raw::GIT_INDEX_ENTRY_NAMEMASK;
- }
-
- unsafe {
- let raw = raw::git_index_entry {
- dev: entry.dev,
- ino: entry.ino,
- mode: entry.mode,
- uid: entry.uid,
- gid: entry.gid,
- file_size: entry.file_size,
- id: *entry.id.raw(),
- flags,
- flags_extended: entry.flags_extended,
- path: path.as_ptr(),
- mtime: raw::git_index_time {
- seconds: entry.mtime.seconds(),
- nanoseconds: entry.mtime.nanoseconds(),
- },
- ctime: raw::git_index_time {
- seconds: entry.ctime.seconds(),
- nanoseconds: entry.ctime.nanoseconds(),
- },
- };
- try_call!(raw::git_index_add(self.raw, &raw));
- Ok(())
- }
- }
-
- /// Add or update an index entry from a buffer in memory
- ///
- /// This method will create a blob in the repository that owns the index and
- /// then add the index entry to the index. The path of the entry represents
- /// the position of the blob relative to the repository's root folder.
- ///
- /// If a previous index entry exists that has the same path as the given
- /// 'entry', it will be replaced. Otherwise, the 'entry' will be added.
- /// The id and the file_size of the 'entry' are updated with the real value
- /// of the blob.
- ///
- /// This forces the file to be added to the index, not looking at gitignore
- /// rules.
- ///
- /// If this file currently is the result of a merge conflict, this file will
- /// no longer be marked as conflicting. The data about the conflict will be
- /// moved to the "resolve undo" (REUC) section.
- pub fn add_frombuffer(&mut self, entry: &IndexEntry, data: &[u8]) -> Result<(), Error> {
- let path = CString::new(&entry.path[..])?;
-
- // libgit2 encodes the length of the path in the lower bits of the
- // `flags` entry, so mask those out and recalculate here to ensure we
- // don't corrupt anything.
- let mut flags = entry.flags & !raw::GIT_INDEX_ENTRY_NAMEMASK;
-
- if entry.path.len() < raw::GIT_INDEX_ENTRY_NAMEMASK as usize {
- flags |= entry.path.len() as u16;
- } else {
- flags |= raw::GIT_INDEX_ENTRY_NAMEMASK;
- }
-
- unsafe {
- let raw = raw::git_index_entry {
- dev: entry.dev,
- ino: entry.ino,
- mode: entry.mode,
- uid: entry.uid,
- gid: entry.gid,
- file_size: entry.file_size,
- id: *entry.id.raw(),
- flags,
- flags_extended: entry.flags_extended,
- path: path.as_ptr(),
- mtime: raw::git_index_time {
- seconds: entry.mtime.seconds(),
- nanoseconds: entry.mtime.nanoseconds(),
- },
- ctime: raw::git_index_time {
- seconds: entry.ctime.seconds(),
- nanoseconds: entry.ctime.nanoseconds(),
- },
- };
-
- let ptr = data.as_ptr() as *const c_void;
- let len = data.len() as size_t;
- try_call!(raw::git_index_add_frombuffer(self.raw, &raw, ptr, len));
- Ok(())
- }
- }
-
- /// Add or update an index entry from a file on disk
- ///
- /// The file path must be relative to the repository's working folder and
- /// must be readable.
- ///
- /// This method will fail in bare index instances.
- ///
- /// This forces the file to be added to the index, not looking at gitignore
- /// rules.
- ///
- /// If this file currently is the result of a merge conflict, this file will
- /// no longer be marked as conflicting. The data about the conflict will be
- /// moved to the "resolve undo" (REUC) section.
- pub fn add_path(&mut self, path: &Path) -> Result<(), Error> {
- let posix_path = path_to_repo_path(path)?;
- unsafe {
- try_call!(raw::git_index_add_bypath(self.raw, posix_path));
- Ok(())
- }
- }
-
- /// Add or update index entries matching files in the working directory.
- ///
- /// This method will fail in bare index instances.
- ///
- /// The `pathspecs` are a list of file names or shell glob patterns that
- /// will matched against files in the repository's working directory. Each
- /// file that matches will be added to the index (either updating an
- /// existing entry or adding a new entry). You can disable glob expansion
- /// and force exact matching with the `AddDisablePathspecMatch` flag.
- ///
- /// Files that are ignored will be skipped (unlike `add_path`). If a file is
- /// already tracked in the index, then it will be updated even if it is
- /// ignored. Pass the `AddForce` flag to skip the checking of ignore rules.
- ///
- /// To emulate `git add -A` and generate an error if the pathspec contains
- /// the exact path of an ignored file (when not using `AddForce`), add the
- /// `AddCheckPathspec` flag. This checks that each entry in `pathspecs`
- /// that is an exact match to a filename on disk is either not ignored or
- /// already in the index. If this check fails, the function will return
- /// an error.
- ///
- /// To emulate `git add -A` with the "dry-run" option, just use a callback
- /// function that always returns a positive value. See below for details.
- ///
- /// If any files are currently the result of a merge conflict, those files
- /// will no longer be marked as conflicting. The data about the conflicts
- /// will be moved to the "resolve undo" (REUC) section.
- ///
- /// If you provide a callback function, it will be invoked on each matching
- /// item in the working directory immediately before it is added to /
- /// updated in the index. Returning zero will add the item to the index,
- /// greater than zero will skip the item, and less than zero will abort the
- /// scan an return an error to the caller.
- ///
- /// # Example
- ///
- /// Emulate `git add *`:
- ///
- /// ```no_run
- /// use git2::{Index, IndexAddOption, Repository};
- ///
- /// let repo = Repository::open("/path/to/a/repo").expect("failed to open");
- /// let mut index = repo.index().expect("cannot get the Index file");
- /// index.add_all(["*"].iter(), IndexAddOption::DEFAULT, None);
- /// index.write();
- /// ```
- pub fn add_all<T, I>(
- &mut self,
- pathspecs: I,
- flag: IndexAddOption,
- mut cb: Option<&mut IndexMatchedPath<'_>>,
- ) -> Result<(), Error>
- where
- T: IntoCString,
- I: IntoIterator<Item = T>,
- {
- let (_a, _b, raw_strarray) = crate::util::iter2cstrs_paths(pathspecs)?;
- let ptr = cb.as_mut();
- let callback = ptr
- .as_ref()
- .map(|_| index_matched_path_cb as extern "C" fn(_, _, _) -> _);
- unsafe {
- try_call!(raw::git_index_add_all(
- self.raw,
- &raw_strarray,
- flag.bits() as c_uint,
- callback,
- ptr.map(|p| p as *mut _).unwrap_or(ptr::null_mut()) as *mut c_void
- ));
- }
- Ok(())
- }
-
- /// Clear the contents (all the entries) of an index object.
- ///
- /// This clears the index object in memory; changes must be explicitly
- /// written to disk for them to take effect persistently via `write_*`.
- pub fn clear(&mut self) -> Result<(), Error> {
- unsafe {
- try_call!(raw::git_index_clear(self.raw));
- }
- Ok(())
- }
-
- /// Get the count of entries currently in the index
- pub fn len(&self) -> usize {
- unsafe { raw::git_index_entrycount(&*self.raw) as usize }
- }
-
- /// Return `true` is there is no entry in the index
- pub fn is_empty(&self) -> bool {
- self.len() == 0
- }
-
- /// Get one of the entries in the index by its position.
- pub fn get(&self, n: usize) -> Option<IndexEntry> {
- unsafe {
- let ptr = raw::git_index_get_byindex(self.raw, n as size_t);
- if ptr.is_null() {
- None
- } else {
- Some(Binding::from_raw(*ptr))
- }
- }
- }
-
- /// Get an iterator over the entries in this index.
- pub fn iter(&self) -> IndexEntries<'_> {
- IndexEntries {
- range: 0..self.len(),
- index: self,
- }
- }
-
- /// Get an iterator over the index entries that have conflicts
- pub fn conflicts(&self) -> Result<IndexConflicts<'_>, Error> {
- crate::init();
- let mut conflict_iter = ptr::null_mut();
- unsafe {
- try_call!(raw::git_index_conflict_iterator_new(
- &mut conflict_iter,
- self.raw
- ));
- Ok(Binding::from_raw(conflict_iter))
- }
- }
-
- /// Get one of the entries in the index by its path.
- pub fn get_path(&self, path: &Path, stage: i32) -> Option<IndexEntry> {
- let path = path_to_repo_path(path).unwrap();
- unsafe {
- let ptr = call!(raw::git_index_get_bypath(self.raw, path, stage as c_int));
- if ptr.is_null() {
- None
- } else {
- Some(Binding::from_raw(*ptr))
- }
- }
- }
-
- /// Does this index have conflicts?
- ///
- /// Returns `true` if the index contains conflicts, `false` if it does not.
- pub fn has_conflicts(&self) -> bool {
- unsafe { raw::git_index_has_conflicts(self.raw) == 1 }
- }
-
- /// Get the full path to the index file on disk.
- ///
- /// Returns `None` if this is an in-memory index.
- pub fn path(&self) -> Option<&Path> {
- unsafe { crate::opt_bytes(self, raw::git_index_path(&*self.raw)).map(util::bytes2path) }
- }
-
- /// Update the contents of an existing index object in memory by reading
- /// from the hard disk.
- ///
- /// If force is true, this performs a "hard" read that discards in-memory
- /// changes and always reloads the on-disk index data. If there is no
- /// on-disk version, the index will be cleared.
- ///
- /// If force is false, this does a "soft" read that reloads the index data
- /// from disk only if it has changed since the last time it was loaded.
- /// Purely in-memory index data will be untouched. Be aware: if there are
- /// changes on disk, unwritten in-memory changes are discarded.
- pub fn read(&mut self, force: bool) -> Result<(), Error> {
- unsafe {
- try_call!(raw::git_index_read(self.raw, force));
- }
- Ok(())
- }
-
- /// Read a tree into the index file with stats
- ///
- /// The current index contents will be replaced by the specified tree.
- pub fn read_tree(&mut self, tree: &Tree<'_>) -> Result<(), Error> {
- unsafe {
- try_call!(raw::git_index_read_tree(self.raw, &*tree.raw()));
- }
- Ok(())
- }
-
- /// Remove an entry from the index
- pub fn remove(&mut self, path: &Path, stage: i32) -> Result<(), Error> {
- let path = path_to_repo_path(path)?;
- unsafe {
- try_call!(raw::git_index_remove(self.raw, path, stage as c_int));
- }
- Ok(())
- }
-
- /// Remove an index entry corresponding to a file on disk.
- ///
- /// The file path must be relative to the repository's working folder. It
- /// may exist.
- ///
- /// If this file currently is the result of a merge conflict, this file will
- /// no longer be marked as conflicting. The data about the conflict will be
- /// moved to the "resolve undo" (REUC) section.
- pub fn remove_path(&mut self, path: &Path) -> Result<(), Error> {
- let path = path_to_repo_path(path)?;
- unsafe {
- try_call!(raw::git_index_remove_bypath(self.raw, path));
- }
- Ok(())
- }
-
- /// Remove all entries from the index under a given directory.
- pub fn remove_dir(&mut self, path: &Path, stage: i32) -> Result<(), Error> {
- let path = path_to_repo_path(path)?;
- unsafe {
- try_call!(raw::git_index_remove_directory(
- self.raw,
- path,
- stage as c_int
- ));
- }
- Ok(())
- }
-
- /// Remove all matching index entries.
- ///
- /// If you provide a callback function, it will be invoked on each matching
- /// item in the index immediately before it is removed. Return 0 to remove
- /// the item, > 0 to skip the item, and < 0 to abort the scan.
- pub fn remove_all<T, I>(
- &mut self,
- pathspecs: I,
- mut cb: Option<&mut IndexMatchedPath<'_>>,
- ) -> Result<(), Error>
- where
- T: IntoCString,
- I: IntoIterator<Item = T>,
- {
- let (_a, _b, raw_strarray) = crate::util::iter2cstrs_paths(pathspecs)?;
- let ptr = cb.as_mut();
- let callback = ptr
- .as_ref()
- .map(|_| index_matched_path_cb as extern "C" fn(_, _, _) -> _);
- unsafe {
- try_call!(raw::git_index_remove_all(
- self.raw,
- &raw_strarray,
- callback,
- ptr.map(|p| p as *mut _).unwrap_or(ptr::null_mut()) as *mut c_void
- ));
- }
- Ok(())
- }
-
- /// Update all index entries to match the working directory
- ///
- /// This method will fail in bare index instances.
- ///
- /// This scans the existing index entries and synchronizes them with the
- /// working directory, deleting them if the corresponding working directory
- /// file no longer exists otherwise updating the information (including
- /// adding the latest version of file to the ODB if needed).
- ///
- /// If you provide a callback function, it will be invoked on each matching
- /// item in the index immediately before it is updated (either refreshed or
- /// removed depending on working directory state). Return 0 to proceed with
- /// updating the item, > 0 to skip the item, and < 0 to abort the scan.
- pub fn update_all<T, I>(
- &mut self,
- pathspecs: I,
- mut cb: Option<&mut IndexMatchedPath<'_>>,
- ) -> Result<(), Error>
- where
- T: IntoCString,
- I: IntoIterator<Item = T>,
- {
- let (_a, _b, raw_strarray) = crate::util::iter2cstrs_paths(pathspecs)?;
- let ptr = cb.as_mut();
- let callback = ptr
- .as_ref()
- .map(|_| index_matched_path_cb as extern "C" fn(_, _, _) -> _);
- unsafe {
- try_call!(raw::git_index_update_all(
- self.raw,
- &raw_strarray,
- callback,
- ptr.map(|p| p as *mut _).unwrap_or(ptr::null_mut()) as *mut c_void
- ));
- }
- Ok(())
- }
-
- /// Write an existing index object from memory back to disk using an atomic
- /// file lock.
- pub fn write(&mut self) -> Result<(), Error> {
- unsafe {
- try_call!(raw::git_index_write(self.raw));
- }
- Ok(())
- }
-
- /// Write the index as a tree.
- ///
- /// This method will scan the index and write a representation of its
- /// current state back to disk; it recursively creates tree objects for each
- /// of the subtrees stored in the index, but only returns the OID of the
- /// root tree. This is the OID that can be used e.g. to create a commit.
- ///
- /// The index instance cannot be bare, and needs to be associated to an
- /// existing repository.
- ///
- /// The index must not contain any file in conflict.
- pub fn write_tree(&mut self) -> Result<Oid, Error> {
- let mut raw = raw::git_oid {
- id: [0; raw::GIT_OID_RAWSZ],
- };
- unsafe {
- try_call!(raw::git_index_write_tree(&mut raw, self.raw));
- Ok(Binding::from_raw(&raw as *const _))
- }
- }
-
- /// Write the index as a tree to the given repository
- ///
- /// This is the same as `write_tree` except that the destination repository
- /// can be chosen.
- pub fn write_tree_to(&mut self, repo: &Repository) -> Result<Oid, Error> {
- let mut raw = raw::git_oid {
- id: [0; raw::GIT_OID_RAWSZ],
- };
- unsafe {
- try_call!(raw::git_index_write_tree_to(&mut raw, self.raw, repo.raw()));
- Ok(Binding::from_raw(&raw as *const _))
- }
- }
-
- /// Find the first position of any entries matching a prefix.
- ///
- /// To find the first position of a path inside a given folder, suffix the prefix with a '/'.
- pub fn find_prefix<T: IntoCString>(&self, prefix: T) -> Result<usize, Error> {
- let mut at_pos: size_t = 0;
- let entry_path = prefix.into_c_string()?;
- unsafe {
- try_call!(raw::git_index_find_prefix(
- &mut at_pos,
- self.raw,
- entry_path
- ));
- Ok(at_pos)
- }
- }
-}
-
-impl Binding for Index {
- type Raw = *mut raw::git_index;
- unsafe fn from_raw(raw: *mut raw::git_index) -> Index {
- Index { raw }
- }
- fn raw(&self) -> *mut raw::git_index {
- self.raw
- }
-}
-
-impl<'index> Binding for IndexConflicts<'index> {
- type Raw = *mut raw::git_index_conflict_iterator;
-
- unsafe fn from_raw(raw: *mut raw::git_index_conflict_iterator) -> IndexConflicts<'index> {
- IndexConflicts {
- conflict_iter: raw,
- _marker: marker::PhantomData,
- }
- }
- fn raw(&self) -> *mut raw::git_index_conflict_iterator {
- self.conflict_iter
- }
-}
-
-extern "C" fn index_matched_path_cb(
- path: *const c_char,
- matched_pathspec: *const c_char,
- payload: *mut c_void,
-) -> c_int {
- unsafe {
- let path = CStr::from_ptr(path).to_bytes();
- let matched_pathspec = CStr::from_ptr(matched_pathspec).to_bytes();
-
- panic::wrap(|| {
- let payload = payload as *mut &mut IndexMatchedPath<'_>;
- (*payload)(util::bytes2path(path), matched_pathspec) as c_int
- })
- .unwrap_or(-1)
- }
-}
-
-impl Drop for Index {
- fn drop(&mut self) {
- unsafe { raw::git_index_free(self.raw) }
- }
-}
-
-impl<'index> Drop for IndexConflicts<'index> {
- fn drop(&mut self) {
- unsafe { raw::git_index_conflict_iterator_free(self.conflict_iter) }
- }
-}
-
-impl<'index> Iterator for IndexEntries<'index> {
- type Item = IndexEntry;
- fn next(&mut self) -> Option<IndexEntry> {
- self.range.next().map(|i| self.index.get(i).unwrap())
- }
-}
-
-impl<'index> Iterator for IndexConflicts<'index> {
- type Item = Result<IndexConflict, Error>;
- fn next(&mut self) -> Option<Result<IndexConflict, Error>> {
- let mut ancestor = ptr::null();
- let mut our = ptr::null();
- let mut their = ptr::null();
- unsafe {
- try_call_iter!(raw::git_index_conflict_next(
- &mut ancestor,
- &mut our,
- &mut their,
- self.conflict_iter
- ));
- Some(Ok(IndexConflict {
- ancestor: match ancestor.is_null() {
- false => Some(IndexEntry::from_raw(*ancestor)),
- true => None,
- },
- our: match our.is_null() {
- false => Some(IndexEntry::from_raw(*our)),
- true => None,
- },
- their: match their.is_null() {
- false => Some(IndexEntry::from_raw(*their)),
- true => None,
- },
- }))
- }
- }
-}
-
-impl Binding for IndexEntry {
- type Raw = raw::git_index_entry;
-
- unsafe fn from_raw(raw: raw::git_index_entry) -> IndexEntry {
- let raw::git_index_entry {
- ctime,
- mtime,
- dev,
- ino,
- mode,
- uid,
- gid,
- file_size,
- id,
- flags,
- flags_extended,
- path,
- } = raw;
-
- // libgit2 encodes the length of the path in the lower bits of `flags`,
- // but if the length exceeds the number of bits then the path is
- // nul-terminated.
- let mut pathlen = (flags & raw::GIT_INDEX_ENTRY_NAMEMASK) as usize;
- if pathlen == raw::GIT_INDEX_ENTRY_NAMEMASK as usize {
- pathlen = CStr::from_ptr(path).to_bytes().len();
- }
-
- let path = slice::from_raw_parts(path as *const u8, pathlen);
-
- IndexEntry {
- dev,
- ino,
- mode,
- uid,
- gid,
- file_size,
- id: Binding::from_raw(&id as *const _),
- flags,
- flags_extended,
- path: path.to_vec(),
- mtime: Binding::from_raw(mtime),
- ctime: Binding::from_raw(ctime),
- }
- }
-
- fn raw(&self) -> raw::git_index_entry {
- // not implemented, may require a CString in storage
- panic!()
- }
-}
-
-#[cfg(test)]
-mod tests {
- use std::fs::{self, File};
- use std::path::Path;
- use tempfile::TempDir;
-
- use crate::{ErrorCode, Index, IndexEntry, IndexTime, Oid, Repository, ResetType};
-
- #[test]
- fn smoke() {
- let mut index = Index::new().unwrap();
- assert!(index.add_path(&Path::new(".")).is_err());
- index.clear().unwrap();
- assert_eq!(index.len(), 0);
- assert!(index.get(0).is_none());
- assert!(index.path().is_none());
- assert!(index.read(true).is_err());
- }
-
- #[test]
- fn smoke_from_repo() {
- let (_td, repo) = crate::test::repo_init();
- let mut index = repo.index().unwrap();
- assert_eq!(
- index.path().map(|s| s.to_path_buf()),
- Some(repo.path().join("index"))
- );
- Index::open(&repo.path().join("index")).unwrap();
-
- index.clear().unwrap();
- index.read(true).unwrap();
- index.write().unwrap();
- index.write_tree().unwrap();
- index.write_tree_to(&repo).unwrap();
- }
-
- #[test]
- fn add_all() {
- let (_td, repo) = crate::test::repo_init();
- let mut index = repo.index().unwrap();
-
- let root = repo.path().parent().unwrap();
- fs::create_dir(&root.join("foo")).unwrap();
- File::create(&root.join("foo/bar")).unwrap();
- let mut called = false;
- index
- .add_all(
- ["foo"].iter(),
- crate::IndexAddOption::DEFAULT,
- Some(&mut |a: &Path, b: &[u8]| {
- assert!(!called);
- called = true;
- assert_eq!(b, b"foo");
- assert_eq!(a, Path::new("foo/bar"));
- 0
- }),
- )
- .unwrap();
- assert!(called);
-
- called = false;
- index
- .remove_all(
- ["."].iter(),
- Some(&mut |a: &Path, b: &[u8]| {
- assert!(!called);
- called = true;
- assert_eq!(b, b".");
- assert_eq!(a, Path::new("foo/bar"));
- 0
- }),
- )
- .unwrap();
- assert!(called);
- }
-
- #[test]
- fn smoke_add() {
- let (_td, repo) = crate::test::repo_init();
- let mut index = repo.index().unwrap();
-
- let root = repo.path().parent().unwrap();
- fs::create_dir(&root.join("foo")).unwrap();
- File::create(&root.join("foo/bar")).unwrap();
- index.add_path(Path::new("foo/bar")).unwrap();
- index.write().unwrap();
- assert_eq!(index.iter().count(), 1);
-
- // Make sure we can use this repo somewhere else now.
- let id = index.write_tree().unwrap();
- let tree = repo.find_tree(id).unwrap();
- let sig = repo.signature().unwrap();
- let id = repo.refname_to_id("HEAD").unwrap();
- let parent = repo.find_commit(id).unwrap();
- let commit = repo
- .commit(Some("HEAD"), &sig, &sig, "commit", &tree, &[&parent])
- .unwrap();
- let obj = repo.find_object(commit, None).unwrap();
- repo.reset(&obj, ResetType::Hard, None).unwrap();
-
- let td2 = TempDir::new().unwrap();
- let url = crate::test::path2url(&root);
- let repo = Repository::clone(&url, td2.path()).unwrap();
- let obj = repo.find_object(commit, None).unwrap();
- repo.reset(&obj, ResetType::Hard, None).unwrap();
- }
-
- #[test]
- fn add_then_read() {
- let mut index = Index::new().unwrap();
- let mut e = entry();
- e.path = b"foobar".to_vec();
- index.add(&e).unwrap();
- let e = index.get(0).unwrap();
- assert_eq!(e.path.len(), 6);
- }
-
- #[test]
- fn add_then_find() {
- let mut index = Index::new().unwrap();
- let mut e = entry();
- e.path = b"foo/bar".to_vec();
- index.add(&e).unwrap();
- let mut e = entry();
- e.path = b"foo2/bar".to_vec();
- index.add(&e).unwrap();
- assert_eq!(index.get(0).unwrap().path, b"foo/bar");
- assert_eq!(
- index.get_path(Path::new("foo/bar"), 0).unwrap().path,
- b"foo/bar"
- );
- assert_eq!(index.find_prefix(Path::new("foo2/")), Ok(1));
- assert_eq!(
- index.find_prefix(Path::new("empty/")).unwrap_err().code(),
- ErrorCode::NotFound
- );
- }
-
- #[test]
- fn add_frombuffer_then_read() {
- let (_td, repo) = crate::test::repo_init();
- let mut index = repo.index().unwrap();
-
- let mut e = entry();
- e.path = b"foobar".to_vec();
- let content = b"the contents";
- index.add_frombuffer(&e, content).unwrap();
- let e = index.get(0).unwrap();
- assert_eq!(e.path.len(), 6);
-
- let b = repo.find_blob(e.id).unwrap();
- assert_eq!(b.content(), content);
- }
-
- fn entry() -> IndexEntry {
- IndexEntry {
- ctime: IndexTime::new(0, 0),
- mtime: IndexTime::new(0, 0),
- dev: 0,
- ino: 0,
- mode: 0o100644,
- uid: 0,
- gid: 0,
- file_size: 0,
- id: Oid::from_bytes(&[0; 20]).unwrap(),
- flags: 0,
- flags_extended: 0,
- path: Vec::new(),
- }
- }
-}
diff --git a/extra/git2/src/indexer.rs b/extra/git2/src/indexer.rs
deleted file mode 100644
index 0aaf353d5..000000000
--- a/extra/git2/src/indexer.rs
+++ /dev/null
@@ -1,255 +0,0 @@
-use std::ffi::CStr;
-use std::path::Path;
-use std::{io, marker, mem, ptr};
-
-use libc::c_void;
-
-use crate::odb::{write_pack_progress_cb, OdbPackwriterCb};
-use crate::util::Binding;
-use crate::{raw, Error, IntoCString, Odb};
-
-/// Struct representing the progress by an in-flight transfer.
-pub struct Progress<'a> {
- pub(crate) raw: ProgressState,
- pub(crate) _marker: marker::PhantomData<&'a raw::git_indexer_progress>,
-}
-
-pub(crate) enum ProgressState {
- Borrowed(*const raw::git_indexer_progress),
- Owned(raw::git_indexer_progress),
-}
-
-/// Callback to be invoked while indexing is in progress.
-///
-/// This callback will be periodically called with updates to the progress of
-/// the indexing so far. The return value indicates whether the indexing or
-/// transfer should continue. A return value of `false` will cancel the
-/// indexing or transfer.
-///
-/// * `progress` - the progress being made so far.
-pub type IndexerProgress<'a> = dyn FnMut(Progress<'_>) -> bool + 'a;
-
-impl<'a> Progress<'a> {
- /// Number of objects in the packfile being downloaded
- pub fn total_objects(&self) -> usize {
- unsafe { (*self.raw()).total_objects as usize }
- }
- /// Received objects that have been hashed
- pub fn indexed_objects(&self) -> usize {
- unsafe { (*self.raw()).indexed_objects as usize }
- }
- /// Objects which have been downloaded
- pub fn received_objects(&self) -> usize {
- unsafe { (*self.raw()).received_objects as usize }
- }
- /// Locally-available objects that have been injected in order to fix a thin
- /// pack.
- pub fn local_objects(&self) -> usize {
- unsafe { (*self.raw()).local_objects as usize }
- }
- /// Number of deltas in the packfile being downloaded
- pub fn total_deltas(&self) -> usize {
- unsafe { (*self.raw()).total_deltas as usize }
- }
- /// Received deltas that have been hashed.
- pub fn indexed_deltas(&self) -> usize {
- unsafe { (*self.raw()).indexed_deltas as usize }
- }
- /// Size of the packfile received up to now
- pub fn received_bytes(&self) -> usize {
- unsafe { (*self.raw()).received_bytes as usize }
- }
-
- /// Convert this to an owned version of `Progress`.
- pub fn to_owned(&self) -> Progress<'static> {
- Progress {
- raw: ProgressState::Owned(unsafe { *self.raw() }),
- _marker: marker::PhantomData,
- }
- }
-}
-
-impl<'a> Binding for Progress<'a> {
- type Raw = *const raw::git_indexer_progress;
- unsafe fn from_raw(raw: *const raw::git_indexer_progress) -> Progress<'a> {
- Progress {
- raw: ProgressState::Borrowed(raw),
- _marker: marker::PhantomData,
- }
- }
-
- fn raw(&self) -> *const raw::git_indexer_progress {
- match self.raw {
- ProgressState::Borrowed(raw) => raw,
- ProgressState::Owned(ref raw) => raw as *const _,
- }
- }
-}
-
-/// Callback to be invoked while a transfer is in progress.
-///
-/// This callback will be periodically called with updates to the progress of
-/// the transfer so far. The return value indicates whether the transfer should
-/// continue. A return value of `false` will cancel the transfer.
-///
-/// * `progress` - the progress being made so far.
-#[deprecated(
- since = "0.11.0",
- note = "renamed to `IndexerProgress` to match upstream"
-)]
-#[allow(dead_code)]
-pub type TransportProgress<'a> = IndexerProgress<'a>;
-
-/// A stream to write and index a packfile
-///
-/// This is equivalent to [`crate::OdbPackwriter`], but allows to store the pack
-/// and index at an arbitrary path. It also does not require access to an object
-/// database if, and only if, the pack file is self-contained (i.e. not "thin").
-pub struct Indexer<'odb> {
- raw: *mut raw::git_indexer,
- progress: raw::git_indexer_progress,
- progress_payload_ptr: *mut OdbPackwriterCb<'odb>,
-}
-
-impl<'a> Indexer<'a> {
- /// Create a new indexer
- ///
- /// The [`Odb`] is used to resolve base objects when fixing thin packs. It
- /// can be `None` if no thin pack is expected, in which case missing bases
- /// will result in an error.
- ///
- /// `mode` is the permissions to use for the output files, use `0` for defaults.
- ///
- /// If `verify` is `false`, the indexer will bypass object connectivity checks.
- pub fn new(odb: Option<&Odb<'a>>, path: &Path, mode: u32, verify: bool) -> Result<Self, Error> {
- let path = path.into_c_string()?;
-
- let odb = odb.map(Binding::raw).unwrap_or_else(ptr::null_mut);
-
- let mut out = ptr::null_mut();
- let progress_cb: raw::git_indexer_progress_cb = Some(write_pack_progress_cb);
- let progress_payload = Box::new(OdbPackwriterCb { cb: None });
- let progress_payload_ptr = Box::into_raw(progress_payload);
-
- unsafe {
- let mut opts = mem::zeroed();
- try_call!(raw::git_indexer_options_init(
- &mut opts,
- raw::GIT_INDEXER_OPTIONS_VERSION
- ));
- opts.progress_cb = progress_cb;
- opts.progress_cb_payload = progress_payload_ptr as *mut c_void;
- opts.verify = verify.into();
-
- try_call!(raw::git_indexer_new(&mut out, path, mode, odb, &mut opts));
- }
-
- Ok(Self {
- raw: out,
- progress: Default::default(),
- progress_payload_ptr,
- })
- }
-
- /// Finalize the pack and index
- ///
- /// Resolves any pending deltas and writes out the index file. The returned
- /// string is the hexadecimal checksum of the packfile, which is also used
- /// to name the pack and index files (`pack-<checksum>.pack` and
- /// `pack-<checksum>.idx` respectively).
- pub fn commit(mut self) -> Result<String, Error> {
- unsafe {
- try_call!(raw::git_indexer_commit(self.raw, &mut self.progress));
-
- let name = CStr::from_ptr(raw::git_indexer_name(self.raw));
- Ok(name.to_str().expect("pack name not utf8").to_owned())
- }
- }
-
- /// The callback through which progress is monitored. Be aware that this is
- /// called inline, so performance may be affected.
- pub fn progress<F>(&mut self, cb: F) -> &mut Self
- where
- F: FnMut(Progress<'_>) -> bool + 'a,
- {
- let progress_payload =
- unsafe { &mut *(self.progress_payload_ptr as *mut OdbPackwriterCb<'_>) };
- progress_payload.cb = Some(Box::new(cb) as Box<IndexerProgress<'a>>);
-
- self
- }
-}
-
-impl io::Write for Indexer<'_> {
- fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
- unsafe {
- let ptr = buf.as_ptr() as *mut c_void;
- let len = buf.len();
-
- let res = raw::git_indexer_append(self.raw, ptr, len, &mut self.progress);
- if res < 0 {
- Err(io::Error::new(
- io::ErrorKind::Other,
- Error::last_error(res).unwrap(),
- ))
- } else {
- Ok(buf.len())
- }
- }
- }
-
- fn flush(&mut self) -> io::Result<()> {
- Ok(())
- }
-}
-
-impl Drop for Indexer<'_> {
- fn drop(&mut self) {
- unsafe {
- raw::git_indexer_free(self.raw);
- drop(Box::from_raw(self.progress_payload_ptr))
- }
- }
-}
-
-#[cfg(test)]
-mod tests {
- use crate::{Buf, Indexer};
- use std::io::prelude::*;
-
- #[test]
- fn indexer() {
- let (_td, repo_source) = crate::test::repo_init();
- let (_td, repo_target) = crate::test::repo_init();
-
- let mut progress_called = false;
-
- // Create an in-memory packfile
- let mut builder = t!(repo_source.packbuilder());
- let mut buf = Buf::new();
- let (commit_source_id, _tree) = crate::test::commit(&repo_source);
- t!(builder.insert_object(commit_source_id, None));
- t!(builder.write_buf(&mut buf));
-
- // Write it to the standard location in the target repo, but via indexer
- let odb = repo_source.odb().unwrap();
- let mut indexer = Indexer::new(
- Some(&odb),
- repo_target.path().join("objects").join("pack").as_path(),
- 0o644,
- true,
- )
- .unwrap();
- indexer.progress(|_| {
- progress_called = true;
- true
- });
- indexer.write(&buf).unwrap();
- indexer.commit().unwrap();
-
- // Assert that target repo picks it up as valid
- let commit_target = repo_target.find_commit(commit_source_id).unwrap();
- assert_eq!(commit_target.id(), commit_source_id);
- assert!(progress_called);
- }
-}
diff --git a/extra/git2/src/lib.rs b/extra/git2/src/lib.rs
deleted file mode 100644
index 3dd6fe92e..000000000
--- a/extra/git2/src/lib.rs
+++ /dev/null
@@ -1,1668 +0,0 @@
-//! # libgit2 bindings for Rust
-//!
-//! This library contains bindings to the [libgit2][1] C library which is used
-//! to manage git repositories. The library itself is a work in progress and is
-//! likely lacking some bindings here and there, so be warned.
-//!
-//! [1]: https://libgit2.github.com/
-//!
-//! The git2-rs library strives to be as close to libgit2 as possible, but also
-//! strives to make using libgit2 as safe as possible. All resource management
-//! is automatic as well as adding strong types to all interfaces (including
-//! `Result`)
-//!
-//! ## Creating a `Repository`
-//!
-//! The `Repository` is the source from which almost all other objects in git-rs
-//! are spawned. A repository can be created through opening, initializing, or
-//! cloning.
-//!
-//! ### Initializing a new repository
-//!
-//! The `init` method will create a new repository, assuming one does not
-//! already exist.
-//!
-//! ```no_run
-//! # #![allow(unstable)]
-//! use git2::Repository;
-//!
-//! let repo = match Repository::init("/path/to/a/repo") {
-//! Ok(repo) => repo,
-//! Err(e) => panic!("failed to init: {}", e),
-//! };
-//! ```
-//!
-//! ### Opening an existing repository
-//!
-//! ```no_run
-//! # #![allow(unstable)]
-//! use git2::Repository;
-//!
-//! let repo = match Repository::open("/path/to/a/repo") {
-//! Ok(repo) => repo,
-//! Err(e) => panic!("failed to open: {}", e),
-//! };
-//! ```
-//!
-//! ### Cloning an existing repository
-//!
-//! ```no_run
-//! # #![allow(unstable)]
-//! use git2::Repository;
-//!
-//! let url = "https://github.com/alexcrichton/git2-rs";
-//! let repo = match Repository::clone(url, "/path/to/a/repo") {
-//! Ok(repo) => repo,
-//! Err(e) => panic!("failed to clone: {}", e),
-//! };
-//! ```
-//!
-//! To clone using SSH, refer to [RepoBuilder](./build/struct.RepoBuilder.html).
-//!
-//! ## Working with a `Repository`
-//!
-//! All derivative objects, references, etc are attached to the lifetime of the
-//! source `Repository`, to ensure that they do not outlive the repository
-//! itself.
-
-#![doc(html_root_url = "https://docs.rs/git2/0.18")]
-#![allow(trivial_numeric_casts, trivial_casts)]
-#![deny(missing_docs)]
-#![warn(rust_2018_idioms)]
-#![cfg_attr(test, deny(warnings))]
-
-use bitflags::bitflags;
-use libgit2_sys as raw;
-
-use std::ffi::{CStr, CString};
-use std::fmt;
-use std::str;
-use std::sync::Once;
-
-pub use crate::apply::{ApplyLocation, ApplyOptions};
-pub use crate::attr::AttrValue;
-pub use crate::blame::{Blame, BlameHunk, BlameIter, BlameOptions};
-pub use crate::blob::{Blob, BlobWriter};
-pub use crate::branch::{Branch, Branches};
-pub use crate::buf::Buf;
-pub use crate::cherrypick::CherrypickOptions;
-pub use crate::commit::{Commit, Parents};
-pub use crate::config::{Config, ConfigEntries, ConfigEntry};
-pub use crate::cred::{Cred, CredentialHelper};
-pub use crate::describe::{Describe, DescribeFormatOptions, DescribeOptions};
-pub use crate::diff::{Deltas, Diff, DiffDelta, DiffFile, DiffOptions};
-pub use crate::diff::{DiffBinary, DiffBinaryFile, DiffBinaryKind, DiffPatchidOptions};
-pub use crate::diff::{DiffFindOptions, DiffHunk, DiffLine, DiffLineType, DiffStats};
-pub use crate::email::{Email, EmailCreateOptions};
-pub use crate::error::Error;
-pub use crate::index::{
- Index, IndexConflict, IndexConflicts, IndexEntries, IndexEntry, IndexMatchedPath,
-};
-pub use crate::indexer::{Indexer, IndexerProgress, Progress};
-pub use crate::mailmap::Mailmap;
-pub use crate::mempack::Mempack;
-pub use crate::merge::{AnnotatedCommit, MergeOptions};
-pub use crate::message::{
- message_prettify, message_trailers_bytes, message_trailers_strs, MessageTrailersBytes,
- MessageTrailersBytesIterator, MessageTrailersStrs, MessageTrailersStrsIterator,
- DEFAULT_COMMENT_CHAR,
-};
-pub use crate::note::{Note, Notes};
-pub use crate::object::Object;
-pub use crate::odb::{Odb, OdbObject, OdbPackwriter, OdbReader, OdbWriter};
-pub use crate::oid::Oid;
-pub use crate::packbuilder::{PackBuilder, PackBuilderStage};
-pub use crate::patch::Patch;
-pub use crate::pathspec::{Pathspec, PathspecFailedEntries, PathspecMatchList};
-pub use crate::pathspec::{PathspecDiffEntries, PathspecEntries};
-pub use crate::proxy_options::ProxyOptions;
-pub use crate::push_update::PushUpdate;
-pub use crate::rebase::{Rebase, RebaseOperation, RebaseOperationType, RebaseOptions};
-pub use crate::reference::{Reference, ReferenceNames, References};
-pub use crate::reflog::{Reflog, ReflogEntry, ReflogIter};
-pub use crate::refspec::Refspec;
-pub use crate::remote::{
- FetchOptions, PushOptions, Refspecs, Remote, RemoteConnection, RemoteHead, RemoteRedirect,
-};
-pub use crate::remote_callbacks::{CertificateCheckStatus, Credentials, RemoteCallbacks};
-pub use crate::remote_callbacks::{TransportMessage, UpdateTips};
-pub use crate::repo::{Repository, RepositoryInitOptions};
-pub use crate::revert::RevertOptions;
-pub use crate::revspec::Revspec;
-pub use crate::revwalk::Revwalk;
-pub use crate::signature::Signature;
-pub use crate::stash::{StashApplyOptions, StashApplyProgressCb, StashCb, StashSaveOptions};
-pub use crate::status::{StatusEntry, StatusIter, StatusOptions, StatusShow, Statuses};
-pub use crate::submodule::{Submodule, SubmoduleUpdateOptions};
-pub use crate::tag::Tag;
-pub use crate::time::{IndexTime, Time};
-pub use crate::tracing::{trace_set, TraceLevel};
-pub use crate::transaction::Transaction;
-pub use crate::tree::{Tree, TreeEntry, TreeIter, TreeWalkMode, TreeWalkResult};
-pub use crate::treebuilder::TreeBuilder;
-pub use crate::util::IntoCString;
-pub use crate::version::Version;
-pub use crate::worktree::{Worktree, WorktreeAddOptions, WorktreeLockStatus, WorktreePruneOptions};
-
-// Create a convinience method on bitflag struct which checks the given flag
-macro_rules! is_bit_set {
- ($name:ident, $flag:expr) => {
- #[allow(missing_docs)]
- pub fn $name(&self) -> bool {
- self.intersects($flag)
- }
- };
-}
-
-/// An enumeration of possible errors that can happen when working with a git
-/// repository.
-// Note: We omit a few native error codes, as they are unlikely to be propagated
-// to the library user. Currently:
-//
-// * GIT_EPASSTHROUGH
-// * GIT_ITEROVER
-// * GIT_RETRY
-#[derive(PartialEq, Eq, Clone, Debug, Copy)]
-pub enum ErrorCode {
- /// Generic error
- GenericError,
- /// Requested object could not be found
- NotFound,
- /// Object exists preventing operation
- Exists,
- /// More than one object matches
- Ambiguous,
- /// Output buffer too short to hold data
- BufSize,
- /// User-generated error
- User,
- /// Operation not allowed on bare repository
- BareRepo,
- /// HEAD refers to branch with no commits
- UnbornBranch,
- /// Merge in progress prevented operation
- Unmerged,
- /// Reference was not fast-forwardable
- NotFastForward,
- /// Name/ref spec was not in a valid format
- InvalidSpec,
- /// Checkout conflicts prevented operation
- Conflict,
- /// Lock file prevented operation
- Locked,
- /// Reference value does not match expected
- Modified,
- /// Authentication error
- Auth,
- /// Server certificate is invalid
- Certificate,
- /// Patch/merge has already been applied
- Applied,
- /// The requested peel operation is not possible
- Peel,
- /// Unexpected EOF
- Eof,
- /// Invalid operation or input
- Invalid,
- /// Uncommitted changes in index prevented operation
- Uncommitted,
- /// Operation was not valid for a directory
- Directory,
- /// A merge conflict exists and cannot continue
- MergeConflict,
- /// Hashsum mismatch in object
- HashsumMismatch,
- /// Unsaved changes in the index would be overwritten
- IndexDirty,
- /// Patch application failed
- ApplyFail,
- /// The object is not owned by the current user
- Owner,
-}
-
-/// An enumeration of possible categories of things that can have
-/// errors when working with a git repository.
-#[derive(PartialEq, Eq, Clone, Debug, Copy)]
-pub enum ErrorClass {
- /// Uncategorized
- None,
- /// Out of memory or insufficient allocated space
- NoMemory,
- /// Syscall or standard system library error
- Os,
- /// Invalid input
- Invalid,
- /// Error resolving or manipulating a reference
- Reference,
- /// ZLib failure
- Zlib,
- /// Bad repository state
- Repository,
- /// Bad configuration
- Config,
- /// Regex failure
- Regex,
- /// Bad object
- Odb,
- /// Invalid index data
- Index,
- /// Error creating or obtaining an object
- Object,
- /// Network error
- Net,
- /// Error manipulating a tag
- Tag,
- /// Invalid value in tree
- Tree,
- /// Hashing or packing error
- Indexer,
- /// Error from SSL
- Ssl,
- /// Error involving submodules
- Submodule,
- /// Threading error
- Thread,
- /// Error manipulating a stash
- Stash,
- /// Checkout failure
- Checkout,
- /// Invalid FETCH_HEAD
- FetchHead,
- /// Merge failure
- Merge,
- /// SSH failure
- Ssh,
- /// Error manipulating filters
- Filter,
- /// Error reverting commit
- Revert,
- /// Error from a user callback
- Callback,
- /// Error cherry-picking commit
- CherryPick,
- /// Can't describe object
- Describe,
- /// Error during rebase
- Rebase,
- /// Filesystem-related error
- Filesystem,
- /// Invalid patch data
- Patch,
- /// Error involving worktrees
- Worktree,
- /// Hash library error or SHA-1 collision
- Sha1,
- /// HTTP error
- Http,
-}
-
-/// A listing of the possible states that a repository can be in.
-#[derive(PartialEq, Eq, Clone, Debug, Copy)]
-#[allow(missing_docs)]
-pub enum RepositoryState {
- Clean,
- Merge,
- Revert,
- RevertSequence,
- CherryPick,
- CherryPickSequence,
- Bisect,
- Rebase,
- RebaseInteractive,
- RebaseMerge,
- ApplyMailbox,
- ApplyMailboxOrRebase,
-}
-
-/// An enumeration of the possible directions for a remote.
-#[derive(Copy, Clone, Debug, PartialEq, Eq)]
-pub enum Direction {
- /// Data will be fetched (read) from this remote.
- Fetch,
- /// Data will be pushed (written) to this remote.
- Push,
-}
-
-/// An enumeration of the operations that can be performed for the `reset`
-/// method on a `Repository`.
-#[derive(Copy, Clone, Debug, PartialEq, Eq)]
-pub enum ResetType {
- /// Move the head to the given commit.
- Soft,
- /// Soft plus reset the index to the commit.
- Mixed,
- /// Mixed plus changes in the working tree are discarded.
- Hard,
-}
-
-/// An enumeration all possible kinds objects may have.
-#[derive(PartialEq, Eq, Copy, Clone, Debug)]
-pub enum ObjectType {
- /// Any kind of git object
- Any,
- /// An object which corresponds to a git commit
- Commit,
- /// An object which corresponds to a git tree
- Tree,
- /// An object which corresponds to a git blob
- Blob,
- /// An object which corresponds to a git tag
- Tag,
-}
-
-/// An enumeration of all possible kinds of references.
-#[derive(PartialEq, Eq, Copy, Clone, Debug)]
-pub enum ReferenceType {
- /// A reference which points at an object id.
- Direct,
-
- /// A reference which points at another reference.
- Symbolic,
-}
-
-/// An enumeration for the possible types of branches
-#[derive(PartialEq, Eq, Debug, Copy, Clone)]
-pub enum BranchType {
- /// A local branch not on a remote.
- Local,
- /// A branch for a remote.
- Remote,
-}
-
-/// An enumeration of the possible priority levels of a config file.
-///
-/// The levels corresponding to the escalation logic (higher to lower) when
-/// searching for config entries.
-#[derive(PartialEq, Eq, Debug, Copy, Clone)]
-pub enum ConfigLevel {
- /// System-wide on Windows, for compatibility with portable git
- ProgramData = 1,
- /// System-wide configuration file, e.g. /etc/gitconfig
- System,
- /// XDG-compatible configuration file, e.g. ~/.config/git/config
- XDG,
- /// User-specific configuration, e.g. ~/.gitconfig
- Global,
- /// Repository specific config, e.g. $PWD/.git/config
- Local,
- /// Application specific configuration file
- App,
- /// Highest level available
- Highest = -1,
-}
-
-/// Merge file favor options for `MergeOptions` instruct the file-level
-/// merging functionality how to deal with conflicting regions of the files.
-#[derive(PartialEq, Eq, Debug, Copy, Clone)]
-pub enum FileFavor {
- /// When a region of a file is changed in both branches, a conflict will be
- /// recorded in the index so that git_checkout can produce a merge file with
- /// conflict markers in the working directory. This is the default.
- Normal,
- /// When a region of a file is changed in both branches, the file created
- /// in the index will contain the "ours" side of any conflicting region.
- /// The index will not record a conflict.
- Ours,
- /// When a region of a file is changed in both branches, the file created
- /// in the index will contain the "theirs" side of any conflicting region.
- /// The index will not record a conflict.
- Theirs,
- /// When a region of a file is changed in both branches, the file created
- /// in the index will contain each unique line from each side, which has
- /// the result of combining both files. The index will not record a conflict.
- Union,
-}
-
-bitflags! {
- /// Orderings that may be specified for Revwalk iteration.
- #[derive(Clone, Copy, Debug, Eq, PartialEq)]
- pub struct Sort: u32 {
- /// Sort the repository contents in no particular ordering.
- ///
- /// This sorting is arbitrary, implementation-specific, and subject to
- /// change at any time. This is the default sorting for new walkers.
- const NONE = raw::GIT_SORT_NONE as u32;
-
- /// Sort the repository contents in topological order (children before
- /// parents).
- ///
- /// This sorting mode can be combined with time sorting.
- const TOPOLOGICAL = raw::GIT_SORT_TOPOLOGICAL as u32;
-
- /// Sort the repository contents by commit time.
- ///
- /// This sorting mode can be combined with topological sorting.
- const TIME = raw::GIT_SORT_TIME as u32;
-
- /// Iterate through the repository contents in reverse order.
- ///
- /// This sorting mode can be combined with any others.
- const REVERSE = raw::GIT_SORT_REVERSE as u32;
- }
-}
-
-impl Sort {
- is_bit_set!(is_none, Sort::NONE);
- is_bit_set!(is_topological, Sort::TOPOLOGICAL);
- is_bit_set!(is_time, Sort::TIME);
- is_bit_set!(is_reverse, Sort::REVERSE);
-}
-
-bitflags! {
- /// Types of credentials that can be requested by a credential callback.
- #[derive(Clone, Copy, Debug, Eq, PartialEq)]
- pub struct CredentialType: u32 {
- #[allow(missing_docs)]
- const USER_PASS_PLAINTEXT = raw::GIT_CREDTYPE_USERPASS_PLAINTEXT as u32;
- #[allow(missing_docs)]
- const SSH_KEY = raw::GIT_CREDTYPE_SSH_KEY as u32;
- #[allow(missing_docs)]
- const SSH_MEMORY = raw::GIT_CREDTYPE_SSH_MEMORY as u32;
- #[allow(missing_docs)]
- const SSH_CUSTOM = raw::GIT_CREDTYPE_SSH_CUSTOM as u32;
- #[allow(missing_docs)]
- const DEFAULT = raw::GIT_CREDTYPE_DEFAULT as u32;
- #[allow(missing_docs)]
- const SSH_INTERACTIVE = raw::GIT_CREDTYPE_SSH_INTERACTIVE as u32;
- #[allow(missing_docs)]
- const USERNAME = raw::GIT_CREDTYPE_USERNAME as u32;
- }
-}
-
-impl CredentialType {
- is_bit_set!(is_user_pass_plaintext, CredentialType::USER_PASS_PLAINTEXT);
- is_bit_set!(is_ssh_key, CredentialType::SSH_KEY);
- is_bit_set!(is_ssh_memory, CredentialType::SSH_MEMORY);
- is_bit_set!(is_ssh_custom, CredentialType::SSH_CUSTOM);
- is_bit_set!(is_default, CredentialType::DEFAULT);
- is_bit_set!(is_ssh_interactive, CredentialType::SSH_INTERACTIVE);
- is_bit_set!(is_username, CredentialType::USERNAME);
-}
-
-impl Default for CredentialType {
- fn default() -> Self {
- CredentialType::DEFAULT
- }
-}
-
-bitflags! {
- /// Flags for the `flags` field of an IndexEntry.
- #[derive(Clone, Copy, Debug, Eq, PartialEq)]
- pub struct IndexEntryFlag: u16 {
- /// Set when the `extended_flags` field is valid.
- const EXTENDED = raw::GIT_INDEX_ENTRY_EXTENDED as u16;
- /// "Assume valid" flag
- const VALID = raw::GIT_INDEX_ENTRY_VALID as u16;
- }
-}
-
-impl IndexEntryFlag {
- is_bit_set!(is_extended, IndexEntryFlag::EXTENDED);
- is_bit_set!(is_valid, IndexEntryFlag::VALID);
-}
-
-bitflags! {
- /// Flags for the `extended_flags` field of an IndexEntry.
- #[derive(Clone, Copy, Debug, Eq, PartialEq)]
- pub struct IndexEntryExtendedFlag: u16 {
- /// An "intent to add" entry from "git add -N"
- const INTENT_TO_ADD = raw::GIT_INDEX_ENTRY_INTENT_TO_ADD as u16;
- /// Skip the associated worktree file, for sparse checkouts
- const SKIP_WORKTREE = raw::GIT_INDEX_ENTRY_SKIP_WORKTREE as u16;
-
- #[allow(missing_docs)]
- const UPTODATE = raw::GIT_INDEX_ENTRY_UPTODATE as u16;
- }
-}
-
-impl IndexEntryExtendedFlag {
- is_bit_set!(is_intent_to_add, IndexEntryExtendedFlag::INTENT_TO_ADD);
- is_bit_set!(is_skip_worktree, IndexEntryExtendedFlag::SKIP_WORKTREE);
- is_bit_set!(is_up_to_date, IndexEntryExtendedFlag::UPTODATE);
-}
-
-bitflags! {
- /// Flags for APIs that add files matching pathspec
- #[derive(Clone, Copy, Debug, Eq, PartialEq)]
- pub struct IndexAddOption: u32 {
- #[allow(missing_docs)]
- const DEFAULT = raw::GIT_INDEX_ADD_DEFAULT as u32;
- #[allow(missing_docs)]
- const FORCE = raw::GIT_INDEX_ADD_FORCE as u32;
- #[allow(missing_docs)]
- const DISABLE_PATHSPEC_MATCH =
- raw::GIT_INDEX_ADD_DISABLE_PATHSPEC_MATCH as u32;
- #[allow(missing_docs)]
- const CHECK_PATHSPEC = raw::GIT_INDEX_ADD_CHECK_PATHSPEC as u32;
- }
-}
-
-impl IndexAddOption {
- is_bit_set!(is_default, IndexAddOption::DEFAULT);
- is_bit_set!(is_force, IndexAddOption::FORCE);
- is_bit_set!(
- is_disable_pathspec_match,
- IndexAddOption::DISABLE_PATHSPEC_MATCH
- );
- is_bit_set!(is_check_pathspec, IndexAddOption::CHECK_PATHSPEC);
-}
-
-impl Default for IndexAddOption {
- fn default() -> Self {
- IndexAddOption::DEFAULT
- }
-}
-
-bitflags! {
- /// Flags for `Repository::open_ext`
- #[derive(Clone, Copy, Debug, Eq, PartialEq)]
- pub struct RepositoryOpenFlags: u32 {
- /// Only open the specified path; don't walk upward searching.
- const NO_SEARCH = raw::GIT_REPOSITORY_OPEN_NO_SEARCH as u32;
- /// Search across filesystem boundaries.
- const CROSS_FS = raw::GIT_REPOSITORY_OPEN_CROSS_FS as u32;
- /// Force opening as bare repository, and defer loading its config.
- const BARE = raw::GIT_REPOSITORY_OPEN_BARE as u32;
- /// Don't try appending `/.git` to the specified repository path.
- const NO_DOTGIT = raw::GIT_REPOSITORY_OPEN_NO_DOTGIT as u32;
- /// Respect environment variables like `$GIT_DIR`.
- const FROM_ENV = raw::GIT_REPOSITORY_OPEN_FROM_ENV as u32;
- }
-}
-
-impl RepositoryOpenFlags {
- is_bit_set!(is_no_search, RepositoryOpenFlags::NO_SEARCH);
- is_bit_set!(is_cross_fs, RepositoryOpenFlags::CROSS_FS);
- is_bit_set!(is_bare, RepositoryOpenFlags::BARE);
- is_bit_set!(is_no_dotgit, RepositoryOpenFlags::NO_DOTGIT);
- is_bit_set!(is_from_env, RepositoryOpenFlags::FROM_ENV);
-}
-
-bitflags! {
- /// Flags for the return value of `Repository::revparse`
- #[derive(Clone, Copy, Debug, Eq, PartialEq)]
- pub struct RevparseMode: u32 {
- /// The spec targeted a single object
- const SINGLE = raw::GIT_REVPARSE_SINGLE as u32;
- /// The spec targeted a range of commits
- const RANGE = raw::GIT_REVPARSE_RANGE as u32;
- /// The spec used the `...` operator, which invokes special semantics.
- const MERGE_BASE = raw::GIT_REVPARSE_MERGE_BASE as u32;
- }
-}
-
-impl RevparseMode {
- is_bit_set!(is_no_single, RevparseMode::SINGLE);
- is_bit_set!(is_range, RevparseMode::RANGE);
- is_bit_set!(is_merge_base, RevparseMode::MERGE_BASE);
-}
-
-bitflags! {
- /// The results of `merge_analysis` indicating the merge opportunities.
- #[derive(Clone, Copy, Debug, Eq, PartialEq)]
- pub struct MergeAnalysis: u32 {
- /// No merge is possible.
- const ANALYSIS_NONE = raw::GIT_MERGE_ANALYSIS_NONE as u32;
- /// A "normal" merge; both HEAD and the given merge input have diverged
- /// from their common ancestor. The divergent commits must be merged.
- const ANALYSIS_NORMAL = raw::GIT_MERGE_ANALYSIS_NORMAL as u32;
- /// All given merge inputs are reachable from HEAD, meaning the
- /// repository is up-to-date and no merge needs to be performed.
- const ANALYSIS_UP_TO_DATE = raw::GIT_MERGE_ANALYSIS_UP_TO_DATE as u32;
- /// The given merge input is a fast-forward from HEAD and no merge
- /// needs to be performed. Instead, the client can check out the
- /// given merge input.
- const ANALYSIS_FASTFORWARD = raw::GIT_MERGE_ANALYSIS_FASTFORWARD as u32;
- /// The HEAD of the current repository is "unborn" and does not point to
- /// a valid commit. No merge can be performed, but the caller may wish
- /// to simply set HEAD to the target commit(s).
- const ANALYSIS_UNBORN = raw::GIT_MERGE_ANALYSIS_UNBORN as u32;
- }
-}
-
-impl MergeAnalysis {
- is_bit_set!(is_none, MergeAnalysis::ANALYSIS_NONE);
- is_bit_set!(is_normal, MergeAnalysis::ANALYSIS_NORMAL);
- is_bit_set!(is_up_to_date, MergeAnalysis::ANALYSIS_UP_TO_DATE);
- is_bit_set!(is_fast_forward, MergeAnalysis::ANALYSIS_FASTFORWARD);
- is_bit_set!(is_unborn, MergeAnalysis::ANALYSIS_UNBORN);
-}
-
-bitflags! {
- /// The user's stated preference for merges.
- #[derive(Clone, Copy, Debug, Eq, PartialEq)]
- pub struct MergePreference: u32 {
- /// No configuration was found that suggests a preferred behavior for
- /// merge.
- const NONE = raw::GIT_MERGE_PREFERENCE_NONE as u32;
- /// There is a `merge.ff=false` configuration setting, suggesting that
- /// the user does not want to allow a fast-forward merge.
- const NO_FAST_FORWARD = raw::GIT_MERGE_PREFERENCE_NO_FASTFORWARD as u32;
- /// There is a `merge.ff=only` configuration setting, suggesting that
- /// the user only wants fast-forward merges.
- const FASTFORWARD_ONLY = raw::GIT_MERGE_PREFERENCE_FASTFORWARD_ONLY as u32;
- }
-}
-
-impl MergePreference {
- is_bit_set!(is_none, MergePreference::NONE);
- is_bit_set!(is_no_fast_forward, MergePreference::NO_FAST_FORWARD);
- is_bit_set!(is_fastforward_only, MergePreference::FASTFORWARD_ONLY);
-}
-
-bitflags! {
- /// Flags controlling the behavior of ODB lookup operations
- #[derive(Clone, Copy, Debug, Eq, PartialEq)]
- pub struct OdbLookupFlags: u32 {
- /// Don't call `git_odb_refresh` if the lookup fails. Useful when doing
- /// a batch of lookup operations for objects that may legitimately not
- /// exist. When using this flag, you may wish to manually call
- /// `git_odb_refresh` before processing a batch of objects.
- const NO_REFRESH = raw::GIT_ODB_LOOKUP_NO_REFRESH as u32;
- }
-}
-
-#[cfg(test)]
-#[macro_use]
-mod test;
-#[macro_use]
-mod panic;
-mod attr;
-mod call;
-mod util;
-
-pub mod build;
-pub mod cert;
-pub mod oid_array;
-pub mod opts;
-pub mod string_array;
-pub mod transport;
-
-mod apply;
-mod blame;
-mod blob;
-mod branch;
-mod buf;
-mod cherrypick;
-mod commit;
-mod config;
-mod cred;
-mod describe;
-mod diff;
-mod email;
-mod error;
-mod index;
-mod indexer;
-mod mailmap;
-mod mempack;
-mod merge;
-mod message;
-mod note;
-mod object;
-mod odb;
-mod oid;
-mod packbuilder;
-mod patch;
-mod pathspec;
-mod proxy_options;
-mod push_update;
-mod rebase;
-mod reference;
-mod reflog;
-mod refspec;
-mod remote;
-mod remote_callbacks;
-mod repo;
-mod revert;
-mod revspec;
-mod revwalk;
-mod signature;
-mod stash;
-mod status;
-mod submodule;
-mod tag;
-mod tagforeach;
-mod time;
-mod tracing;
-mod transaction;
-mod tree;
-mod treebuilder;
-mod version;
-mod worktree;
-
-fn init() {
- static INIT: Once = Once::new();
-
- INIT.call_once(|| {
- openssl_env_init();
- });
-
- raw::init();
-}
-
-#[cfg(all(
- unix,
- not(target_os = "macos"),
- not(target_os = "ios"),
- feature = "https"
-))]
-fn openssl_env_init() {
- // Currently, libgit2 leverages OpenSSL for SSL support when cloning
- // repositories over HTTPS. This means that we're picking up an OpenSSL
- // dependency on non-Windows platforms (where it has its own HTTPS
- // subsystem). As a result, we need to link to OpenSSL.
- //
- // Now actually *linking* to OpenSSL isn't so hard. We just need to make
- // sure to use pkg-config to discover any relevant system dependencies for
- // differences between distributions like CentOS and Ubuntu. The actual
- // trickiness comes about when we start *distributing* the resulting
- // binaries. Currently Cargo is distributed in binary form as nightlies,
- // which means we're distributing a binary with OpenSSL linked in.
- //
- // For historical reasons, the Linux nightly builder is running a CentOS
- // distribution in order to have as much ABI compatibility with other
- // distributions as possible. Sadly, however, this compatibility does not
- // extend to OpenSSL. Currently OpenSSL has two major versions, 0.9 and 1.0,
- // which are incompatible (many ABI differences). The CentOS builder we
- // build on has version 1.0, as do most distributions today. Some still have
- // 0.9, however. This means that if we are to distribute the binaries built
- // by the CentOS machine, we would only be compatible with OpenSSL 1.0 and
- // we would fail to run (a dynamic linker error at runtime) on systems with
- // only 9.8 installed (hopefully).
- //
- // But wait, the plot thickens! Apparently CentOS has dubbed their OpenSSL
- // library as `libssl.so.10`, notably the `10` is included at the end. On
- // the other hand Ubuntu, for example, only distributes `libssl.so`. This
- // means that the binaries created at CentOS are hard-wired to probe for a
- // file called `libssl.so.10` at runtime (using the LD_LIBRARY_PATH), which
- // will not be found on ubuntu. The conclusion of this is that binaries
- // built on CentOS cannot be distributed to Ubuntu and run successfully.
- //
- // There are a number of sneaky things we could do, including, but not
- // limited to:
- //
- // 1. Create a shim program which runs "just before" cargo runs. The
- // responsibility of this shim program would be to locate `libssl.so`,
- // whatever it's called, on the current system, make sure there's a
- // symlink *somewhere* called `libssl.so.10`, and then set up
- // LD_LIBRARY_PATH and run the actual cargo.
- //
- // This approach definitely seems unconventional, and is borderline
- // overkill for this problem. It's also dubious if we can find a
- // libssl.so reliably on the target system.
- //
- // 2. Somehow re-work the CentOS installation so that the linked-against
- // library is called libssl.so instead of libssl.so.10
- //
- // The problem with this approach is that systems with 0.9 installed will
- // start to silently fail, due to also having libraries called libssl.so
- // (probably symlinked under a more appropriate version).
- //
- // 3. Compile Cargo against both OpenSSL 1.0 *and* OpenSSL 0.9, and
- // distribute both. Also make sure that the linked-against name of the
- // library is `libssl.so`. At runtime we determine which version is
- // installed, and we then the appropriate binary.
- //
- // This approach clearly has drawbacks in terms of infrastructure and
- // feasibility.
- //
- // 4. Build a nightly of Cargo for each distribution we'd like to support.
- // You would then pick the appropriate Cargo nightly to install locally.
- //
- // So, with all this in mind, the decision was made to *statically* link
- // OpenSSL. This solves any problem of relying on a downstream OpenSSL
- // version being available. This does, however, open a can of worms related
- // to security issues. It's generally a good idea to dynamically link
- // OpenSSL as you'll get security updates over time without having to do
- // anything (the system administrator will update the local openssl
- // package). By statically linking, we're forfeiting this feature.
- //
- // The conclusion was made it is likely appropriate for the Cargo nightlies
- // to statically link OpenSSL, but highly encourage distributions and
- // packagers of Cargo to dynamically link OpenSSL. Packagers are targeting
- // one system and are distributing to only that system, so none of the
- // problems mentioned above would arise.
- //
- // In order to support this, a new package was made: openssl-static-sys.
- // This package currently performs a fairly simple task:
- //
- // 1. Run pkg-config to discover where openssl is installed.
- // 2. If openssl is installed in a nonstandard location, *and* static copies
- // of the libraries are available, copy them to $OUT_DIR.
- //
- // This library will bring in libssl.a and libcrypto.a into the local build,
- // allowing them to be picked up by this crate. This allows us to configure
- // our own buildbots to have pkg-config point to these local pre-built
- // copies of a static OpenSSL (with very few dependencies) while allowing
- // most other builds of Cargo to naturally dynamically link OpenSSL.
- //
- // So in summary, if you're with me so far, we've statically linked OpenSSL
- // to the Cargo binary (or any binary, for that matter) and we're ready to
- // distribute it to *all* linux distributions. Remember that our original
- // intent for openssl was for HTTPS support, which implies that we need some
- // for of CA certificate store to validate certificates. This is normally
- // installed in a standard system location.
- //
- // Unfortunately, as one might imagine, OpenSSL is configured for where this
- // standard location is at *build time*, but it often varies widely
- // per-system. Consequently, it was discovered that OpenSSL will respect the
- // SSL_CERT_FILE and SSL_CERT_DIR environment variables in order to assist
- // in discovering the location of this file (hurray!).
- //
- // So, finally getting to the point, this function solely exists to support
- // our static builds of OpenSSL by probing for the "standard system
- // location" of certificates and setting relevant environment variable to
- // point to them.
- //
- // Ah, and as a final note, this is only a problem on Linux, not on OS X. On
- // OS X the OpenSSL binaries are stable enough that we can just rely on
- // dynamic linkage (plus they have some weird modifications to OpenSSL which
- // means we wouldn't want to link statically).
- openssl_probe::init_ssl_cert_env_vars();
-}
-
-#[cfg(any(
- windows,
- target_os = "macos",
- target_os = "ios",
- not(feature = "https")
-))]
-fn openssl_env_init() {}
-
-unsafe fn opt_bytes<'a, T>(_anchor: &'a T, c: *const libc::c_char) -> Option<&'a [u8]> {
- if c.is_null() {
- None
- } else {
- Some(CStr::from_ptr(c).to_bytes())
- }
-}
-
-fn opt_cstr<T: IntoCString>(o: Option<T>) -> Result<Option<CString>, Error> {
- match o {
- Some(s) => s.into_c_string().map(Some),
- None => Ok(None),
- }
-}
-
-impl ObjectType {
- /// Convert an object type to its string representation.
- pub fn str(&self) -> &'static str {
- unsafe {
- let ptr = call!(raw::git_object_type2string(*self)) as *const _;
- let data = CStr::from_ptr(ptr).to_bytes();
- str::from_utf8(data).unwrap()
- }
- }
-
- /// Determine if the given git_object_t is a valid loose object type.
- pub fn is_loose(&self) -> bool {
- unsafe { call!(raw::git_object_typeisloose(*self)) == 1 }
- }
-
- /// Convert a raw git_object_t to an ObjectType
- pub fn from_raw(raw: raw::git_object_t) -> Option<ObjectType> {
- match raw {
- raw::GIT_OBJECT_ANY => Some(ObjectType::Any),
- raw::GIT_OBJECT_COMMIT => Some(ObjectType::Commit),
- raw::GIT_OBJECT_TREE => Some(ObjectType::Tree),
- raw::GIT_OBJECT_BLOB => Some(ObjectType::Blob),
- raw::GIT_OBJECT_TAG => Some(ObjectType::Tag),
- _ => None,
- }
- }
-
- /// Convert this kind into its raw representation
- pub fn raw(&self) -> raw::git_object_t {
- call::convert(self)
- }
-
- /// Convert a string object type representation to its object type.
- pub fn from_str(s: &str) -> Option<ObjectType> {
- let raw = unsafe { call!(raw::git_object_string2type(CString::new(s).unwrap())) };
- ObjectType::from_raw(raw)
- }
-}
-
-impl fmt::Display for ObjectType {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- self.str().fmt(f)
- }
-}
-
-impl ReferenceType {
- /// Convert an object type to its string representation.
- pub fn str(&self) -> &'static str {
- match self {
- ReferenceType::Direct => "direct",
- ReferenceType::Symbolic => "symbolic",
- }
- }
-
- /// Convert a raw git_reference_t to a ReferenceType.
- pub fn from_raw(raw: raw::git_reference_t) -> Option<ReferenceType> {
- match raw {
- raw::GIT_REFERENCE_DIRECT => Some(ReferenceType::Direct),
- raw::GIT_REFERENCE_SYMBOLIC => Some(ReferenceType::Symbolic),
- _ => None,
- }
- }
-}
-
-impl fmt::Display for ReferenceType {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- self.str().fmt(f)
- }
-}
-
-impl ConfigLevel {
- /// Converts a raw configuration level to a ConfigLevel
- pub fn from_raw(raw: raw::git_config_level_t) -> ConfigLevel {
- match raw {
- raw::GIT_CONFIG_LEVEL_PROGRAMDATA => ConfigLevel::ProgramData,
- raw::GIT_CONFIG_LEVEL_SYSTEM => ConfigLevel::System,
- raw::GIT_CONFIG_LEVEL_XDG => ConfigLevel::XDG,
- raw::GIT_CONFIG_LEVEL_GLOBAL => ConfigLevel::Global,
- raw::GIT_CONFIG_LEVEL_LOCAL => ConfigLevel::Local,
- raw::GIT_CONFIG_LEVEL_APP => ConfigLevel::App,
- raw::GIT_CONFIG_HIGHEST_LEVEL => ConfigLevel::Highest,
- n => panic!("unknown config level: {}", n),
- }
- }
-}
-
-impl SubmoduleIgnore {
- /// Converts a [`raw::git_submodule_ignore_t`] to a [`SubmoduleIgnore`]
- pub fn from_raw(raw: raw::git_submodule_ignore_t) -> Self {
- match raw {
- raw::GIT_SUBMODULE_IGNORE_UNSPECIFIED => SubmoduleIgnore::Unspecified,
- raw::GIT_SUBMODULE_IGNORE_NONE => SubmoduleIgnore::None,
- raw::GIT_SUBMODULE_IGNORE_UNTRACKED => SubmoduleIgnore::Untracked,
- raw::GIT_SUBMODULE_IGNORE_DIRTY => SubmoduleIgnore::Dirty,
- raw::GIT_SUBMODULE_IGNORE_ALL => SubmoduleIgnore::All,
- n => panic!("unknown submodule ignore rule: {}", n),
- }
- }
-}
-
-impl SubmoduleUpdate {
- /// Converts a [`raw::git_submodule_update_t`] to a [`SubmoduleUpdate`]
- pub fn from_raw(raw: raw::git_submodule_update_t) -> Self {
- match raw {
- raw::GIT_SUBMODULE_UPDATE_CHECKOUT => SubmoduleUpdate::Checkout,
- raw::GIT_SUBMODULE_UPDATE_REBASE => SubmoduleUpdate::Rebase,
- raw::GIT_SUBMODULE_UPDATE_MERGE => SubmoduleUpdate::Merge,
- raw::GIT_SUBMODULE_UPDATE_NONE => SubmoduleUpdate::None,
- raw::GIT_SUBMODULE_UPDATE_DEFAULT => SubmoduleUpdate::Default,
- n => panic!("unknown submodule update strategy: {}", n),
- }
- }
-}
-
-bitflags! {
- /// Status flags for a single file
- ///
- /// A combination of these values will be returned to indicate the status of
- /// a file. Status compares the working directory, the index, and the
- /// current HEAD of the repository. The `STATUS_INDEX_*` set of flags
- /// represents the status of file in the index relative to the HEAD, and the
- /// `STATUS_WT_*` set of flags represent the status of the file in the
- /// working directory relative to the index.
- #[derive(Clone, Copy, Debug, Eq, PartialEq)]
- pub struct Status: u32 {
- #[allow(missing_docs)]
- const CURRENT = raw::GIT_STATUS_CURRENT as u32;
-
- #[allow(missing_docs)]
- const INDEX_NEW = raw::GIT_STATUS_INDEX_NEW as u32;
- #[allow(missing_docs)]
- const INDEX_MODIFIED = raw::GIT_STATUS_INDEX_MODIFIED as u32;
- #[allow(missing_docs)]
- const INDEX_DELETED = raw::GIT_STATUS_INDEX_DELETED as u32;
- #[allow(missing_docs)]
- const INDEX_RENAMED = raw::GIT_STATUS_INDEX_RENAMED as u32;
- #[allow(missing_docs)]
- const INDEX_TYPECHANGE = raw::GIT_STATUS_INDEX_TYPECHANGE as u32;
-
- #[allow(missing_docs)]
- const WT_NEW = raw::GIT_STATUS_WT_NEW as u32;
- #[allow(missing_docs)]
- const WT_MODIFIED = raw::GIT_STATUS_WT_MODIFIED as u32;
- #[allow(missing_docs)]
- const WT_DELETED = raw::GIT_STATUS_WT_DELETED as u32;
- #[allow(missing_docs)]
- const WT_TYPECHANGE = raw::GIT_STATUS_WT_TYPECHANGE as u32;
- #[allow(missing_docs)]
- const WT_RENAMED = raw::GIT_STATUS_WT_RENAMED as u32;
-
- #[allow(missing_docs)]
- const IGNORED = raw::GIT_STATUS_IGNORED as u32;
- #[allow(missing_docs)]
- const CONFLICTED = raw::GIT_STATUS_CONFLICTED as u32;
- }
-}
-
-impl Status {
- is_bit_set!(is_index_new, Status::INDEX_NEW);
- is_bit_set!(is_index_modified, Status::INDEX_MODIFIED);
- is_bit_set!(is_index_deleted, Status::INDEX_DELETED);
- is_bit_set!(is_index_renamed, Status::INDEX_RENAMED);
- is_bit_set!(is_index_typechange, Status::INDEX_TYPECHANGE);
- is_bit_set!(is_wt_new, Status::WT_NEW);
- is_bit_set!(is_wt_modified, Status::WT_MODIFIED);
- is_bit_set!(is_wt_deleted, Status::WT_DELETED);
- is_bit_set!(is_wt_typechange, Status::WT_TYPECHANGE);
- is_bit_set!(is_wt_renamed, Status::WT_RENAMED);
- is_bit_set!(is_ignored, Status::IGNORED);
- is_bit_set!(is_conflicted, Status::CONFLICTED);
-}
-
-bitflags! {
- /// Mode options for RepositoryInitOptions
- #[derive(Clone, Copy, Debug, Eq, PartialEq)]
- pub struct RepositoryInitMode: u32 {
- /// Use permissions configured by umask - the default
- const SHARED_UMASK = raw::GIT_REPOSITORY_INIT_SHARED_UMASK as u32;
- /// Use `--shared=group` behavior, chmod'ing the new repo to be
- /// group writable and \"g+sx\" for sticky group assignment
- const SHARED_GROUP = raw::GIT_REPOSITORY_INIT_SHARED_GROUP as u32;
- /// Use `--shared=all` behavior, adding world readability.
- const SHARED_ALL = raw::GIT_REPOSITORY_INIT_SHARED_ALL as u32;
- }
-}
-
-impl RepositoryInitMode {
- is_bit_set!(is_shared_umask, RepositoryInitMode::SHARED_UMASK);
- is_bit_set!(is_shared_group, RepositoryInitMode::SHARED_GROUP);
- is_bit_set!(is_shared_all, RepositoryInitMode::SHARED_ALL);
-}
-
-/// What type of change is described by a `DiffDelta`?
-#[derive(Copy, Clone, Debug, PartialEq, Eq)]
-pub enum Delta {
- /// No changes
- Unmodified,
- /// Entry does not exist in old version
- Added,
- /// Entry does not exist in new version
- Deleted,
- /// Entry content changed between old and new
- Modified,
- /// Entry was renamed between old and new
- Renamed,
- /// Entry was copied from another old entry
- Copied,
- /// Entry is ignored item in workdir
- Ignored,
- /// Entry is untracked item in workdir
- Untracked,
- /// Type of entry changed between old and new
- Typechange,
- /// Entry is unreadable
- Unreadable,
- /// Entry in the index is conflicted
- Conflicted,
-}
-
-/// Valid modes for index and tree entries.
-#[derive(Copy, Clone, Debug, PartialEq, Eq)]
-pub enum FileMode {
- /// Unreadable
- Unreadable,
- /// Tree
- Tree,
- /// Blob
- Blob,
- /// Group writable blob. Obsolete mode kept for compatibility reasons
- BlobGroupWritable,
- /// Blob executable
- BlobExecutable,
- /// Link
- Link,
- /// Commit
- Commit,
-}
-
-impl From<FileMode> for i32 {
- fn from(mode: FileMode) -> i32 {
- match mode {
- FileMode::Unreadable => raw::GIT_FILEMODE_UNREADABLE as i32,
- FileMode::Tree => raw::GIT_FILEMODE_TREE as i32,
- FileMode::Blob => raw::GIT_FILEMODE_BLOB as i32,
- FileMode::BlobGroupWritable => raw::GIT_FILEMODE_BLOB_GROUP_WRITABLE as i32,
- FileMode::BlobExecutable => raw::GIT_FILEMODE_BLOB_EXECUTABLE as i32,
- FileMode::Link => raw::GIT_FILEMODE_LINK as i32,
- FileMode::Commit => raw::GIT_FILEMODE_COMMIT as i32,
- }
- }
-}
-
-impl From<FileMode> for u32 {
- fn from(mode: FileMode) -> u32 {
- match mode {
- FileMode::Unreadable => raw::GIT_FILEMODE_UNREADABLE as u32,
- FileMode::Tree => raw::GIT_FILEMODE_TREE as u32,
- FileMode::Blob => raw::GIT_FILEMODE_BLOB as u32,
- FileMode::BlobGroupWritable => raw::GIT_FILEMODE_BLOB_GROUP_WRITABLE as u32,
- FileMode::BlobExecutable => raw::GIT_FILEMODE_BLOB_EXECUTABLE as u32,
- FileMode::Link => raw::GIT_FILEMODE_LINK as u32,
- FileMode::Commit => raw::GIT_FILEMODE_COMMIT as u32,
- }
- }
-}
-
-bitflags! {
- /// Return codes for submodule status.
- ///
- /// A combination of these flags will be returned to describe the status of a
- /// submodule. Depending on the "ignore" property of the submodule, some of
- /// the flags may never be returned because they indicate changes that are
- /// supposed to be ignored.
- ///
- /// Submodule info is contained in 4 places: the HEAD tree, the index, config
- /// files (both .git/config and .gitmodules), and the working directory. Any
- /// or all of those places might be missing information about the submodule
- /// depending on what state the repo is in. We consider all four places to
- /// build the combination of status flags.
- ///
- /// There are four values that are not really status, but give basic info
- /// about what sources of submodule data are available. These will be
- /// returned even if ignore is set to "ALL".
- ///
- /// * IN_HEAD - superproject head contains submodule
- /// * IN_INDEX - superproject index contains submodule
- /// * IN_CONFIG - superproject gitmodules has submodule
- /// * IN_WD - superproject workdir has submodule
- ///
- /// The following values will be returned so long as ignore is not "ALL".
- ///
- /// * INDEX_ADDED - in index, not in head
- /// * INDEX_DELETED - in head, not in index
- /// * INDEX_MODIFIED - index and head don't match
- /// * WD_UNINITIALIZED - workdir contains empty directory
- /// * WD_ADDED - in workdir, not index
- /// * WD_DELETED - in index, not workdir
- /// * WD_MODIFIED - index and workdir head don't match
- ///
- /// The following can only be returned if ignore is "NONE" or "UNTRACKED".
- ///
- /// * WD_INDEX_MODIFIED - submodule workdir index is dirty
- /// * WD_WD_MODIFIED - submodule workdir has modified files
- ///
- /// Lastly, the following will only be returned for ignore "NONE".
- ///
- /// * WD_UNTRACKED - workdir contains untracked files
- #[derive(Clone, Copy, Debug, Eq, PartialEq)]
- pub struct SubmoduleStatus: u32 {
- #[allow(missing_docs)]
- const IN_HEAD = raw::GIT_SUBMODULE_STATUS_IN_HEAD as u32;
- #[allow(missing_docs)]
- const IN_INDEX = raw::GIT_SUBMODULE_STATUS_IN_INDEX as u32;
- #[allow(missing_docs)]
- const IN_CONFIG = raw::GIT_SUBMODULE_STATUS_IN_CONFIG as u32;
- #[allow(missing_docs)]
- const IN_WD = raw::GIT_SUBMODULE_STATUS_IN_WD as u32;
- #[allow(missing_docs)]
- const INDEX_ADDED = raw::GIT_SUBMODULE_STATUS_INDEX_ADDED as u32;
- #[allow(missing_docs)]
- const INDEX_DELETED = raw::GIT_SUBMODULE_STATUS_INDEX_DELETED as u32;
- #[allow(missing_docs)]
- const INDEX_MODIFIED = raw::GIT_SUBMODULE_STATUS_INDEX_MODIFIED as u32;
- #[allow(missing_docs)]
- const WD_UNINITIALIZED =
- raw::GIT_SUBMODULE_STATUS_WD_UNINITIALIZED as u32;
- #[allow(missing_docs)]
- const WD_ADDED = raw::GIT_SUBMODULE_STATUS_WD_ADDED as u32;
- #[allow(missing_docs)]
- const WD_DELETED = raw::GIT_SUBMODULE_STATUS_WD_DELETED as u32;
- #[allow(missing_docs)]
- const WD_MODIFIED = raw::GIT_SUBMODULE_STATUS_WD_MODIFIED as u32;
- #[allow(missing_docs)]
- const WD_INDEX_MODIFIED =
- raw::GIT_SUBMODULE_STATUS_WD_INDEX_MODIFIED as u32;
- #[allow(missing_docs)]
- const WD_WD_MODIFIED = raw::GIT_SUBMODULE_STATUS_WD_WD_MODIFIED as u32;
- #[allow(missing_docs)]
- const WD_UNTRACKED = raw::GIT_SUBMODULE_STATUS_WD_UNTRACKED as u32;
- }
-}
-
-impl SubmoduleStatus {
- is_bit_set!(is_in_head, SubmoduleStatus::IN_HEAD);
- is_bit_set!(is_in_index, SubmoduleStatus::IN_INDEX);
- is_bit_set!(is_in_config, SubmoduleStatus::IN_CONFIG);
- is_bit_set!(is_in_wd, SubmoduleStatus::IN_WD);
- is_bit_set!(is_index_added, SubmoduleStatus::INDEX_ADDED);
- is_bit_set!(is_index_deleted, SubmoduleStatus::INDEX_DELETED);
- is_bit_set!(is_index_modified, SubmoduleStatus::INDEX_MODIFIED);
- is_bit_set!(is_wd_uninitialized, SubmoduleStatus::WD_UNINITIALIZED);
- is_bit_set!(is_wd_added, SubmoduleStatus::WD_ADDED);
- is_bit_set!(is_wd_deleted, SubmoduleStatus::WD_DELETED);
- is_bit_set!(is_wd_modified, SubmoduleStatus::WD_MODIFIED);
- is_bit_set!(is_wd_wd_modified, SubmoduleStatus::WD_WD_MODIFIED);
- is_bit_set!(is_wd_untracked, SubmoduleStatus::WD_UNTRACKED);
-}
-
-/// Submodule ignore values
-///
-/// These values represent settings for the `submodule.$name.ignore`
-/// configuration value which says how deeply to look at the working
-/// directory when getting the submodule status.
-#[derive(Copy, Clone, Debug, PartialEq, Eq)]
-pub enum SubmoduleIgnore {
- /// Use the submodule's configuration
- Unspecified,
- /// Any change or untracked file is considered dirty
- None,
- /// Only dirty if tracked files have changed
- Untracked,
- /// Only dirty if HEAD has moved
- Dirty,
- /// Never dirty
- All,
-}
-
-/// Submodule update values
-///
-/// These values represent settings for the `submodule.$name.update`
-/// configuration value which says how to handle `git submodule update`
-/// for this submodule. The value is usually set in the ".gitmodules"
-/// file and copied to ".git/config" when the submodule is initialized.
-#[derive(Copy, Clone, Debug, PartialEq, Eq)]
-pub enum SubmoduleUpdate {
- /// The default; when a submodule is updated, checkout the new detached
- /// HEAD to the submodule directory.
- Checkout,
- /// Update by rebasing the current checked out branch onto the commit from
- /// the superproject.
- Rebase,
- /// Update by merging the commit in the superproject into the current
- /// checkout out branch of the submodule.
- Merge,
- /// Do not update this submodule even when the commit in the superproject
- /// is updated.
- None,
- /// Not used except as static initializer when we don't want any particular
- /// update rule to be specified.
- Default,
-}
-
-bitflags! {
- /// ...
- #[derive(Clone, Copy, Debug, Eq, PartialEq)]
- pub struct PathspecFlags: u32 {
- /// Use the default pathspec matching configuration.
- const DEFAULT = raw::GIT_PATHSPEC_DEFAULT as u32;
- /// Force matching to ignore case, otherwise matching will use native
- /// case sensitivity of the platform filesystem.
- const IGNORE_CASE = raw::GIT_PATHSPEC_IGNORE_CASE as u32;
- /// Force case sensitive matches, otherwise match will use the native
- /// case sensitivity of the platform filesystem.
- const USE_CASE = raw::GIT_PATHSPEC_USE_CASE as u32;
- /// Disable glob patterns and just use simple string comparison for
- /// matching.
- const NO_GLOB = raw::GIT_PATHSPEC_NO_GLOB as u32;
- /// Means that match functions return the error code `NotFound` if no
- /// matches are found. By default no matches is a success.
- const NO_MATCH_ERROR = raw::GIT_PATHSPEC_NO_MATCH_ERROR as u32;
- /// Means that the list returned should track which patterns matched
- /// which files so that at the end of the match we can identify patterns
- /// that did not match any files.
- const FIND_FAILURES = raw::GIT_PATHSPEC_FIND_FAILURES as u32;
- /// Means that the list returned does not need to keep the actual
- /// matching filenames. Use this to just test if there were any matches
- /// at all or in combination with `PATHSPEC_FAILURES` to validate a
- /// pathspec.
- const FAILURES_ONLY = raw::GIT_PATHSPEC_FAILURES_ONLY as u32;
- }
-}
-
-impl PathspecFlags {
- is_bit_set!(is_default, PathspecFlags::DEFAULT);
- is_bit_set!(is_ignore_case, PathspecFlags::IGNORE_CASE);
- is_bit_set!(is_use_case, PathspecFlags::USE_CASE);
- is_bit_set!(is_no_glob, PathspecFlags::NO_GLOB);
- is_bit_set!(is_no_match_error, PathspecFlags::NO_MATCH_ERROR);
- is_bit_set!(is_find_failures, PathspecFlags::FIND_FAILURES);
- is_bit_set!(is_failures_only, PathspecFlags::FAILURES_ONLY);
-}
-
-impl Default for PathspecFlags {
- fn default() -> Self {
- PathspecFlags::DEFAULT
- }
-}
-
-bitflags! {
- /// Types of notifications emitted from checkouts.
- #[derive(Clone, Copy, Debug, Eq, PartialEq)]
- pub struct CheckoutNotificationType: u32 {
- /// Notification about a conflict.
- const CONFLICT = raw::GIT_CHECKOUT_NOTIFY_CONFLICT as u32;
- /// Notification about a dirty file.
- const DIRTY = raw::GIT_CHECKOUT_NOTIFY_DIRTY as u32;
- /// Notification about an updated file.
- const UPDATED = raw::GIT_CHECKOUT_NOTIFY_UPDATED as u32;
- /// Notification about an untracked file.
- const UNTRACKED = raw::GIT_CHECKOUT_NOTIFY_UNTRACKED as u32;
- /// Notification about an ignored file.
- const IGNORED = raw::GIT_CHECKOUT_NOTIFY_IGNORED as u32;
- }
-}
-
-impl CheckoutNotificationType {
- is_bit_set!(is_conflict, CheckoutNotificationType::CONFLICT);
- is_bit_set!(is_dirty, CheckoutNotificationType::DIRTY);
- is_bit_set!(is_updated, CheckoutNotificationType::UPDATED);
- is_bit_set!(is_untracked, CheckoutNotificationType::UNTRACKED);
- is_bit_set!(is_ignored, CheckoutNotificationType::IGNORED);
-}
-
-/// Possible output formats for diff data
-#[derive(Copy, Clone, Debug, PartialEq, Eq)]
-pub enum DiffFormat {
- /// full git diff
- Patch,
- /// just the headers of the patch
- PatchHeader,
- /// like git diff --raw
- Raw,
- /// like git diff --name-only
- NameOnly,
- /// like git diff --name-status
- NameStatus,
- /// git diff as used by git patch-id
- PatchId,
-}
-
-bitflags! {
- /// Formatting options for diff stats
- #[derive(Clone, Copy, Debug, Eq, PartialEq)]
- pub struct DiffStatsFormat: raw::git_diff_stats_format_t {
- /// Don't generate any stats
- const NONE = raw::GIT_DIFF_STATS_NONE;
- /// Equivalent of `--stat` in git
- const FULL = raw::GIT_DIFF_STATS_FULL;
- /// Equivalent of `--shortstat` in git
- const SHORT = raw::GIT_DIFF_STATS_SHORT;
- /// Equivalent of `--numstat` in git
- const NUMBER = raw::GIT_DIFF_STATS_NUMBER;
- /// Extended header information such as creations, renames and mode
- /// changes, equivalent of `--summary` in git
- const INCLUDE_SUMMARY = raw::GIT_DIFF_STATS_INCLUDE_SUMMARY;
- }
-}
-
-impl DiffStatsFormat {
- is_bit_set!(is_none, DiffStatsFormat::NONE);
- is_bit_set!(is_full, DiffStatsFormat::FULL);
- is_bit_set!(is_short, DiffStatsFormat::SHORT);
- is_bit_set!(is_number, DiffStatsFormat::NUMBER);
- is_bit_set!(is_include_summary, DiffStatsFormat::INCLUDE_SUMMARY);
-}
-
-/// Automatic tag following options.
-#[derive(Copy, Clone, Debug, PartialEq, Eq)]
-pub enum AutotagOption {
- /// Use the setting from the remote's configuration
- Unspecified,
- /// Ask the server for tags pointing to objects we're already downloading
- Auto,
- /// Don't ask for any tags beyond the refspecs
- None,
- /// Ask for all the tags
- All,
-}
-
-/// Configuration for how pruning is done on a fetch
-#[derive(Copy, Clone, Debug, PartialEq, Eq)]
-pub enum FetchPrune {
- /// Use the setting from the configuration
- Unspecified,
- /// Force pruning on
- On,
- /// Force pruning off
- Off,
-}
-
-#[allow(missing_docs)]
-#[derive(Copy, Clone, Debug, PartialEq, Eq)]
-pub enum StashApplyProgress {
- /// None
- None,
- /// Loading the stashed data from the object database
- LoadingStash,
- /// The stored index is being analyzed
- AnalyzeIndex,
- /// The modified files are being analyzed
- AnalyzeModified,
- /// The untracked and ignored files are being analyzed
- AnalyzeUntracked,
- /// The untracked files are being written to disk
- CheckoutUntracked,
- /// The modified files are being written to disk
- CheckoutModified,
- /// The stash was applied successfully
- Done,
-}
-
-bitflags! {
- #[allow(missing_docs)]
- #[derive(Clone, Copy, Debug, Eq, PartialEq)]
- pub struct StashApplyFlags: u32 {
- #[allow(missing_docs)]
- const DEFAULT = raw::GIT_STASH_APPLY_DEFAULT as u32;
- /// Try to reinstate not only the working tree's changes,
- /// but also the index's changes.
- const REINSTATE_INDEX = raw::GIT_STASH_APPLY_REINSTATE_INDEX as u32;
- }
-}
-
-impl StashApplyFlags {
- is_bit_set!(is_default, StashApplyFlags::DEFAULT);
- is_bit_set!(is_reinstate_index, StashApplyFlags::REINSTATE_INDEX);
-}
-
-impl Default for StashApplyFlags {
- fn default() -> Self {
- StashApplyFlags::DEFAULT
- }
-}
-
-bitflags! {
- #[allow(missing_docs)]
- #[derive(Clone, Copy, Debug, Eq, PartialEq)]
- pub struct StashFlags: u32 {
- #[allow(missing_docs)]
- const DEFAULT = raw::GIT_STASH_DEFAULT as u32;
- /// All changes already added to the index are left intact in
- /// the working directory
- const KEEP_INDEX = raw::GIT_STASH_KEEP_INDEX as u32;
- /// All untracked files are also stashed and then cleaned up
- /// from the working directory
- const INCLUDE_UNTRACKED = raw::GIT_STASH_INCLUDE_UNTRACKED as u32;
- /// All ignored files are also stashed and then cleaned up from
- /// the working directory
- const INCLUDE_IGNORED = raw::GIT_STASH_INCLUDE_IGNORED as u32;
- /// All changes in the index and working directory are left intact
- const KEEP_ALL = raw::GIT_STASH_KEEP_ALL as u32;
- }
-}
-
-impl StashFlags {
- is_bit_set!(is_default, StashFlags::DEFAULT);
- is_bit_set!(is_keep_index, StashFlags::KEEP_INDEX);
- is_bit_set!(is_include_untracked, StashFlags::INCLUDE_UNTRACKED);
- is_bit_set!(is_include_ignored, StashFlags::INCLUDE_IGNORED);
-}
-
-impl Default for StashFlags {
- fn default() -> Self {
- StashFlags::DEFAULT
- }
-}
-
-bitflags! {
- #[allow(missing_docs)]
- #[derive(Clone, Copy, Debug, Eq, PartialEq)]
- pub struct AttrCheckFlags: u32 {
- /// Check the working directory, then the index.
- const FILE_THEN_INDEX = raw::GIT_ATTR_CHECK_FILE_THEN_INDEX as u32;
- /// Check the index, then the working directory.
- const INDEX_THEN_FILE = raw::GIT_ATTR_CHECK_INDEX_THEN_FILE as u32;
- /// Check the index only.
- const INDEX_ONLY = raw::GIT_ATTR_CHECK_INDEX_ONLY as u32;
- /// Do not use the system gitattributes file.
- const NO_SYSTEM = raw::GIT_ATTR_CHECK_NO_SYSTEM as u32;
- }
-}
-
-impl Default for AttrCheckFlags {
- fn default() -> Self {
- AttrCheckFlags::FILE_THEN_INDEX
- }
-}
-
-bitflags! {
- #[allow(missing_docs)]
- #[derive(Clone, Copy, Debug, Eq, PartialEq)]
- pub struct DiffFlags: u32 {
- /// File(s) treated as binary data.
- const BINARY = raw::GIT_DIFF_FLAG_BINARY as u32;
- /// File(s) treated as text data.
- const NOT_BINARY = raw::GIT_DIFF_FLAG_NOT_BINARY as u32;
- /// `id` value is known correct.
- const VALID_ID = raw::GIT_DIFF_FLAG_VALID_ID as u32;
- /// File exists at this side of the delta.
- const EXISTS = raw::GIT_DIFF_FLAG_EXISTS as u32;
- }
-}
-
-impl DiffFlags {
- is_bit_set!(is_binary, DiffFlags::BINARY);
- is_bit_set!(is_not_binary, DiffFlags::NOT_BINARY);
- is_bit_set!(has_valid_id, DiffFlags::VALID_ID);
- is_bit_set!(exists, DiffFlags::EXISTS);
-}
-
-bitflags! {
- /// Options for [`Reference::normalize_name`].
- #[derive(Clone, Copy, Debug, Eq, PartialEq)]
- pub struct ReferenceFormat: u32 {
- /// No particular normalization.
- const NORMAL = raw::GIT_REFERENCE_FORMAT_NORMAL as u32;
- /// Control whether one-level refname are accepted (i.e., refnames that
- /// do not contain multiple `/`-separated components). Those are
- /// expected to be written only using uppercase letters and underscore
- /// (e.g. `HEAD`, `FETCH_HEAD`).
- const ALLOW_ONELEVEL = raw::GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL as u32;
- /// Interpret the provided name as a reference pattern for a refspec (as
- /// used with remote repositories). If this option is enabled, the name
- /// is allowed to contain a single `*` in place of a full pathname
- /// components (e.g., `foo/*/bar` but not `foo/bar*`).
- const REFSPEC_PATTERN = raw::GIT_REFERENCE_FORMAT_REFSPEC_PATTERN as u32;
- /// Interpret the name as part of a refspec in shorthand form so the
- /// `ALLOW_ONELEVEL` naming rules aren't enforced and `main` becomes a
- /// valid name.
- const REFSPEC_SHORTHAND = raw::GIT_REFERENCE_FORMAT_REFSPEC_SHORTHAND as u32;
- }
-}
-
-impl ReferenceFormat {
- is_bit_set!(is_allow_onelevel, ReferenceFormat::ALLOW_ONELEVEL);
- is_bit_set!(is_refspec_pattern, ReferenceFormat::REFSPEC_PATTERN);
- is_bit_set!(is_refspec_shorthand, ReferenceFormat::REFSPEC_SHORTHAND);
-}
-
-impl Default for ReferenceFormat {
- fn default() -> Self {
- ReferenceFormat::NORMAL
- }
-}
-
-#[cfg(test)]
-mod tests {
- use super::{FileMode, ObjectType};
-
- #[test]
- fn convert() {
- assert_eq!(ObjectType::Blob.str(), "blob");
- assert_eq!(ObjectType::from_str("blob"), Some(ObjectType::Blob));
- assert!(ObjectType::Blob.is_loose());
- }
-
- #[test]
- fn convert_filemode() {
- assert_eq!(i32::from(FileMode::Blob), 0o100644);
- assert_eq!(i32::from(FileMode::BlobGroupWritable), 0o100664);
- assert_eq!(i32::from(FileMode::BlobExecutable), 0o100755);
- assert_eq!(u32::from(FileMode::Blob), 0o100644);
- assert_eq!(u32::from(FileMode::BlobGroupWritable), 0o100664);
- assert_eq!(u32::from(FileMode::BlobExecutable), 0o100755);
- }
-
- #[test]
- fn bitflags_partial_eq() {
- use super::{
- AttrCheckFlags, CheckoutNotificationType, CredentialType, DiffFlags, DiffStatsFormat,
- IndexAddOption, IndexEntryExtendedFlag, IndexEntryFlag, MergeAnalysis, MergePreference,
- OdbLookupFlags, PathspecFlags, ReferenceFormat, RepositoryInitMode,
- RepositoryOpenFlags, RevparseMode, Sort, StashApplyFlags, StashFlags, Status,
- SubmoduleStatus,
- };
-
- assert_eq!(
- AttrCheckFlags::FILE_THEN_INDEX,
- AttrCheckFlags::FILE_THEN_INDEX
- );
- assert_eq!(
- CheckoutNotificationType::CONFLICT,
- CheckoutNotificationType::CONFLICT
- );
- assert_eq!(
- CredentialType::USER_PASS_PLAINTEXT,
- CredentialType::USER_PASS_PLAINTEXT
- );
- assert_eq!(DiffFlags::BINARY, DiffFlags::BINARY);
- assert_eq!(
- DiffStatsFormat::INCLUDE_SUMMARY,
- DiffStatsFormat::INCLUDE_SUMMARY
- );
- assert_eq!(
- IndexAddOption::CHECK_PATHSPEC,
- IndexAddOption::CHECK_PATHSPEC
- );
- assert_eq!(
- IndexEntryExtendedFlag::INTENT_TO_ADD,
- IndexEntryExtendedFlag::INTENT_TO_ADD
- );
- assert_eq!(IndexEntryFlag::EXTENDED, IndexEntryFlag::EXTENDED);
- assert_eq!(
- MergeAnalysis::ANALYSIS_FASTFORWARD,
- MergeAnalysis::ANALYSIS_FASTFORWARD
- );
- assert_eq!(
- MergePreference::FASTFORWARD_ONLY,
- MergePreference::FASTFORWARD_ONLY
- );
- assert_eq!(OdbLookupFlags::NO_REFRESH, OdbLookupFlags::NO_REFRESH);
- assert_eq!(PathspecFlags::FAILURES_ONLY, PathspecFlags::FAILURES_ONLY);
- assert_eq!(
- ReferenceFormat::ALLOW_ONELEVEL,
- ReferenceFormat::ALLOW_ONELEVEL
- );
- assert_eq!(
- RepositoryInitMode::SHARED_ALL,
- RepositoryInitMode::SHARED_ALL
- );
- assert_eq!(RepositoryOpenFlags::CROSS_FS, RepositoryOpenFlags::CROSS_FS);
- assert_eq!(RevparseMode::RANGE, RevparseMode::RANGE);
- assert_eq!(Sort::REVERSE, Sort::REVERSE);
- assert_eq!(
- StashApplyFlags::REINSTATE_INDEX,
- StashApplyFlags::REINSTATE_INDEX
- );
- assert_eq!(StashFlags::INCLUDE_IGNORED, StashFlags::INCLUDE_IGNORED);
- assert_eq!(Status::WT_MODIFIED, Status::WT_MODIFIED);
- assert_eq!(SubmoduleStatus::WD_ADDED, SubmoduleStatus::WD_ADDED);
- }
-}
diff --git a/extra/git2/src/mailmap.rs b/extra/git2/src/mailmap.rs
deleted file mode 100644
index 096b3227c..000000000
--- a/extra/git2/src/mailmap.rs
+++ /dev/null
@@ -1,134 +0,0 @@
-use std::ffi::CString;
-use std::ptr;
-
-use crate::util::Binding;
-use crate::{raw, Error, Signature};
-
-/// A structure to represent a repository's .mailmap file.
-///
-/// The representation cannot be written to disk.
-pub struct Mailmap {
- raw: *mut raw::git_mailmap,
-}
-
-impl Binding for Mailmap {
- type Raw = *mut raw::git_mailmap;
-
- unsafe fn from_raw(ptr: *mut raw::git_mailmap) -> Mailmap {
- Mailmap { raw: ptr }
- }
-
- fn raw(&self) -> *mut raw::git_mailmap {
- self.raw
- }
-}
-
-impl Drop for Mailmap {
- fn drop(&mut self) {
- unsafe {
- raw::git_mailmap_free(self.raw);
- }
- }
-}
-
-impl Mailmap {
- /// Creates an empty, in-memory mailmap object.
- pub fn new() -> Result<Mailmap, Error> {
- crate::init();
- let mut ret = ptr::null_mut();
- unsafe {
- try_call!(raw::git_mailmap_new(&mut ret));
- Ok(Binding::from_raw(ret))
- }
- }
-
- /// Creates an in-memory mailmap object representing the given buffer.
- pub fn from_buffer(buf: &str) -> Result<Mailmap, Error> {
- crate::init();
- let mut ret = ptr::null_mut();
- let len = buf.len();
- let buf = CString::new(buf)?;
- unsafe {
- try_call!(raw::git_mailmap_from_buffer(&mut ret, buf, len));
- Ok(Binding::from_raw(ret))
- }
- }
-
- /// Adds a new entry to this in-memory mailmap object.
- pub fn add_entry(
- &mut self,
- real_name: Option<&str>,
- real_email: Option<&str>,
- replace_name: Option<&str>,
- replace_email: &str,
- ) -> Result<(), Error> {
- let real_name = crate::opt_cstr(real_name)?;
- let real_email = crate::opt_cstr(real_email)?;
- let replace_name = crate::opt_cstr(replace_name)?;
- let replace_email = CString::new(replace_email)?;
- unsafe {
- try_call!(raw::git_mailmap_add_entry(
- self.raw,
- real_name,
- real_email,
- replace_name,
- replace_email
- ));
- Ok(())
- }
- }
-
- /// Resolves a signature to its real name and email address.
- pub fn resolve_signature(&self, sig: &Signature<'_>) -> Result<Signature<'static>, Error> {
- let mut ret = ptr::null_mut();
- unsafe {
- try_call!(raw::git_mailmap_resolve_signature(
- &mut ret,
- &*self.raw,
- sig.raw()
- ));
- Ok(Binding::from_raw(ret))
- }
- }
-}
-
-#[cfg(test)]
-mod tests {
- use super::*;
-
- #[test]
- fn smoke() {
- let sig_name = "name";
- let sig_email = "email";
- let sig = t!(Signature::now(sig_name, sig_email));
-
- let mut mm = t!(Mailmap::new());
-
- let mailmapped_sig = t!(mm.resolve_signature(&sig));
- assert_eq!(mailmapped_sig.name(), Some(sig_name));
- assert_eq!(mailmapped_sig.email(), Some(sig_email));
-
- t!(mm.add_entry(None, None, None, sig_email));
- t!(mm.add_entry(
- Some("real name"),
- Some("real@email"),
- Some(sig_name),
- sig_email,
- ));
-
- let mailmapped_sig = t!(mm.resolve_signature(&sig));
- assert_eq!(mailmapped_sig.name(), Some("real name"));
- assert_eq!(mailmapped_sig.email(), Some("real@email"));
- }
-
- #[test]
- fn from_buffer() {
- let buf = "<prøper@emæil> <email>";
- let mm = t!(Mailmap::from_buffer(&buf));
-
- let sig = t!(Signature::now("name", "email"));
- let mailmapped_sig = t!(mm.resolve_signature(&sig));
- assert_eq!(mailmapped_sig.name(), Some("name"));
- assert_eq!(mailmapped_sig.email(), Some("prøper@emæil"));
- }
-}
diff --git a/extra/git2/src/mempack.rs b/extra/git2/src/mempack.rs
deleted file mode 100644
index a78070791..000000000
--- a/extra/git2/src/mempack.rs
+++ /dev/null
@@ -1,49 +0,0 @@
-use std::marker;
-
-use crate::util::Binding;
-use crate::{raw, Buf, Error, Odb, Repository};
-
-/// A structure to represent a mempack backend for the object database. The
-/// Mempack is bound to the Odb that it was created from, and cannot outlive
-/// that Odb.
-pub struct Mempack<'odb> {
- raw: *mut raw::git_odb_backend,
- _marker: marker::PhantomData<&'odb Odb<'odb>>,
-}
-
-impl<'odb> Binding for Mempack<'odb> {
- type Raw = *mut raw::git_odb_backend;
-
- unsafe fn from_raw(raw: *mut raw::git_odb_backend) -> Mempack<'odb> {
- Mempack {
- raw,
- _marker: marker::PhantomData,
- }
- }
-
- fn raw(&self) -> *mut raw::git_odb_backend {
- self.raw
- }
-}
-
-// We don't need to implement `Drop` for Mempack because it is owned by the
-// odb to which it is attached, and that will take care of freeing the mempack
-// and associated memory.
-
-impl<'odb> Mempack<'odb> {
- /// Dumps the contents of the mempack into the provided buffer.
- pub fn dump(&self, repo: &Repository, buf: &mut Buf) -> Result<(), Error> {
- unsafe {
- try_call!(raw::git_mempack_dump(buf.raw(), repo.raw(), self.raw));
- }
- Ok(())
- }
-
- /// Clears all data in the mempack.
- pub fn reset(&self) -> Result<(), Error> {
- unsafe {
- try_call!(raw::git_mempack_reset(self.raw));
- }
- Ok(())
- }
-}
diff --git a/extra/git2/src/merge.rs b/extra/git2/src/merge.rs
deleted file mode 100644
index 6bd30c10d..000000000
--- a/extra/git2/src/merge.rs
+++ /dev/null
@@ -1,194 +0,0 @@
-use libc::c_uint;
-use std::marker;
-use std::mem;
-use std::str;
-
-use crate::call::Convert;
-use crate::util::Binding;
-use crate::{raw, Commit, FileFavor, Oid};
-
-/// A structure to represent an annotated commit, the input to merge and rebase.
-///
-/// An annotated commit contains information about how it was looked up, which
-/// may be useful for functions like merge or rebase to provide context to the
-/// operation.
-pub struct AnnotatedCommit<'repo> {
- raw: *mut raw::git_annotated_commit,
- _marker: marker::PhantomData<Commit<'repo>>,
-}
-
-/// Options to specify when merging.
-pub struct MergeOptions {
- raw: raw::git_merge_options,
-}
-
-impl<'repo> AnnotatedCommit<'repo> {
- /// Gets the commit ID that the given git_annotated_commit refers to
- pub fn id(&self) -> Oid {
- unsafe { Binding::from_raw(raw::git_annotated_commit_id(self.raw)) }
- }
-
- /// Get the refname that the given git_annotated_commit refers to
- ///
- /// Returns None if it is not valid utf8
- pub fn refname(&self) -> Option<&str> {
- str::from_utf8(self.refname_bytes()).ok()
- }
-
- /// Get the refname that the given git_annotated_commit refers to.
- pub fn refname_bytes(&self) -> &[u8] {
- unsafe { crate::opt_bytes(self, raw::git_annotated_commit_ref(&*self.raw)).unwrap() }
- }
-}
-
-impl Default for MergeOptions {
- fn default() -> Self {
- Self::new()
- }
-}
-
-impl MergeOptions {
- /// Creates a default set of merge options.
- pub fn new() -> MergeOptions {
- let mut opts = MergeOptions {
- raw: unsafe { mem::zeroed() },
- };
- assert_eq!(unsafe { raw::git_merge_init_options(&mut opts.raw, 1) }, 0);
- opts
- }
-
- fn flag(&mut self, opt: u32, val: bool) -> &mut MergeOptions {
- if val {
- self.raw.flags |= opt;
- } else {
- self.raw.flags &= !opt;
- }
- self
- }
-
- /// Detect file renames
- pub fn find_renames(&mut self, find: bool) -> &mut MergeOptions {
- self.flag(raw::GIT_MERGE_FIND_RENAMES as u32, find)
- }
-
- /// If a conflict occurs, exit immediately instead of attempting to continue
- /// resolving conflicts
- pub fn fail_on_conflict(&mut self, fail: bool) -> &mut MergeOptions {
- self.flag(raw::GIT_MERGE_FAIL_ON_CONFLICT as u32, fail)
- }
-
- /// Do not write the REUC extension on the generated index
- pub fn skip_reuc(&mut self, skip: bool) -> &mut MergeOptions {
- self.flag(raw::GIT_MERGE_FAIL_ON_CONFLICT as u32, skip)
- }
-
- /// If the commits being merged have multiple merge bases, do not build a
- /// recursive merge base (by merging the multiple merge bases), instead
- /// simply use the first base.
- pub fn no_recursive(&mut self, disable: bool) -> &mut MergeOptions {
- self.flag(raw::GIT_MERGE_NO_RECURSIVE as u32, disable)
- }
-
- /// Similarity to consider a file renamed (default 50)
- pub fn rename_threshold(&mut self, thresh: u32) -> &mut MergeOptions {
- self.raw.rename_threshold = thresh;
- self
- }
-
- /// Maximum similarity sources to examine for renames (default 200).
- /// If the number of rename candidates (add / delete pairs) is greater
- /// than this value, inexact rename detection is aborted. This setting
- /// overrides the `merge.renameLimit` configuration value.
- pub fn target_limit(&mut self, limit: u32) -> &mut MergeOptions {
- self.raw.target_limit = limit as c_uint;
- self
- }
-
- /// Maximum number of times to merge common ancestors to build a
- /// virtual merge base when faced with criss-cross merges. When
- /// this limit is reached, the next ancestor will simply be used
- /// instead of attempting to merge it. The default is unlimited.
- pub fn recursion_limit(&mut self, limit: u32) -> &mut MergeOptions {
- self.raw.recursion_limit = limit as c_uint;
- self
- }
-
- /// Specify a side to favor for resolving conflicts
- pub fn file_favor(&mut self, favor: FileFavor) -> &mut MergeOptions {
- self.raw.file_favor = favor.convert();
- self
- }
-
- fn file_flag(&mut self, opt: u32, val: bool) -> &mut MergeOptions {
- if val {
- self.raw.file_flags |= opt;
- } else {
- self.raw.file_flags &= !opt;
- }
- self
- }
-
- /// Create standard conflicted merge files
- pub fn standard_style(&mut self, standard: bool) -> &mut MergeOptions {
- self.file_flag(raw::GIT_MERGE_FILE_STYLE_MERGE as u32, standard)
- }
-
- /// Create diff3-style file
- pub fn diff3_style(&mut self, diff3: bool) -> &mut MergeOptions {
- self.file_flag(raw::GIT_MERGE_FILE_STYLE_DIFF3 as u32, diff3)
- }
-
- /// Condense non-alphanumeric regions for simplified diff file
- pub fn simplify_alnum(&mut self, simplify: bool) -> &mut MergeOptions {
- self.file_flag(raw::GIT_MERGE_FILE_SIMPLIFY_ALNUM as u32, simplify)
- }
-
- /// Ignore all whitespace
- pub fn ignore_whitespace(&mut self, ignore: bool) -> &mut MergeOptions {
- self.file_flag(raw::GIT_MERGE_FILE_IGNORE_WHITESPACE as u32, ignore)
- }
-
- /// Ignore changes in amount of whitespace
- pub fn ignore_whitespace_change(&mut self, ignore: bool) -> &mut MergeOptions {
- self.file_flag(raw::GIT_MERGE_FILE_IGNORE_WHITESPACE_CHANGE as u32, ignore)
- }
-
- /// Ignore whitespace at end of line
- pub fn ignore_whitespace_eol(&mut self, ignore: bool) -> &mut MergeOptions {
- self.file_flag(raw::GIT_MERGE_FILE_IGNORE_WHITESPACE_EOL as u32, ignore)
- }
-
- /// Use the "patience diff" algorithm
- pub fn patience(&mut self, patience: bool) -> &mut MergeOptions {
- self.file_flag(raw::GIT_MERGE_FILE_DIFF_PATIENCE as u32, patience)
- }
-
- /// Take extra time to find minimal diff
- pub fn minimal(&mut self, minimal: bool) -> &mut MergeOptions {
- self.file_flag(raw::GIT_MERGE_FILE_DIFF_MINIMAL as u32, minimal)
- }
-
- /// Acquire a pointer to the underlying raw options.
- pub unsafe fn raw(&self) -> *const raw::git_merge_options {
- &self.raw as *const _
- }
-}
-
-impl<'repo> Binding for AnnotatedCommit<'repo> {
- type Raw = *mut raw::git_annotated_commit;
- unsafe fn from_raw(raw: *mut raw::git_annotated_commit) -> AnnotatedCommit<'repo> {
- AnnotatedCommit {
- raw,
- _marker: marker::PhantomData,
- }
- }
- fn raw(&self) -> *mut raw::git_annotated_commit {
- self.raw
- }
-}
-
-impl<'repo> Drop for AnnotatedCommit<'repo> {
- fn drop(&mut self) {
- unsafe { raw::git_annotated_commit_free(self.raw) }
- }
-}
diff --git a/extra/git2/src/message.rs b/extra/git2/src/message.rs
deleted file mode 100644
index a7041da3a..000000000
--- a/extra/git2/src/message.rs
+++ /dev/null
@@ -1,349 +0,0 @@
-use core::ops::Range;
-use std::ffi::CStr;
-use std::ffi::CString;
-use std::iter::FusedIterator;
-use std::ptr;
-
-use libc::{c_char, c_int};
-
-use crate::util::Binding;
-use crate::{raw, Buf, Error, IntoCString};
-
-/// Clean up a message, removing extraneous whitespace, and ensure that the
-/// message ends with a newline. If `comment_char` is `Some`, also remove comment
-/// lines starting with that character.
-pub fn message_prettify<T: IntoCString>(
- message: T,
- comment_char: Option<u8>,
-) -> Result<String, Error> {
- _message_prettify(message.into_c_string()?, comment_char)
-}
-
-fn _message_prettify(message: CString, comment_char: Option<u8>) -> Result<String, Error> {
- let ret = Buf::new();
- unsafe {
- try_call!(raw::git_message_prettify(
- ret.raw(),
- message,
- comment_char.is_some() as c_int,
- comment_char.unwrap_or(0) as c_char
- ));
- }
- Ok(ret.as_str().unwrap().to_string())
-}
-
-/// The default comment character for `message_prettify` ('#')
-pub const DEFAULT_COMMENT_CHAR: Option<u8> = Some(b'#');
-
-/// Get the trailers for the given message.
-///
-/// Use this function when you are dealing with a UTF-8-encoded message.
-pub fn message_trailers_strs(message: &str) -> Result<MessageTrailersStrs, Error> {
- _message_trailers(message.into_c_string()?).map(|res| MessageTrailersStrs(res))
-}
-
-/// Get the trailers for the given message.
-///
-/// Use this function when the message might not be UTF-8-encoded,
-/// or if you want to handle the returned trailer key–value pairs
-/// as bytes.
-pub fn message_trailers_bytes<S: IntoCString>(message: S) -> Result<MessageTrailersBytes, Error> {
- _message_trailers(message.into_c_string()?).map(|res| MessageTrailersBytes(res))
-}
-
-fn _message_trailers(message: CString) -> Result<MessageTrailers, Error> {
- let ret = MessageTrailers::new();
- unsafe {
- try_call!(raw::git_message_trailers(ret.raw(), message));
- }
- Ok(ret)
-}
-
-/// Collection of UTF-8-encoded trailers.
-///
-/// Use `iter()` to get access to the values.
-pub struct MessageTrailersStrs(MessageTrailers);
-
-impl MessageTrailersStrs {
- /// Create a borrowed iterator.
- pub fn iter(&self) -> MessageTrailersStrsIterator<'_> {
- MessageTrailersStrsIterator(self.0.iter())
- }
- /// The number of trailer key–value pairs.
- pub fn len(&self) -> usize {
- self.0.len()
- }
- /// Convert to the “bytes” variant.
- pub fn to_bytes(self) -> MessageTrailersBytes {
- MessageTrailersBytes(self.0)
- }
-}
-
-/// Collection of unencoded (bytes) trailers.
-///
-/// Use `iter()` to get access to the values.
-pub struct MessageTrailersBytes(MessageTrailers);
-
-impl MessageTrailersBytes {
- /// Create a borrowed iterator.
- pub fn iter(&self) -> MessageTrailersBytesIterator<'_> {
- MessageTrailersBytesIterator(self.0.iter())
- }
- /// The number of trailer key–value pairs.
- pub fn len(&self) -> usize {
- self.0.len()
- }
-}
-
-struct MessageTrailers {
- raw: raw::git_message_trailer_array,
-}
-
-impl MessageTrailers {
- fn new() -> MessageTrailers {
- crate::init();
- unsafe {
- Binding::from_raw(&mut raw::git_message_trailer_array {
- trailers: ptr::null_mut(),
- count: 0,
- _trailer_block: ptr::null_mut(),
- } as *mut _)
- }
- }
- fn iter(&self) -> MessageTrailersIterator<'_> {
- MessageTrailersIterator {
- trailers: self,
- range: Range {
- start: 0,
- end: self.raw.count,
- },
- }
- }
- fn len(&self) -> usize {
- self.raw.count
- }
-}
-
-impl Drop for MessageTrailers {
- fn drop(&mut self) {
- unsafe {
- raw::git_message_trailer_array_free(&mut self.raw);
- }
- }
-}
-
-impl Binding for MessageTrailers {
- type Raw = *mut raw::git_message_trailer_array;
- unsafe fn from_raw(raw: *mut raw::git_message_trailer_array) -> MessageTrailers {
- MessageTrailers { raw: *raw }
- }
- fn raw(&self) -> *mut raw::git_message_trailer_array {
- &self.raw as *const _ as *mut _
- }
-}
-
-struct MessageTrailersIterator<'a> {
- trailers: &'a MessageTrailers,
- range: Range<usize>,
-}
-
-fn to_raw_tuple(trailers: &MessageTrailers, index: usize) -> (*const c_char, *const c_char) {
- unsafe {
- let addr = trailers.raw.trailers.wrapping_add(index);
- ((*addr).key, (*addr).value)
- }
-}
-
-/// Borrowed iterator over the UTF-8-encoded trailers.
-pub struct MessageTrailersStrsIterator<'a>(MessageTrailersIterator<'a>);
-
-impl<'pair> Iterator for MessageTrailersStrsIterator<'pair> {
- type Item = (&'pair str, &'pair str);
-
- fn next(&mut self) -> Option<Self::Item> {
- self.0
- .range
- .next()
- .map(|index| to_str_tuple(&self.0.trailers, index))
- }
-
- fn size_hint(&self) -> (usize, Option<usize>) {
- self.0.range.size_hint()
- }
-}
-
-impl FusedIterator for MessageTrailersStrsIterator<'_> {}
-
-impl ExactSizeIterator for MessageTrailersStrsIterator<'_> {
- fn len(&self) -> usize {
- self.0.range.len()
- }
-}
-
-impl DoubleEndedIterator for MessageTrailersStrsIterator<'_> {
- fn next_back(&mut self) -> Option<Self::Item> {
- self.0
- .range
- .next_back()
- .map(|index| to_str_tuple(&self.0.trailers, index))
- }
-}
-
-fn to_str_tuple(trailers: &MessageTrailers, index: usize) -> (&str, &str) {
- unsafe {
- let (rkey, rvalue) = to_raw_tuple(&trailers, index);
- let key = CStr::from_ptr(rkey).to_str().unwrap();
- let value = CStr::from_ptr(rvalue).to_str().unwrap();
- (key, value)
- }
-}
-
-/// Borrowed iterator over the raw (bytes) trailers.
-pub struct MessageTrailersBytesIterator<'a>(MessageTrailersIterator<'a>);
-
-impl<'pair> Iterator for MessageTrailersBytesIterator<'pair> {
- type Item = (&'pair [u8], &'pair [u8]);
-
- fn next(&mut self) -> Option<Self::Item> {
- self.0
- .range
- .next()
- .map(|index| to_bytes_tuple(&self.0.trailers, index))
- }
-
- fn size_hint(&self) -> (usize, Option<usize>) {
- self.0.range.size_hint()
- }
-}
-
-impl FusedIterator for MessageTrailersBytesIterator<'_> {}
-
-impl ExactSizeIterator for MessageTrailersBytesIterator<'_> {
- fn len(&self) -> usize {
- self.0.range.len()
- }
-}
-
-impl DoubleEndedIterator for MessageTrailersBytesIterator<'_> {
- fn next_back(&mut self) -> Option<Self::Item> {
- self.0
- .range
- .next_back()
- .map(|index| to_bytes_tuple(&self.0.trailers, index))
- }
-}
-
-fn to_bytes_tuple(trailers: &MessageTrailers, index: usize) -> (&[u8], &[u8]) {
- unsafe {
- let (rkey, rvalue) = to_raw_tuple(&trailers, index);
- let key = CStr::from_ptr(rkey).to_bytes();
- let value = CStr::from_ptr(rvalue).to_bytes();
- (key, value)
- }
-}
-
-#[cfg(test)]
-mod tests {
-
- #[test]
- fn prettify() {
- use crate::{message_prettify, DEFAULT_COMMENT_CHAR};
-
- // This does not attempt to duplicate the extensive tests for
- // git_message_prettify in libgit2, just a few representative values to
- // make sure the interface works as expected.
- assert_eq!(message_prettify("1\n\n\n2", None).unwrap(), "1\n\n2\n");
- assert_eq!(
- message_prettify("1\n\n\n2\n\n\n3", None).unwrap(),
- "1\n\n2\n\n3\n"
- );
- assert_eq!(
- message_prettify("1\n# comment\n# more", None).unwrap(),
- "1\n# comment\n# more\n"
- );
- assert_eq!(
- message_prettify("1\n# comment\n# more", DEFAULT_COMMENT_CHAR).unwrap(),
- "1\n"
- );
- assert_eq!(
- message_prettify("1\n; comment\n; more", Some(';' as u8)).unwrap(),
- "1\n"
- );
- }
-
- #[test]
- fn trailers() {
- use crate::{message_trailers_bytes, message_trailers_strs, MessageTrailersStrs};
- use std::collections::HashMap;
-
- // no trailers
- let message1 = "
-WHAT ARE WE HERE FOR
-
-What are we here for?
-
-Just to be eaten?
-";
- let expected: HashMap<&str, &str> = HashMap::new();
- assert_eq!(expected, to_map(&message_trailers_strs(message1).unwrap()));
-
- // standard PSA
- let message2 = "
-Attention all
-
-We are out of tomatoes.
-
-Spoken-by: Major Turnips
-Transcribed-by: Seargant Persimmons
-Signed-off-by: Colonel Kale
-";
- let expected: HashMap<&str, &str> = vec![
- ("Spoken-by", "Major Turnips"),
- ("Transcribed-by", "Seargant Persimmons"),
- ("Signed-off-by", "Colonel Kale"),
- ]
- .into_iter()
- .collect();
- assert_eq!(expected, to_map(&message_trailers_strs(message2).unwrap()));
-
- // ignore everything after `---`
- let message3 = "
-The fate of Seargant Green-Peppers
-
-Seargant Green-Peppers was killed by Caterpillar Battalion 44.
-
-Signed-off-by: Colonel Kale
----
-I never liked that guy, anyway.
-
-Opined-by: Corporal Garlic
-";
- let expected: HashMap<&str, &str> = vec![("Signed-off-by", "Colonel Kale")]
- .into_iter()
- .collect();
- assert_eq!(expected, to_map(&message_trailers_strs(message3).unwrap()));
-
- // Raw bytes message; not valid UTF-8
- // Source: https://stackoverflow.com/a/3886015/1725151
- let message4 = b"
-Be honest guys
-
-Am I a malformed brussels sprout?
-
-Signed-off-by: Lieutenant \xe2\x28\xa1prout
-";
-
- let trailer = message_trailers_bytes(&message4[..]).unwrap();
- let expected = (&b"Signed-off-by"[..], &b"Lieutenant \xe2\x28\xa1prout"[..]);
- let actual = trailer.iter().next().unwrap();
- assert_eq!(expected, actual);
-
- fn to_map(trailers: &MessageTrailersStrs) -> HashMap<&str, &str> {
- let mut map = HashMap::with_capacity(trailers.len());
- for (key, value) in trailers.iter() {
- map.insert(key, value);
- }
- map
- }
- }
-}
diff --git a/extra/git2/src/note.rs b/extra/git2/src/note.rs
deleted file mode 100644
index 50e5800fe..000000000
--- a/extra/git2/src/note.rs
+++ /dev/null
@@ -1,147 +0,0 @@
-use std::marker;
-use std::str;
-
-use crate::util::Binding;
-use crate::{raw, signature, Error, Oid, Repository, Signature};
-
-/// A structure representing a [note][note] in git.
-///
-/// [note]: http://alblue.bandlem.com/2011/11/git-tip-of-week-git-notes.html
-pub struct Note<'repo> {
- raw: *mut raw::git_note,
-
- // Hmm, the current libgit2 version does not have this inside of it, but
- // perhaps it's a good idea to keep it around? Can always remove it later I
- // suppose...
- _marker: marker::PhantomData<&'repo Repository>,
-}
-
-/// An iterator over all of the notes within a repository.
-pub struct Notes<'repo> {
- raw: *mut raw::git_note_iterator,
- _marker: marker::PhantomData<&'repo Repository>,
-}
-
-impl<'repo> Note<'repo> {
- /// Get the note author
- pub fn author(&self) -> Signature<'_> {
- unsafe { signature::from_raw_const(self, raw::git_note_author(&*self.raw)) }
- }
-
- /// Get the note committer
- pub fn committer(&self) -> Signature<'_> {
- unsafe { signature::from_raw_const(self, raw::git_note_committer(&*self.raw)) }
- }
-
- /// Get the note message, in bytes.
- pub fn message_bytes(&self) -> &[u8] {
- unsafe { crate::opt_bytes(self, raw::git_note_message(&*self.raw)).unwrap() }
- }
-
- /// Get the note message as a string, returning `None` if it is not UTF-8.
- pub fn message(&self) -> Option<&str> {
- str::from_utf8(self.message_bytes()).ok()
- }
-
- /// Get the note object's id
- pub fn id(&self) -> Oid {
- unsafe { Binding::from_raw(raw::git_note_id(&*self.raw)) }
- }
-}
-
-impl<'repo> Binding for Note<'repo> {
- type Raw = *mut raw::git_note;
- unsafe fn from_raw(raw: *mut raw::git_note) -> Note<'repo> {
- Note {
- raw,
- _marker: marker::PhantomData,
- }
- }
- fn raw(&self) -> *mut raw::git_note {
- self.raw
- }
-}
-
-impl<'repo> std::fmt::Debug for Note<'repo> {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
- f.debug_struct("Note").field("id", &self.id()).finish()
- }
-}
-
-impl<'repo> Drop for Note<'repo> {
- fn drop(&mut self) {
- unsafe {
- raw::git_note_free(self.raw);
- }
- }
-}
-
-impl<'repo> Binding for Notes<'repo> {
- type Raw = *mut raw::git_note_iterator;
- unsafe fn from_raw(raw: *mut raw::git_note_iterator) -> Notes<'repo> {
- Notes {
- raw,
- _marker: marker::PhantomData,
- }
- }
- fn raw(&self) -> *mut raw::git_note_iterator {
- self.raw
- }
-}
-
-impl<'repo> Iterator for Notes<'repo> {
- type Item = Result<(Oid, Oid), Error>;
- fn next(&mut self) -> Option<Result<(Oid, Oid), Error>> {
- let mut note_id = raw::git_oid {
- id: [0; raw::GIT_OID_RAWSZ],
- };
- let mut annotated_id = note_id;
- unsafe {
- try_call_iter!(raw::git_note_next(
- &mut note_id,
- &mut annotated_id,
- self.raw
- ));
- Some(Ok((
- Binding::from_raw(&note_id as *const _),
- Binding::from_raw(&annotated_id as *const _),
- )))
- }
- }
-}
-
-impl<'repo> Drop for Notes<'repo> {
- fn drop(&mut self) {
- unsafe {
- raw::git_note_iterator_free(self.raw);
- }
- }
-}
-
-#[cfg(test)]
-mod tests {
- #[test]
- fn smoke() {
- let (_td, repo) = crate::test::repo_init();
- assert!(repo.notes(None).is_err());
-
- let sig = repo.signature().unwrap();
- let head = repo.head().unwrap().target().unwrap();
- let note = repo.note(&sig, &sig, None, head, "foo", false).unwrap();
- assert_eq!(repo.notes(None).unwrap().count(), 1);
-
- let note_obj = repo.find_note(None, head).unwrap();
- assert_eq!(note_obj.id(), note);
- assert_eq!(note_obj.message(), Some("foo"));
-
- let (a, b) = repo.notes(None).unwrap().next().unwrap().unwrap();
- assert_eq!(a, note);
- assert_eq!(b, head);
-
- assert_eq!(repo.note_default_ref().unwrap(), "refs/notes/commits");
-
- assert_eq!(sig.name(), note_obj.author().name());
- assert_eq!(sig.name(), note_obj.committer().name());
- assert!(sig.when() == note_obj.committer().when());
- }
-}
diff --git a/extra/git2/src/object.rs b/extra/git2/src/object.rs
deleted file mode 100644
index fcae0066c..000000000
--- a/extra/git2/src/object.rs
+++ /dev/null
@@ -1,248 +0,0 @@
-use std::marker;
-use std::mem;
-use std::ptr;
-
-use crate::util::Binding;
-use crate::{raw, Blob, Buf, Commit, Error, ObjectType, Oid, Repository, Tag, Tree};
-use crate::{Describe, DescribeOptions};
-
-/// A structure to represent a git [object][1]
-///
-/// [1]: http://git-scm.com/book/en/Git-Internals-Git-Objects
-pub struct Object<'repo> {
- raw: *mut raw::git_object,
- _marker: marker::PhantomData<&'repo Repository>,
-}
-
-impl<'repo> Object<'repo> {
- /// Get the id (SHA1) of a repository object
- pub fn id(&self) -> Oid {
- unsafe { Binding::from_raw(raw::git_object_id(&*self.raw)) }
- }
-
- /// Get the object type of an object.
- ///
- /// If the type is unknown, then `None` is returned.
- pub fn kind(&self) -> Option<ObjectType> {
- ObjectType::from_raw(unsafe { raw::git_object_type(&*self.raw) })
- }
-
- /// Recursively peel an object until an object of the specified type is met.
- ///
- /// If you pass `Any` as the target type, then the object will be
- /// peeled until the type changes (e.g. a tag will be chased until the
- /// referenced object is no longer a tag).
- pub fn peel(&self, kind: ObjectType) -> Result<Object<'repo>, Error> {
- let mut raw = ptr::null_mut();
- unsafe {
- try_call!(raw::git_object_peel(&mut raw, &*self.raw(), kind));
- Ok(Binding::from_raw(raw))
- }
- }
-
- /// Recursively peel an object until a blob is found
- pub fn peel_to_blob(&self) -> Result<Blob<'repo>, Error> {
- self.peel(ObjectType::Blob)
- .map(|o| o.cast_or_panic(ObjectType::Blob))
- }
-
- /// Recursively peel an object until a commit is found
- pub fn peel_to_commit(&self) -> Result<Commit<'repo>, Error> {
- self.peel(ObjectType::Commit)
- .map(|o| o.cast_or_panic(ObjectType::Commit))
- }
-
- /// Recursively peel an object until a tag is found
- pub fn peel_to_tag(&self) -> Result<Tag<'repo>, Error> {
- self.peel(ObjectType::Tag)
- .map(|o| o.cast_or_panic(ObjectType::Tag))
- }
-
- /// Recursively peel an object until a tree is found
- pub fn peel_to_tree(&self) -> Result<Tree<'repo>, Error> {
- self.peel(ObjectType::Tree)
- .map(|o| o.cast_or_panic(ObjectType::Tree))
- }
-
- /// Get a short abbreviated OID string for the object
- ///
- /// This starts at the "core.abbrev" length (default 7 characters) and
- /// iteratively extends to a longer string if that length is ambiguous. The
- /// result will be unambiguous (at least until new objects are added to the
- /// repository).
- pub fn short_id(&self) -> Result<Buf, Error> {
- unsafe {
- let buf = Buf::new();
- try_call!(raw::git_object_short_id(buf.raw(), &*self.raw()));
- Ok(buf)
- }
- }
-
- /// Attempt to view this object as a commit.
- ///
- /// Returns `None` if the object is not actually a commit.
- pub fn as_commit(&self) -> Option<&Commit<'repo>> {
- self.cast(ObjectType::Commit)
- }
-
- /// Attempt to consume this object and return a commit.
- ///
- /// Returns `Err(self)` if this object is not actually a commit.
- pub fn into_commit(self) -> Result<Commit<'repo>, Object<'repo>> {
- self.cast_into(ObjectType::Commit)
- }
-
- /// Attempt to view this object as a tag.
- ///
- /// Returns `None` if the object is not actually a tag.
- pub fn as_tag(&self) -> Option<&Tag<'repo>> {
- self.cast(ObjectType::Tag)
- }
-
- /// Attempt to consume this object and return a tag.
- ///
- /// Returns `Err(self)` if this object is not actually a tag.
- pub fn into_tag(self) -> Result<Tag<'repo>, Object<'repo>> {
- self.cast_into(ObjectType::Tag)
- }
-
- /// Attempt to view this object as a tree.
- ///
- /// Returns `None` if the object is not actually a tree.
- pub fn as_tree(&self) -> Option<&Tree<'repo>> {
- self.cast(ObjectType::Tree)
- }
-
- /// Attempt to consume this object and return a tree.
- ///
- /// Returns `Err(self)` if this object is not actually a tree.
- pub fn into_tree(self) -> Result<Tree<'repo>, Object<'repo>> {
- self.cast_into(ObjectType::Tree)
- }
-
- /// Attempt to view this object as a blob.
- ///
- /// Returns `None` if the object is not actually a blob.
- pub fn as_blob(&self) -> Option<&Blob<'repo>> {
- self.cast(ObjectType::Blob)
- }
-
- /// Attempt to consume this object and return a blob.
- ///
- /// Returns `Err(self)` if this object is not actually a blob.
- pub fn into_blob(self) -> Result<Blob<'repo>, Object<'repo>> {
- self.cast_into(ObjectType::Blob)
- }
-
- /// Describes a commit
- ///
- /// Performs a describe operation on this commitish object.
- pub fn describe(&self, opts: &DescribeOptions) -> Result<Describe<'_>, Error> {
- let mut ret = ptr::null_mut();
- unsafe {
- try_call!(raw::git_describe_commit(&mut ret, self.raw, opts.raw()));
- Ok(Binding::from_raw(ret))
- }
- }
-
- fn cast<T>(&self, kind: ObjectType) -> Option<&T> {
- assert_eq!(mem::size_of::<Object<'_>>(), mem::size_of::<T>());
- if self.kind() == Some(kind) {
- unsafe { Some(&*(self as *const _ as *const T)) }
- } else {
- None
- }
- }
-
- fn cast_into<T>(self, kind: ObjectType) -> Result<T, Object<'repo>> {
- assert_eq!(mem::size_of_val(&self), mem::size_of::<T>());
- if self.kind() == Some(kind) {
- Ok(unsafe {
- let other = ptr::read(&self as *const _ as *const T);
- mem::forget(self);
- other
- })
- } else {
- Err(self)
- }
- }
-}
-
-/// This trait is useful to export cast_or_panic into crate but not outside
-pub trait CastOrPanic {
- fn cast_or_panic<T>(self, kind: ObjectType) -> T;
-}
-
-impl<'repo> CastOrPanic for Object<'repo> {
- fn cast_or_panic<T>(self, kind: ObjectType) -> T {
- assert_eq!(mem::size_of_val(&self), mem::size_of::<T>());
- if self.kind() == Some(kind) {
- unsafe {
- let other = ptr::read(&self as *const _ as *const T);
- mem::forget(self);
- other
- }
- } else {
- let buf;
- let akind = match self.kind() {
- Some(akind) => akind.str(),
- None => {
- buf = format!("unknown ({})", unsafe { raw::git_object_type(&*self.raw) });
- &buf
- }
- };
- panic!(
- "Expected object {} to be {} but it is {}",
- self.id(),
- kind.str(),
- akind
- )
- }
- }
-}
-
-impl<'repo> Clone for Object<'repo> {
- fn clone(&self) -> Object<'repo> {
- let mut raw = ptr::null_mut();
- unsafe {
- let rc = raw::git_object_dup(&mut raw, self.raw);
- assert_eq!(rc, 0);
- Binding::from_raw(raw)
- }
- }
-}
-
-impl<'repo> std::fmt::Debug for Object<'repo> {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
- let mut ds = f.debug_struct("Object");
- match self.kind() {
- Some(kind) => ds.field("kind", &kind),
- None => ds.field(
- "kind",
- &format!("Unknow ({})", unsafe { raw::git_object_type(&*self.raw) }),
- ),
- };
- ds.field("id", &self.id());
- ds.finish()
- }
-}
-
-impl<'repo> Binding for Object<'repo> {
- type Raw = *mut raw::git_object;
-
- unsafe fn from_raw(raw: *mut raw::git_object) -> Object<'repo> {
- Object {
- raw,
- _marker: marker::PhantomData,
- }
- }
- fn raw(&self) -> *mut raw::git_object {
- self.raw
- }
-}
-
-impl<'repo> Drop for Object<'repo> {
- fn drop(&mut self) {
- unsafe { raw::git_object_free(self.raw) }
- }
-}
diff --git a/extra/git2/src/odb.rs b/extra/git2/src/odb.rs
deleted file mode 100644
index 7f6da5eb3..000000000
--- a/extra/git2/src/odb.rs
+++ /dev/null
@@ -1,729 +0,0 @@
-use std::io;
-use std::marker;
-use std::ptr;
-use std::slice;
-
-use std::ffi::CString;
-
-use libc::{c_char, c_int, c_uint, c_void, size_t};
-
-use crate::panic;
-use crate::util::Binding;
-use crate::{
- raw, Error, IndexerProgress, Mempack, Object, ObjectType, OdbLookupFlags, Oid, Progress,
-};
-
-/// A structure to represent a git object database
-pub struct Odb<'repo> {
- raw: *mut raw::git_odb,
- _marker: marker::PhantomData<Object<'repo>>,
-}
-
-// `git_odb` uses locking and atomics internally.
-unsafe impl<'repo> Send for Odb<'repo> {}
-unsafe impl<'repo> Sync for Odb<'repo> {}
-
-impl<'repo> Binding for Odb<'repo> {
- type Raw = *mut raw::git_odb;
-
- unsafe fn from_raw(raw: *mut raw::git_odb) -> Odb<'repo> {
- Odb {
- raw,
- _marker: marker::PhantomData,
- }
- }
- fn raw(&self) -> *mut raw::git_odb {
- self.raw
- }
-}
-
-impl<'repo> Drop for Odb<'repo> {
- fn drop(&mut self) {
- unsafe { raw::git_odb_free(self.raw) }
- }
-}
-
-impl<'repo> Odb<'repo> {
- /// Creates an object database without any backends.
- pub fn new<'a>() -> Result<Odb<'a>, Error> {
- crate::init();
- unsafe {
- let mut out = ptr::null_mut();
- try_call!(raw::git_odb_new(&mut out));
- Ok(Odb::from_raw(out))
- }
- }
-
- /// Create object database reading stream.
- ///
- /// Note that most backends do not support streaming reads because they store their objects as compressed/delta'ed blobs.
- /// If the backend does not support streaming reads, use the `read` method instead.
- pub fn reader(&self, oid: Oid) -> Result<(OdbReader<'_>, usize, ObjectType), Error> {
- let mut out = ptr::null_mut();
- let mut size = 0usize;
- let mut otype: raw::git_object_t = ObjectType::Any.raw();
- unsafe {
- try_call!(raw::git_odb_open_rstream(
- &mut out,
- &mut size,
- &mut otype,
- self.raw,
- oid.raw()
- ));
- Ok((
- OdbReader::from_raw(out),
- size,
- ObjectType::from_raw(otype).unwrap(),
- ))
- }
- }
-
- /// Create object database writing stream.
- ///
- /// The type and final length of the object must be specified when opening the stream.
- /// If the backend does not support streaming writes, use the `write` method instead.
- pub fn writer(&self, size: usize, obj_type: ObjectType) -> Result<OdbWriter<'_>, Error> {
- let mut out = ptr::null_mut();
- unsafe {
- try_call!(raw::git_odb_open_wstream(
- &mut out,
- self.raw,
- size as raw::git_object_size_t,
- obj_type.raw()
- ));
- Ok(OdbWriter::from_raw(out))
- }
- }
-
- /// Iterate over all objects in the object database.s
- pub fn foreach<C>(&self, mut callback: C) -> Result<(), Error>
- where
- C: FnMut(&Oid) -> bool,
- {
- unsafe {
- let mut data = ForeachCbData {
- callback: &mut callback,
- };
- let cb: raw::git_odb_foreach_cb = Some(foreach_cb);
- try_call!(raw::git_odb_foreach(
- self.raw(),
- cb,
- &mut data as *mut _ as *mut _
- ));
- Ok(())
- }
- }
-
- /// Read an object from the database.
- pub fn read(&self, oid: Oid) -> Result<OdbObject<'_>, Error> {
- let mut out = ptr::null_mut();
- unsafe {
- try_call!(raw::git_odb_read(&mut out, self.raw, oid.raw()));
- Ok(OdbObject::from_raw(out))
- }
- }
-
- /// Reads the header of an object from the database
- /// without reading the full content.
- pub fn read_header(&self, oid: Oid) -> Result<(usize, ObjectType), Error> {
- let mut size: usize = 0;
- let mut kind_id: i32 = ObjectType::Any.raw();
-
- unsafe {
- try_call!(raw::git_odb_read_header(
- &mut size as *mut size_t,
- &mut kind_id as *mut raw::git_object_t,
- self.raw,
- oid.raw()
- ));
-
- Ok((size, ObjectType::from_raw(kind_id).unwrap()))
- }
- }
-
- /// Write an object to the database.
- pub fn write(&self, kind: ObjectType, data: &[u8]) -> Result<Oid, Error> {
- unsafe {
- let mut out = raw::git_oid {
- id: [0; raw::GIT_OID_RAWSZ],
- };
- try_call!(raw::git_odb_write(
- &mut out,
- self.raw,
- data.as_ptr() as *const c_void,
- data.len(),
- kind.raw()
- ));
- Ok(Oid::from_raw(&mut out))
- }
- }
-
- /// Create stream for writing a pack file to the ODB
- pub fn packwriter(&self) -> Result<OdbPackwriter<'_>, Error> {
- let mut out = ptr::null_mut();
- let progress_cb: raw::git_indexer_progress_cb = Some(write_pack_progress_cb);
- let progress_payload = Box::new(OdbPackwriterCb { cb: None });
- let progress_payload_ptr = Box::into_raw(progress_payload);
-
- unsafe {
- try_call!(raw::git_odb_write_pack(
- &mut out,
- self.raw,
- progress_cb,
- progress_payload_ptr as *mut c_void
- ));
- }
-
- Ok(OdbPackwriter {
- raw: out,
- progress: Default::default(),
- progress_payload_ptr,
- })
- }
-
- /// Checks if the object database has an object.
- pub fn exists(&self, oid: Oid) -> bool {
- unsafe { raw::git_odb_exists(self.raw, oid.raw()) != 0 }
- }
-
- /// Checks if the object database has an object, with extended flags.
- pub fn exists_ext(&self, oid: Oid, flags: OdbLookupFlags) -> bool {
- unsafe { raw::git_odb_exists_ext(self.raw, oid.raw(), flags.bits() as c_uint) != 0 }
- }
-
- /// Potentially finds an object that starts with the given prefix.
- pub fn exists_prefix(&self, short_oid: Oid, len: usize) -> Result<Oid, Error> {
- unsafe {
- let mut out = raw::git_oid {
- id: [0; raw::GIT_OID_RAWSZ],
- };
- try_call!(raw::git_odb_exists_prefix(
- &mut out,
- self.raw,
- short_oid.raw(),
- len
- ));
- Ok(Oid::from_raw(&out))
- }
- }
-
- /// Refresh the object database.
- /// This should never be needed, and is
- /// provided purely for convenience.
- /// The object database will automatically
- /// refresh when an object is not found when
- /// requested.
- pub fn refresh(&self) -> Result<(), Error> {
- unsafe {
- try_call!(raw::git_odb_refresh(self.raw));
- Ok(())
- }
- }
-
- /// Adds an alternate disk backend to the object database.
- pub fn add_disk_alternate(&self, path: &str) -> Result<(), Error> {
- unsafe {
- let path = CString::new(path)?;
- try_call!(raw::git_odb_add_disk_alternate(self.raw, path));
- Ok(())
- }
- }
-
- /// Create a new mempack backend, and add it to this odb with the given
- /// priority. Higher values give the backend higher precedence. The default
- /// loose and pack backends have priorities 1 and 2 respectively (hard-coded
- /// in libgit2). A reference to the new mempack backend is returned on
- /// success. The lifetime of the backend must be contained within the
- /// lifetime of this odb, since deletion of the odb will also result in
- /// deletion of the mempack backend.
- ///
- /// Here is an example that fails to compile because it tries to hold the
- /// mempack reference beyond the Odb's lifetime:
- ///
- /// ```compile_fail
- /// use git2::Odb;
- /// let mempack = {
- /// let odb = Odb::new().unwrap();
- /// odb.add_new_mempack_backend(1000).unwrap()
- /// };
- /// ```
- pub fn add_new_mempack_backend<'odb>(
- &'odb self,
- priority: i32,
- ) -> Result<Mempack<'odb>, Error> {
- unsafe {
- let mut mempack = ptr::null_mut();
- // The mempack backend object in libgit2 is only ever freed by an
- // odb that has the backend in its list. So to avoid potentially
- // leaking the mempack backend, this API ensures that the backend
- // is added to the odb before returning it. The lifetime of the
- // mempack is also bound to the lifetime of the odb, so that users
- // can't end up with a dangling reference to a mempack object that
- // was actually freed when the odb was destroyed.
- try_call!(raw::git_mempack_new(&mut mempack));
- try_call!(raw::git_odb_add_backend(
- self.raw,
- mempack,
- priority as c_int
- ));
- Ok(Mempack::from_raw(mempack))
- }
- }
-}
-
-/// An object from the Object Database.
-pub struct OdbObject<'a> {
- raw: *mut raw::git_odb_object,
- _marker: marker::PhantomData<Object<'a>>,
-}
-
-impl<'a> Binding for OdbObject<'a> {
- type Raw = *mut raw::git_odb_object;
-
- unsafe fn from_raw(raw: *mut raw::git_odb_object) -> OdbObject<'a> {
- OdbObject {
- raw,
- _marker: marker::PhantomData,
- }
- }
-
- fn raw(&self) -> *mut raw::git_odb_object {
- self.raw
- }
-}
-
-impl<'a> Drop for OdbObject<'a> {
- fn drop(&mut self) {
- unsafe { raw::git_odb_object_free(self.raw) }
- }
-}
-
-impl<'a> OdbObject<'a> {
- /// Get the object type.
- pub fn kind(&self) -> ObjectType {
- unsafe { ObjectType::from_raw(raw::git_odb_object_type(self.raw)).unwrap() }
- }
-
- /// Get the object size.
- pub fn len(&self) -> usize {
- unsafe { raw::git_odb_object_size(self.raw) }
- }
-
- /// Get the object data.
- pub fn data(&self) -> &[u8] {
- unsafe {
- let size = self.len();
- let ptr: *const u8 = raw::git_odb_object_data(self.raw) as *const u8;
- let buffer = slice::from_raw_parts(ptr, size);
- return buffer;
- }
- }
-
- /// Get the object id.
- pub fn id(&self) -> Oid {
- unsafe { Oid::from_raw(raw::git_odb_object_id(self.raw)) }
- }
-}
-
-/// A structure to represent a git ODB rstream
-pub struct OdbReader<'repo> {
- raw: *mut raw::git_odb_stream,
- _marker: marker::PhantomData<Object<'repo>>,
-}
-
-// `git_odb_stream` is not thread-safe internally, so it can't use `Sync`, but moving it to another
-// thread and continuing to read will work.
-unsafe impl<'repo> Send for OdbReader<'repo> {}
-
-impl<'repo> Binding for OdbReader<'repo> {
- type Raw = *mut raw::git_odb_stream;
-
- unsafe fn from_raw(raw: *mut raw::git_odb_stream) -> OdbReader<'repo> {
- OdbReader {
- raw,
- _marker: marker::PhantomData,
- }
- }
- fn raw(&self) -> *mut raw::git_odb_stream {
- self.raw
- }
-}
-
-impl<'repo> Drop for OdbReader<'repo> {
- fn drop(&mut self) {
- unsafe { raw::git_odb_stream_free(self.raw) }
- }
-}
-
-impl<'repo> io::Read for OdbReader<'repo> {
- fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
- unsafe {
- let ptr = buf.as_ptr() as *mut c_char;
- let len = buf.len();
- let res = raw::git_odb_stream_read(self.raw, ptr, len);
- if res < 0 {
- Err(io::Error::new(io::ErrorKind::Other, "Read error"))
- } else {
- Ok(len)
- }
- }
- }
-}
-
-/// A structure to represent a git ODB wstream
-pub struct OdbWriter<'repo> {
- raw: *mut raw::git_odb_stream,
- _marker: marker::PhantomData<Object<'repo>>,
-}
-
-// `git_odb_stream` is not thread-safe internally, so it can't use `Sync`, but moving it to another
-// thread and continuing to write will work.
-unsafe impl<'repo> Send for OdbWriter<'repo> {}
-
-impl<'repo> OdbWriter<'repo> {
- /// Finish writing to an ODB stream
- ///
- /// This method can be used to finalize writing object to the database and get an identifier.
- /// The object will take its final name and will be available to the odb.
- /// This method will fail if the total number of received bytes differs from the size declared with odb_writer()
- /// Attempting write after finishing will be ignored.
- pub fn finalize(&mut self) -> Result<Oid, Error> {
- let mut raw = raw::git_oid {
- id: [0; raw::GIT_OID_RAWSZ],
- };
- unsafe {
- try_call!(raw::git_odb_stream_finalize_write(&mut raw, self.raw));
- Ok(Binding::from_raw(&raw as *const _))
- }
- }
-}
-
-impl<'repo> Binding for OdbWriter<'repo> {
- type Raw = *mut raw::git_odb_stream;
-
- unsafe fn from_raw(raw: *mut raw::git_odb_stream) -> OdbWriter<'repo> {
- OdbWriter {
- raw,
- _marker: marker::PhantomData,
- }
- }
- fn raw(&self) -> *mut raw::git_odb_stream {
- self.raw
- }
-}
-
-impl<'repo> Drop for OdbWriter<'repo> {
- fn drop(&mut self) {
- unsafe { raw::git_odb_stream_free(self.raw) }
- }
-}
-
-impl<'repo> io::Write for OdbWriter<'repo> {
- fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
- unsafe {
- let ptr = buf.as_ptr() as *const c_char;
- let len = buf.len();
- let res = raw::git_odb_stream_write(self.raw, ptr, len);
- if res < 0 {
- Err(io::Error::new(io::ErrorKind::Other, "Write error"))
- } else {
- Ok(buf.len())
- }
- }
- }
- fn flush(&mut self) -> io::Result<()> {
- Ok(())
- }
-}
-
-pub(crate) struct OdbPackwriterCb<'repo> {
- pub(crate) cb: Option<Box<IndexerProgress<'repo>>>,
-}
-
-/// A stream to write a packfile to the ODB
-pub struct OdbPackwriter<'repo> {
- raw: *mut raw::git_odb_writepack,
- progress: raw::git_indexer_progress,
- progress_payload_ptr: *mut OdbPackwriterCb<'repo>,
-}
-
-impl<'repo> OdbPackwriter<'repo> {
- /// Finish writing the packfile
- pub fn commit(&mut self) -> Result<i32, Error> {
- unsafe {
- let writepack = &*self.raw;
- let res = match writepack.commit {
- Some(commit) => commit(self.raw, &mut self.progress),
- None => -1,
- };
-
- if res < 0 {
- Err(Error::last_error(res).unwrap())
- } else {
- Ok(res)
- }
- }
- }
-
- /// The callback through which progress is monitored. Be aware that this is
- /// called inline, so performance may be affected.
- pub fn progress<F>(&mut self, cb: F) -> &mut OdbPackwriter<'repo>
- where
- F: FnMut(Progress<'_>) -> bool + 'repo,
- {
- let progress_payload =
- unsafe { &mut *(self.progress_payload_ptr as *mut OdbPackwriterCb<'_>) };
-
- progress_payload.cb = Some(Box::new(cb) as Box<IndexerProgress<'repo>>);
- self
- }
-}
-
-impl<'repo> io::Write for OdbPackwriter<'repo> {
- fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
- unsafe {
- let ptr = buf.as_ptr() as *mut c_void;
- let len = buf.len();
-
- let writepack = &*self.raw;
- let res = match writepack.append {
- Some(append) => append(self.raw, ptr, len, &mut self.progress),
- None => -1,
- };
-
- if res < 0 {
- Err(io::Error::new(io::ErrorKind::Other, "Write error"))
- } else {
- Ok(buf.len())
- }
- }
- }
- fn flush(&mut self) -> io::Result<()> {
- Ok(())
- }
-}
-
-impl<'repo> Drop for OdbPackwriter<'repo> {
- fn drop(&mut self) {
- unsafe {
- let writepack = &*self.raw;
- match writepack.free {
- Some(free) => free(self.raw),
- None => (),
- };
-
- drop(Box::from_raw(self.progress_payload_ptr));
- }
- }
-}
-
-pub type ForeachCb<'a> = dyn FnMut(&Oid) -> bool + 'a;
-
-struct ForeachCbData<'a> {
- pub callback: &'a mut ForeachCb<'a>,
-}
-
-extern "C" fn foreach_cb(id: *const raw::git_oid, payload: *mut c_void) -> c_int {
- panic::wrap(|| unsafe {
- let data = &mut *(payload as *mut ForeachCbData<'_>);
- let res = {
- let callback = &mut data.callback;
- callback(&Binding::from_raw(id))
- };
-
- if res {
- 0
- } else {
- 1
- }
- })
- .unwrap_or(1)
-}
-
-pub(crate) extern "C" fn write_pack_progress_cb(
- stats: *const raw::git_indexer_progress,
- payload: *mut c_void,
-) -> c_int {
- let ok = panic::wrap(|| unsafe {
- let payload = &mut *(payload as *mut OdbPackwriterCb<'_>);
-
- let callback = match payload.cb {
- Some(ref mut cb) => cb,
- None => return true,
- };
-
- let progress: Progress<'_> = Binding::from_raw(stats);
- callback(progress)
- });
- if ok == Some(true) {
- 0
- } else {
- -1
- }
-}
-
-#[cfg(test)]
-mod tests {
- use crate::{Buf, ObjectType, Oid, Repository};
- use std::io::prelude::*;
- use tempfile::TempDir;
-
- #[test]
- fn read() {
- let td = TempDir::new().unwrap();
- let repo = Repository::init(td.path()).unwrap();
- let dat = [4, 3, 5, 6, 9];
- let id = repo.blob(&dat).unwrap();
- let db = repo.odb().unwrap();
- let obj = db.read(id).unwrap();
- let data = obj.data();
- let size = obj.len();
- assert_eq!(size, 5);
- assert_eq!(dat, data);
- assert_eq!(id, obj.id());
- }
-
- #[test]
- fn read_header() {
- let td = TempDir::new().unwrap();
- let repo = Repository::init(td.path()).unwrap();
- let dat = [4, 3, 5, 6, 9];
- let id = repo.blob(&dat).unwrap();
- let db = repo.odb().unwrap();
- let (size, kind) = db.read_header(id).unwrap();
-
- assert_eq!(size, 5);
- assert_eq!(kind, ObjectType::Blob);
- }
-
- #[test]
- fn write() {
- let td = TempDir::new().unwrap();
- let repo = Repository::init(td.path()).unwrap();
- let dat = [4, 3, 5, 6, 9];
- let db = repo.odb().unwrap();
- let id = db.write(ObjectType::Blob, &dat).unwrap();
- let blob = repo.find_blob(id).unwrap();
- assert_eq!(blob.content(), dat);
- }
-
- #[test]
- fn writer() {
- let td = TempDir::new().unwrap();
- let repo = Repository::init(td.path()).unwrap();
- let dat = [4, 3, 5, 6, 9];
- let db = repo.odb().unwrap();
- let mut ws = db.writer(dat.len(), ObjectType::Blob).unwrap();
- let wl = ws.write(&dat[0..3]).unwrap();
- assert_eq!(wl, 3);
- let wl = ws.write(&dat[3..5]).unwrap();
- assert_eq!(wl, 2);
- let id = ws.finalize().unwrap();
- let blob = repo.find_blob(id).unwrap();
- assert_eq!(blob.content(), dat);
- }
-
- #[test]
- fn exists() {
- let td = TempDir::new().unwrap();
- let repo = Repository::init(td.path()).unwrap();
- let dat = [4, 3, 5, 6, 9];
- let db = repo.odb().unwrap();
- let id = db.write(ObjectType::Blob, &dat).unwrap();
- assert!(db.exists(id));
- }
-
- #[test]
- fn exists_prefix() {
- let td = TempDir::new().unwrap();
- let repo = Repository::init(td.path()).unwrap();
- let dat = [4, 3, 5, 6, 9];
- let db = repo.odb().unwrap();
- let id = db.write(ObjectType::Blob, &dat).unwrap();
- let id_prefix_str = &id.to_string()[0..10];
- let id_prefix = Oid::from_str(id_prefix_str).unwrap();
- let found_oid = db.exists_prefix(id_prefix, 10).unwrap();
- assert_eq!(found_oid, id);
- }
-
- #[test]
- fn packwriter() {
- let (_td, repo_source) = crate::test::repo_init();
- let (_td, repo_target) = crate::test::repo_init();
- let mut builder = t!(repo_source.packbuilder());
- let mut buf = Buf::new();
- let (commit_source_id, _tree) = crate::test::commit(&repo_source);
- t!(builder.insert_object(commit_source_id, None));
- t!(builder.write_buf(&mut buf));
- let db = repo_target.odb().unwrap();
- let mut packwriter = db.packwriter().unwrap();
- packwriter.write(&buf).unwrap();
- packwriter.commit().unwrap();
- let commit_target = repo_target.find_commit(commit_source_id).unwrap();
- assert_eq!(commit_target.id(), commit_source_id);
- }
-
- #[test]
- fn packwriter_progress() {
- let mut progress_called = false;
- {
- let (_td, repo_source) = crate::test::repo_init();
- let (_td, repo_target) = crate::test::repo_init();
- let mut builder = t!(repo_source.packbuilder());
- let mut buf = Buf::new();
- let (commit_source_id, _tree) = crate::test::commit(&repo_source);
- t!(builder.insert_object(commit_source_id, None));
- t!(builder.write_buf(&mut buf));
- let db = repo_target.odb().unwrap();
- let mut packwriter = db.packwriter().unwrap();
- packwriter.progress(|_| {
- progress_called = true;
- true
- });
- packwriter.write(&buf).unwrap();
- packwriter.commit().unwrap();
- }
- assert_eq!(progress_called, true);
- }
-
- #[test]
- fn write_with_mempack() {
- use crate::{Buf, ResetType};
- use std::io::Write;
- use std::path::Path;
-
- // Create a repo, add a mempack backend
- let (_td, repo) = crate::test::repo_init();
- let odb = repo.odb().unwrap();
- let mempack = odb.add_new_mempack_backend(1000).unwrap();
-
- // Sanity check that foo doesn't exist initially
- let foo_file = Path::new(repo.workdir().unwrap()).join("foo");
- assert!(!foo_file.exists());
-
- // Make a commit that adds foo. This writes new stuff into the mempack
- // backend.
- let (oid1, _id) = crate::test::commit(&repo);
- let commit1 = repo.find_commit(oid1).unwrap();
- t!(repo.reset(commit1.as_object(), ResetType::Hard, None));
- assert!(foo_file.exists());
-
- // Dump the mempack modifications into a buf, and reset it. This "erases"
- // commit-related objects from the repository. Ensure the commit appears
- // to have become invalid, by checking for failure in `reset --hard`.
- let mut buf = Buf::new();
- mempack.dump(&repo, &mut buf).unwrap();
- mempack.reset().unwrap();
- assert!(repo
- .reset(commit1.as_object(), ResetType::Hard, None)
- .is_err());
-
- // Write the buf into a packfile in the repo. This brings back the
- // missing objects, and we verify everything is good again.
- let mut packwriter = odb.packwriter().unwrap();
- packwriter.write(&buf).unwrap();
- packwriter.commit().unwrap();
- t!(repo.reset(commit1.as_object(), ResetType::Hard, None));
- assert!(foo_file.exists());
- }
-}
diff --git a/extra/git2/src/oid.rs b/extra/git2/src/oid.rs
deleted file mode 100644
index 145458aec..000000000
--- a/extra/git2/src/oid.rs
+++ /dev/null
@@ -1,259 +0,0 @@
-use libc;
-use std::cmp::Ordering;
-use std::fmt;
-use std::hash::{Hash, Hasher};
-use std::path::Path;
-use std::str;
-
-use crate::{raw, Error, IntoCString, ObjectType};
-
-use crate::util::{c_cmp_to_ordering, Binding};
-
-/// Unique identity of any object (commit, tree, blob, tag).
-#[derive(Copy, Clone)]
-#[repr(C)]
-pub struct Oid {
- raw: raw::git_oid,
-}
-
-impl Oid {
- /// Parse a hex-formatted object id into an Oid structure.
- ///
- /// # Errors
- ///
- /// Returns an error if the string is empty, is longer than 40 hex
- /// characters, or contains any non-hex characters.
- pub fn from_str(s: &str) -> Result<Oid, Error> {
- crate::init();
- let mut raw = raw::git_oid {
- id: [0; raw::GIT_OID_RAWSZ],
- };
- unsafe {
- try_call!(raw::git_oid_fromstrn(
- &mut raw,
- s.as_bytes().as_ptr() as *const libc::c_char,
- s.len() as libc::size_t
- ));
- }
- Ok(Oid { raw })
- }
-
- /// Parse a raw object id into an Oid structure.
- ///
- /// If the array given is not 20 bytes in length, an error is returned.
- pub fn from_bytes(bytes: &[u8]) -> Result<Oid, Error> {
- crate::init();
- let mut raw = raw::git_oid {
- id: [0; raw::GIT_OID_RAWSZ],
- };
- if bytes.len() != raw::GIT_OID_RAWSZ {
- Err(Error::from_str("raw byte array must be 20 bytes"))
- } else {
- unsafe {
- try_call!(raw::git_oid_fromraw(&mut raw, bytes.as_ptr()));
- }
- Ok(Oid { raw })
- }
- }
-
- /// Creates an all zero Oid structure.
- pub fn zero() -> Oid {
- let out = raw::git_oid {
- id: [0; raw::GIT_OID_RAWSZ],
- };
- Oid { raw: out }
- }
-
- /// Hashes the provided data as an object of the provided type, and returns
- /// an Oid corresponding to the result. This does not store the object
- /// inside any object database or repository.
- pub fn hash_object(kind: ObjectType, bytes: &[u8]) -> Result<Oid, Error> {
- crate::init();
-
- let mut out = raw::git_oid {
- id: [0; raw::GIT_OID_RAWSZ],
- };
- unsafe {
- try_call!(raw::git_odb_hash(
- &mut out,
- bytes.as_ptr() as *const libc::c_void,
- bytes.len(),
- kind.raw()
- ));
- }
-
- Ok(Oid { raw: out })
- }
-
- /// Hashes the content of the provided file as an object of the provided type,
- /// and returns an Oid corresponding to the result. This does not store the object
- /// inside any object database or repository.
- pub fn hash_file<P: AsRef<Path>>(kind: ObjectType, path: P) -> Result<Oid, Error> {
- crate::init();
-
- // Normal file path OK (does not need Windows conversion).
- let rpath = path.as_ref().into_c_string()?;
-
- let mut out = raw::git_oid {
- id: [0; raw::GIT_OID_RAWSZ],
- };
- unsafe {
- try_call!(raw::git_odb_hashfile(&mut out, rpath, kind.raw()));
- }
-
- Ok(Oid { raw: out })
- }
-
- /// View this OID as a byte-slice 20 bytes in length.
- pub fn as_bytes(&self) -> &[u8] {
- &self.raw.id
- }
-
- /// Test if this OID is all zeros.
- pub fn is_zero(&self) -> bool {
- unsafe { raw::git_oid_iszero(&self.raw) == 1 }
- }
-}
-
-impl Binding for Oid {
- type Raw = *const raw::git_oid;
-
- unsafe fn from_raw(oid: *const raw::git_oid) -> Oid {
- Oid { raw: *oid }
- }
- fn raw(&self) -> *const raw::git_oid {
- &self.raw as *const _
- }
-}
-
-impl fmt::Debug for Oid {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- fmt::Display::fmt(self, f)
- }
-}
-
-impl fmt::Display for Oid {
- /// Hex-encode this Oid into a formatter.
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- let mut dst = [0u8; raw::GIT_OID_HEXSZ + 1];
- unsafe {
- raw::git_oid_tostr(
- dst.as_mut_ptr() as *mut libc::c_char,
- dst.len() as libc::size_t,
- &self.raw,
- );
- }
- let s = &dst[..dst.iter().position(|&a| a == 0).unwrap()];
- str::from_utf8(s).unwrap().fmt(f)
- }
-}
-
-impl str::FromStr for Oid {
- type Err = Error;
-
- /// Parse a hex-formatted object id into an Oid structure.
- ///
- /// # Errors
- ///
- /// Returns an error if the string is empty, is longer than 40 hex
- /// characters, or contains any non-hex characters.
- fn from_str(s: &str) -> Result<Oid, Error> {
- Oid::from_str(s)
- }
-}
-
-impl PartialEq for Oid {
- fn eq(&self, other: &Oid) -> bool {
- unsafe { raw::git_oid_equal(&self.raw, &other.raw) != 0 }
- }
-}
-impl Eq for Oid {}
-
-impl PartialOrd for Oid {
- fn partial_cmp(&self, other: &Oid) -> Option<Ordering> {
- Some(self.cmp(other))
- }
-}
-
-impl Ord for Oid {
- fn cmp(&self, other: &Oid) -> Ordering {
- c_cmp_to_ordering(unsafe { raw::git_oid_cmp(&self.raw, &other.raw) })
- }
-}
-
-impl Hash for Oid {
- fn hash<H: Hasher>(&self, into: &mut H) {
- self.raw.id.hash(into)
- }
-}
-
-impl AsRef<[u8]> for Oid {
- fn as_ref(&self) -> &[u8] {
- self.as_bytes()
- }
-}
-
-#[cfg(test)]
-mod tests {
- use std::fs::File;
- use std::io::prelude::*;
-
- use super::Error;
- use super::Oid;
- use crate::ObjectType;
- use tempfile::TempDir;
-
- #[test]
- fn conversions() {
- assert!(Oid::from_str("foo").is_err());
- assert!(Oid::from_str("decbf2be529ab6557d5429922251e5ee36519817").is_ok());
- assert!(Oid::from_bytes(b"foo").is_err());
- assert!(Oid::from_bytes(b"00000000000000000000").is_ok());
- }
-
- #[test]
- fn comparisons() -> Result<(), Error> {
- assert_eq!(Oid::from_str("decbf2b")?, Oid::from_str("decbf2b")?);
- assert!(Oid::from_str("decbf2b")? <= Oid::from_str("decbf2b")?);
- assert!(Oid::from_str("decbf2b")? >= Oid::from_str("decbf2b")?);
- {
- let o = Oid::from_str("decbf2b")?;
- assert_eq!(o, o);
- assert!(o <= o);
- assert!(o >= o);
- }
- assert_eq!(
- Oid::from_str("decbf2b")?,
- Oid::from_str("decbf2b000000000000000000000000000000000")?
- );
- assert!(
- Oid::from_bytes(b"00000000000000000000")? < Oid::from_bytes(b"00000000000000000001")?
- );
- assert!(Oid::from_bytes(b"00000000000000000000")? < Oid::from_str("decbf2b")?);
- assert_eq!(
- Oid::from_bytes(b"00000000000000000000")?,
- Oid::from_str("3030303030303030303030303030303030303030")?
- );
- Ok(())
- }
-
- #[test]
- fn zero_is_zero() {
- assert!(Oid::zero().is_zero());
- }
-
- #[test]
- fn hash_object() {
- let bytes = "Hello".as_bytes();
- assert!(Oid::hash_object(ObjectType::Blob, bytes).is_ok());
- }
-
- #[test]
- fn hash_file() {
- let td = TempDir::new().unwrap();
- let path = td.path().join("hello.txt");
- let mut file = File::create(&path).unwrap();
- file.write_all("Hello".as_bytes()).unwrap();
- assert!(Oid::hash_file(ObjectType::Blob, &path).is_ok());
- }
-}
diff --git a/extra/git2/src/oid_array.rs b/extra/git2/src/oid_array.rs
deleted file mode 100644
index 0d87ce995..000000000
--- a/extra/git2/src/oid_array.rs
+++ /dev/null
@@ -1,52 +0,0 @@
-//! Bindings to libgit2's raw `git_oidarray` type
-
-use std::ops::Deref;
-
-use crate::oid::Oid;
-use crate::raw;
-use crate::util::Binding;
-use std::mem;
-use std::slice;
-
-/// An oid array structure used by libgit2
-///
-/// Some APIs return arrays of OIDs which originate from libgit2. This
-/// wrapper type behaves a little like `Vec<&Oid>` but does so without copying
-/// the underlying Oids until necessary.
-pub struct OidArray {
- raw: raw::git_oidarray,
-}
-
-impl Deref for OidArray {
- type Target = [Oid];
-
- fn deref(&self) -> &[Oid] {
- unsafe {
- debug_assert_eq!(mem::size_of::<Oid>(), mem::size_of_val(&*self.raw.ids));
-
- slice::from_raw_parts(self.raw.ids as *const Oid, self.raw.count as usize)
- }
- }
-}
-
-impl Binding for OidArray {
- type Raw = raw::git_oidarray;
- unsafe fn from_raw(raw: raw::git_oidarray) -> OidArray {
- OidArray { raw }
- }
- fn raw(&self) -> raw::git_oidarray {
- self.raw
- }
-}
-
-impl<'repo> std::fmt::Debug for OidArray {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
- f.debug_tuple("OidArray").field(&self.deref()).finish()
- }
-}
-
-impl Drop for OidArray {
- fn drop(&mut self) {
- unsafe { raw::git_oidarray_free(&mut self.raw) }
- }
-}
diff --git a/extra/git2/src/opts.rs b/extra/git2/src/opts.rs
deleted file mode 100644
index e90bea0b1..000000000
--- a/extra/git2/src/opts.rs
+++ /dev/null
@@ -1,206 +0,0 @@
-//! Bindings to libgit2's git_libgit2_opts function.
-
-use std::ffi::CString;
-use std::ptr;
-
-use crate::string_array::StringArray;
-use crate::util::Binding;
-use crate::{raw, Buf, ConfigLevel, Error, IntoCString};
-
-/// Set the search path for a level of config data. The search path applied to
-/// shared attributes and ignore files, too.
-///
-/// `level` must be one of [`ConfigLevel::System`], [`ConfigLevel::Global`],
-/// [`ConfigLevel::XDG`], [`ConfigLevel::ProgramData`].
-///
-/// `path` lists directories delimited by `GIT_PATH_LIST_SEPARATOR`.
-/// Use magic path `$PATH` to include the old value of the path
-/// (if you want to prepend or append, for instance).
-///
-/// This function is unsafe as it mutates the global state but cannot guarantee
-/// thread-safety. It needs to be externally synchronized with calls to access
-/// the global state.
-pub unsafe fn set_search_path<P>(level: ConfigLevel, path: P) -> Result<(), Error>
-where
- P: IntoCString,
-{
- crate::init();
- try_call!(raw::git_libgit2_opts(
- raw::GIT_OPT_SET_SEARCH_PATH as libc::c_int,
- level as libc::c_int,
- path.into_c_string()?.as_ptr()
- ));
- Ok(())
-}
-
-/// Reset the search path for a given level of config data to the default
-/// (generally based on environment variables).
-///
-/// `level` must be one of [`ConfigLevel::System`], [`ConfigLevel::Global`],
-/// [`ConfigLevel::XDG`], [`ConfigLevel::ProgramData`].
-///
-/// This function is unsafe as it mutates the global state but cannot guarantee
-/// thread-safety. It needs to be externally synchronized with calls to access
-/// the global state.
-pub unsafe fn reset_search_path(level: ConfigLevel) -> Result<(), Error> {
- crate::init();
- try_call!(raw::git_libgit2_opts(
- raw::GIT_OPT_SET_SEARCH_PATH as libc::c_int,
- level as libc::c_int,
- core::ptr::null::<u8>()
- ));
- Ok(())
-}
-
-/// Get the search path for a given level of config data.
-///
-/// `level` must be one of [`ConfigLevel::System`], [`ConfigLevel::Global`],
-/// [`ConfigLevel::XDG`], [`ConfigLevel::ProgramData`].
-///
-/// This function is unsafe as it mutates the global state but cannot guarantee
-/// thread-safety. It needs to be externally synchronized with calls to access
-/// the global state.
-pub unsafe fn get_search_path(level: ConfigLevel) -> Result<CString, Error> {
- crate::init();
- let buf = Buf::new();
- try_call!(raw::git_libgit2_opts(
- raw::GIT_OPT_GET_SEARCH_PATH as libc::c_int,
- level as libc::c_int,
- buf.raw() as *const _
- ));
- buf.into_c_string()
-}
-
-/// Controls whether or not libgit2 will cache loaded objects. Enabled by
-/// default, but disabling this can improve performance and memory usage if
-/// loading a large number of objects that will not be referenced again.
-/// Disabling this will cause repository objects to clear their caches when next
-/// accessed.
-pub fn enable_caching(enabled: bool) {
- crate::init();
- let error = unsafe {
- raw::git_libgit2_opts(
- raw::GIT_OPT_ENABLE_CACHING as libc::c_int,
- enabled as libc::c_int,
- )
- };
- // This function cannot actually fail, but the function has an error return
- // for other options that can.
- debug_assert!(error >= 0);
-}
-
-/// Controls whether or not libgit2 will verify when writing an object that all
-/// objects it references are valid. Enabled by default, but disabling this can
-/// significantly improve performance, at the cost of potentially allowing the
-/// creation of objects that reference invalid objects (due to programming
-/// error or repository corruption).
-pub fn strict_object_creation(enabled: bool) {
- crate::init();
- let error = unsafe {
- raw::git_libgit2_opts(
- raw::GIT_OPT_ENABLE_STRICT_OBJECT_CREATION as libc::c_int,
- enabled as libc::c_int,
- )
- };
- // This function cannot actually fail, but the function has an error return
- // for other options that can.
- debug_assert!(error >= 0);
-}
-
-/// Controls whether or not libgit2 will verify that objects loaded have the
-/// expected hash. Enabled by default, but disabling this can significantly
-/// improve performance, at the cost of relying on repository integrity
-/// without checking it.
-pub fn strict_hash_verification(enabled: bool) {
- crate::init();
- let error = unsafe {
- raw::git_libgit2_opts(
- raw::GIT_OPT_ENABLE_STRICT_HASH_VERIFICATION as libc::c_int,
- enabled as libc::c_int,
- )
- };
- // This function cannot actually fail, but the function has an error return
- // for other options that can.
- debug_assert!(error >= 0);
-}
-
-/// Returns the list of git extensions that are supported. This is the list of
-/// built-in extensions supported by libgit2 and custom extensions that have
-/// been added with [`set_extensions`]. Extensions that have been negated will
-/// not be returned.
-///
-/// # Safety
-///
-/// libgit2 stores user extensions in a static variable.
-/// This function is effectively reading a `static mut` and should be treated as such
-pub unsafe fn get_extensions() -> Result<StringArray, Error> {
- crate::init();
-
- let mut extensions = raw::git_strarray {
- strings: ptr::null_mut(),
- count: 0,
- };
-
- try_call!(raw::git_libgit2_opts(
- raw::GIT_OPT_GET_EXTENSIONS as libc::c_int,
- &mut extensions
- ));
-
- Ok(StringArray::from_raw(extensions))
-}
-
-/// Set that the given git extensions are supported by the caller. Extensions
-/// supported by libgit2 may be negated by prefixing them with a `!`.
-/// For example: setting extensions to `[ "!noop", "newext" ]` indicates that
-/// the caller does not want to support repositories with the `noop` extension
-/// but does want to support repositories with the `newext` extension.
-///
-/// # Safety
-///
-/// libgit2 stores user extensions in a static variable.
-/// This function is effectively modifying a `static mut` and should be treated as such
-pub unsafe fn set_extensions<E>(extensions: &[E]) -> Result<(), Error>
-where
- for<'x> &'x E: IntoCString,
-{
- crate::init();
-
- let extensions = extensions
- .iter()
- .map(|e| e.into_c_string())
- .collect::<Result<Vec<_>, _>>()?;
-
- let extension_ptrs = extensions.iter().map(|e| e.as_ptr()).collect::<Vec<_>>();
-
- try_call!(raw::git_libgit2_opts(
- raw::GIT_OPT_SET_EXTENSIONS as libc::c_int,
- extension_ptrs.as_ptr(),
- extension_ptrs.len() as libc::size_t
- ));
-
- Ok(())
-}
-
-/// Set whether or not to verify ownership before performing a repository.
-/// Enabled by default, but disabling this can lead to code execution vulnerabilities.
-pub unsafe fn set_verify_owner_validation(enabled: bool) -> Result<(), Error> {
- crate::init();
- let error = raw::git_libgit2_opts(
- raw::GIT_OPT_SET_OWNER_VALIDATION as libc::c_int,
- enabled as libc::c_int,
- );
- // This function cannot actually fail, but the function has an error return
- // for other options that can.
- debug_assert!(error >= 0);
- Ok(())
-}
-
-#[cfg(test)]
-mod test {
- use super::*;
-
- #[test]
- fn smoke() {
- strict_hash_verification(false);
- }
-}
diff --git a/extra/git2/src/packbuilder.rs b/extra/git2/src/packbuilder.rs
deleted file mode 100644
index 9b93e7654..000000000
--- a/extra/git2/src/packbuilder.rs
+++ /dev/null
@@ -1,413 +0,0 @@
-use libc::{c_int, c_uint, c_void, size_t};
-use std::marker;
-use std::ptr;
-use std::slice;
-use std::str;
-
-use crate::util::Binding;
-use crate::{panic, raw, Buf, Error, Oid, Repository, Revwalk};
-
-#[derive(PartialEq, Eq, Clone, Debug, Copy)]
-/// Stages that are reported by the `PackBuilder` progress callback.
-pub enum PackBuilderStage {
- /// Adding objects to the pack
- AddingObjects,
- /// Deltafication of the pack
- Deltafication,
-}
-
-pub type ProgressCb<'a> = dyn FnMut(PackBuilderStage, u32, u32) -> bool + 'a;
-pub type ForEachCb<'a> = dyn FnMut(&[u8]) -> bool + 'a;
-
-/// A builder for creating a packfile
-pub struct PackBuilder<'repo> {
- raw: *mut raw::git_packbuilder,
- _progress: Option<Box<Box<ProgressCb<'repo>>>>,
- _marker: marker::PhantomData<&'repo Repository>,
-}
-
-impl<'repo> PackBuilder<'repo> {
- /// Insert a single object. For an optimal pack it's mandatory to insert
- /// objects in recency order, commits followed by trees and blobs.
- pub fn insert_object(&mut self, id: Oid, name: Option<&str>) -> Result<(), Error> {
- let name = crate::opt_cstr(name)?;
- unsafe {
- try_call!(raw::git_packbuilder_insert(self.raw, id.raw(), name));
- }
- Ok(())
- }
-
- /// Insert a root tree object. This will add the tree as well as all
- /// referenced trees and blobs.
- pub fn insert_tree(&mut self, id: Oid) -> Result<(), Error> {
- unsafe {
- try_call!(raw::git_packbuilder_insert_tree(self.raw, id.raw()));
- }
- Ok(())
- }
-
- /// Insert a commit object. This will add a commit as well as the completed
- /// referenced tree.
- pub fn insert_commit(&mut self, id: Oid) -> Result<(), Error> {
- unsafe {
- try_call!(raw::git_packbuilder_insert_commit(self.raw, id.raw()));
- }
- Ok(())
- }
-
- /// Insert objects as given by the walk. Those commits and all objects they
- /// reference will be inserted into the packbuilder.
- pub fn insert_walk(&mut self, walk: &mut Revwalk<'_>) -> Result<(), Error> {
- unsafe {
- try_call!(raw::git_packbuilder_insert_walk(self.raw, walk.raw()));
- }
- Ok(())
- }
-
- /// Recursively insert an object and its referenced objects. Insert the
- /// object as well as any object it references.
- pub fn insert_recursive(&mut self, id: Oid, name: Option<&str>) -> Result<(), Error> {
- let name = crate::opt_cstr(name)?;
- unsafe {
- try_call!(raw::git_packbuilder_insert_recur(self.raw, id.raw(), name));
- }
- Ok(())
- }
-
- /// Write the contents of the packfile to an in-memory buffer. The contents
- /// of the buffer will become a valid packfile, even though there will be
- /// no attached index.
- pub fn write_buf(&mut self, buf: &mut Buf) -> Result<(), Error> {
- unsafe {
- try_call!(raw::git_packbuilder_write_buf(buf.raw(), self.raw));
- }
- Ok(())
- }
-
- /// Create the new pack and pass each object to the callback.
- pub fn foreach<F>(&mut self, mut cb: F) -> Result<(), Error>
- where
- F: FnMut(&[u8]) -> bool,
- {
- let mut cb = &mut cb as &mut ForEachCb<'_>;
- let ptr = &mut cb as *mut _;
- let foreach: raw::git_packbuilder_foreach_cb = Some(foreach_c);
- unsafe {
- try_call!(raw::git_packbuilder_foreach(
- self.raw,
- foreach,
- ptr as *mut _
- ));
- }
- Ok(())
- }
-
- /// `progress` will be called with progress information during pack
- /// building. Be aware that this is called inline with pack building
- /// operations, so performance may be affected.
- ///
- /// There can only be one progress callback attached, this will replace any
- /// existing one. See `unset_progress_callback` to remove the current
- /// progress callback without attaching a new one.
- pub fn set_progress_callback<F>(&mut self, progress: F) -> Result<(), Error>
- where
- F: FnMut(PackBuilderStage, u32, u32) -> bool + 'repo,
- {
- let mut progress = Box::new(Box::new(progress) as Box<ProgressCb<'_>>);
- let ptr = &mut *progress as *mut _;
- let progress_c: raw::git_packbuilder_progress = Some(progress_c);
- unsafe {
- try_call!(raw::git_packbuilder_set_callbacks(
- self.raw,
- progress_c,
- ptr as *mut _
- ));
- }
- self._progress = Some(progress);
- Ok(())
- }
-
- /// Remove the current progress callback. See `set_progress_callback` to
- /// set the progress callback.
- pub fn unset_progress_callback(&mut self) -> Result<(), Error> {
- unsafe {
- try_call!(raw::git_packbuilder_set_callbacks(
- self.raw,
- None,
- ptr::null_mut()
- ));
- self._progress = None;
- }
- Ok(())
- }
-
- /// Set the number of threads to be used.
- ///
- /// Returns the number of threads to be used.
- pub fn set_threads(&mut self, threads: u32) -> u32 {
- unsafe { raw::git_packbuilder_set_threads(self.raw, threads) }
- }
-
- /// Get the total number of objects the packbuilder will write out.
- pub fn object_count(&self) -> usize {
- unsafe { raw::git_packbuilder_object_count(self.raw) }
- }
-
- /// Get the number of objects the packbuilder has already written out.
- pub fn written(&self) -> usize {
- unsafe { raw::git_packbuilder_written(self.raw) }
- }
-
- /// Get the packfile's hash. A packfile's name is derived from the sorted
- /// hashing of all object names. This is only correct after the packfile
- /// has been written.
- #[deprecated = "use `name()` to retrieve the filename"]
- #[allow(deprecated)]
- pub fn hash(&self) -> Option<Oid> {
- if self.object_count() == 0 {
- unsafe { Some(Binding::from_raw(raw::git_packbuilder_hash(self.raw))) }
- } else {
- None
- }
- }
-
- /// Get the unique name for the resulting packfile.
- ///
- /// The packfile's name is derived from the packfile's content. This is only
- /// correct after the packfile has been written.
- ///
- /// Returns `None` if the packfile has not been written or if the name is
- /// not valid utf-8.
- pub fn name(&self) -> Option<&str> {
- self.name_bytes().and_then(|s| str::from_utf8(s).ok())
- }
-
- /// Get the unique name for the resulting packfile, in bytes.
- ///
- /// The packfile's name is derived from the packfile's content. This is only
- /// correct after the packfile has been written.
- pub fn name_bytes(&self) -> Option<&[u8]> {
- unsafe { crate::opt_bytes(self, raw::git_packbuilder_name(self.raw)) }
- }
-}
-
-impl<'repo> Binding for PackBuilder<'repo> {
- type Raw = *mut raw::git_packbuilder;
- unsafe fn from_raw(ptr: *mut raw::git_packbuilder) -> PackBuilder<'repo> {
- PackBuilder {
- raw: ptr,
- _progress: None,
- _marker: marker::PhantomData,
- }
- }
- fn raw(&self) -> *mut raw::git_packbuilder {
- self.raw
- }
-}
-
-impl<'repo> Drop for PackBuilder<'repo> {
- fn drop(&mut self) {
- unsafe {
- raw::git_packbuilder_set_callbacks(self.raw, None, ptr::null_mut());
- raw::git_packbuilder_free(self.raw);
- }
- }
-}
-
-impl Binding for PackBuilderStage {
- type Raw = raw::git_packbuilder_stage_t;
- unsafe fn from_raw(raw: raw::git_packbuilder_stage_t) -> PackBuilderStage {
- match raw {
- raw::GIT_PACKBUILDER_ADDING_OBJECTS => PackBuilderStage::AddingObjects,
- raw::GIT_PACKBUILDER_DELTAFICATION => PackBuilderStage::Deltafication,
- _ => panic!("Unknown git diff binary kind"),
- }
- }
- fn raw(&self) -> raw::git_packbuilder_stage_t {
- match *self {
- PackBuilderStage::AddingObjects => raw::GIT_PACKBUILDER_ADDING_OBJECTS,
- PackBuilderStage::Deltafication => raw::GIT_PACKBUILDER_DELTAFICATION,
- }
- }
-}
-
-extern "C" fn foreach_c(buf: *const c_void, size: size_t, data: *mut c_void) -> c_int {
- unsafe {
- let buf = slice::from_raw_parts(buf as *const u8, size as usize);
-
- let r = panic::wrap(|| {
- let data = data as *mut &mut ForEachCb<'_>;
- (*data)(buf)
- });
- if r == Some(true) {
- 0
- } else {
- -1
- }
- }
-}
-
-extern "C" fn progress_c(
- stage: raw::git_packbuilder_stage_t,
- current: c_uint,
- total: c_uint,
- data: *mut c_void,
-) -> c_int {
- unsafe {
- let stage = Binding::from_raw(stage);
-
- let r = panic::wrap(|| {
- let data = data as *mut Box<ProgressCb<'_>>;
- (*data)(stage, current, total)
- });
- if r == Some(true) {
- 0
- } else {
- -1
- }
- }
-}
-
-#[cfg(test)]
-mod tests {
- use crate::Buf;
-
- fn pack_header(len: u8) -> Vec<u8> {
- [].iter()
- .chain(b"PACK") // signature
- .chain(&[0, 0, 0, 2]) // version number
- .chain(&[0, 0, 0, len]) // number of objects
- .cloned()
- .collect::<Vec<u8>>()
- }
-
- fn empty_pack_header() -> Vec<u8> {
- pack_header(0)
- .iter()
- .chain(&[
- 0x02, 0x9d, 0x08, 0x82, 0x3b, // ^
- 0xd8, 0xa8, 0xea, 0xb5, 0x10, // | SHA-1 of the zero
- 0xad, 0x6a, 0xc7, 0x5c, 0x82, // | object pack header
- 0x3c, 0xfd, 0x3e, 0xd3, 0x1e,
- ]) // v
- .cloned()
- .collect::<Vec<u8>>()
- }
-
- #[test]
- fn smoke() {
- let (_td, repo) = crate::test::repo_init();
- let _builder = t!(repo.packbuilder());
- }
-
- #[test]
- fn smoke_write_buf() {
- let (_td, repo) = crate::test::repo_init();
- let mut builder = t!(repo.packbuilder());
- let mut buf = Buf::new();
- t!(builder.write_buf(&mut buf));
- #[allow(deprecated)]
- {
- assert!(builder.hash().unwrap().is_zero());
- }
- assert!(builder.name().is_none());
- assert_eq!(&*buf, &*empty_pack_header());
- }
-
- #[test]
- fn smoke_foreach() {
- let (_td, repo) = crate::test::repo_init();
- let mut builder = t!(repo.packbuilder());
- let mut buf = Vec::<u8>::new();
- t!(builder.foreach(|bytes| {
- buf.extend(bytes);
- true
- }));
- assert_eq!(&*buf, &*empty_pack_header());
- }
-
- #[test]
- fn insert_write_buf() {
- let (_td, repo) = crate::test::repo_init();
- let mut builder = t!(repo.packbuilder());
- let mut buf = Buf::new();
- let (commit, _tree) = crate::test::commit(&repo);
- t!(builder.insert_object(commit, None));
- assert_eq!(builder.object_count(), 1);
- t!(builder.write_buf(&mut buf));
- // Just check that the correct number of objects are written
- assert_eq!(&buf[0..12], &*pack_header(1));
- }
-
- #[test]
- fn insert_tree_write_buf() {
- let (_td, repo) = crate::test::repo_init();
- let mut builder = t!(repo.packbuilder());
- let mut buf = Buf::new();
- let (_commit, tree) = crate::test::commit(&repo);
- // will insert the tree itself and the blob, 2 objects
- t!(builder.insert_tree(tree));
- assert_eq!(builder.object_count(), 2);
- t!(builder.write_buf(&mut buf));
- // Just check that the correct number of objects are written
- assert_eq!(&buf[0..12], &*pack_header(2));
- }
-
- #[test]
- fn insert_commit_write_buf() {
- let (_td, repo) = crate::test::repo_init();
- let mut builder = t!(repo.packbuilder());
- let mut buf = Buf::new();
- let (commit, _tree) = crate::test::commit(&repo);
- // will insert the commit, its tree and the blob, 3 objects
- t!(builder.insert_commit(commit));
- assert_eq!(builder.object_count(), 3);
- t!(builder.write_buf(&mut buf));
- // Just check that the correct number of objects are written
- assert_eq!(&buf[0..12], &*pack_header(3));
- }
-
- #[test]
- fn progress_callback() {
- let mut progress_called = false;
- {
- let (_td, repo) = crate::test::repo_init();
- let mut builder = t!(repo.packbuilder());
- let (commit, _tree) = crate::test::commit(&repo);
- t!(builder.set_progress_callback(|_, _, _| {
- progress_called = true;
- true
- }));
- t!(builder.insert_commit(commit));
- t!(builder.write_buf(&mut Buf::new()));
- }
- assert_eq!(progress_called, true);
- }
-
- #[test]
- fn clear_progress_callback() {
- let mut progress_called = false;
- {
- let (_td, repo) = crate::test::repo_init();
- let mut builder = t!(repo.packbuilder());
- let (commit, _tree) = crate::test::commit(&repo);
- t!(builder.set_progress_callback(|_, _, _| {
- progress_called = true;
- true
- }));
- t!(builder.unset_progress_callback());
- t!(builder.insert_commit(commit));
- t!(builder.write_buf(&mut Buf::new()));
- }
- assert_eq!(progress_called, false);
- }
-
- #[test]
- fn set_threads() {
- let (_td, repo) = crate::test::repo_init();
- let mut builder = t!(repo.packbuilder());
- let used = builder.set_threads(4);
- // Will be 1 if not compiled with threading.
- assert!(used == 1 || used == 4);
- }
-}
diff --git a/extra/git2/src/panic.rs b/extra/git2/src/panic.rs
deleted file mode 100644
index 3e1b208bc..000000000
--- a/extra/git2/src/panic.rs
+++ /dev/null
@@ -1,33 +0,0 @@
-use std::any::Any;
-use std::cell::RefCell;
-
-thread_local!(static LAST_ERROR: RefCell<Option<Box<dyn Any + Send>>> = {
- RefCell::new(None)
-});
-
-pub fn wrap<T, F: FnOnce() -> T + std::panic::UnwindSafe>(f: F) -> Option<T> {
- use std::panic;
- if LAST_ERROR.with(|slot| slot.borrow().is_some()) {
- return None;
- }
- match panic::catch_unwind(f) {
- Ok(ret) => Some(ret),
- Err(e) => {
- LAST_ERROR.with(move |slot| {
- *slot.borrow_mut() = Some(e);
- });
- None
- }
- }
-}
-
-pub fn check() {
- let err = LAST_ERROR.with(|slot| slot.borrow_mut().take());
- if let Some(err) = err {
- std::panic::resume_unwind(err);
- }
-}
-
-pub fn panicked() -> bool {
- LAST_ERROR.with(|slot| slot.borrow().is_some())
-}
diff --git a/extra/git2/src/patch.rs b/extra/git2/src/patch.rs
deleted file mode 100644
index 67b84c0f0..000000000
--- a/extra/git2/src/patch.rs
+++ /dev/null
@@ -1,235 +0,0 @@
-use libc::{c_int, c_void};
-use std::marker::PhantomData;
-use std::path::Path;
-use std::ptr;
-
-use crate::diff::{print_cb, LineCb};
-use crate::util::{into_opt_c_string, Binding};
-use crate::{raw, Blob, Buf, Diff, DiffDelta, DiffHunk, DiffLine, DiffOptions, Error};
-
-/// A structure representing the text changes in a single diff delta.
-///
-/// This is an opaque structure.
-pub struct Patch<'buffers> {
- raw: *mut raw::git_patch,
- buffers: PhantomData<&'buffers ()>,
-}
-
-unsafe impl<'buffers> Send for Patch<'buffers> {}
-
-impl<'buffers> Binding for Patch<'buffers> {
- type Raw = *mut raw::git_patch;
- unsafe fn from_raw(raw: Self::Raw) -> Self {
- Patch {
- raw,
- buffers: PhantomData,
- }
- }
- fn raw(&self) -> Self::Raw {
- self.raw
- }
-}
-
-impl<'buffers> Drop for Patch<'buffers> {
- fn drop(&mut self) {
- unsafe { raw::git_patch_free(self.raw) }
- }
-}
-
-impl<'buffers> Patch<'buffers> {
- /// Return a Patch for one file in a Diff.
- ///
- /// Returns Ok(None) for an unchanged or binary file.
- pub fn from_diff(diff: &Diff<'buffers>, idx: usize) -> Result<Option<Self>, Error> {
- let mut ret = ptr::null_mut();
- unsafe {
- try_call!(raw::git_patch_from_diff(&mut ret, diff.raw(), idx));
- Ok(Binding::from_raw_opt(ret))
- }
- }
-
- /// Generate a Patch by diffing two blobs.
- pub fn from_blobs(
- old_blob: &Blob<'buffers>,
- old_path: Option<&Path>,
- new_blob: &Blob<'buffers>,
- new_path: Option<&Path>,
- opts: Option<&mut DiffOptions>,
- ) -> Result<Self, Error> {
- let mut ret = ptr::null_mut();
- let old_path = into_opt_c_string(old_path)?;
- let new_path = into_opt_c_string(new_path)?;
- unsafe {
- try_call!(raw::git_patch_from_blobs(
- &mut ret,
- old_blob.raw(),
- old_path,
- new_blob.raw(),
- new_path,
- opts.map(|s| s.raw())
- ));
- Ok(Binding::from_raw(ret))
- }
- }
-
- /// Generate a Patch by diffing a blob and a buffer.
- pub fn from_blob_and_buffer(
- old_blob: &Blob<'buffers>,
- old_path: Option<&Path>,
- new_buffer: &'buffers [u8],
- new_path: Option<&Path>,
- opts: Option<&mut DiffOptions>,
- ) -> Result<Self, Error> {
- let mut ret = ptr::null_mut();
- let old_path = into_opt_c_string(old_path)?;
- let new_path = into_opt_c_string(new_path)?;
- unsafe {
- try_call!(raw::git_patch_from_blob_and_buffer(
- &mut ret,
- old_blob.raw(),
- old_path,
- new_buffer.as_ptr() as *const c_void,
- new_buffer.len(),
- new_path,
- opts.map(|s| s.raw())
- ));
- Ok(Binding::from_raw(ret))
- }
- }
-
- /// Generate a Patch by diffing two buffers.
- pub fn from_buffers(
- old_buffer: &'buffers [u8],
- old_path: Option<&Path>,
- new_buffer: &'buffers [u8],
- new_path: Option<&Path>,
- opts: Option<&mut DiffOptions>,
- ) -> Result<Self, Error> {
- crate::init();
- let mut ret = ptr::null_mut();
- let old_path = into_opt_c_string(old_path)?;
- let new_path = into_opt_c_string(new_path)?;
- unsafe {
- try_call!(raw::git_patch_from_buffers(
- &mut ret,
- old_buffer.as_ptr() as *const c_void,
- old_buffer.len(),
- old_path,
- new_buffer.as_ptr() as *const c_void,
- new_buffer.len(),
- new_path,
- opts.map(|s| s.raw())
- ));
- Ok(Binding::from_raw(ret))
- }
- }
-
- /// Get the DiffDelta associated with the Patch.
- pub fn delta(&self) -> DiffDelta<'buffers> {
- unsafe { Binding::from_raw(raw::git_patch_get_delta(self.raw) as *mut _) }
- }
-
- /// Get the number of hunks in the Patch.
- pub fn num_hunks(&self) -> usize {
- unsafe { raw::git_patch_num_hunks(self.raw) }
- }
-
- /// Get the number of lines of context, additions, and deletions in the Patch.
- pub fn line_stats(&self) -> Result<(usize, usize, usize), Error> {
- let mut context = 0;
- let mut additions = 0;
- let mut deletions = 0;
- unsafe {
- try_call!(raw::git_patch_line_stats(
- &mut context,
- &mut additions,
- &mut deletions,
- self.raw
- ));
- }
- Ok((context, additions, deletions))
- }
-
- /// Get a DiffHunk and its total line count from the Patch.
- pub fn hunk(&self, hunk_idx: usize) -> Result<(DiffHunk<'buffers>, usize), Error> {
- let mut ret = ptr::null();
- let mut lines = 0;
- unsafe {
- try_call!(raw::git_patch_get_hunk(
- &mut ret, &mut lines, self.raw, hunk_idx
- ));
- Ok((Binding::from_raw(ret), lines))
- }
- }
-
- /// Get the number of lines in a hunk.
- pub fn num_lines_in_hunk(&self, hunk_idx: usize) -> Result<usize, Error> {
- unsafe { Ok(try_call!(raw::git_patch_num_lines_in_hunk(self.raw, hunk_idx)) as usize) }
- }
-
- /// Get a DiffLine from a hunk of the Patch.
- pub fn line_in_hunk(
- &self,
- hunk_idx: usize,
- line_of_hunk: usize,
- ) -> Result<DiffLine<'buffers>, Error> {
- let mut ret = ptr::null();
- unsafe {
- try_call!(raw::git_patch_get_line_in_hunk(
- &mut ret,
- self.raw,
- hunk_idx,
- line_of_hunk
- ));
- Ok(Binding::from_raw(ret))
- }
- }
-
- /// Get the size of a Patch's diff data in bytes.
- pub fn size(
- &self,
- include_context: bool,
- include_hunk_headers: bool,
- include_file_headers: bool,
- ) -> usize {
- unsafe {
- raw::git_patch_size(
- self.raw,
- include_context as c_int,
- include_hunk_headers as c_int,
- include_file_headers as c_int,
- )
- }
- }
-
- /// Print the Patch to text via a callback.
- pub fn print(&mut self, mut line_cb: &mut LineCb<'_>) -> Result<(), Error> {
- let ptr = &mut line_cb as *mut _ as *mut c_void;
- unsafe {
- let cb: raw::git_diff_line_cb = Some(print_cb);
- try_call!(raw::git_patch_print(self.raw, cb, ptr));
- Ok(())
- }
- }
-
- /// Get the Patch text as a Buf.
- pub fn to_buf(&mut self) -> Result<Buf, Error> {
- let buf = Buf::new();
- unsafe {
- try_call!(raw::git_patch_to_buf(buf.raw(), self.raw));
- }
- Ok(buf)
- }
-}
-
-impl<'buffers> std::fmt::Debug for Patch<'buffers> {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
- let mut ds = f.debug_struct("Patch");
- ds.field("delta", &self.delta())
- .field("num_hunks", &self.num_hunks());
- if let Ok(line_stats) = &self.line_stats() {
- ds.field("line_stats", line_stats);
- }
- ds.finish()
- }
-}
diff --git a/extra/git2/src/pathspec.rs b/extra/git2/src/pathspec.rs
deleted file mode 100644
index 48174fcc1..000000000
--- a/extra/git2/src/pathspec.rs
+++ /dev/null
@@ -1,368 +0,0 @@
-use libc::size_t;
-use std::iter::{FusedIterator, IntoIterator};
-use std::marker;
-use std::ops::Range;
-use std::path::Path;
-use std::ptr;
-
-use crate::util::{path_to_repo_path, Binding};
-use crate::{raw, Diff, DiffDelta, Error, Index, IntoCString, PathspecFlags, Repository, Tree};
-
-/// Structure representing a compiled pathspec used for matching against various
-/// structures.
-pub struct Pathspec {
- raw: *mut raw::git_pathspec,
-}
-
-/// List of filenames matching a pathspec.
-pub struct PathspecMatchList<'ps> {
- raw: *mut raw::git_pathspec_match_list,
- _marker: marker::PhantomData<&'ps Pathspec>,
-}
-
-/// Iterator over the matched paths in a pathspec.
-pub struct PathspecEntries<'list> {
- range: Range<usize>,
- list: &'list PathspecMatchList<'list>,
-}
-
-/// Iterator over the matching diff deltas.
-pub struct PathspecDiffEntries<'list> {
- range: Range<usize>,
- list: &'list PathspecMatchList<'list>,
-}
-
-/// Iterator over the failed list of pathspec items that did not match.
-pub struct PathspecFailedEntries<'list> {
- range: Range<usize>,
- list: &'list PathspecMatchList<'list>,
-}
-
-impl Pathspec {
- /// Creates a new pathspec from a list of specs to match against.
- pub fn new<I, T>(specs: I) -> Result<Pathspec, Error>
- where
- T: IntoCString,
- I: IntoIterator<Item = T>,
- {
- crate::init();
- let (_a, _b, arr) = crate::util::iter2cstrs_paths(specs)?;
- unsafe {
- let mut ret = ptr::null_mut();
- try_call!(raw::git_pathspec_new(&mut ret, &arr));
- Ok(Binding::from_raw(ret))
- }
- }
-
- /// Match a pathspec against files in a diff.
- ///
- /// The list returned contains the list of all matched filenames (unless you
- /// pass `PATHSPEC_FAILURES_ONLY` in the flags) and may also contain the
- /// list of pathspecs with no match if the `PATHSPEC_FIND_FAILURES` flag is
- /// specified.
- pub fn match_diff(
- &self,
- diff: &Diff<'_>,
- flags: PathspecFlags,
- ) -> Result<PathspecMatchList<'_>, Error> {
- let mut ret = ptr::null_mut();
- unsafe {
- try_call!(raw::git_pathspec_match_diff(
- &mut ret,
- diff.raw(),
- flags.bits(),
- self.raw
- ));
- Ok(Binding::from_raw(ret))
- }
- }
-
- /// Match a pathspec against files in a tree.
- ///
- /// The list returned contains the list of all matched filenames (unless you
- /// pass `PATHSPEC_FAILURES_ONLY` in the flags) and may also contain the
- /// list of pathspecs with no match if the `PATHSPEC_FIND_FAILURES` flag is
- /// specified.
- pub fn match_tree(
- &self,
- tree: &Tree<'_>,
- flags: PathspecFlags,
- ) -> Result<PathspecMatchList<'_>, Error> {
- let mut ret = ptr::null_mut();
- unsafe {
- try_call!(raw::git_pathspec_match_tree(
- &mut ret,
- tree.raw(),
- flags.bits(),
- self.raw
- ));
- Ok(Binding::from_raw(ret))
- }
- }
-
- /// This matches the pathspec against the files in the repository index.
- ///
- /// The list returned contains the list of all matched filenames (unless you
- /// pass `PATHSPEC_FAILURES_ONLY` in the flags) and may also contain the
- /// list of pathspecs with no match if the `PATHSPEC_FIND_FAILURES` flag is
- /// specified.
- pub fn match_index(
- &self,
- index: &Index,
- flags: PathspecFlags,
- ) -> Result<PathspecMatchList<'_>, Error> {
- let mut ret = ptr::null_mut();
- unsafe {
- try_call!(raw::git_pathspec_match_index(
- &mut ret,
- index.raw(),
- flags.bits(),
- self.raw
- ));
- Ok(Binding::from_raw(ret))
- }
- }
-
- /// Match a pathspec against the working directory of a repository.
- ///
- /// This matches the pathspec against the current files in the working
- /// directory of the repository. It is an error to invoke this on a bare
- /// repo. This handles git ignores (i.e. ignored files will not be
- /// considered to match the pathspec unless the file is tracked in the
- /// index).
- ///
- /// The list returned contains the list of all matched filenames (unless you
- /// pass `PATHSPEC_FAILURES_ONLY` in the flags) and may also contain the
- /// list of pathspecs with no match if the `PATHSPEC_FIND_FAILURES` flag is
- /// specified.
- pub fn match_workdir(
- &self,
- repo: &Repository,
- flags: PathspecFlags,
- ) -> Result<PathspecMatchList<'_>, Error> {
- let mut ret = ptr::null_mut();
- unsafe {
- try_call!(raw::git_pathspec_match_workdir(
- &mut ret,
- repo.raw(),
- flags.bits(),
- self.raw
- ));
- Ok(Binding::from_raw(ret))
- }
- }
-
- /// Try to match a path against a pathspec
- ///
- /// Unlike most of the other pathspec matching functions, this will not fall
- /// back on the native case-sensitivity for your platform. You must
- /// explicitly pass flags to control case sensitivity or else this will fall
- /// back on being case sensitive.
- pub fn matches_path(&self, path: &Path, flags: PathspecFlags) -> bool {
- let path = path_to_repo_path(path).unwrap();
- unsafe { raw::git_pathspec_matches_path(&*self.raw, flags.bits(), path.as_ptr()) == 1 }
- }
-}
-
-impl Binding for Pathspec {
- type Raw = *mut raw::git_pathspec;
-
- unsafe fn from_raw(raw: *mut raw::git_pathspec) -> Pathspec {
- Pathspec { raw }
- }
- fn raw(&self) -> *mut raw::git_pathspec {
- self.raw
- }
-}
-
-impl Drop for Pathspec {
- fn drop(&mut self) {
- unsafe { raw::git_pathspec_free(self.raw) }
- }
-}
-
-impl<'ps> PathspecMatchList<'ps> {
- fn entrycount(&self) -> usize {
- unsafe { raw::git_pathspec_match_list_entrycount(&*self.raw) as usize }
- }
-
- fn failed_entrycount(&self) -> usize {
- unsafe { raw::git_pathspec_match_list_failed_entrycount(&*self.raw) as usize }
- }
-
- /// Returns an iterator over the matching filenames in this list.
- pub fn entries(&self) -> PathspecEntries<'_> {
- let n = self.entrycount();
- let n = if n > 0 && self.entry(0).is_none() {
- 0
- } else {
- n
- };
- PathspecEntries {
- range: 0..n,
- list: self,
- }
- }
-
- /// Get a matching filename by position.
- ///
- /// If this list was generated from a diff, then the return value will
- /// always be `None.
- pub fn entry(&self, i: usize) -> Option<&[u8]> {
- unsafe {
- let ptr = raw::git_pathspec_match_list_entry(&*self.raw, i as size_t);
- crate::opt_bytes(self, ptr)
- }
- }
-
- /// Returns an iterator over the matching diff entries in this list.
- pub fn diff_entries(&self) -> PathspecDiffEntries<'_> {
- let n = self.entrycount();
- let n = if n > 0 && self.diff_entry(0).is_none() {
- 0
- } else {
- n
- };
- PathspecDiffEntries {
- range: 0..n,
- list: self,
- }
- }
-
- /// Get a matching diff delta by position.
- ///
- /// If the list was not generated from a diff, then the return value will
- /// always be `None`.
- pub fn diff_entry(&self, i: usize) -> Option<DiffDelta<'_>> {
- unsafe {
- let ptr = raw::git_pathspec_match_list_diff_entry(&*self.raw, i as size_t);
- Binding::from_raw_opt(ptr as *mut _)
- }
- }
-
- /// Returns an iterator over the non-matching entries in this list.
- pub fn failed_entries(&self) -> PathspecFailedEntries<'_> {
- let n = self.failed_entrycount();
- let n = if n > 0 && self.failed_entry(0).is_none() {
- 0
- } else {
- n
- };
- PathspecFailedEntries {
- range: 0..n,
- list: self,
- }
- }
-
- /// Get an original pathspec string that had no matches.
- pub fn failed_entry(&self, i: usize) -> Option<&[u8]> {
- unsafe {
- let ptr = raw::git_pathspec_match_list_failed_entry(&*self.raw, i as size_t);
- crate::opt_bytes(self, ptr)
- }
- }
-}
-
-impl<'ps> Binding for PathspecMatchList<'ps> {
- type Raw = *mut raw::git_pathspec_match_list;
-
- unsafe fn from_raw(raw: *mut raw::git_pathspec_match_list) -> PathspecMatchList<'ps> {
- PathspecMatchList {
- raw,
- _marker: marker::PhantomData,
- }
- }
- fn raw(&self) -> *mut raw::git_pathspec_match_list {
- self.raw
- }
-}
-
-impl<'ps> Drop for PathspecMatchList<'ps> {
- fn drop(&mut self) {
- unsafe { raw::git_pathspec_match_list_free(self.raw) }
- }
-}
-
-impl<'list> Iterator for PathspecEntries<'list> {
- type Item = &'list [u8];
- fn next(&mut self) -> Option<&'list [u8]> {
- self.range.next().and_then(|i| self.list.entry(i))
- }
- fn size_hint(&self) -> (usize, Option<usize>) {
- self.range.size_hint()
- }
-}
-impl<'list> DoubleEndedIterator for PathspecEntries<'list> {
- fn next_back(&mut self) -> Option<&'list [u8]> {
- self.range.next_back().and_then(|i| self.list.entry(i))
- }
-}
-impl<'list> FusedIterator for PathspecEntries<'list> {}
-impl<'list> ExactSizeIterator for PathspecEntries<'list> {}
-
-impl<'list> Iterator for PathspecDiffEntries<'list> {
- type Item = DiffDelta<'list>;
- fn next(&mut self) -> Option<DiffDelta<'list>> {
- self.range.next().and_then(|i| self.list.diff_entry(i))
- }
- fn size_hint(&self) -> (usize, Option<usize>) {
- self.range.size_hint()
- }
-}
-impl<'list> DoubleEndedIterator for PathspecDiffEntries<'list> {
- fn next_back(&mut self) -> Option<DiffDelta<'list>> {
- self.range.next_back().and_then(|i| self.list.diff_entry(i))
- }
-}
-impl<'list> FusedIterator for PathspecDiffEntries<'list> {}
-impl<'list> ExactSizeIterator for PathspecDiffEntries<'list> {}
-
-impl<'list> Iterator for PathspecFailedEntries<'list> {
- type Item = &'list [u8];
- fn next(&mut self) -> Option<&'list [u8]> {
- self.range.next().and_then(|i| self.list.failed_entry(i))
- }
- fn size_hint(&self) -> (usize, Option<usize>) {
- self.range.size_hint()
- }
-}
-impl<'list> DoubleEndedIterator for PathspecFailedEntries<'list> {
- fn next_back(&mut self) -> Option<&'list [u8]> {
- self.range
- .next_back()
- .and_then(|i| self.list.failed_entry(i))
- }
-}
-impl<'list> FusedIterator for PathspecFailedEntries<'list> {}
-impl<'list> ExactSizeIterator for PathspecFailedEntries<'list> {}
-
-#[cfg(test)]
-mod tests {
- use super::Pathspec;
- use crate::PathspecFlags;
- use std::fs::File;
- use std::path::Path;
-
- #[test]
- fn smoke() {
- let ps = Pathspec::new(["a"].iter()).unwrap();
- assert!(ps.matches_path(Path::new("a"), PathspecFlags::DEFAULT));
- assert!(ps.matches_path(Path::new("a/b"), PathspecFlags::DEFAULT));
- assert!(!ps.matches_path(Path::new("b"), PathspecFlags::DEFAULT));
- assert!(!ps.matches_path(Path::new("ab/c"), PathspecFlags::DEFAULT));
-
- let (td, repo) = crate::test::repo_init();
- let list = ps.match_workdir(&repo, PathspecFlags::DEFAULT).unwrap();
- assert_eq!(list.entries().len(), 0);
- assert_eq!(list.diff_entries().len(), 0);
- assert_eq!(list.failed_entries().len(), 0);
-
- File::create(&td.path().join("a")).unwrap();
-
- let list = ps
- .match_workdir(&repo, crate::PathspecFlags::FIND_FAILURES)
- .unwrap();
- assert_eq!(list.entries().len(), 1);
- assert_eq!(list.entries().next(), Some("a".as_bytes()));
- }
-}
diff --git a/extra/git2/src/proxy_options.rs b/extra/git2/src/proxy_options.rs
deleted file mode 100644
index b19ba3a52..000000000
--- a/extra/git2/src/proxy_options.rs
+++ /dev/null
@@ -1,56 +0,0 @@
-use std::ffi::CString;
-use std::marker;
-use std::ptr;
-
-use crate::raw;
-use crate::util::Binding;
-
-/// Options which can be specified to various fetch operations.
-#[derive(Default)]
-pub struct ProxyOptions<'a> {
- url: Option<CString>,
- proxy_kind: raw::git_proxy_t,
- _marker: marker::PhantomData<&'a i32>,
-}
-
-impl<'a> ProxyOptions<'a> {
- /// Creates a new set of proxy options ready to be configured.
- pub fn new() -> ProxyOptions<'a> {
- Default::default()
- }
-
- /// Try to auto-detect the proxy from the git configuration.
- ///
- /// Note that this will override `url` specified before.
- pub fn auto(&mut self) -> &mut Self {
- self.proxy_kind = raw::GIT_PROXY_AUTO;
- self
- }
-
- /// Specify the exact URL of the proxy to use.
- ///
- /// Note that this will override `auto` specified before.
- pub fn url(&mut self, url: &str) -> &mut Self {
- self.proxy_kind = raw::GIT_PROXY_SPECIFIED;
- self.url = Some(CString::new(url).unwrap());
- self
- }
-}
-
-impl<'a> Binding for ProxyOptions<'a> {
- type Raw = raw::git_proxy_options;
- unsafe fn from_raw(_raw: raw::git_proxy_options) -> ProxyOptions<'a> {
- panic!("can't create proxy from raw options")
- }
-
- fn raw(&self) -> raw::git_proxy_options {
- raw::git_proxy_options {
- version: raw::GIT_PROXY_OPTIONS_VERSION,
- kind: self.proxy_kind,
- url: self.url.as_ref().map(|s| s.as_ptr()).unwrap_or(ptr::null()),
- credentials: None,
- certificate_check: None,
- payload: ptr::null_mut(),
- }
- }
-}
diff --git a/extra/git2/src/push_update.rs b/extra/git2/src/push_update.rs
deleted file mode 100644
index 3f74a2506..000000000
--- a/extra/git2/src/push_update.rs
+++ /dev/null
@@ -1,55 +0,0 @@
-use crate::util::Binding;
-use crate::{raw, Oid};
-use std::marker;
-use std::str;
-
-/// Represents an update which will be performed on the remote during push.
-pub struct PushUpdate<'a> {
- raw: *const raw::git_push_update,
- _marker: marker::PhantomData<&'a raw::git_push_update>,
-}
-
-impl<'a> Binding for PushUpdate<'a> {
- type Raw = *const raw::git_push_update;
- unsafe fn from_raw(raw: *const raw::git_push_update) -> PushUpdate<'a> {
- PushUpdate {
- raw,
- _marker: marker::PhantomData,
- }
- }
- fn raw(&self) -> Self::Raw {
- self.raw
- }
-}
-
-impl PushUpdate<'_> {
- /// Returns the source name of the reference as a byte slice.
- pub fn src_refname_bytes(&self) -> &[u8] {
- unsafe { crate::opt_bytes(self, (*self.raw).src_refname).unwrap() }
- }
-
- /// Returns the source name of the reference.
- pub fn src_refname(&self) -> Option<&str> {
- str::from_utf8(self.src_refname_bytes()).ok()
- }
-
- /// Returns the destination name of the reference as a byte slice.
- pub fn dst_refname_bytes(&self) -> &[u8] {
- unsafe { crate::opt_bytes(self, (*self.raw).dst_refname).unwrap() }
- }
-
- /// Returns the destination name of the reference.
- pub fn dst_refname(&self) -> Option<&str> {
- str::from_utf8(self.dst_refname_bytes()).ok()
- }
-
- /// Returns the current target of the reference.
- pub fn src(&self) -> Oid {
- unsafe { Binding::from_raw(&(*self.raw).src as *const _) }
- }
-
- /// Returns the new target for the reference.
- pub fn dst(&self) -> Oid {
- unsafe { Binding::from_raw(&(*self.raw).dst as *const _) }
- }
-}
diff --git a/extra/git2/src/rebase.rs b/extra/git2/src/rebase.rs
deleted file mode 100644
index 2bf8fe3e8..000000000
--- a/extra/git2/src/rebase.rs
+++ /dev/null
@@ -1,441 +0,0 @@
-use std::ffi::CString;
-use std::{marker, mem, ptr, str};
-
-use crate::build::CheckoutBuilder;
-use crate::util::Binding;
-use crate::{raw, Error, Index, MergeOptions, Oid, Signature};
-
-/// Rebase options
-///
-/// Use to tell the rebase machinery how to operate.
-pub struct RebaseOptions<'cb> {
- raw: raw::git_rebase_options,
- rewrite_notes_ref: Option<CString>,
- merge_options: Option<MergeOptions>,
- checkout_options: Option<CheckoutBuilder<'cb>>,
-}
-
-impl<'cb> Default for RebaseOptions<'cb> {
- fn default() -> Self {
- Self::new()
- }
-}
-
-impl<'cb> RebaseOptions<'cb> {
- /// Creates a new default set of rebase options.
- pub fn new() -> RebaseOptions<'cb> {
- let mut opts = RebaseOptions {
- raw: unsafe { mem::zeroed() },
- rewrite_notes_ref: None,
- merge_options: None,
- checkout_options: None,
- };
- assert_eq!(unsafe { raw::git_rebase_init_options(&mut opts.raw, 1) }, 0);
- opts
- }
-
- /// Used by `Repository::rebase`, this will instruct other clients working on this
- /// rebase that you want a quiet rebase experience, which they may choose to
- /// provide in an application-specific manner. This has no effect upon
- /// libgit2 directly, but is provided for interoperability between Git
- /// tools.
- pub fn quiet(&mut self, quiet: bool) -> &mut RebaseOptions<'cb> {
- self.raw.quiet = quiet as i32;
- self
- }
-
- /// Used by `Repository::rebase`, this will begin an in-memory rebase,
- /// which will allow callers to step through the rebase operations and
- /// commit the rebased changes, but will not rewind HEAD or update the
- /// repository to be in a rebasing state. This will not interfere with
- /// the working directory (if there is one).
- pub fn inmemory(&mut self, inmemory: bool) -> &mut RebaseOptions<'cb> {
- self.raw.inmemory = inmemory as i32;
- self
- }
-
- /// Used by `finish()`, this is the name of the notes reference
- /// used to rewrite notes for rebased commits when finishing the rebase;
- /// if NULL, the contents of the configuration option `notes.rewriteRef`
- /// is examined, unless the configuration option `notes.rewrite.rebase`
- /// is set to false. If `notes.rewriteRef` is also NULL, notes will
- /// not be rewritten.
- pub fn rewrite_notes_ref(&mut self, rewrite_notes_ref: &str) -> &mut RebaseOptions<'cb> {
- self.rewrite_notes_ref = Some(CString::new(rewrite_notes_ref).unwrap());
- self
- }
-
- /// Options to control how trees are merged during `next()`.
- pub fn merge_options(&mut self, opts: MergeOptions) -> &mut RebaseOptions<'cb> {
- self.merge_options = Some(opts);
- self
- }
-
- /// Options to control how files are written during `Repository::rebase`,
- /// `next()` and `abort()`. Note that a minimum strategy of
- /// `GIT_CHECKOUT_SAFE` is defaulted in `init` and `next`, and a minimum
- /// strategy of `GIT_CHECKOUT_FORCE` is defaulted in `abort` to match git
- /// semantics.
- pub fn checkout_options(&mut self, opts: CheckoutBuilder<'cb>) -> &mut RebaseOptions<'cb> {
- self.checkout_options = Some(opts);
- self
- }
-
- /// Acquire a pointer to the underlying raw options.
- pub fn raw(&mut self) -> *const raw::git_rebase_options {
- unsafe {
- if let Some(opts) = self.merge_options.as_mut().take() {
- ptr::copy_nonoverlapping(opts.raw(), &mut self.raw.merge_options, 1);
- }
- if let Some(opts) = self.checkout_options.as_mut() {
- opts.configure(&mut self.raw.checkout_options);
- }
- self.raw.rewrite_notes_ref = self
- .rewrite_notes_ref
- .as_ref()
- .map(|s| s.as_ptr())
- .unwrap_or(ptr::null());
- }
- &self.raw
- }
-}
-
-/// Representation of a rebase
-pub struct Rebase<'repo> {
- raw: *mut raw::git_rebase,
- _marker: marker::PhantomData<&'repo raw::git_rebase>,
-}
-
-impl<'repo> Rebase<'repo> {
- /// Gets the count of rebase operations that are to be applied.
- pub fn len(&self) -> usize {
- unsafe { raw::git_rebase_operation_entrycount(self.raw) }
- }
-
- /// Gets the original `HEAD` ref name for merge rebases.
- pub fn orig_head_name(&self) -> Option<&str> {
- let name_bytes =
- unsafe { crate::opt_bytes(self, raw::git_rebase_orig_head_name(self.raw)) };
- name_bytes.and_then(|s| str::from_utf8(s).ok())
- }
-
- /// Gets the original HEAD id for merge rebases.
- pub fn orig_head_id(&self) -> Option<Oid> {
- unsafe { Oid::from_raw_opt(raw::git_rebase_orig_head_id(self.raw)) }
- }
-
- /// Gets the rebase operation specified by the given index.
- pub fn nth(&mut self, n: usize) -> Option<RebaseOperation<'_>> {
- unsafe {
- let op = raw::git_rebase_operation_byindex(self.raw, n);
- if op.is_null() {
- None
- } else {
- Some(RebaseOperation::from_raw(op))
- }
- }
- }
-
- /// Gets the index of the rebase operation that is currently being applied.
- /// If the first operation has not yet been applied (because you have called
- /// `init` but not yet `next`) then this returns None.
- pub fn operation_current(&mut self) -> Option<usize> {
- let cur = unsafe { raw::git_rebase_operation_current(self.raw) };
- if cur == raw::GIT_REBASE_NO_OPERATION {
- None
- } else {
- Some(cur)
- }
- }
-
- /// Gets the index produced by the last operation, which is the result of
- /// `next()` and which will be committed by the next invocation of
- /// `commit()`. This is useful for resolving conflicts in an in-memory
- /// rebase before committing them.
- ///
- /// This is only applicable for in-memory rebases; for rebases within a
- /// working directory, the changes were applied to the repository's index.
- pub fn inmemory_index(&mut self) -> Result<Index, Error> {
- let mut idx = ptr::null_mut();
- unsafe {
- try_call!(raw::git_rebase_inmemory_index(&mut idx, self.raw));
- Ok(Binding::from_raw(idx))
- }
- }
-
- /// Commits the current patch. You must have resolved any conflicts that
- /// were introduced during the patch application from the `git_rebase_next`
- /// invocation. To keep the author and message from the original commit leave
- /// them as None
- pub fn commit(
- &mut self,
- author: Option<&Signature<'_>>,
- committer: &Signature<'_>,
- message: Option<&str>,
- ) -> Result<Oid, Error> {
- let mut id: raw::git_oid = unsafe { mem::zeroed() };
- let message = crate::opt_cstr(message)?;
- unsafe {
- try_call!(raw::git_rebase_commit(
- &mut id,
- self.raw,
- author.map(|a| a.raw()),
- committer.raw(),
- ptr::null(),
- message
- ));
- Ok(Binding::from_raw(&id as *const _))
- }
- }
-
- /// Aborts a rebase that is currently in progress, resetting the repository
- /// and working directory to their state before rebase began.
- pub fn abort(&mut self) -> Result<(), Error> {
- unsafe {
- try_call!(raw::git_rebase_abort(self.raw));
- }
-
- Ok(())
- }
-
- /// Finishes a rebase that is currently in progress once all patches have
- /// been applied.
- pub fn finish(&mut self, signature: Option<&Signature<'_>>) -> Result<(), Error> {
- unsafe {
- try_call!(raw::git_rebase_finish(self.raw, signature.map(|s| s.raw())));
- }
-
- Ok(())
- }
-}
-
-impl<'rebase> Iterator for Rebase<'rebase> {
- type Item = Result<RebaseOperation<'rebase>, Error>;
-
- /// Performs the next rebase operation and returns the information about it.
- /// If the operation is one that applies a patch (which is any operation except
- /// GitRebaseOperation::Exec) then the patch will be applied and the index and
- /// working directory will be updated with the changes. If there are conflicts,
- /// you will need to address those before committing the changes.
- fn next(&mut self) -> Option<Result<RebaseOperation<'rebase>, Error>> {
- let mut out = ptr::null_mut();
- unsafe {
- try_call_iter!(raw::git_rebase_next(&mut out, self.raw));
- Some(Ok(RebaseOperation::from_raw(out)))
- }
- }
-}
-
-impl<'repo> Binding for Rebase<'repo> {
- type Raw = *mut raw::git_rebase;
- unsafe fn from_raw(raw: *mut raw::git_rebase) -> Rebase<'repo> {
- Rebase {
- raw,
- _marker: marker::PhantomData,
- }
- }
- fn raw(&self) -> *mut raw::git_rebase {
- self.raw
- }
-}
-
-impl<'repo> Drop for Rebase<'repo> {
- fn drop(&mut self) {
- unsafe { raw::git_rebase_free(self.raw) }
- }
-}
-
-/// A rebase operation
-///
-/// Describes a single instruction/operation to be performed during the
-/// rebase.
-#[derive(Debug, PartialEq)]
-pub enum RebaseOperationType {
- /// The given commit is to be cherry-picked. The client should commit the
- /// changes and continue if there are no conflicts.
- Pick,
-
- /// The given commit is to be cherry-picked, but the client should prompt
- /// the user to provide an updated commit message.
- Reword,
-
- /// The given commit is to be cherry-picked, but the client should stop to
- /// allow the user to edit the changes before committing them.
- Edit,
-
- /// The given commit is to be squashed into the previous commit. The commit
- /// message will be merged with the previous message.
- Squash,
-
- /// The given commit is to be squashed into the previous commit. The commit
- /// message from this commit will be discarded.
- Fixup,
-
- /// No commit will be cherry-picked. The client should run the given command
- /// and (if successful) continue.
- Exec,
-}
-
-impl RebaseOperationType {
- /// Convert from the int into an enum. Returns None if invalid.
- pub fn from_raw(raw: raw::git_rebase_operation_t) -> Option<RebaseOperationType> {
- match raw {
- raw::GIT_REBASE_OPERATION_PICK => Some(RebaseOperationType::Pick),
- raw::GIT_REBASE_OPERATION_REWORD => Some(RebaseOperationType::Reword),
- raw::GIT_REBASE_OPERATION_EDIT => Some(RebaseOperationType::Edit),
- raw::GIT_REBASE_OPERATION_SQUASH => Some(RebaseOperationType::Squash),
- raw::GIT_REBASE_OPERATION_FIXUP => Some(RebaseOperationType::Fixup),
- raw::GIT_REBASE_OPERATION_EXEC => Some(RebaseOperationType::Exec),
- _ => None,
- }
- }
-}
-
-/// A rebase operation
-///
-/// Describes a single instruction/operation to be performed during the
-/// rebase.
-#[derive(Debug)]
-pub struct RebaseOperation<'rebase> {
- raw: *const raw::git_rebase_operation,
- _marker: marker::PhantomData<Rebase<'rebase>>,
-}
-
-impl<'rebase> RebaseOperation<'rebase> {
- /// The type of rebase operation
- pub fn kind(&self) -> Option<RebaseOperationType> {
- unsafe { RebaseOperationType::from_raw((*self.raw).kind) }
- }
-
- /// The commit ID being cherry-picked. This will be populated for all
- /// operations except those of type `GIT_REBASE_OPERATION_EXEC`.
- pub fn id(&self) -> Oid {
- unsafe { Binding::from_raw(&(*self.raw).id as *const _) }
- }
-
- ///The executable the user has requested be run. This will only
- /// be populated for operations of type RebaseOperationType::Exec
- pub fn exec(&self) -> Option<&str> {
- unsafe { str::from_utf8(crate::opt_bytes(self, (*self.raw).exec).unwrap()).ok() }
- }
-}
-
-impl<'rebase> Binding for RebaseOperation<'rebase> {
- type Raw = *const raw::git_rebase_operation;
- unsafe fn from_raw(raw: *const raw::git_rebase_operation) -> RebaseOperation<'rebase> {
- RebaseOperation {
- raw,
- _marker: marker::PhantomData,
- }
- }
- fn raw(&self) -> *const raw::git_rebase_operation {
- self.raw
- }
-}
-
-#[cfg(test)]
-mod tests {
- use crate::{RebaseOperationType, RebaseOptions, Signature};
- use std::{fs, path};
-
- #[test]
- fn smoke() {
- let (_td, repo) = crate::test::repo_init();
- let head_target = repo.head().unwrap().target().unwrap();
- let tip = repo.find_commit(head_target).unwrap();
- let sig = tip.author();
- let tree = tip.tree().unwrap();
-
- // We just want to see the iteration work so we can create commits with
- // no changes
- let c1 = repo
- .commit(Some("refs/heads/main"), &sig, &sig, "foo", &tree, &[&tip])
- .unwrap();
- let c1 = repo.find_commit(c1).unwrap();
- let c2 = repo
- .commit(Some("refs/heads/main"), &sig, &sig, "foo", &tree, &[&c1])
- .unwrap();
-
- let head = repo.find_reference("refs/heads/main").unwrap();
- let branch = repo.reference_to_annotated_commit(&head).unwrap();
- let upstream = repo.find_annotated_commit(tip.id()).unwrap();
- let mut rebase = repo
- .rebase(Some(&branch), Some(&upstream), None, None)
- .unwrap();
-
- assert_eq!(Some("refs/heads/main"), rebase.orig_head_name());
- assert_eq!(Some(c2), rebase.orig_head_id());
-
- assert_eq!(rebase.len(), 2);
- {
- let op = rebase.next().unwrap().unwrap();
- assert_eq!(op.kind(), Some(RebaseOperationType::Pick));
- assert_eq!(op.id(), c1.id());
- }
- {
- let op = rebase.next().unwrap().unwrap();
- assert_eq!(op.kind(), Some(RebaseOperationType::Pick));
- assert_eq!(op.id(), c2);
- }
- {
- let op = rebase.next();
- assert!(op.is_none());
- }
- }
-
- #[test]
- fn keeping_original_author_msg() {
- let (td, repo) = crate::test::repo_init();
- let head_target = repo.head().unwrap().target().unwrap();
- let tip = repo.find_commit(head_target).unwrap();
- let sig = Signature::now("testname", "testemail").unwrap();
- let mut index = repo.index().unwrap();
-
- fs::File::create(td.path().join("file_a")).unwrap();
- index.add_path(path::Path::new("file_a")).unwrap();
- index.write().unwrap();
- let tree_id_a = index.write_tree().unwrap();
- let tree_a = repo.find_tree(tree_id_a).unwrap();
- let c1 = repo
- .commit(Some("refs/heads/main"), &sig, &sig, "A", &tree_a, &[&tip])
- .unwrap();
- let c1 = repo.find_commit(c1).unwrap();
-
- fs::File::create(td.path().join("file_b")).unwrap();
- index.add_path(path::Path::new("file_b")).unwrap();
- index.write().unwrap();
- let tree_id_b = index.write_tree().unwrap();
- let tree_b = repo.find_tree(tree_id_b).unwrap();
- let c2 = repo
- .commit(Some("refs/heads/main"), &sig, &sig, "B", &tree_b, &[&c1])
- .unwrap();
-
- let branch = repo.find_annotated_commit(c2).unwrap();
- let upstream = repo.find_annotated_commit(tip.id()).unwrap();
- let mut opts: RebaseOptions<'_> = Default::default();
- let mut rebase = repo
- .rebase(Some(&branch), Some(&upstream), None, Some(&mut opts))
- .unwrap();
-
- assert_eq!(rebase.len(), 2);
-
- {
- rebase.next().unwrap().unwrap();
- let id = rebase.commit(None, &sig, None).unwrap();
- let commit = repo.find_commit(id).unwrap();
- assert_eq!(commit.message(), Some("A"));
- assert_eq!(commit.author().name(), Some("testname"));
- assert_eq!(commit.author().email(), Some("testemail"));
- }
-
- {
- rebase.next().unwrap().unwrap();
- let id = rebase.commit(None, &sig, None).unwrap();
- let commit = repo.find_commit(id).unwrap();
- assert_eq!(commit.message(), Some("B"));
- assert_eq!(commit.author().name(), Some("testname"));
- assert_eq!(commit.author().email(), Some("testemail"));
- }
- rebase.finish(None).unwrap();
- }
-}
diff --git a/extra/git2/src/reference.rs b/extra/git2/src/reference.rs
deleted file mode 100644
index 92eb18c63..000000000
--- a/extra/git2/src/reference.rs
+++ /dev/null
@@ -1,586 +0,0 @@
-use std::cmp::Ordering;
-use std::ffi::CString;
-use std::marker;
-use std::mem;
-use std::ptr;
-use std::str;
-
-use crate::object::CastOrPanic;
-use crate::util::{c_cmp_to_ordering, Binding};
-use crate::{
- call, raw, Blob, Commit, Error, Object, ObjectType, Oid, ReferenceFormat, ReferenceType,
- Repository, Tag, Tree,
-};
-
-// Not in the public header files (yet?), but a hard limit used by libgit2
-// internally
-const GIT_REFNAME_MAX: usize = 1024;
-
-struct Refdb<'repo>(&'repo Repository);
-
-/// A structure to represent a git [reference][1].
-///
-/// [1]: http://git-scm.com/book/en/Git-Internals-Git-References
-pub struct Reference<'repo> {
- raw: *mut raw::git_reference,
- _marker: marker::PhantomData<Refdb<'repo>>,
-}
-
-/// An iterator over the references in a repository.
-pub struct References<'repo> {
- raw: *mut raw::git_reference_iterator,
- _marker: marker::PhantomData<Refdb<'repo>>,
-}
-
-/// An iterator over the names of references in a repository.
-pub struct ReferenceNames<'repo, 'references> {
- inner: &'references mut References<'repo>,
-}
-
-impl<'repo> Reference<'repo> {
- /// Ensure the reference name is well-formed.
- ///
- /// Validation is performed as if [`ReferenceFormat::ALLOW_ONELEVEL`]
- /// was given to [`Reference::normalize_name`]. No normalization is
- /// performed, however.
- ///
- /// ```rust
- /// use git2::Reference;
- ///
- /// assert!(Reference::is_valid_name("HEAD"));
- /// assert!(Reference::is_valid_name("refs/heads/main"));
- ///
- /// // But:
- /// assert!(!Reference::is_valid_name("main"));
- /// assert!(!Reference::is_valid_name("refs/heads/*"));
- /// assert!(!Reference::is_valid_name("foo//bar"));
- /// ```
- ///
- /// [`ReferenceFormat::ALLOW_ONELEVEL`]:
- /// struct.ReferenceFormat#associatedconstant.ALLOW_ONELEVEL
- /// [`Reference::normalize_name`]: struct.Reference#method.normalize_name
- pub fn is_valid_name(refname: &str) -> bool {
- crate::init();
- let refname = CString::new(refname).unwrap();
- let mut valid: libc::c_int = 0;
- unsafe {
- call::c_try(raw::git_reference_name_is_valid(
- &mut valid,
- refname.as_ptr(),
- ))
- .unwrap();
- }
- valid == 1
- }
-
- /// Normalize reference name and check validity.
- ///
- /// This will normalize the reference name by collapsing runs of adjacent
- /// slashes between name components into a single slash. It also validates
- /// the name according to the following rules:
- ///
- /// 1. If [`ReferenceFormat::ALLOW_ONELEVEL`] is given, the name may
- /// contain only capital letters and underscores, and must begin and end
- /// with a letter. (e.g. "HEAD", "ORIG_HEAD").
- /// 2. The flag [`ReferenceFormat::REFSPEC_SHORTHAND`] has an effect
- /// only when combined with [`ReferenceFormat::ALLOW_ONELEVEL`]. If
- /// it is given, "shorthand" branch names (i.e. those not prefixed by
- /// `refs/`, but consisting of a single word without `/` separators)
- /// become valid. For example, "main" would be accepted.
- /// 3. If [`ReferenceFormat::REFSPEC_PATTERN`] is given, the name may
- /// contain a single `*` in place of a full pathname component (e.g.
- /// `foo/*/bar`, `foo/bar*`).
- /// 4. Names prefixed with "refs/" can be almost anything. You must avoid
- /// the characters '~', '^', ':', '\\', '?', '[', and '*', and the
- /// sequences ".." and "@{" which have special meaning to revparse.
- ///
- /// If the reference passes validation, it is returned in normalized form,
- /// otherwise an [`Error`] with [`ErrorCode::InvalidSpec`] is returned.
- ///
- /// ```rust
- /// use git2::{Reference, ReferenceFormat};
- ///
- /// assert_eq!(
- /// Reference::normalize_name(
- /// "foo//bar",
- /// ReferenceFormat::NORMAL
- /// )
- /// .unwrap(),
- /// "foo/bar".to_owned()
- /// );
- ///
- /// assert_eq!(
- /// Reference::normalize_name(
- /// "HEAD",
- /// ReferenceFormat::ALLOW_ONELEVEL
- /// )
- /// .unwrap(),
- /// "HEAD".to_owned()
- /// );
- ///
- /// assert_eq!(
- /// Reference::normalize_name(
- /// "refs/heads/*",
- /// ReferenceFormat::REFSPEC_PATTERN
- /// )
- /// .unwrap(),
- /// "refs/heads/*".to_owned()
- /// );
- ///
- /// assert_eq!(
- /// Reference::normalize_name(
- /// "main",
- /// ReferenceFormat::ALLOW_ONELEVEL | ReferenceFormat::REFSPEC_SHORTHAND
- /// )
- /// .unwrap(),
- /// "main".to_owned()
- /// );
- /// ```
- ///
- /// [`ReferenceFormat::ALLOW_ONELEVEL`]:
- /// struct.ReferenceFormat#associatedconstant.ALLOW_ONELEVEL
- /// [`ReferenceFormat::REFSPEC_SHORTHAND`]:
- /// struct.ReferenceFormat#associatedconstant.REFSPEC_SHORTHAND
- /// [`ReferenceFormat::REFSPEC_PATTERN`]:
- /// struct.ReferenceFormat#associatedconstant.REFSPEC_PATTERN
- /// [`Error`]: struct.Error
- /// [`ErrorCode::InvalidSpec`]: enum.ErrorCode#variant.InvalidSpec
- pub fn normalize_name(refname: &str, flags: ReferenceFormat) -> Result<String, Error> {
- crate::init();
- let mut dst = [0u8; GIT_REFNAME_MAX];
- let refname = CString::new(refname)?;
- unsafe {
- try_call!(raw::git_reference_normalize_name(
- dst.as_mut_ptr() as *mut libc::c_char,
- dst.len() as libc::size_t,
- refname,
- flags.bits()
- ));
- let s = &dst[..dst.iter().position(|&a| a == 0).unwrap()];
- Ok(str::from_utf8(s).unwrap().to_owned())
- }
- }
-
- /// Get access to the underlying raw pointer.
- pub fn raw(&self) -> *mut raw::git_reference {
- self.raw
- }
-
- /// Delete an existing reference.
- ///
- /// This method works for both direct and symbolic references. The reference
- /// will be immediately removed on disk.
- ///
- /// This function will return an error if the reference has changed from the
- /// time it was looked up.
- pub fn delete(&mut self) -> Result<(), Error> {
- unsafe {
- try_call!(raw::git_reference_delete(self.raw));
- }
- Ok(())
- }
-
- /// Check if a reference is a local branch.
- pub fn is_branch(&self) -> bool {
- unsafe { raw::git_reference_is_branch(&*self.raw) == 1 }
- }
-
- /// Check if a reference is a note.
- pub fn is_note(&self) -> bool {
- unsafe { raw::git_reference_is_note(&*self.raw) == 1 }
- }
-
- /// Check if a reference is a remote tracking branch
- pub fn is_remote(&self) -> bool {
- unsafe { raw::git_reference_is_remote(&*self.raw) == 1 }
- }
-
- /// Check if a reference is a tag
- pub fn is_tag(&self) -> bool {
- unsafe { raw::git_reference_is_tag(&*self.raw) == 1 }
- }
-
- /// Get the reference type of a reference.
- ///
- /// If the type is unknown, then `None` is returned.
- pub fn kind(&self) -> Option<ReferenceType> {
- ReferenceType::from_raw(unsafe { raw::git_reference_type(&*self.raw) })
- }
-
- /// Get the full name of a reference.
- ///
- /// Returns `None` if the name is not valid utf-8.
- pub fn name(&self) -> Option<&str> {
- str::from_utf8(self.name_bytes()).ok()
- }
-
- /// Get the full name of a reference.
- pub fn name_bytes(&self) -> &[u8] {
- unsafe { crate::opt_bytes(self, raw::git_reference_name(&*self.raw)).unwrap() }
- }
-
- /// Get the full shorthand of a reference.
- ///
- /// This will transform the reference name into a name "human-readable"
- /// version. If no shortname is appropriate, it will return the full name.
- ///
- /// Returns `None` if the shorthand is not valid utf-8.
- pub fn shorthand(&self) -> Option<&str> {
- str::from_utf8(self.shorthand_bytes()).ok()
- }
-
- /// Get the full shorthand of a reference.
- pub fn shorthand_bytes(&self) -> &[u8] {
- unsafe { crate::opt_bytes(self, raw::git_reference_shorthand(&*self.raw)).unwrap() }
- }
-
- /// Get the OID pointed to by a direct reference.
- ///
- /// Only available if the reference is direct (i.e. an object id reference,
- /// not a symbolic one).
- pub fn target(&self) -> Option<Oid> {
- unsafe { Binding::from_raw_opt(raw::git_reference_target(&*self.raw)) }
- }
-
- /// Return the peeled OID target of this reference.
- ///
- /// This peeled OID only applies to direct references that point to a hard
- /// Tag object: it is the result of peeling such Tag.
- pub fn target_peel(&self) -> Option<Oid> {
- unsafe { Binding::from_raw_opt(raw::git_reference_target_peel(&*self.raw)) }
- }
-
- /// Get full name to the reference pointed to by a symbolic reference.
- ///
- /// May return `None` if the reference is either not symbolic or not a
- /// valid utf-8 string.
- pub fn symbolic_target(&self) -> Option<&str> {
- self.symbolic_target_bytes()
- .and_then(|s| str::from_utf8(s).ok())
- }
-
- /// Get full name to the reference pointed to by a symbolic reference.
- ///
- /// Only available if the reference is symbolic.
- pub fn symbolic_target_bytes(&self) -> Option<&[u8]> {
- unsafe { crate::opt_bytes(self, raw::git_reference_symbolic_target(&*self.raw)) }
- }
-
- /// Resolve a symbolic reference to a direct reference.
- ///
- /// This method iteratively peels a symbolic reference until it resolves to
- /// a direct reference to an OID.
- ///
- /// If a direct reference is passed as an argument, a copy of that
- /// reference is returned.
- pub fn resolve(&self) -> Result<Reference<'repo>, Error> {
- let mut raw = ptr::null_mut();
- unsafe {
- try_call!(raw::git_reference_resolve(&mut raw, &*self.raw));
- Ok(Binding::from_raw(raw))
- }
- }
-
- /// Peel a reference to an object
- ///
- /// This method recursively peels the reference until it reaches
- /// an object of the specified type.
- pub fn peel(&self, kind: ObjectType) -> Result<Object<'repo>, Error> {
- let mut raw = ptr::null_mut();
- unsafe {
- try_call!(raw::git_reference_peel(&mut raw, self.raw, kind));
- Ok(Binding::from_raw(raw))
- }
- }
-
- /// Peel a reference to a blob
- ///
- /// This method recursively peels the reference until it reaches
- /// a blob.
- pub fn peel_to_blob(&self) -> Result<Blob<'repo>, Error> {
- Ok(self.peel(ObjectType::Blob)?.cast_or_panic(ObjectType::Blob))
- }
-
- /// Peel a reference to a commit
- ///
- /// This method recursively peels the reference until it reaches
- /// a commit.
- pub fn peel_to_commit(&self) -> Result<Commit<'repo>, Error> {
- Ok(self
- .peel(ObjectType::Commit)?
- .cast_or_panic(ObjectType::Commit))
- }
-
- /// Peel a reference to a tree
- ///
- /// This method recursively peels the reference until it reaches
- /// a tree.
- pub fn peel_to_tree(&self) -> Result<Tree<'repo>, Error> {
- Ok(self.peel(ObjectType::Tree)?.cast_or_panic(ObjectType::Tree))
- }
-
- /// Peel a reference to a tag
- ///
- /// This method recursively peels the reference until it reaches
- /// a tag.
- pub fn peel_to_tag(&self) -> Result<Tag<'repo>, Error> {
- Ok(self.peel(ObjectType::Tag)?.cast_or_panic(ObjectType::Tag))
- }
-
- /// Rename an existing reference.
- ///
- /// This method works for both direct and symbolic references.
- ///
- /// If the force flag is not enabled, and there's already a reference with
- /// the given name, the renaming will fail.
- pub fn rename(
- &mut self,
- new_name: &str,
- force: bool,
- msg: &str,
- ) -> Result<Reference<'repo>, Error> {
- let mut raw = ptr::null_mut();
- let new_name = CString::new(new_name)?;
- let msg = CString::new(msg)?;
- unsafe {
- try_call!(raw::git_reference_rename(
- &mut raw, self.raw, new_name, force, msg
- ));
- Ok(Binding::from_raw(raw))
- }
- }
-
- /// Conditionally create a new reference with the same name as the given
- /// reference but a different OID target. The reference must be a direct
- /// reference, otherwise this will fail.
- ///
- /// The new reference will be written to disk, overwriting the given
- /// reference.
- pub fn set_target(&mut self, id: Oid, reflog_msg: &str) -> Result<Reference<'repo>, Error> {
- let mut raw = ptr::null_mut();
- let msg = CString::new(reflog_msg)?;
- unsafe {
- try_call!(raw::git_reference_set_target(
- &mut raw,
- self.raw,
- id.raw(),
- msg
- ));
- Ok(Binding::from_raw(raw))
- }
- }
-
- /// Create a new reference with the same name as the given reference but a
- /// different symbolic target. The reference must be a symbolic reference,
- /// otherwise this will fail.
- ///
- /// The new reference will be written to disk, overwriting the given
- /// reference.
- ///
- /// The target name will be checked for validity. See
- /// [`Repository::reference_symbolic`] for rules about valid names.
- ///
- /// The message for the reflog will be ignored if the reference does not
- /// belong in the standard set (HEAD, branches and remote-tracking
- /// branches) and it does not have a reflog.
- pub fn symbolic_set_target(
- &mut self,
- target: &str,
- reflog_msg: &str,
- ) -> Result<Reference<'repo>, Error> {
- let mut raw = ptr::null_mut();
- let target = CString::new(target)?;
- let msg = CString::new(reflog_msg)?;
- unsafe {
- try_call!(raw::git_reference_symbolic_set_target(
- &mut raw, self.raw, target, msg
- ));
- Ok(Binding::from_raw(raw))
- }
- }
-}
-
-impl<'repo> PartialOrd for Reference<'repo> {
- fn partial_cmp(&self, other: &Reference<'repo>) -> Option<Ordering> {
- Some(self.cmp(other))
- }
-}
-
-impl<'repo> Ord for Reference<'repo> {
- fn cmp(&self, other: &Reference<'repo>) -> Ordering {
- c_cmp_to_ordering(unsafe { raw::git_reference_cmp(&*self.raw, &*other.raw) })
- }
-}
-
-impl<'repo> PartialEq for Reference<'repo> {
- fn eq(&self, other: &Reference<'repo>) -> bool {
- self.cmp(other) == Ordering::Equal
- }
-}
-
-impl<'repo> Eq for Reference<'repo> {}
-
-impl<'repo> Binding for Reference<'repo> {
- type Raw = *mut raw::git_reference;
- unsafe fn from_raw(raw: *mut raw::git_reference) -> Reference<'repo> {
- Reference {
- raw,
- _marker: marker::PhantomData,
- }
- }
- fn raw(&self) -> *mut raw::git_reference {
- self.raw
- }
-}
-
-impl<'repo> Drop for Reference<'repo> {
- fn drop(&mut self) {
- unsafe { raw::git_reference_free(self.raw) }
- }
-}
-
-impl<'repo> References<'repo> {
- /// Consumes a `References` iterator to create an iterator over just the
- /// name of some references.
- ///
- /// This is more efficient if only the names are desired of references as
- /// the references themselves don't have to be allocated and deallocated.
- ///
- /// The returned iterator will yield strings as opposed to a `Reference`.
- pub fn names<'a>(&'a mut self) -> ReferenceNames<'repo, 'a> {
- ReferenceNames { inner: self }
- }
-}
-
-impl<'repo> Binding for References<'repo> {
- type Raw = *mut raw::git_reference_iterator;
- unsafe fn from_raw(raw: *mut raw::git_reference_iterator) -> References<'repo> {
- References {
- raw,
- _marker: marker::PhantomData,
- }
- }
- fn raw(&self) -> *mut raw::git_reference_iterator {
- self.raw
- }
-}
-
-impl<'repo> Iterator for References<'repo> {
- type Item = Result<Reference<'repo>, Error>;
- fn next(&mut self) -> Option<Result<Reference<'repo>, Error>> {
- let mut out = ptr::null_mut();
- unsafe {
- try_call_iter!(raw::git_reference_next(&mut out, self.raw));
- Some(Ok(Binding::from_raw(out)))
- }
- }
-}
-
-impl<'repo> Drop for References<'repo> {
- fn drop(&mut self) {
- unsafe { raw::git_reference_iterator_free(self.raw) }
- }
-}
-
-impl<'repo, 'references> Iterator for ReferenceNames<'repo, 'references> {
- type Item = Result<&'references str, Error>;
- fn next(&mut self) -> Option<Result<&'references str, Error>> {
- let mut out = ptr::null();
- unsafe {
- try_call_iter!(raw::git_reference_next_name(&mut out, self.inner.raw));
- let bytes = crate::opt_bytes(self, out).unwrap();
- let s = str::from_utf8(bytes).unwrap();
- Some(Ok(mem::transmute::<&str, &'references str>(s)))
- }
- }
-}
-
-#[cfg(test)]
-mod tests {
- use crate::{ObjectType, Reference, ReferenceType};
-
- #[test]
- fn is_valid_name() {
- assert!(Reference::is_valid_name("refs/foo"));
- assert!(!Reference::is_valid_name("foo"));
- assert!(Reference::is_valid_name("FOO_BAR"));
-
- assert!(!Reference::is_valid_name("foo"));
- assert!(!Reference::is_valid_name("_FOO_BAR"));
- }
-
- #[test]
- #[should_panic]
- fn is_valid_name_for_invalid_ref() {
- Reference::is_valid_name("ab\012");
- }
-
- #[test]
- fn smoke() {
- let (_td, repo) = crate::test::repo_init();
- let mut head = repo.head().unwrap();
- assert!(head.is_branch());
- assert!(!head.is_remote());
- assert!(!head.is_tag());
- assert!(!head.is_note());
-
- // HEAD is a symbolic reference but git_repository_head resolves it
- // so it is a GIT_REFERENCE_DIRECT.
- assert_eq!(head.kind().unwrap(), ReferenceType::Direct);
-
- assert!(head == repo.head().unwrap());
- assert_eq!(head.name(), Some("refs/heads/main"));
-
- assert!(head == repo.find_reference("refs/heads/main").unwrap());
- assert_eq!(
- repo.refname_to_id("refs/heads/main").unwrap(),
- head.target().unwrap()
- );
-
- assert!(head.symbolic_target().is_none());
- assert!(head.target_peel().is_none());
-
- assert_eq!(head.shorthand(), Some("main"));
- assert!(head.resolve().unwrap() == head);
-
- let mut tag1 = repo
- .reference("refs/tags/tag1", head.target().unwrap(), false, "test")
- .unwrap();
- assert!(tag1.is_tag());
- assert_eq!(tag1.kind().unwrap(), ReferenceType::Direct);
-
- let peeled_commit = tag1.peel(ObjectType::Commit).unwrap();
- assert_eq!(ObjectType::Commit, peeled_commit.kind().unwrap());
- assert_eq!(tag1.target().unwrap(), peeled_commit.id());
-
- tag1.delete().unwrap();
-
- let mut sym1 = repo
- .reference_symbolic("refs/tags/tag1", "refs/heads/main", false, "test")
- .unwrap();
- assert_eq!(sym1.kind().unwrap(), ReferenceType::Symbolic);
- let mut sym2 = repo
- .reference_symbolic("refs/tags/tag2", "refs/heads/main", false, "test")
- .unwrap()
- .symbolic_set_target("refs/tags/tag1", "test")
- .unwrap();
- assert_eq!(sym2.kind().unwrap(), ReferenceType::Symbolic);
- assert_eq!(sym2.symbolic_target().unwrap(), "refs/tags/tag1");
- sym2.delete().unwrap();
- sym1.delete().unwrap();
-
- {
- assert!(repo.references().unwrap().count() == 1);
- assert!(repo.references().unwrap().next().unwrap().unwrap() == head);
- let mut names = repo.references().unwrap();
- let mut names = names.names();
- assert_eq!(names.next().unwrap().unwrap(), "refs/heads/main");
- assert!(names.next().is_none());
- assert!(repo.references_glob("foo").unwrap().count() == 0);
- assert!(repo.references_glob("refs/heads/*").unwrap().count() == 1);
- }
-
- let mut head = head.rename("refs/foo", true, "test").unwrap();
- head.delete().unwrap();
- }
-}
diff --git a/extra/git2/src/reflog.rs b/extra/git2/src/reflog.rs
deleted file mode 100644
index bbd2140ab..000000000
--- a/extra/git2/src/reflog.rs
+++ /dev/null
@@ -1,196 +0,0 @@
-use libc::size_t;
-use std::iter::FusedIterator;
-use std::marker;
-use std::ops::Range;
-use std::str;
-
-use crate::util::Binding;
-use crate::{raw, signature, Error, Oid, Signature};
-
-/// A reference log of a git repository.
-pub struct Reflog {
- raw: *mut raw::git_reflog,
-}
-
-/// An entry inside the reflog of a repository
-pub struct ReflogEntry<'reflog> {
- raw: *const raw::git_reflog_entry,
- _marker: marker::PhantomData<&'reflog Reflog>,
-}
-
-/// An iterator over the entries inside of a reflog.
-pub struct ReflogIter<'reflog> {
- range: Range<usize>,
- reflog: &'reflog Reflog,
-}
-
-impl Reflog {
- /// Add a new entry to the in-memory reflog.
- pub fn append(
- &mut self,
- new_oid: Oid,
- committer: &Signature<'_>,
- msg: Option<&str>,
- ) -> Result<(), Error> {
- let msg = crate::opt_cstr(msg)?;
- unsafe {
- try_call!(raw::git_reflog_append(
- self.raw,
- new_oid.raw(),
- committer.raw(),
- msg
- ));
- }
- Ok(())
- }
-
- /// Remove an entry from the reflog by its index
- ///
- /// To ensure there's no gap in the log history, set rewrite_previous_entry
- /// param value to `true`. When deleting entry n, member old_oid of entry
- /// n-1 (if any) will be updated with the value of member new_oid of entry
- /// n+1.
- pub fn remove(&mut self, i: usize, rewrite_previous_entry: bool) -> Result<(), Error> {
- unsafe {
- try_call!(raw::git_reflog_drop(
- self.raw,
- i as size_t,
- rewrite_previous_entry
- ));
- }
- Ok(())
- }
-
- /// Lookup an entry by its index
- ///
- /// Requesting the reflog entry with an index of 0 (zero) will return the
- /// most recently created entry.
- pub fn get(&self, i: usize) -> Option<ReflogEntry<'_>> {
- unsafe {
- let ptr = raw::git_reflog_entry_byindex(self.raw, i as size_t);
- Binding::from_raw_opt(ptr)
- }
- }
-
- /// Get the number of log entries in a reflog
- pub fn len(&self) -> usize {
- unsafe { raw::git_reflog_entrycount(self.raw) as usize }
- }
-
- /// Return `true ` is there is no log entry in a reflog
- pub fn is_empty(&self) -> bool {
- self.len() == 0
- }
-
- /// Get an iterator to all entries inside of this reflog
- pub fn iter(&self) -> ReflogIter<'_> {
- ReflogIter {
- range: 0..self.len(),
- reflog: self,
- }
- }
-
- /// Write an existing in-memory reflog object back to disk using an atomic
- /// file lock.
- pub fn write(&mut self) -> Result<(), Error> {
- unsafe {
- try_call!(raw::git_reflog_write(self.raw));
- }
- Ok(())
- }
-}
-
-impl Binding for Reflog {
- type Raw = *mut raw::git_reflog;
-
- unsafe fn from_raw(raw: *mut raw::git_reflog) -> Reflog {
- Reflog { raw }
- }
- fn raw(&self) -> *mut raw::git_reflog {
- self.raw
- }
-}
-
-impl Drop for Reflog {
- fn drop(&mut self) {
- unsafe { raw::git_reflog_free(self.raw) }
- }
-}
-
-impl<'reflog> ReflogEntry<'reflog> {
- /// Get the committer of this entry
- pub fn committer(&self) -> Signature<'_> {
- unsafe {
- let ptr = raw::git_reflog_entry_committer(self.raw);
- signature::from_raw_const(self, ptr)
- }
- }
-
- /// Get the new oid
- pub fn id_new(&self) -> Oid {
- unsafe { Binding::from_raw(raw::git_reflog_entry_id_new(self.raw)) }
- }
-
- /// Get the old oid
- pub fn id_old(&self) -> Oid {
- unsafe { Binding::from_raw(raw::git_reflog_entry_id_old(self.raw)) }
- }
-
- /// Get the log message, returning `None` on invalid UTF-8.
- pub fn message(&self) -> Option<&str> {
- self.message_bytes().and_then(|s| str::from_utf8(s).ok())
- }
-
- /// Get the log message as a byte array.
- pub fn message_bytes(&self) -> Option<&[u8]> {
- unsafe { crate::opt_bytes(self, raw::git_reflog_entry_message(self.raw)) }
- }
-}
-
-impl<'reflog> Binding for ReflogEntry<'reflog> {
- type Raw = *const raw::git_reflog_entry;
-
- unsafe fn from_raw(raw: *const raw::git_reflog_entry) -> ReflogEntry<'reflog> {
- ReflogEntry {
- raw,
- _marker: marker::PhantomData,
- }
- }
- fn raw(&self) -> *const raw::git_reflog_entry {
- self.raw
- }
-}
-
-impl<'reflog> Iterator for ReflogIter<'reflog> {
- type Item = ReflogEntry<'reflog>;
- fn next(&mut self) -> Option<ReflogEntry<'reflog>> {
- self.range.next().and_then(|i| self.reflog.get(i))
- }
- fn size_hint(&self) -> (usize, Option<usize>) {
- self.range.size_hint()
- }
-}
-impl<'reflog> DoubleEndedIterator for ReflogIter<'reflog> {
- fn next_back(&mut self) -> Option<ReflogEntry<'reflog>> {
- self.range.next_back().and_then(|i| self.reflog.get(i))
- }
-}
-impl<'reflog> FusedIterator for ReflogIter<'reflog> {}
-impl<'reflog> ExactSizeIterator for ReflogIter<'reflog> {}
-
-#[cfg(test)]
-mod tests {
- #[test]
- fn smoke() {
- let (_td, repo) = crate::test::repo_init();
- let mut reflog = repo.reflog("HEAD").unwrap();
- assert_eq!(reflog.iter().len(), 1);
- reflog.write().unwrap();
-
- let entry = reflog.iter().next().unwrap();
- assert!(entry.message().is_some());
-
- repo.reflog_rename("HEAD", "refs/heads/foo").unwrap();
- repo.reflog_delete("refs/heads/foo").unwrap();
- }
-}
diff --git a/extra/git2/src/refspec.rs b/extra/git2/src/refspec.rs
deleted file mode 100644
index 3f62e991c..000000000
--- a/extra/git2/src/refspec.rs
+++ /dev/null
@@ -1,122 +0,0 @@
-use std::ffi::CString;
-use std::marker;
-use std::str;
-
-use crate::util::Binding;
-use crate::{raw, Buf, Direction, Error};
-
-/// A structure to represent a git [refspec][1].
-///
-/// Refspecs are currently mainly accessed/created through a `Remote`.
-///
-/// [1]: http://git-scm.com/book/en/Git-Internals-The-Refspec
-pub struct Refspec<'remote> {
- raw: *const raw::git_refspec,
- _marker: marker::PhantomData<&'remote raw::git_remote>,
-}
-
-impl<'remote> Refspec<'remote> {
- /// Get the refspec's direction.
- pub fn direction(&self) -> Direction {
- match unsafe { raw::git_refspec_direction(self.raw) } {
- raw::GIT_DIRECTION_FETCH => Direction::Fetch,
- raw::GIT_DIRECTION_PUSH => Direction::Push,
- n => panic!("unknown refspec direction: {}", n),
- }
- }
-
- /// Get the destination specifier.
- ///
- /// If the destination is not utf-8, None is returned.
- pub fn dst(&self) -> Option<&str> {
- str::from_utf8(self.dst_bytes()).ok()
- }
-
- /// Get the destination specifier, in bytes.
- pub fn dst_bytes(&self) -> &[u8] {
- unsafe { crate::opt_bytes(self, raw::git_refspec_dst(self.raw)).unwrap() }
- }
-
- /// Check if a refspec's destination descriptor matches a reference
- pub fn dst_matches(&self, refname: &str) -> bool {
- let refname = CString::new(refname).unwrap();
- unsafe { raw::git_refspec_dst_matches(self.raw, refname.as_ptr()) == 1 }
- }
-
- /// Get the source specifier.
- ///
- /// If the source is not utf-8, None is returned.
- pub fn src(&self) -> Option<&str> {
- str::from_utf8(self.src_bytes()).ok()
- }
-
- /// Get the source specifier, in bytes.
- pub fn src_bytes(&self) -> &[u8] {
- unsafe { crate::opt_bytes(self, raw::git_refspec_src(self.raw)).unwrap() }
- }
-
- /// Check if a refspec's source descriptor matches a reference
- pub fn src_matches(&self, refname: &str) -> bool {
- let refname = CString::new(refname).unwrap();
- unsafe { raw::git_refspec_src_matches(self.raw, refname.as_ptr()) == 1 }
- }
-
- /// Get the force update setting.
- pub fn is_force(&self) -> bool {
- unsafe { raw::git_refspec_force(self.raw) == 1 }
- }
-
- /// Get the refspec's string.
- ///
- /// Returns None if the string is not valid utf8.
- pub fn str(&self) -> Option<&str> {
- str::from_utf8(self.bytes()).ok()
- }
-
- /// Get the refspec's string as a byte array
- pub fn bytes(&self) -> &[u8] {
- unsafe { crate::opt_bytes(self, raw::git_refspec_string(self.raw)).unwrap() }
- }
-
- /// Transform a reference to its target following the refspec's rules
- pub fn transform(&self, name: &str) -> Result<Buf, Error> {
- let name = CString::new(name).unwrap();
- unsafe {
- let buf = Buf::new();
- try_call!(raw::git_refspec_transform(
- buf.raw(),
- self.raw,
- name.as_ptr()
- ));
- Ok(buf)
- }
- }
-
- /// Transform a target reference to its source reference following the refspec's rules
- pub fn rtransform(&self, name: &str) -> Result<Buf, Error> {
- let name = CString::new(name).unwrap();
- unsafe {
- let buf = Buf::new();
- try_call!(raw::git_refspec_rtransform(
- buf.raw(),
- self.raw,
- name.as_ptr()
- ));
- Ok(buf)
- }
- }
-}
-
-impl<'remote> Binding for Refspec<'remote> {
- type Raw = *const raw::git_refspec;
-
- unsafe fn from_raw(raw: *const raw::git_refspec) -> Refspec<'remote> {
- Refspec {
- raw,
- _marker: marker::PhantomData,
- }
- }
- fn raw(&self) -> *const raw::git_refspec {
- self.raw
- }
-}
diff --git a/extra/git2/src/remote.rs b/extra/git2/src/remote.rs
deleted file mode 100644
index c8f5a935a..000000000
--- a/extra/git2/src/remote.rs
+++ /dev/null
@@ -1,1123 +0,0 @@
-use libc;
-use raw::git_strarray;
-use std::iter::FusedIterator;
-use std::marker;
-use std::mem;
-use std::ops::Range;
-use std::ptr;
-use std::slice;
-use std::str;
-use std::{ffi::CString, os::raw::c_char};
-
-use crate::string_array::StringArray;
-use crate::util::Binding;
-use crate::{call, raw, Buf, Direction, Error, FetchPrune, Oid, ProxyOptions, Refspec};
-use crate::{AutotagOption, Progress, RemoteCallbacks, Repository};
-
-/// A structure representing a [remote][1] of a git repository.
-///
-/// [1]: http://git-scm.com/book/en/Git-Basics-Working-with-Remotes
-///
-/// The lifetime is the lifetime of the repository that it is attached to. The
-/// remote is used to manage fetches and pushes as well as refspecs.
-pub struct Remote<'repo> {
- raw: *mut raw::git_remote,
- _marker: marker::PhantomData<&'repo Repository>,
-}
-
-/// An iterator over the refspecs that a remote contains.
-pub struct Refspecs<'remote> {
- range: Range<usize>,
- remote: &'remote Remote<'remote>,
-}
-
-/// Description of a reference advertised by a remote server, given out on calls
-/// to `list`.
-pub struct RemoteHead<'remote> {
- raw: *const raw::git_remote_head,
- _marker: marker::PhantomData<&'remote str>,
-}
-
-/// Options which can be specified to various fetch operations.
-pub struct FetchOptions<'cb> {
- callbacks: Option<RemoteCallbacks<'cb>>,
- depth: i32,
- proxy: Option<ProxyOptions<'cb>>,
- prune: FetchPrune,
- update_fetchhead: bool,
- download_tags: AutotagOption,
- follow_redirects: RemoteRedirect,
- custom_headers: Vec<CString>,
- custom_headers_ptrs: Vec<*const c_char>,
-}
-
-/// Options to control the behavior of a git push.
-pub struct PushOptions<'cb> {
- callbacks: Option<RemoteCallbacks<'cb>>,
- proxy: Option<ProxyOptions<'cb>>,
- pb_parallelism: u32,
- follow_redirects: RemoteRedirect,
- custom_headers: Vec<CString>,
- custom_headers_ptrs: Vec<*const c_char>,
-}
-
-/// Holds callbacks for a connection to a `Remote`. Disconnects when dropped
-pub struct RemoteConnection<'repo, 'connection, 'cb> {
- _callbacks: Box<RemoteCallbacks<'cb>>,
- _proxy: ProxyOptions<'cb>,
- remote: &'connection mut Remote<'repo>,
-}
-
-/// Remote redirection settings; whether redirects to another host are
-/// permitted.
-///
-/// By default, git will follow a redirect on the initial request
-/// (`/info/refs`), but not subsequent requests.
-pub enum RemoteRedirect {
- /// Do not follow any off-site redirects at any stage of the fetch or push.
- None,
- /// Allow off-site redirects only upon the initial request. This is the
- /// default.
- Initial,
- /// Allow redirects at any stage in the fetch or push.
- All,
-}
-
-pub fn remote_into_raw(remote: Remote<'_>) -> *mut raw::git_remote {
- let ret = remote.raw;
- mem::forget(remote);
- ret
-}
-
-impl<'repo> Remote<'repo> {
- /// Ensure the remote name is well-formed.
- pub fn is_valid_name(remote_name: &str) -> bool {
- crate::init();
- let remote_name = CString::new(remote_name).unwrap();
- let mut valid: libc::c_int = 0;
- unsafe {
- call::c_try(raw::git_remote_name_is_valid(
- &mut valid,
- remote_name.as_ptr(),
- ))
- .unwrap();
- }
- valid == 1
- }
-
- /// Create a detached remote
- ///
- /// Create a remote with the given URL in-memory. You can use this
- /// when you have a URL instead of a remote's name.
- /// Contrasted with an anonymous remote, a detached remote will not
- /// consider any repo configuration values.
- pub fn create_detached<S: Into<Vec<u8>>>(url: S) -> Result<Remote<'repo>, Error> {
- crate::init();
- let mut ret = ptr::null_mut();
- let url = CString::new(url)?;
- unsafe {
- try_call!(raw::git_remote_create_detached(&mut ret, url));
- Ok(Binding::from_raw(ret))
- }
- }
-
- /// Get the remote's name.
- ///
- /// Returns `None` if this remote has not yet been named or if the name is
- /// not valid utf-8
- pub fn name(&self) -> Option<&str> {
- self.name_bytes().and_then(|s| str::from_utf8(s).ok())
- }
-
- /// Get the remote's name, in bytes.
- ///
- /// Returns `None` if this remote has not yet been named
- pub fn name_bytes(&self) -> Option<&[u8]> {
- unsafe { crate::opt_bytes(self, raw::git_remote_name(&*self.raw)) }
- }
-
- /// Get the remote's URL.
- ///
- /// Returns `None` if the URL is not valid utf-8
- pub fn url(&self) -> Option<&str> {
- str::from_utf8(self.url_bytes()).ok()
- }
-
- /// Get the remote's URL as a byte array.
- pub fn url_bytes(&self) -> &[u8] {
- unsafe { crate::opt_bytes(self, raw::git_remote_url(&*self.raw)).unwrap() }
- }
-
- /// Get the remote's pushurl.
- ///
- /// Returns `None` if the pushurl is not valid utf-8
- pub fn pushurl(&self) -> Option<&str> {
- self.pushurl_bytes().and_then(|s| str::from_utf8(s).ok())
- }
-
- /// Get the remote's pushurl as a byte array.
- pub fn pushurl_bytes(&self) -> Option<&[u8]> {
- unsafe { crate::opt_bytes(self, raw::git_remote_pushurl(&*self.raw)) }
- }
-
- /// Get the remote's default branch.
- ///
- /// The remote (or more exactly its transport) must have connected to the
- /// remote repository. This default branch is available as soon as the
- /// connection to the remote is initiated and it remains available after
- /// disconnecting.
- pub fn default_branch(&self) -> Result<Buf, Error> {
- unsafe {
- let buf = Buf::new();
- try_call!(raw::git_remote_default_branch(buf.raw(), self.raw));
- Ok(buf)
- }
- }
-
- /// Open a connection to a remote.
- pub fn connect(&mut self, dir: Direction) -> Result<(), Error> {
- // TODO: can callbacks be exposed safely?
- unsafe {
- try_call!(raw::git_remote_connect(
- self.raw,
- dir,
- ptr::null(),
- ptr::null(),
- ptr::null()
- ));
- }
- Ok(())
- }
-
- /// Open a connection to a remote with callbacks and proxy settings
- ///
- /// Returns a `RemoteConnection` that will disconnect once dropped
- pub fn connect_auth<'connection, 'cb>(
- &'connection mut self,
- dir: Direction,
- cb: Option<RemoteCallbacks<'cb>>,
- proxy_options: Option<ProxyOptions<'cb>>,
- ) -> Result<RemoteConnection<'repo, 'connection, 'cb>, Error> {
- let cb = Box::new(cb.unwrap_or_else(RemoteCallbacks::new));
- let proxy_options = proxy_options.unwrap_or_else(ProxyOptions::new);
- unsafe {
- try_call!(raw::git_remote_connect(
- self.raw,
- dir,
- &cb.raw(),
- &proxy_options.raw(),
- ptr::null()
- ));
- }
-
- Ok(RemoteConnection {
- _callbacks: cb,
- _proxy: proxy_options,
- remote: self,
- })
- }
-
- /// Check whether the remote is connected
- pub fn connected(&mut self) -> bool {
- unsafe { raw::git_remote_connected(self.raw) == 1 }
- }
-
- /// Disconnect from the remote
- pub fn disconnect(&mut self) -> Result<(), Error> {
- unsafe {
- try_call!(raw::git_remote_disconnect(self.raw));
- }
- Ok(())
- }
-
- /// Download and index the packfile
- ///
- /// Connect to the remote if it hasn't been done yet, negotiate with the
- /// remote git which objects are missing, download and index the packfile.
- ///
- /// The .idx file will be created and both it and the packfile with be
- /// renamed to their final name.
- ///
- /// The `specs` argument is a list of refspecs to use for this negotiation
- /// and download. Use an empty array to use the base refspecs.
- pub fn download<Str: AsRef<str> + crate::IntoCString + Clone>(
- &mut self,
- specs: &[Str],
- opts: Option<&mut FetchOptions<'_>>,
- ) -> Result<(), Error> {
- let (_a, _b, arr) = crate::util::iter2cstrs(specs.iter())?;
- let raw = opts.map(|o| o.raw());
- unsafe {
- try_call!(raw::git_remote_download(self.raw, &arr, raw.as_ref()));
- }
- Ok(())
- }
-
- /// Cancel the operation
- ///
- /// At certain points in its operation, the network code checks whether the
- /// operation has been canceled and if so stops the operation.
- pub fn stop(&mut self) -> Result<(), Error> {
- unsafe {
- try_call!(raw::git_remote_stop(self.raw));
- }
- Ok(())
- }
-
- /// Get the number of refspecs for a remote
- pub fn refspecs(&self) -> Refspecs<'_> {
- let cnt = unsafe { raw::git_remote_refspec_count(&*self.raw) as usize };
- Refspecs {
- range: 0..cnt,
- remote: self,
- }
- }
-
- /// Get the `nth` refspec from this remote.
- ///
- /// The `refspecs` iterator can be used to iterate over all refspecs.
- pub fn get_refspec(&self, i: usize) -> Option<Refspec<'repo>> {
- unsafe {
- let ptr = raw::git_remote_get_refspec(&*self.raw, i as libc::size_t);
- Binding::from_raw_opt(ptr)
- }
- }
-
- /// Download new data and update tips
- ///
- /// Convenience function to connect to a remote, download the data,
- /// disconnect and update the remote-tracking branches.
- ///
- /// # Examples
- ///
- /// Example of functionality similar to `git fetch origin/main`:
- ///
- /// ```no_run
- /// fn fetch_origin_main(repo: git2::Repository) -> Result<(), git2::Error> {
- /// repo.find_remote("origin")?.fetch(&["main"], None, None)
- /// }
- ///
- /// let repo = git2::Repository::discover("rust").unwrap();
- /// fetch_origin_main(repo).unwrap();
- /// ```
- pub fn fetch<Str: AsRef<str> + crate::IntoCString + Clone>(
- &mut self,
- refspecs: &[Str],
- opts: Option<&mut FetchOptions<'_>>,
- reflog_msg: Option<&str>,
- ) -> Result<(), Error> {
- let (_a, _b, arr) = crate::util::iter2cstrs(refspecs.iter())?;
- let msg = crate::opt_cstr(reflog_msg)?;
- let raw = opts.map(|o| o.raw());
- unsafe {
- try_call!(raw::git_remote_fetch(self.raw, &arr, raw.as_ref(), msg));
- }
- Ok(())
- }
-
- /// Update the tips to the new state
- pub fn update_tips(
- &mut self,
- callbacks: Option<&mut RemoteCallbacks<'_>>,
- update_fetchhead: bool,
- download_tags: AutotagOption,
- msg: Option<&str>,
- ) -> Result<(), Error> {
- let msg = crate::opt_cstr(msg)?;
- let cbs = callbacks.map(|cb| cb.raw());
- unsafe {
- try_call!(raw::git_remote_update_tips(
- self.raw,
- cbs.as_ref(),
- update_fetchhead,
- download_tags,
- msg
- ));
- }
- Ok(())
- }
-
- /// Perform a push
- ///
- /// Perform all the steps for a push. If no refspecs are passed then the
- /// configured refspecs will be used.
- ///
- /// Note that you'll likely want to use `RemoteCallbacks` and set
- /// `push_update_reference` to test whether all the references were pushed
- /// successfully.
- pub fn push<Str: AsRef<str> + crate::IntoCString + Clone>(
- &mut self,
- refspecs: &[Str],
- opts: Option<&mut PushOptions<'_>>,
- ) -> Result<(), Error> {
- let (_a, _b, arr) = crate::util::iter2cstrs(refspecs.iter())?;
- let raw = opts.map(|o| o.raw());
- unsafe {
- try_call!(raw::git_remote_push(self.raw, &arr, raw.as_ref()));
- }
- Ok(())
- }
-
- /// Get the statistics structure that is filled in by the fetch operation.
- pub fn stats(&self) -> Progress<'_> {
- unsafe { Binding::from_raw(raw::git_remote_stats(self.raw)) }
- }
-
- /// Get the remote repository's reference advertisement list.
- ///
- /// Get the list of references with which the server responds to a new
- /// connection.
- ///
- /// The remote (or more exactly its transport) must have connected to the
- /// remote repository. This list is available as soon as the connection to
- /// the remote is initiated and it remains available after disconnecting.
- pub fn list(&self) -> Result<&[RemoteHead<'_>], Error> {
- let mut size = 0;
- let mut base = ptr::null_mut();
- unsafe {
- try_call!(raw::git_remote_ls(&mut base, &mut size, self.raw));
- assert_eq!(
- mem::size_of::<RemoteHead<'_>>(),
- mem::size_of::<*const raw::git_remote_head>()
- );
- let slice = slice::from_raw_parts(base as *const _, size as usize);
- Ok(mem::transmute::<
- &[*const raw::git_remote_head],
- &[RemoteHead<'_>],
- >(slice))
- }
- }
-
- /// Prune tracking refs that are no longer present on remote
- pub fn prune(&mut self, callbacks: Option<RemoteCallbacks<'_>>) -> Result<(), Error> {
- let cbs = Box::new(callbacks.unwrap_or_else(RemoteCallbacks::new));
- unsafe {
- try_call!(raw::git_remote_prune(self.raw, &cbs.raw()));
- }
- Ok(())
- }
-
- /// Get the remote's list of fetch refspecs
- pub fn fetch_refspecs(&self) -> Result<StringArray, Error> {
- unsafe {
- let mut raw: raw::git_strarray = mem::zeroed();
- try_call!(raw::git_remote_get_fetch_refspecs(&mut raw, self.raw));
- Ok(StringArray::from_raw(raw))
- }
- }
-
- /// Get the remote's list of push refspecs
- pub fn push_refspecs(&self) -> Result<StringArray, Error> {
- unsafe {
- let mut raw: raw::git_strarray = mem::zeroed();
- try_call!(raw::git_remote_get_push_refspecs(&mut raw, self.raw));
- Ok(StringArray::from_raw(raw))
- }
- }
-}
-
-impl<'repo> Clone for Remote<'repo> {
- fn clone(&self) -> Remote<'repo> {
- let mut ret = ptr::null_mut();
- let rc = unsafe { call!(raw::git_remote_dup(&mut ret, self.raw)) };
- assert_eq!(rc, 0);
- Remote {
- raw: ret,
- _marker: marker::PhantomData,
- }
- }
-}
-
-impl<'repo> Binding for Remote<'repo> {
- type Raw = *mut raw::git_remote;
-
- unsafe fn from_raw(raw: *mut raw::git_remote) -> Remote<'repo> {
- Remote {
- raw,
- _marker: marker::PhantomData,
- }
- }
- fn raw(&self) -> *mut raw::git_remote {
- self.raw
- }
-}
-
-impl<'repo> Drop for Remote<'repo> {
- fn drop(&mut self) {
- unsafe { raw::git_remote_free(self.raw) }
- }
-}
-
-impl<'repo> Iterator for Refspecs<'repo> {
- type Item = Refspec<'repo>;
- fn next(&mut self) -> Option<Refspec<'repo>> {
- self.range.next().and_then(|i| self.remote.get_refspec(i))
- }
- fn size_hint(&self) -> (usize, Option<usize>) {
- self.range.size_hint()
- }
-}
-impl<'repo> DoubleEndedIterator for Refspecs<'repo> {
- fn next_back(&mut self) -> Option<Refspec<'repo>> {
- self.range
- .next_back()
- .and_then(|i| self.remote.get_refspec(i))
- }
-}
-impl<'repo> FusedIterator for Refspecs<'repo> {}
-impl<'repo> ExactSizeIterator for Refspecs<'repo> {}
-
-#[allow(missing_docs)] // not documented in libgit2 :(
-impl<'remote> RemoteHead<'remote> {
- /// Flag if this is available locally.
- pub fn is_local(&self) -> bool {
- unsafe { (*self.raw).local != 0 }
- }
-
- pub fn oid(&self) -> Oid {
- unsafe { Binding::from_raw(&(*self.raw).oid as *const _) }
- }
- pub fn loid(&self) -> Oid {
- unsafe { Binding::from_raw(&(*self.raw).loid as *const _) }
- }
-
- pub fn name(&self) -> &str {
- let b = unsafe { crate::opt_bytes(self, (*self.raw).name).unwrap() };
- str::from_utf8(b).unwrap()
- }
-
- pub fn symref_target(&self) -> Option<&str> {
- let b = unsafe { crate::opt_bytes(self, (*self.raw).symref_target) };
- b.map(|b| str::from_utf8(b).unwrap())
- }
-}
-
-impl<'cb> Default for FetchOptions<'cb> {
- fn default() -> Self {
- Self::new()
- }
-}
-
-impl<'cb> FetchOptions<'cb> {
- /// Creates a new blank set of fetch options
- pub fn new() -> FetchOptions<'cb> {
- FetchOptions {
- callbacks: None,
- proxy: None,
- prune: FetchPrune::Unspecified,
- update_fetchhead: true,
- download_tags: AutotagOption::Unspecified,
- follow_redirects: RemoteRedirect::Initial,
- custom_headers: Vec::new(),
- custom_headers_ptrs: Vec::new(),
- depth: 0, // Not limited depth
- }
- }
-
- /// Set the callbacks to use for the fetch operation.
- pub fn remote_callbacks(&mut self, cbs: RemoteCallbacks<'cb>) -> &mut Self {
- self.callbacks = Some(cbs);
- self
- }
-
- /// Set the proxy options to use for the fetch operation.
- pub fn proxy_options(&mut self, opts: ProxyOptions<'cb>) -> &mut Self {
- self.proxy = Some(opts);
- self
- }
-
- /// Set whether to perform a prune after the fetch.
- pub fn prune(&mut self, prune: FetchPrune) -> &mut Self {
- self.prune = prune;
- self
- }
-
- /// Set whether to write the results to FETCH_HEAD.
- ///
- /// Defaults to `true`.
- pub fn update_fetchhead(&mut self, update: bool) -> &mut Self {
- self.update_fetchhead = update;
- self
- }
-
- /// Set fetch depth, a value less or equal to 0 is interpreted as pull
- /// everything (effectively the same as not declaring a limit depth).
-
- // FIXME(blyxyas): We currently don't have a test for shallow functions
- // because libgit2 doesn't support local shallow clones.
- // https://github.com/rust-lang/git2-rs/pull/979#issuecomment-1716299900
- pub fn depth(&mut self, depth: i32) -> &mut Self {
- self.depth = depth.max(0);
- self
- }
-
- /// Set how to behave regarding tags on the remote, such as auto-downloading
- /// tags for objects we're downloading or downloading all of them.
- ///
- /// The default is to auto-follow tags.
- pub fn download_tags(&mut self, opt: AutotagOption) -> &mut Self {
- self.download_tags = opt;
- self
- }
-
- /// Set remote redirection settings; whether redirects to another host are
- /// permitted.
- ///
- /// By default, git will follow a redirect on the initial request
- /// (`/info/refs`), but not subsequent requests.
- pub fn follow_redirects(&mut self, redirect: RemoteRedirect) -> &mut Self {
- self.follow_redirects = redirect;
- self
- }
-
- /// Set extra headers for this fetch operation.
- pub fn custom_headers(&mut self, custom_headers: &[&str]) -> &mut Self {
- self.custom_headers = custom_headers
- .iter()
- .map(|&s| CString::new(s).unwrap())
- .collect();
- self.custom_headers_ptrs = self.custom_headers.iter().map(|s| s.as_ptr()).collect();
- self
- }
-}
-
-impl<'cb> Binding for FetchOptions<'cb> {
- type Raw = raw::git_fetch_options;
-
- unsafe fn from_raw(_raw: raw::git_fetch_options) -> FetchOptions<'cb> {
- panic!("unimplemented");
- }
- fn raw(&self) -> raw::git_fetch_options {
- raw::git_fetch_options {
- version: 1,
- callbacks: self
- .callbacks
- .as_ref()
- .map(|m| m.raw())
- .unwrap_or_else(|| RemoteCallbacks::new().raw()),
- proxy_opts: self
- .proxy
- .as_ref()
- .map(|m| m.raw())
- .unwrap_or_else(|| ProxyOptions::new().raw()),
- prune: crate::call::convert(&self.prune),
- update_fetchhead: crate::call::convert(&self.update_fetchhead),
- download_tags: crate::call::convert(&self.download_tags),
- depth: self.depth,
- follow_redirects: self.follow_redirects.raw(),
- custom_headers: git_strarray {
- count: self.custom_headers_ptrs.len(),
- strings: self.custom_headers_ptrs.as_ptr() as *mut _,
- },
- }
- }
-}
-
-impl<'cb> Default for PushOptions<'cb> {
- fn default() -> Self {
- Self::new()
- }
-}
-
-impl<'cb> PushOptions<'cb> {
- /// Creates a new blank set of push options
- pub fn new() -> PushOptions<'cb> {
- PushOptions {
- callbacks: None,
- proxy: None,
- pb_parallelism: 1,
- follow_redirects: RemoteRedirect::Initial,
- custom_headers: Vec::new(),
- custom_headers_ptrs: Vec::new(),
- }
- }
-
- /// Set the callbacks to use for the push operation.
- pub fn remote_callbacks(&mut self, cbs: RemoteCallbacks<'cb>) -> &mut Self {
- self.callbacks = Some(cbs);
- self
- }
-
- /// Set the proxy options to use for the push operation.
- pub fn proxy_options(&mut self, opts: ProxyOptions<'cb>) -> &mut Self {
- self.proxy = Some(opts);
- self
- }
-
- /// If the transport being used to push to the remote requires the creation
- /// of a pack file, this controls the number of worker threads used by the
- /// packbuilder when creating that pack file to be sent to the remote.
- ///
- /// if set to 0 the packbuilder will auto-detect the number of threads to
- /// create, and the default value is 1.
- pub fn packbuilder_parallelism(&mut self, parallel: u32) -> &mut Self {
- self.pb_parallelism = parallel;
- self
- }
-
- /// Set remote redirection settings; whether redirects to another host are
- /// permitted.
- ///
- /// By default, git will follow a redirect on the initial request
- /// (`/info/refs`), but not subsequent requests.
- pub fn follow_redirects(&mut self, redirect: RemoteRedirect) -> &mut Self {
- self.follow_redirects = redirect;
- self
- }
-
- /// Set extra headers for this push operation.
- pub fn custom_headers(&mut self, custom_headers: &[&str]) -> &mut Self {
- self.custom_headers = custom_headers
- .iter()
- .map(|&s| CString::new(s).unwrap())
- .collect();
- self.custom_headers_ptrs = self.custom_headers.iter().map(|s| s.as_ptr()).collect();
- self
- }
-}
-
-impl<'cb> Binding for PushOptions<'cb> {
- type Raw = raw::git_push_options;
-
- unsafe fn from_raw(_raw: raw::git_push_options) -> PushOptions<'cb> {
- panic!("unimplemented");
- }
- fn raw(&self) -> raw::git_push_options {
- raw::git_push_options {
- version: 1,
- callbacks: self
- .callbacks
- .as_ref()
- .map(|m| m.raw())
- .unwrap_or_else(|| RemoteCallbacks::new().raw()),
- proxy_opts: self
- .proxy
- .as_ref()
- .map(|m| m.raw())
- .unwrap_or_else(|| ProxyOptions::new().raw()),
- pb_parallelism: self.pb_parallelism as libc::c_uint,
- follow_redirects: self.follow_redirects.raw(),
- custom_headers: git_strarray {
- count: self.custom_headers_ptrs.len(),
- strings: self.custom_headers_ptrs.as_ptr() as *mut _,
- },
- }
- }
-}
-
-impl<'repo, 'connection, 'cb> RemoteConnection<'repo, 'connection, 'cb> {
- /// Check whether the remote is (still) connected
- pub fn connected(&mut self) -> bool {
- self.remote.connected()
- }
-
- /// Get the remote repository's reference advertisement list.
- ///
- /// This list is available as soon as the connection to
- /// the remote is initiated and it remains available after disconnecting.
- pub fn list(&self) -> Result<&[RemoteHead<'_>], Error> {
- self.remote.list()
- }
-
- /// Get the remote's default branch.
- ///
- /// This default branch is available as soon as the connection to the remote
- /// is initiated and it remains available after disconnecting.
- pub fn default_branch(&self) -> Result<Buf, Error> {
- self.remote.default_branch()
- }
-
- /// access remote bound to this connection
- pub fn remote(&mut self) -> &mut Remote<'repo> {
- self.remote
- }
-}
-
-impl<'repo, 'connection, 'cb> Drop for RemoteConnection<'repo, 'connection, 'cb> {
- fn drop(&mut self) {
- drop(self.remote.disconnect());
- }
-}
-
-impl Default for RemoteRedirect {
- fn default() -> Self {
- RemoteRedirect::Initial
- }
-}
-
-impl RemoteRedirect {
- fn raw(&self) -> raw::git_remote_redirect_t {
- match self {
- RemoteRedirect::None => raw::GIT_REMOTE_REDIRECT_NONE,
- RemoteRedirect::Initial => raw::GIT_REMOTE_REDIRECT_INITIAL,
- RemoteRedirect::All => raw::GIT_REMOTE_REDIRECT_ALL,
- }
- }
-}
-
-#[cfg(test)]
-mod tests {
- use crate::{AutotagOption, PushOptions};
- use crate::{Direction, FetchOptions, Remote, RemoteCallbacks, Repository};
- use std::cell::Cell;
- use tempfile::TempDir;
-
- #[test]
- fn smoke() {
- let (td, repo) = crate::test::repo_init();
- t!(repo.remote("origin", "/path/to/nowhere"));
- drop(repo);
-
- let repo = t!(Repository::init(td.path()));
- let mut origin = t!(repo.find_remote("origin"));
- assert_eq!(origin.name(), Some("origin"));
- assert_eq!(origin.url(), Some("/path/to/nowhere"));
- assert_eq!(origin.pushurl(), None);
-
- t!(repo.remote_set_url("origin", "/path/to/elsewhere"));
- t!(repo.remote_set_pushurl("origin", Some("/path/to/elsewhere")));
-
- let stats = origin.stats();
- assert_eq!(stats.total_objects(), 0);
-
- t!(origin.stop());
- }
-
- #[test]
- fn create_remote() {
- let td = TempDir::new().unwrap();
- let remote = td.path().join("remote");
- Repository::init_bare(&remote).unwrap();
-
- let (_td, repo) = crate::test::repo_init();
- let url = if cfg!(unix) {
- format!("file://{}", remote.display())
- } else {
- format!(
- "file:///{}",
- remote.display().to_string().replace("\\", "/")
- )
- };
-
- let mut origin = repo.remote("origin", &url).unwrap();
- assert_eq!(origin.name(), Some("origin"));
- assert_eq!(origin.url(), Some(&url[..]));
- assert_eq!(origin.pushurl(), None);
-
- {
- let mut specs = origin.refspecs();
- let spec = specs.next().unwrap();
- assert!(specs.next().is_none());
- assert_eq!(spec.str(), Some("+refs/heads/*:refs/remotes/origin/*"));
- assert_eq!(spec.dst(), Some("refs/remotes/origin/*"));
- assert_eq!(spec.src(), Some("refs/heads/*"));
- assert!(spec.is_force());
- }
- assert!(origin.refspecs().next_back().is_some());
- {
- let remotes = repo.remotes().unwrap();
- assert_eq!(remotes.len(), 1);
- assert_eq!(remotes.get(0), Some("origin"));
- assert_eq!(remotes.iter().count(), 1);
- assert_eq!(remotes.iter().next().unwrap(), Some("origin"));
- }
-
- origin.connect(Direction::Push).unwrap();
- assert!(origin.connected());
- origin.disconnect().unwrap();
-
- origin.connect(Direction::Fetch).unwrap();
- assert!(origin.connected());
- origin.download(&[] as &[&str], None).unwrap();
- origin.disconnect().unwrap();
-
- {
- let mut connection = origin.connect_auth(Direction::Push, None, None).unwrap();
- assert!(connection.connected());
- }
- assert!(!origin.connected());
-
- {
- let mut connection = origin.connect_auth(Direction::Fetch, None, None).unwrap();
- assert!(connection.connected());
- }
- assert!(!origin.connected());
-
- origin.fetch(&[] as &[&str], None, None).unwrap();
- origin.fetch(&[] as &[&str], None, Some("foo")).unwrap();
- origin
- .update_tips(None, true, AutotagOption::Unspecified, None)
- .unwrap();
- origin
- .update_tips(None, true, AutotagOption::All, Some("foo"))
- .unwrap();
-
- t!(repo.remote_add_fetch("origin", "foo"));
- t!(repo.remote_add_fetch("origin", "bar"));
- }
-
- #[test]
- fn rename_remote() {
- let (_td, repo) = crate::test::repo_init();
- repo.remote("origin", "foo").unwrap();
- drop(repo.remote_rename("origin", "foo"));
- drop(repo.remote_delete("foo"));
- }
-
- #[test]
- fn create_remote_anonymous() {
- let td = TempDir::new().unwrap();
- let repo = Repository::init(td.path()).unwrap();
-
- let origin = repo.remote_anonymous("/path/to/nowhere").unwrap();
- assert_eq!(origin.name(), None);
- drop(origin.clone());
- }
-
- #[test]
- fn is_valid_name() {
- assert!(Remote::is_valid_name("foobar"));
- assert!(!Remote::is_valid_name("\x01"));
- }
-
- #[test]
- #[should_panic]
- fn is_valid_name_for_invalid_remote() {
- Remote::is_valid_name("ab\012");
- }
-
- #[test]
- fn transfer_cb() {
- let (td, _repo) = crate::test::repo_init();
- let td2 = TempDir::new().unwrap();
- let url = crate::test::path2url(&td.path());
-
- let repo = Repository::init(td2.path()).unwrap();
- let progress_hit = Cell::new(false);
- {
- let mut callbacks = RemoteCallbacks::new();
- let mut origin = repo.remote("origin", &url).unwrap();
-
- callbacks.transfer_progress(|_progress| {
- progress_hit.set(true);
- true
- });
- origin
- .fetch(
- &[] as &[&str],
- Some(FetchOptions::new().remote_callbacks(callbacks)),
- None,
- )
- .unwrap();
-
- let list = t!(origin.list());
- assert_eq!(list.len(), 2);
- assert_eq!(list[0].name(), "HEAD");
- assert!(!list[0].is_local());
- assert_eq!(list[1].name(), "refs/heads/main");
- assert!(!list[1].is_local());
- }
- assert!(progress_hit.get());
- }
-
- /// This test is meant to assure that the callbacks provided to connect will not cause
- /// segfaults
- #[test]
- fn connect_list() {
- let (td, _repo) = crate::test::repo_init();
- let td2 = TempDir::new().unwrap();
- let url = crate::test::path2url(&td.path());
-
- let repo = Repository::init(td2.path()).unwrap();
- let mut callbacks = RemoteCallbacks::new();
- callbacks.sideband_progress(|_progress| {
- // no-op
- true
- });
-
- let mut origin = repo.remote("origin", &url).unwrap();
-
- {
- let mut connection = origin
- .connect_auth(Direction::Fetch, Some(callbacks), None)
- .unwrap();
- assert!(connection.connected());
-
- let list = t!(connection.list());
- assert_eq!(list.len(), 2);
- assert_eq!(list[0].name(), "HEAD");
- assert!(!list[0].is_local());
- assert_eq!(list[1].name(), "refs/heads/main");
- assert!(!list[1].is_local());
- }
- assert!(!origin.connected());
- }
-
- #[test]
- fn push() {
- let (_td, repo) = crate::test::repo_init();
- let td2 = TempDir::new().unwrap();
- let td3 = TempDir::new().unwrap();
- let url = crate::test::path2url(&td2.path());
-
- let mut opts = crate::RepositoryInitOptions::new();
- opts.bare(true);
- opts.initial_head("main");
- Repository::init_opts(td2.path(), &opts).unwrap();
- // git push
- let mut remote = repo.remote("origin", &url).unwrap();
- let mut updated = false;
- {
- let mut callbacks = RemoteCallbacks::new();
- callbacks.push_update_reference(|refname, status| {
- updated = true;
- assert_eq!(refname, "refs/heads/main");
- assert_eq!(status, None);
- Ok(())
- });
- let mut options = PushOptions::new();
- options.remote_callbacks(callbacks);
- remote
- .push(&["refs/heads/main"], Some(&mut options))
- .unwrap();
- }
- assert!(updated);
-
- let repo = Repository::clone(&url, td3.path()).unwrap();
- let commit = repo.head().unwrap().target().unwrap();
- let commit = repo.find_commit(commit).unwrap();
- assert_eq!(commit.message(), Some("initial\n\nbody"));
- }
-
- #[test]
- fn prune() {
- let (td, remote_repo) = crate::test::repo_init();
- let oid = remote_repo.head().unwrap().target().unwrap();
- let commit = remote_repo.find_commit(oid).unwrap();
- remote_repo.branch("stale", &commit, true).unwrap();
-
- let td2 = TempDir::new().unwrap();
- let url = crate::test::path2url(&td.path());
- let repo = Repository::clone(&url, &td2).unwrap();
-
- fn assert_branch_count(repo: &Repository, count: usize) {
- assert_eq!(
- repo.branches(Some(crate::BranchType::Remote))
- .unwrap()
- .filter(|b| b.as_ref().unwrap().0.name().unwrap() == Some("origin/stale"))
- .count(),
- count,
- );
- }
-
- assert_branch_count(&repo, 1);
-
- // delete `stale` branch on remote repo
- let mut stale_branch = remote_repo
- .find_branch("stale", crate::BranchType::Local)
- .unwrap();
- stale_branch.delete().unwrap();
-
- // prune
- let mut remote = repo.find_remote("origin").unwrap();
- remote.connect(Direction::Push).unwrap();
- let mut callbacks = RemoteCallbacks::new();
- callbacks.update_tips(|refname, _a, b| {
- assert_eq!(refname, "refs/remotes/origin/stale");
- assert!(b.is_zero());
- true
- });
- remote.prune(Some(callbacks)).unwrap();
- assert_branch_count(&repo, 0);
- }
-
- #[test]
- fn push_negotiation() {
- let (_td, repo) = crate::test::repo_init();
- let oid = repo.head().unwrap().target().unwrap();
-
- let td2 = TempDir::new().unwrap();
- let url = crate::test::path2url(td2.path());
- let mut opts = crate::RepositoryInitOptions::new();
- opts.bare(true);
- opts.initial_head("main");
- let remote_repo = Repository::init_opts(td2.path(), &opts).unwrap();
-
- // reject pushing a branch
- let mut remote = repo.remote("origin", &url).unwrap();
- let mut updated = false;
- {
- let mut callbacks = RemoteCallbacks::new();
- callbacks.push_negotiation(|updates| {
- assert!(!updated);
- updated = true;
- assert_eq!(updates.len(), 1);
- let u = &updates[0];
- assert_eq!(u.src_refname().unwrap(), "refs/heads/main");
- assert!(u.src().is_zero());
- assert_eq!(u.dst_refname().unwrap(), "refs/heads/main");
- assert_eq!(u.dst(), oid);
- Err(crate::Error::from_str("rejected"))
- });
- let mut options = PushOptions::new();
- options.remote_callbacks(callbacks);
- assert!(remote
- .push(&["refs/heads/main"], Some(&mut options))
- .is_err());
- }
- assert!(updated);
- assert_eq!(remote_repo.branches(None).unwrap().count(), 0);
-
- // push 3 branches
- let commit = repo.find_commit(oid).unwrap();
- repo.branch("new1", &commit, true).unwrap();
- repo.branch("new2", &commit, true).unwrap();
- let mut flag = 0;
- updated = false;
- {
- let mut callbacks = RemoteCallbacks::new();
- callbacks.push_negotiation(|updates| {
- assert!(!updated);
- updated = true;
- assert_eq!(updates.len(), 3);
- for u in updates {
- assert!(u.src().is_zero());
- assert_eq!(u.dst(), oid);
- let src_name = u.src_refname().unwrap();
- let dst_name = u.dst_refname().unwrap();
- match src_name {
- "refs/heads/main" => {
- assert_eq!(dst_name, src_name);
- flag |= 1;
- }
- "refs/heads/new1" => {
- assert_eq!(dst_name, "refs/heads/dev1");
- flag |= 2;
- }
- "refs/heads/new2" => {
- assert_eq!(dst_name, "refs/heads/dev2");
- flag |= 4;
- }
- _ => panic!("unexpected refname: {}", src_name),
- }
- }
- Ok(())
- });
- let mut options = PushOptions::new();
- options.remote_callbacks(callbacks);
- remote
- .push(
- &[
- "refs/heads/main",
- "refs/heads/new1:refs/heads/dev1",
- "refs/heads/new2:refs/heads/dev2",
- ],
- Some(&mut options),
- )
- .unwrap();
- }
- assert!(updated);
- assert_eq!(flag, 7);
- assert_eq!(remote_repo.branches(None).unwrap().count(), 3);
- }
-}
diff --git a/extra/git2/src/remote_callbacks.rs b/extra/git2/src/remote_callbacks.rs
deleted file mode 100644
index 1169420bd..000000000
--- a/extra/git2/src/remote_callbacks.rs
+++ /dev/null
@@ -1,518 +0,0 @@
-use libc::{c_char, c_int, c_uint, c_void, size_t};
-use std::ffi::{CStr, CString};
-use std::mem;
-use std::ptr;
-use std::slice;
-use std::str;
-
-use crate::cert::Cert;
-use crate::util::Binding;
-use crate::{
- panic, raw, Cred, CredentialType, Error, IndexerProgress, Oid, PackBuilderStage, Progress,
- PushUpdate,
-};
-
-/// A structure to contain the callbacks which are invoked when a repository is
-/// being updated or downloaded.
-///
-/// These callbacks are used to manage facilities such as authentication,
-/// transfer progress, etc.
-pub struct RemoteCallbacks<'a> {
- push_progress: Option<Box<PushTransferProgress<'a>>>,
- progress: Option<Box<IndexerProgress<'a>>>,
- pack_progress: Option<Box<PackProgress<'a>>>,
- credentials: Option<Box<Credentials<'a>>>,
- sideband_progress: Option<Box<TransportMessage<'a>>>,
- update_tips: Option<Box<UpdateTips<'a>>>,
- certificate_check: Option<Box<CertificateCheck<'a>>>,
- push_update_reference: Option<Box<PushUpdateReference<'a>>>,
- push_negotiation: Option<Box<PushNegotiation<'a>>>,
-}
-
-/// Callback used to acquire credentials for when a remote is fetched.
-///
-/// * `url` - the resource for which the credentials are required.
-/// * `username_from_url` - the username that was embedded in the URL, or `None`
-/// if it was not included.
-/// * `allowed_types` - a bitmask stating which cred types are OK to return.
-pub type Credentials<'a> =
- dyn FnMut(&str, Option<&str>, CredentialType) -> Result<Cred, Error> + 'a;
-
-/// Callback for receiving messages delivered by the transport.
-///
-/// The return value indicates whether the network operation should continue.
-pub type TransportMessage<'a> = dyn FnMut(&[u8]) -> bool + 'a;
-
-/// Callback for whenever a reference is updated locally.
-pub type UpdateTips<'a> = dyn FnMut(&str, Oid, Oid) -> bool + 'a;
-
-/// Callback for a custom certificate check.
-///
-/// The first argument is the certificate received on the connection.
-/// Certificates are typically either an SSH or X509 certificate.
-///
-/// The second argument is the hostname for the connection is passed as the last
-/// argument.
-pub type CertificateCheck<'a> =
- dyn FnMut(&Cert<'_>, &str) -> Result<CertificateCheckStatus, Error> + 'a;
-
-/// The return value for the [`RemoteCallbacks::certificate_check`] callback.
-pub enum CertificateCheckStatus {
- /// Indicates that the certificate should be accepted.
- CertificateOk,
- /// Indicates that the certificate callback is neither accepting nor
- /// rejecting the certificate. The result of the certificate checks
- /// built-in to libgit2 will be used instead.
- CertificatePassthrough,
-}
-
-/// Callback for each updated reference on push.
-///
-/// The first argument here is the `refname` of the reference, and the second is
-/// the status message sent by a server. If the status is `Some` then the update
-/// was rejected by the remote server with a reason why.
-pub type PushUpdateReference<'a> = dyn FnMut(&str, Option<&str>) -> Result<(), Error> + 'a;
-
-/// Callback for push transfer progress
-///
-/// Parameters:
-/// * current
-/// * total
-/// * bytes
-pub type PushTransferProgress<'a> = dyn FnMut(usize, usize, usize) + 'a;
-
-/// Callback for pack progress
-///
-/// Parameters:
-/// * stage
-/// * current
-/// * total
-pub type PackProgress<'a> = dyn FnMut(PackBuilderStage, usize, usize) + 'a;
-
-/// Callback used to inform of upcoming updates.
-///
-/// The argument is a slice containing the updates which will be sent as
-/// commands to the destination.
-///
-/// The push is cancelled if an error is returned.
-pub type PushNegotiation<'a> = dyn FnMut(&[PushUpdate<'_>]) -> Result<(), Error> + 'a;
-
-impl<'a> Default for RemoteCallbacks<'a> {
- fn default() -> Self {
- Self::new()
- }
-}
-
-impl<'a> RemoteCallbacks<'a> {
- /// Creates a new set of empty callbacks
- pub fn new() -> RemoteCallbacks<'a> {
- RemoteCallbacks {
- credentials: None,
- progress: None,
- pack_progress: None,
- sideband_progress: None,
- update_tips: None,
- certificate_check: None,
- push_update_reference: None,
- push_progress: None,
- push_negotiation: None,
- }
- }
-
- /// The callback through which to fetch credentials if required.
- ///
- /// # Example
- ///
- /// Prepare a callback to authenticate using the `$HOME/.ssh/id_rsa` SSH key, and
- /// extracting the username from the URL (i.e. git@github.com:rust-lang/git2-rs.git):
- ///
- /// ```no_run
- /// use git2::{Cred, RemoteCallbacks};
- /// use std::env;
- ///
- /// let mut callbacks = RemoteCallbacks::new();
- /// callbacks.credentials(|_url, username_from_url, _allowed_types| {
- /// Cred::ssh_key(
- /// username_from_url.unwrap(),
- /// None,
- /// std::path::Path::new(&format!("{}/.ssh/id_rsa", env::var("HOME").unwrap())),
- /// None,
- /// )
- /// });
- /// ```
- pub fn credentials<F>(&mut self, cb: F) -> &mut RemoteCallbacks<'a>
- where
- F: FnMut(&str, Option<&str>, CredentialType) -> Result<Cred, Error> + 'a,
- {
- self.credentials = Some(Box::new(cb) as Box<Credentials<'a>>);
- self
- }
-
- /// The callback through which progress is monitored.
- pub fn transfer_progress<F>(&mut self, cb: F) -> &mut RemoteCallbacks<'a>
- where
- F: FnMut(Progress<'_>) -> bool + 'a,
- {
- self.progress = Some(Box::new(cb) as Box<IndexerProgress<'a>>);
- self
- }
-
- /// Textual progress from the remote.
- ///
- /// Text sent over the progress side-band will be passed to this function
- /// (this is the 'counting objects' output).
- pub fn sideband_progress<F>(&mut self, cb: F) -> &mut RemoteCallbacks<'a>
- where
- F: FnMut(&[u8]) -> bool + 'a,
- {
- self.sideband_progress = Some(Box::new(cb) as Box<TransportMessage<'a>>);
- self
- }
-
- /// Each time a reference is updated locally, the callback will be called
- /// with information about it.
- pub fn update_tips<F>(&mut self, cb: F) -> &mut RemoteCallbacks<'a>
- where
- F: FnMut(&str, Oid, Oid) -> bool + 'a,
- {
- self.update_tips = Some(Box::new(cb) as Box<UpdateTips<'a>>);
- self
- }
-
- /// If certificate verification fails, then this callback will be invoked to
- /// let the caller make the final decision of whether to allow the
- /// connection to proceed.
- pub fn certificate_check<F>(&mut self, cb: F) -> &mut RemoteCallbacks<'a>
- where
- F: FnMut(&Cert<'_>, &str) -> Result<CertificateCheckStatus, Error> + 'a,
- {
- self.certificate_check = Some(Box::new(cb) as Box<CertificateCheck<'a>>);
- self
- }
-
- /// Set a callback to get invoked for each updated reference on a push.
- ///
- /// The first argument to the callback is the name of the reference and the
- /// second is a status message sent by the server. If the status is `Some`
- /// then the push was rejected.
- pub fn push_update_reference<F>(&mut self, cb: F) -> &mut RemoteCallbacks<'a>
- where
- F: FnMut(&str, Option<&str>) -> Result<(), Error> + 'a,
- {
- self.push_update_reference = Some(Box::new(cb) as Box<PushUpdateReference<'a>>);
- self
- }
-
- /// The callback through which progress of push transfer is monitored
- pub fn push_transfer_progress<F>(&mut self, cb: F) -> &mut RemoteCallbacks<'a>
- where
- F: FnMut(usize, usize, usize) + 'a,
- {
- self.push_progress = Some(Box::new(cb) as Box<PushTransferProgress<'a>>);
- self
- }
-
- /// Function to call with progress information during pack building.
- /// Be aware that this is called inline with pack building operations,
- /// so performance may be affected.
- pub fn pack_progress<F>(&mut self, cb: F) -> &mut RemoteCallbacks<'a>
- where
- F: FnMut(PackBuilderStage, usize, usize) + 'a,
- {
- self.pack_progress = Some(Box::new(cb) as Box<PackProgress<'a>>);
- self
- }
-
- /// The callback is called once between the negotiation step and the upload.
- /// It provides information about what updates will be performed.
- pub fn push_negotiation<F>(&mut self, cb: F) -> &mut RemoteCallbacks<'a>
- where
- F: FnMut(&[PushUpdate<'_>]) -> Result<(), Error> + 'a,
- {
- self.push_negotiation = Some(Box::new(cb) as Box<PushNegotiation<'a>>);
- self
- }
-}
-
-impl<'a> Binding for RemoteCallbacks<'a> {
- type Raw = raw::git_remote_callbacks;
- unsafe fn from_raw(_raw: raw::git_remote_callbacks) -> RemoteCallbacks<'a> {
- panic!("unimplemented");
- }
-
- fn raw(&self) -> raw::git_remote_callbacks {
- unsafe {
- let mut callbacks: raw::git_remote_callbacks = mem::zeroed();
- assert_eq!(
- raw::git_remote_init_callbacks(&mut callbacks, raw::GIT_REMOTE_CALLBACKS_VERSION),
- 0
- );
- if self.progress.is_some() {
- callbacks.transfer_progress = Some(transfer_progress_cb);
- }
- if self.credentials.is_some() {
- callbacks.credentials = Some(credentials_cb);
- }
- if self.sideband_progress.is_some() {
- callbacks.sideband_progress = Some(sideband_progress_cb);
- }
- if self.certificate_check.is_some() {
- callbacks.certificate_check = Some(certificate_check_cb);
- }
- if self.push_update_reference.is_some() {
- callbacks.push_update_reference = Some(push_update_reference_cb);
- }
- if self.push_progress.is_some() {
- callbacks.push_transfer_progress = Some(push_transfer_progress_cb);
- }
- if self.pack_progress.is_some() {
- callbacks.pack_progress = Some(pack_progress_cb);
- }
- if self.update_tips.is_some() {
- let f: extern "C" fn(
- *const c_char,
- *const raw::git_oid,
- *const raw::git_oid,
- *mut c_void,
- ) -> c_int = update_tips_cb;
- callbacks.update_tips = Some(f);
- }
- if self.push_negotiation.is_some() {
- callbacks.push_negotiation = Some(push_negotiation_cb);
- }
- callbacks.payload = self as *const _ as *mut _;
- callbacks
- }
- }
-}
-
-extern "C" fn credentials_cb(
- ret: *mut *mut raw::git_cred,
- url: *const c_char,
- username_from_url: *const c_char,
- allowed_types: c_uint,
- payload: *mut c_void,
-) -> c_int {
- unsafe {
- let ok = panic::wrap(|| {
- let payload = &mut *(payload as *mut RemoteCallbacks<'_>);
- let callback = payload
- .credentials
- .as_mut()
- .ok_or(raw::GIT_PASSTHROUGH as c_int)?;
- *ret = ptr::null_mut();
- let url = str::from_utf8(CStr::from_ptr(url).to_bytes())
- .map_err(|_| raw::GIT_PASSTHROUGH as c_int)?;
- let username_from_url = match crate::opt_bytes(&url, username_from_url) {
- Some(username) => {
- Some(str::from_utf8(username).map_err(|_| raw::GIT_PASSTHROUGH as c_int)?)
- }
- None => None,
- };
-
- let cred_type = CredentialType::from_bits_truncate(allowed_types as u32);
-
- callback(url, username_from_url, cred_type).map_err(|e| {
- let s = CString::new(e.to_string()).unwrap();
- raw::git_error_set_str(e.class() as c_int, s.as_ptr());
- e.raw_code() as c_int
- })
- });
- match ok {
- Some(Ok(cred)) => {
- // Turns out it's a memory safety issue if we pass through any
- // and all credentials into libgit2
- if allowed_types & (cred.credtype() as c_uint) != 0 {
- *ret = cred.unwrap();
- 0
- } else {
- raw::GIT_PASSTHROUGH as c_int
- }
- }
- Some(Err(e)) => e,
- None => -1,
- }
- }
-}
-
-extern "C" fn transfer_progress_cb(
- stats: *const raw::git_indexer_progress,
- payload: *mut c_void,
-) -> c_int {
- let ok = panic::wrap(|| unsafe {
- let payload = &mut *(payload as *mut RemoteCallbacks<'_>);
- let callback = match payload.progress {
- Some(ref mut c) => c,
- None => return true,
- };
- let progress = Binding::from_raw(stats);
- callback(progress)
- });
- if ok == Some(true) {
- 0
- } else {
- -1
- }
-}
-
-extern "C" fn sideband_progress_cb(str: *const c_char, len: c_int, payload: *mut c_void) -> c_int {
- let ok = panic::wrap(|| unsafe {
- let payload = &mut *(payload as *mut RemoteCallbacks<'_>);
- let callback = match payload.sideband_progress {
- Some(ref mut c) => c,
- None => return true,
- };
- let buf = slice::from_raw_parts(str as *const u8, len as usize);
- callback(buf)
- });
- if ok == Some(true) {
- 0
- } else {
- -1
- }
-}
-
-extern "C" fn update_tips_cb(
- refname: *const c_char,
- a: *const raw::git_oid,
- b: *const raw::git_oid,
- data: *mut c_void,
-) -> c_int {
- let ok = panic::wrap(|| unsafe {
- let payload = &mut *(data as *mut RemoteCallbacks<'_>);
- let callback = match payload.update_tips {
- Some(ref mut c) => c,
- None => return true,
- };
- let refname = str::from_utf8(CStr::from_ptr(refname).to_bytes()).unwrap();
- let a = Binding::from_raw(a);
- let b = Binding::from_raw(b);
- callback(refname, a, b)
- });
- if ok == Some(true) {
- 0
- } else {
- -1
- }
-}
-
-extern "C" fn certificate_check_cb(
- cert: *mut raw::git_cert,
- _valid: c_int,
- hostname: *const c_char,
- data: *mut c_void,
-) -> c_int {
- let ok = panic::wrap(|| unsafe {
- let payload = &mut *(data as *mut RemoteCallbacks<'_>);
- let callback = match payload.certificate_check {
- Some(ref mut c) => c,
- None => return Ok(CertificateCheckStatus::CertificatePassthrough),
- };
- let cert = Binding::from_raw(cert);
- let hostname = str::from_utf8(CStr::from_ptr(hostname).to_bytes()).unwrap();
- callback(&cert, hostname)
- });
- match ok {
- Some(Ok(CertificateCheckStatus::CertificateOk)) => 0,
- Some(Ok(CertificateCheckStatus::CertificatePassthrough)) => raw::GIT_PASSTHROUGH as c_int,
- Some(Err(e)) => {
- let s = CString::new(e.message()).unwrap();
- unsafe {
- raw::git_error_set_str(e.class() as c_int, s.as_ptr());
- }
- e.raw_code() as c_int
- }
- None => {
- // Panic. The *should* get resumed by some future call to check().
- -1
- }
- }
-}
-
-extern "C" fn push_update_reference_cb(
- refname: *const c_char,
- status: *const c_char,
- data: *mut c_void,
-) -> c_int {
- panic::wrap(|| unsafe {
- let payload = &mut *(data as *mut RemoteCallbacks<'_>);
- let callback = match payload.push_update_reference {
- Some(ref mut c) => c,
- None => return 0,
- };
- let refname = str::from_utf8(CStr::from_ptr(refname).to_bytes()).unwrap();
- let status = if status.is_null() {
- None
- } else {
- Some(str::from_utf8(CStr::from_ptr(status).to_bytes()).unwrap())
- };
- match callback(refname, status) {
- Ok(()) => 0,
- Err(e) => e.raw_code(),
- }
- })
- .unwrap_or(-1)
-}
-
-extern "C" fn push_transfer_progress_cb(
- progress: c_uint,
- total: c_uint,
- bytes: size_t,
- data: *mut c_void,
-) -> c_int {
- panic::wrap(|| unsafe {
- let payload = &mut *(data as *mut RemoteCallbacks<'_>);
- let callback = match payload.push_progress {
- Some(ref mut c) => c,
- None => return 0,
- };
-
- callback(progress as usize, total as usize, bytes as usize);
-
- 0
- })
- .unwrap_or(-1)
-}
-
-extern "C" fn pack_progress_cb(
- stage: raw::git_packbuilder_stage_t,
- current: c_uint,
- total: c_uint,
- data: *mut c_void,
-) -> c_int {
- panic::wrap(|| unsafe {
- let payload = &mut *(data as *mut RemoteCallbacks<'_>);
- let callback = match payload.pack_progress {
- Some(ref mut c) => c,
- None => return 0,
- };
-
- let stage = Binding::from_raw(stage);
-
- callback(stage, current as usize, total as usize);
-
- 0
- })
- .unwrap_or(-1)
-}
-
-extern "C" fn push_negotiation_cb(
- updates: *mut *const raw::git_push_update,
- len: size_t,
- payload: *mut c_void,
-) -> c_int {
- panic::wrap(|| unsafe {
- let payload = &mut *(payload as *mut RemoteCallbacks<'_>);
- let callback = match payload.push_negotiation {
- Some(ref mut c) => c,
- None => return 0,
- };
-
- let updates = slice::from_raw_parts(updates as *mut PushUpdate<'_>, len);
- match callback(updates) {
- Ok(()) => 0,
- Err(e) => e.raw_code(),
- }
- })
- .unwrap_or(-1)
-}
diff --git a/extra/git2/src/repo.rs b/extra/git2/src/repo.rs
deleted file mode 100644
index 921e2b30e..000000000
--- a/extra/git2/src/repo.rs
+++ /dev/null
@@ -1,4226 +0,0 @@
-use libc::{c_char, c_int, c_uint, c_void, size_t};
-use std::env;
-use std::ffi::{CStr, CString, OsStr};
-use std::iter::IntoIterator;
-use std::mem;
-use std::path::{Path, PathBuf};
-use std::ptr;
-use std::str;
-
-use crate::build::{CheckoutBuilder, RepoBuilder};
-use crate::diff::{
- binary_cb_c, file_cb_c, hunk_cb_c, line_cb_c, BinaryCb, DiffCallbacks, FileCb, HunkCb, LineCb,
-};
-use crate::oid_array::OidArray;
-use crate::stash::{stash_cb, StashApplyOptions, StashCbData, StashSaveOptions};
-use crate::string_array::StringArray;
-use crate::tagforeach::{tag_foreach_cb, TagForeachCB, TagForeachData};
-use crate::util::{self, path_to_repo_path, Binding};
-use crate::worktree::{Worktree, WorktreeAddOptions};
-use crate::CherrypickOptions;
-use crate::RevertOptions;
-use crate::{mailmap::Mailmap, panic};
-use crate::{
- raw, AttrCheckFlags, Buf, Error, Object, Remote, RepositoryOpenFlags, RepositoryState, Revspec,
- StashFlags,
-};
-use crate::{
- AnnotatedCommit, MergeAnalysis, MergeOptions, MergePreference, SubmoduleIgnore,
- SubmoduleStatus, SubmoduleUpdate,
-};
-use crate::{ApplyLocation, ApplyOptions, Rebase, RebaseOptions};
-use crate::{Blame, BlameOptions, Reference, References, ResetType, Signature, Submodule};
-use crate::{Blob, BlobWriter, Branch, BranchType, Branches, Commit, Config, Index, Oid, Tree};
-use crate::{Describe, IntoCString, Reflog, RepositoryInitMode, RevparseMode};
-use crate::{DescribeOptions, Diff, DiffOptions, Odb, PackBuilder, TreeBuilder};
-use crate::{Note, Notes, ObjectType, Revwalk, Status, StatusOptions, Statuses, Tag, Transaction};
-
-type MergeheadForeachCb<'a> = dyn FnMut(&Oid) -> bool + 'a;
-type FetchheadForeachCb<'a> = dyn FnMut(&str, &[u8], &Oid, bool) -> bool + 'a;
-
-struct FetchheadForeachCbData<'a> {
- callback: &'a mut FetchheadForeachCb<'a>,
-}
-
-struct MergeheadForeachCbData<'a> {
- callback: &'a mut MergeheadForeachCb<'a>,
-}
-
-extern "C" fn mergehead_foreach_cb(oid: *const raw::git_oid, payload: *mut c_void) -> c_int {
- panic::wrap(|| unsafe {
- let data = &mut *(payload as *mut MergeheadForeachCbData<'_>);
- let res = {
- let callback = &mut data.callback;
- callback(&Binding::from_raw(oid))
- };
-
- if res {
- 0
- } else {
- 1
- }
- })
- .unwrap_or(1)
-}
-
-extern "C" fn fetchhead_foreach_cb(
- ref_name: *const c_char,
- remote_url: *const c_char,
- oid: *const raw::git_oid,
- is_merge: c_uint,
- payload: *mut c_void,
-) -> c_int {
- panic::wrap(|| unsafe {
- let data = &mut *(payload as *mut FetchheadForeachCbData<'_>);
- let res = {
- let callback = &mut data.callback;
-
- assert!(!ref_name.is_null());
- assert!(!remote_url.is_null());
- assert!(!oid.is_null());
-
- let ref_name = str::from_utf8(CStr::from_ptr(ref_name).to_bytes()).unwrap();
- let remote_url = CStr::from_ptr(remote_url).to_bytes();
- let oid = Binding::from_raw(oid);
- let is_merge = is_merge == 1;
-
- callback(&ref_name, remote_url, &oid, is_merge)
- };
-
- if res {
- 0
- } else {
- 1
- }
- })
- .unwrap_or(1)
-}
-
-/// An owned git repository, representing all state associated with the
-/// underlying filesystem.
-///
-/// This structure corresponds to a `git_repository` in libgit2. Many other
-/// types in git2-rs are derivative from this structure and are attached to its
-/// lifetime.
-///
-/// When a repository goes out of scope it is freed in memory but not deleted
-/// from the filesystem.
-pub struct Repository {
- raw: *mut raw::git_repository,
-}
-
-// It is the current belief that a `Repository` can be sent among threads, or
-// even shared among threads in a mutex.
-unsafe impl Send for Repository {}
-
-/// Options which can be used to configure how a repository is initialized
-pub struct RepositoryInitOptions {
- flags: u32,
- mode: u32,
- workdir_path: Option<CString>,
- description: Option<CString>,
- template_path: Option<CString>,
- initial_head: Option<CString>,
- origin_url: Option<CString>,
-}
-
-impl Repository {
- /// Attempt to open an already-existing repository at `path`.
- ///
- /// The path can point to either a normal or bare repository.
- pub fn open<P: AsRef<Path>>(path: P) -> Result<Repository, Error> {
- crate::init();
- // Normal file path OK (does not need Windows conversion).
- let path = path.as_ref().into_c_string()?;
- let mut ret = ptr::null_mut();
- unsafe {
- try_call!(raw::git_repository_open(&mut ret, path));
- Ok(Binding::from_raw(ret))
- }
- }
-
- /// Attempt to open an already-existing bare repository at `path`.
- ///
- /// The path can point to only a bare repository.
- pub fn open_bare<P: AsRef<Path>>(path: P) -> Result<Repository, Error> {
- crate::init();
- // Normal file path OK (does not need Windows conversion).
- let path = path.as_ref().into_c_string()?;
- let mut ret = ptr::null_mut();
- unsafe {
- try_call!(raw::git_repository_open_bare(&mut ret, path));
- Ok(Binding::from_raw(ret))
- }
- }
-
- /// Find and open an existing repository, respecting git environment
- /// variables. This acts like `open_ext` with the
- /// [FROM_ENV](RepositoryOpenFlags::FROM_ENV) flag, but additionally respects `$GIT_DIR`.
- /// With `$GIT_DIR` unset, this will search for a repository starting in
- /// the current directory.
- pub fn open_from_env() -> Result<Repository, Error> {
- crate::init();
- let mut ret = ptr::null_mut();
- let flags = raw::GIT_REPOSITORY_OPEN_FROM_ENV;
- unsafe {
- try_call!(raw::git_repository_open_ext(
- &mut ret,
- ptr::null(),
- flags as c_uint,
- ptr::null()
- ));
- Ok(Binding::from_raw(ret))
- }
- }
-
- /// Find and open an existing repository, with additional options.
- ///
- /// If flags contains [NO_SEARCH](RepositoryOpenFlags::NO_SEARCH), the path must point
- /// directly to a repository; otherwise, this may point to a subdirectory
- /// of a repository, and `open_ext` will search up through parent
- /// directories.
- ///
- /// If flags contains [CROSS_FS](RepositoryOpenFlags::CROSS_FS), the search through parent
- /// directories will not cross a filesystem boundary (detected when the
- /// stat st_dev field changes).
- ///
- /// If flags contains [BARE](RepositoryOpenFlags::BARE), force opening the repository as
- /// bare even if it isn't, ignoring any working directory, and defer
- /// loading the repository configuration for performance.
- ///
- /// If flags contains [NO_DOTGIT](RepositoryOpenFlags::NO_DOTGIT), don't try appending
- /// `/.git` to `path`.
- ///
- /// If flags contains [FROM_ENV](RepositoryOpenFlags::FROM_ENV), `open_ext` will ignore
- /// other flags and `ceiling_dirs`, and respect the same environment
- /// variables git does. Note, however, that `path` overrides `$GIT_DIR`; to
- /// respect `$GIT_DIR` as well, use `open_from_env`.
- ///
- /// ceiling_dirs specifies a list of paths that the search through parent
- /// directories will stop before entering. Use the functions in std::env
- /// to construct or manipulate such a path list. (You can use `&[] as
- /// &[&std::ffi::OsStr]` as an argument if there are no ceiling
- /// directories.)
- pub fn open_ext<P, O, I>(
- path: P,
- flags: RepositoryOpenFlags,
- ceiling_dirs: I,
- ) -> Result<Repository, Error>
- where
- P: AsRef<Path>,
- O: AsRef<OsStr>,
- I: IntoIterator<Item = O>,
- {
- crate::init();
- // Normal file path OK (does not need Windows conversion).
- let path = path.as_ref().into_c_string()?;
- let ceiling_dirs_os = env::join_paths(ceiling_dirs)?;
- let ceiling_dirs = ceiling_dirs_os.into_c_string()?;
- let mut ret = ptr::null_mut();
- unsafe {
- try_call!(raw::git_repository_open_ext(
- &mut ret,
- path,
- flags.bits() as c_uint,
- ceiling_dirs
- ));
- Ok(Binding::from_raw(ret))
- }
- }
-
- /// Attempt to open an already-existing repository from a worktree.
- pub fn open_from_worktree(worktree: &Worktree) -> Result<Repository, Error> {
- let mut ret = ptr::null_mut();
- unsafe {
- try_call!(raw::git_repository_open_from_worktree(
- &mut ret,
- worktree.raw()
- ));
- Ok(Binding::from_raw(ret))
- }
- }
-
- /// Attempt to open an already-existing repository at or above `path`
- ///
- /// This starts at `path` and looks up the filesystem hierarchy
- /// until it finds a repository.
- pub fn discover<P: AsRef<Path>>(path: P) -> Result<Repository, Error> {
- // TODO: this diverges significantly from the libgit2 API
- crate::init();
- let buf = Buf::new();
- // Normal file path OK (does not need Windows conversion).
- let path = path.as_ref().into_c_string()?;
- unsafe {
- try_call!(raw::git_repository_discover(
- buf.raw(),
- path,
- 1,
- ptr::null()
- ));
- }
- Repository::open(util::bytes2path(&*buf))
- }
-
- /// Attempt to find the path to a git repo for a given path
- ///
- /// This starts at `path` and looks up the filesystem hierarchy
- /// until it finds a repository, stopping if it finds a member of ceiling_dirs
- pub fn discover_path<P: AsRef<Path>, I, O>(path: P, ceiling_dirs: I) -> Result<PathBuf, Error>
- where
- O: AsRef<OsStr>,
- I: IntoIterator<Item = O>,
- {
- crate::init();
- let buf = Buf::new();
- // Normal file path OK (does not need Windows conversion).
- let path = path.as_ref().into_c_string()?;
- let ceiling_dirs_os = env::join_paths(ceiling_dirs)?;
- let ceiling_dirs = ceiling_dirs_os.into_c_string()?;
- unsafe {
- try_call!(raw::git_repository_discover(
- buf.raw(),
- path,
- 1,
- ceiling_dirs
- ));
- }
-
- Ok(util::bytes2path(&*buf).to_path_buf())
- }
-
- /// Creates a new repository in the specified folder.
- ///
- /// This by default will create any necessary directories to create the
- /// repository, and it will read any user-specified templates when creating
- /// the repository. This behavior can be configured through `init_opts`.
- pub fn init<P: AsRef<Path>>(path: P) -> Result<Repository, Error> {
- Repository::init_opts(path, &RepositoryInitOptions::new())
- }
-
- /// Creates a new `--bare` repository in the specified folder.
- ///
- /// The folder must exist prior to invoking this function.
- pub fn init_bare<P: AsRef<Path>>(path: P) -> Result<Repository, Error> {
- Repository::init_opts(path, RepositoryInitOptions::new().bare(true))
- }
-
- /// Creates a new repository in the specified folder with the given options.
- ///
- /// See `RepositoryInitOptions` struct for more information.
- pub fn init_opts<P: AsRef<Path>>(
- path: P,
- opts: &RepositoryInitOptions,
- ) -> Result<Repository, Error> {
- crate::init();
- // Normal file path OK (does not need Windows conversion).
- let path = path.as_ref().into_c_string()?;
- let mut ret = ptr::null_mut();
- unsafe {
- let mut opts = opts.raw();
- try_call!(raw::git_repository_init_ext(&mut ret, path, &mut opts));
- Ok(Binding::from_raw(ret))
- }
- }
-
- /// Clone a remote repository.
- ///
- /// See the `RepoBuilder` struct for more information. This function will
- /// delegate to a fresh `RepoBuilder`
- pub fn clone<P: AsRef<Path>>(url: &str, into: P) -> Result<Repository, Error> {
- crate::init();
- RepoBuilder::new().clone(url, into.as_ref())
- }
-
- /// Clone a remote repository, initialize and update its submodules
- /// recursively.
- ///
- /// This is similar to `git clone --recursive`.
- pub fn clone_recurse<P: AsRef<Path>>(url: &str, into: P) -> Result<Repository, Error> {
- let repo = Repository::clone(url, into)?;
- repo.update_submodules()?;
- Ok(repo)
- }
-
- /// Attempt to wrap an object database as a repository.
- pub fn from_odb(odb: Odb<'_>) -> Result<Repository, Error> {
- crate::init();
- let mut ret = ptr::null_mut();
- unsafe {
- try_call!(raw::git_repository_wrap_odb(&mut ret, odb.raw()));
- Ok(Binding::from_raw(ret))
- }
- }
-
- /// Update submodules recursively.
- ///
- /// Uninitialized submodules will be initialized.
- fn update_submodules(&self) -> Result<(), Error> {
- fn add_subrepos(repo: &Repository, list: &mut Vec<Repository>) -> Result<(), Error> {
- for mut subm in repo.submodules()? {
- subm.update(true, None)?;
- list.push(subm.open()?);
- }
- Ok(())
- }
-
- let mut repos = Vec::new();
- add_subrepos(self, &mut repos)?;
- while let Some(repo) = repos.pop() {
- add_subrepos(&repo, &mut repos)?;
- }
- Ok(())
- }
-
- /// Execute a rev-parse operation against the `spec` listed.
- ///
- /// The resulting revision specification is returned, or an error is
- /// returned if one occurs.
- pub fn revparse(&self, spec: &str) -> Result<Revspec<'_>, Error> {
- let mut raw = raw::git_revspec {
- from: ptr::null_mut(),
- to: ptr::null_mut(),
- flags: 0,
- };
- let spec = CString::new(spec)?;
- unsafe {
- try_call!(raw::git_revparse(&mut raw, self.raw, spec));
- let to = Binding::from_raw_opt(raw.to);
- let from = Binding::from_raw_opt(raw.from);
- let mode = RevparseMode::from_bits_truncate(raw.flags as u32);
- Ok(Revspec::from_objects(from, to, mode))
- }
- }
-
- /// Find a single object, as specified by a revision string.
- pub fn revparse_single(&self, spec: &str) -> Result<Object<'_>, Error> {
- let spec = CString::new(spec)?;
- let mut obj = ptr::null_mut();
- unsafe {
- try_call!(raw::git_revparse_single(&mut obj, self.raw, spec));
- assert!(!obj.is_null());
- Ok(Binding::from_raw(obj))
- }
- }
-
- /// Find a single object and intermediate reference by a revision string.
- ///
- /// See `man gitrevisions`, or
- /// <http://git-scm.com/docs/git-rev-parse.html#_specifying_revisions> for
- /// information on the syntax accepted.
- ///
- /// In some cases (`@{<-n>}` or `<branchname>@{upstream}`), the expression
- /// may point to an intermediate reference. When such expressions are being
- /// passed in, this intermediate reference is returned.
- pub fn revparse_ext(&self, spec: &str) -> Result<(Object<'_>, Option<Reference<'_>>), Error> {
- let spec = CString::new(spec)?;
- let mut git_obj = ptr::null_mut();
- let mut git_ref = ptr::null_mut();
- unsafe {
- try_call!(raw::git_revparse_ext(
- &mut git_obj,
- &mut git_ref,
- self.raw,
- spec
- ));
- assert!(!git_obj.is_null());
- Ok((Binding::from_raw(git_obj), Binding::from_raw_opt(git_ref)))
- }
- }
-
- /// Tests whether this repository is a bare repository or not.
- pub fn is_bare(&self) -> bool {
- unsafe { raw::git_repository_is_bare(self.raw) == 1 }
- }
-
- /// Tests whether this repository is a shallow clone.
- pub fn is_shallow(&self) -> bool {
- unsafe { raw::git_repository_is_shallow(self.raw) == 1 }
- }
-
- /// Tests whether this repository is a worktree.
- pub fn is_worktree(&self) -> bool {
- unsafe { raw::git_repository_is_worktree(self.raw) == 1 }
- }
-
- /// Tests whether this repository is empty.
- pub fn is_empty(&self) -> Result<bool, Error> {
- let empty = unsafe { try_call!(raw::git_repository_is_empty(self.raw)) };
- Ok(empty == 1)
- }
-
- /// Returns the path to the `.git` folder for normal repositories or the
- /// repository itself for bare repositories.
- pub fn path(&self) -> &Path {
- unsafe {
- let ptr = raw::git_repository_path(self.raw);
- util::bytes2path(crate::opt_bytes(self, ptr).unwrap())
- }
- }
-
- /// Returns the current state of this repository
- pub fn state(&self) -> RepositoryState {
- let state = unsafe { raw::git_repository_state(self.raw) };
- macro_rules! check( ($($raw:ident => $real:ident),*) => (
- $(if state == raw::$raw as c_int {
- super::RepositoryState::$real
- }) else *
- else {
- panic!("unknown repository state: {}", state)
- }
- ) );
-
- check!(
- GIT_REPOSITORY_STATE_NONE => Clean,
- GIT_REPOSITORY_STATE_MERGE => Merge,
- GIT_REPOSITORY_STATE_REVERT => Revert,
- GIT_REPOSITORY_STATE_REVERT_SEQUENCE => RevertSequence,
- GIT_REPOSITORY_STATE_CHERRYPICK => CherryPick,
- GIT_REPOSITORY_STATE_CHERRYPICK_SEQUENCE => CherryPickSequence,
- GIT_REPOSITORY_STATE_BISECT => Bisect,
- GIT_REPOSITORY_STATE_REBASE => Rebase,
- GIT_REPOSITORY_STATE_REBASE_INTERACTIVE => RebaseInteractive,
- GIT_REPOSITORY_STATE_REBASE_MERGE => RebaseMerge,
- GIT_REPOSITORY_STATE_APPLY_MAILBOX => ApplyMailbox,
- GIT_REPOSITORY_STATE_APPLY_MAILBOX_OR_REBASE => ApplyMailboxOrRebase
- )
- }
-
- /// Get the path of the working directory for this repository.
- ///
- /// If this repository is bare, then `None` is returned.
- pub fn workdir(&self) -> Option<&Path> {
- unsafe {
- let ptr = raw::git_repository_workdir(self.raw);
- if ptr.is_null() {
- None
- } else {
- Some(util::bytes2path(CStr::from_ptr(ptr).to_bytes()))
- }
- }
- }
-
- /// Set the path to the working directory for this repository.
- ///
- /// If `update_link` is true, create/update the gitlink file in the workdir
- /// and set config "core.worktree" (if workdir is not the parent of the .git
- /// directory).
- pub fn set_workdir(&self, path: &Path, update_gitlink: bool) -> Result<(), Error> {
- // Normal file path OK (does not need Windows conversion).
- let path = path.into_c_string()?;
- unsafe {
- try_call!(raw::git_repository_set_workdir(
- self.raw(),
- path,
- update_gitlink
- ));
- }
- Ok(())
- }
-
- /// Get the currently active namespace for this repository.
- ///
- /// If there is no namespace, or the namespace is not a valid utf8 string,
- /// `None` is returned.
- pub fn namespace(&self) -> Option<&str> {
- self.namespace_bytes().and_then(|s| str::from_utf8(s).ok())
- }
-
- /// Get the currently active namespace for this repository as a byte array.
- ///
- /// If there is no namespace, `None` is returned.
- pub fn namespace_bytes(&self) -> Option<&[u8]> {
- unsafe { crate::opt_bytes(self, raw::git_repository_get_namespace(self.raw)) }
- }
-
- /// Set the active namespace for this repository.
- pub fn set_namespace(&self, namespace: &str) -> Result<(), Error> {
- self.set_namespace_bytes(namespace.as_bytes())
- }
-
- /// Set the active namespace for this repository as a byte array.
- pub fn set_namespace_bytes(&self, namespace: &[u8]) -> Result<(), Error> {
- unsafe {
- let namespace = CString::new(namespace)?;
- try_call!(raw::git_repository_set_namespace(self.raw, namespace));
- Ok(())
- }
- }
-
- /// Remove the active namespace for this repository.
- pub fn remove_namespace(&self) -> Result<(), Error> {
- unsafe {
- try_call!(raw::git_repository_set_namespace(self.raw, ptr::null()));
- Ok(())
- }
- }
-
- /// Retrieves the Git merge message.
- /// Remember to remove the message when finished.
- pub fn message(&self) -> Result<String, Error> {
- unsafe {
- let buf = Buf::new();
- try_call!(raw::git_repository_message(buf.raw(), self.raw));
- Ok(str::from_utf8(&buf).unwrap().to_string())
- }
- }
-
- /// Remove the Git merge message.
- pub fn remove_message(&self) -> Result<(), Error> {
- unsafe {
- try_call!(raw::git_repository_message_remove(self.raw));
- Ok(())
- }
- }
-
- /// List all remotes for a given repository
- pub fn remotes(&self) -> Result<StringArray, Error> {
- let mut arr = raw::git_strarray {
- strings: ptr::null_mut(),
- count: 0,
- };
- unsafe {
- try_call!(raw::git_remote_list(&mut arr, self.raw));
- Ok(Binding::from_raw(arr))
- }
- }
-
- /// Get the information for a particular remote
- pub fn find_remote(&self, name: &str) -> Result<Remote<'_>, Error> {
- let mut ret = ptr::null_mut();
- let name = CString::new(name)?;
- unsafe {
- try_call!(raw::git_remote_lookup(&mut ret, self.raw, name));
- Ok(Binding::from_raw(ret))
- }
- }
-
- /// Add a remote with the default fetch refspec to the repository's
- /// configuration.
- pub fn remote(&self, name: &str, url: &str) -> Result<Remote<'_>, Error> {
- let mut ret = ptr::null_mut();
- let name = CString::new(name)?;
- let url = CString::new(url)?;
- unsafe {
- try_call!(raw::git_remote_create(&mut ret, self.raw, name, url));
- Ok(Binding::from_raw(ret))
- }
- }
-
- /// Add a remote with the provided fetch refspec to the repository's
- /// configuration.
- pub fn remote_with_fetch(
- &self,
- name: &str,
- url: &str,
- fetch: &str,
- ) -> Result<Remote<'_>, Error> {
- let mut ret = ptr::null_mut();
- let name = CString::new(name)?;
- let url = CString::new(url)?;
- let fetch = CString::new(fetch)?;
- unsafe {
- try_call!(raw::git_remote_create_with_fetchspec(
- &mut ret, self.raw, name, url, fetch
- ));
- Ok(Binding::from_raw(ret))
- }
- }
-
- /// Create an anonymous remote
- ///
- /// Create a remote with the given URL and refspec in memory. You can use
- /// this when you have a URL instead of a remote's name. Note that anonymous
- /// remotes cannot be converted to persisted remotes.
- pub fn remote_anonymous(&self, url: &str) -> Result<Remote<'_>, Error> {
- let mut ret = ptr::null_mut();
- let url = CString::new(url)?;
- unsafe {
- try_call!(raw::git_remote_create_anonymous(&mut ret, self.raw, url));
- Ok(Binding::from_raw(ret))
- }
- }
-
- /// Give a remote a new name
- ///
- /// All remote-tracking branches and configuration settings for the remote
- /// are updated.
- ///
- /// A temporary in-memory remote cannot be given a name with this method.
- ///
- /// No loaded instances of the remote with the old name will change their
- /// name or their list of refspecs.
- ///
- /// The returned array of strings is a list of the non-default refspecs
- /// which cannot be renamed and are returned for further processing by the
- /// caller.
- pub fn remote_rename(&self, name: &str, new_name: &str) -> Result<StringArray, Error> {
- let name = CString::new(name)?;
- let new_name = CString::new(new_name)?;
- let mut problems = raw::git_strarray {
- count: 0,
- strings: ptr::null_mut(),
- };
- unsafe {
- try_call!(raw::git_remote_rename(
- &mut problems,
- self.raw,
- name,
- new_name
- ));
- Ok(Binding::from_raw(problems))
- }
- }
-
- /// Delete an existing persisted remote.
- ///
- /// All remote-tracking branches and configuration settings for the remote
- /// will be removed.
- pub fn remote_delete(&self, name: &str) -> Result<(), Error> {
- let name = CString::new(name)?;
- unsafe {
- try_call!(raw::git_remote_delete(self.raw, name));
- }
- Ok(())
- }
-
- /// Add a fetch refspec to the remote's configuration
- ///
- /// Add the given refspec to the fetch list in the configuration. No loaded
- /// remote instances will be affected.
- pub fn remote_add_fetch(&self, name: &str, spec: &str) -> Result<(), Error> {
- let name = CString::new(name)?;
- let spec = CString::new(spec)?;
- unsafe {
- try_call!(raw::git_remote_add_fetch(self.raw, name, spec));
- }
- Ok(())
- }
-
- /// Add a push refspec to the remote's configuration.
- ///
- /// Add the given refspec to the push list in the configuration. No
- /// loaded remote instances will be affected.
- pub fn remote_add_push(&self, name: &str, spec: &str) -> Result<(), Error> {
- let name = CString::new(name)?;
- let spec = CString::new(spec)?;
- unsafe {
- try_call!(raw::git_remote_add_push(self.raw, name, spec));
- }
- Ok(())
- }
-
- /// Set the remote's URL in the configuration
- ///
- /// Remote objects already in memory will not be affected. This assumes
- /// the common case of a single-url remote and will otherwise return an
- /// error.
- pub fn remote_set_url(&self, name: &str, url: &str) -> Result<(), Error> {
- let name = CString::new(name)?;
- let url = CString::new(url)?;
- unsafe {
- try_call!(raw::git_remote_set_url(self.raw, name, url));
- }
- Ok(())
- }
-
- /// Set the remote's URL for pushing in the configuration.
- ///
- /// Remote objects already in memory will not be affected. This assumes
- /// the common case of a single-url remote and will otherwise return an
- /// error.
- ///
- /// `None` indicates that it should be cleared.
- pub fn remote_set_pushurl(&self, name: &str, pushurl: Option<&str>) -> Result<(), Error> {
- let name = CString::new(name)?;
- let pushurl = crate::opt_cstr(pushurl)?;
- unsafe {
- try_call!(raw::git_remote_set_pushurl(self.raw, name, pushurl));
- }
- Ok(())
- }
-
- /// Sets the current head to the specified object and optionally resets
- /// the index and working tree to match.
- ///
- /// A soft reset means the head will be moved to the commit.
- ///
- /// A mixed reset will trigger a soft reset, plus the index will be
- /// replaced with the content of the commit tree.
- ///
- /// A hard reset will trigger a mixed reset and the working directory will
- /// be replaced with the content of the index. (Untracked and ignored files
- /// will be left alone, however.)
- ///
- /// The `target` is a commit-ish to which the head should be moved to. The
- /// object can either be a commit or a tag, but tags must be dereferenceable
- /// to a commit.
- ///
- /// The `checkout` options will only be used for a hard reset.
- pub fn reset(
- &self,
- target: &Object<'_>,
- kind: ResetType,
- checkout: Option<&mut CheckoutBuilder<'_>>,
- ) -> Result<(), Error> {
- unsafe {
- let mut opts: raw::git_checkout_options = mem::zeroed();
- try_call!(raw::git_checkout_init_options(
- &mut opts,
- raw::GIT_CHECKOUT_OPTIONS_VERSION
- ));
- let opts = checkout.map(|c| {
- c.configure(&mut opts);
- &mut opts
- });
- try_call!(raw::git_reset(self.raw, target.raw(), kind, opts));
- }
- Ok(())
- }
-
- /// Updates some entries in the index from the target commit tree.
- ///
- /// The scope of the updated entries is determined by the paths being
- /// in the iterator provided.
- ///
- /// Passing a `None` target will result in removing entries in the index
- /// matching the provided pathspecs.
- pub fn reset_default<T, I>(&self, target: Option<&Object<'_>>, paths: I) -> Result<(), Error>
- where
- T: IntoCString,
- I: IntoIterator<Item = T>,
- {
- let (_a, _b, mut arr) = crate::util::iter2cstrs_paths(paths)?;
- let target = target.map(|t| t.raw());
- unsafe {
- try_call!(raw::git_reset_default(self.raw, target, &mut arr));
- }
- Ok(())
- }
-
- /// Retrieve and resolve the reference pointed at by HEAD.
- pub fn head(&self) -> Result<Reference<'_>, Error> {
- let mut ret = ptr::null_mut();
- unsafe {
- try_call!(raw::git_repository_head(&mut ret, self.raw));
- Ok(Binding::from_raw(ret))
- }
- }
-
- /// Make the repository HEAD point to the specified reference.
- ///
- /// If the provided reference points to a tree or a blob, the HEAD is
- /// unaltered and an error is returned.
- ///
- /// If the provided reference points to a branch, the HEAD will point to
- /// that branch, staying attached, or become attached if it isn't yet. If
- /// the branch doesn't exist yet, no error will be returned. The HEAD will
- /// then be attached to an unborn branch.
- ///
- /// Otherwise, the HEAD will be detached and will directly point to the
- /// commit.
- pub fn set_head(&self, refname: &str) -> Result<(), Error> {
- self.set_head_bytes(refname.as_bytes())
- }
-
- /// Make the repository HEAD point to the specified reference as a byte array.
- ///
- /// If the provided reference points to a tree or a blob, the HEAD is
- /// unaltered and an error is returned.
- ///
- /// If the provided reference points to a branch, the HEAD will point to
- /// that branch, staying attached, or become attached if it isn't yet. If
- /// the branch doesn't exist yet, no error will be returned. The HEAD will
- /// then be attached to an unborn branch.
- ///
- /// Otherwise, the HEAD will be detached and will directly point to the
- /// commit.
- pub fn set_head_bytes(&self, refname: &[u8]) -> Result<(), Error> {
- let refname = CString::new(refname)?;
- unsafe {
- try_call!(raw::git_repository_set_head(self.raw, refname));
- }
- Ok(())
- }
-
- /// Determines whether the repository HEAD is detached.
- pub fn head_detached(&self) -> Result<bool, Error> {
- unsafe {
- let value = raw::git_repository_head_detached(self.raw);
- match value {
- 0 => Ok(false),
- 1 => Ok(true),
- _ => Err(Error::last_error(value).unwrap()),
- }
- }
- }
-
- /// Make the repository HEAD directly point to the commit.
- ///
- /// If the provided commitish cannot be found in the repository, the HEAD
- /// is unaltered and an error is returned.
- ///
- /// If the provided commitish cannot be peeled into a commit, the HEAD is
- /// unaltered and an error is returned.
- ///
- /// Otherwise, the HEAD will eventually be detached and will directly point
- /// to the peeled commit.
- pub fn set_head_detached(&self, commitish: Oid) -> Result<(), Error> {
- unsafe {
- try_call!(raw::git_repository_set_head_detached(
- self.raw,
- commitish.raw()
- ));
- }
- Ok(())
- }
-
- /// Make the repository HEAD directly point to the commit.
- ///
- /// If the provided commitish cannot be found in the repository, the HEAD
- /// is unaltered and an error is returned.
- /// If the provided commitish cannot be peeled into a commit, the HEAD is
- /// unaltered and an error is returned.
- /// Otherwise, the HEAD will eventually be detached and will directly point
- /// to the peeled commit.
- pub fn set_head_detached_from_annotated(
- &self,
- commitish: AnnotatedCommit<'_>,
- ) -> Result<(), Error> {
- unsafe {
- try_call!(raw::git_repository_set_head_detached_from_annotated(
- self.raw,
- commitish.raw()
- ));
- }
- Ok(())
- }
-
- /// Create an iterator for the repo's references
- pub fn references(&self) -> Result<References<'_>, Error> {
- let mut ret = ptr::null_mut();
- unsafe {
- try_call!(raw::git_reference_iterator_new(&mut ret, self.raw));
- Ok(Binding::from_raw(ret))
- }
- }
-
- /// Create an iterator for the repo's references that match the specified
- /// glob
- pub fn references_glob(&self, glob: &str) -> Result<References<'_>, Error> {
- let mut ret = ptr::null_mut();
- let glob = CString::new(glob)?;
- unsafe {
- try_call!(raw::git_reference_iterator_glob_new(
- &mut ret, self.raw, glob
- ));
-
- Ok(Binding::from_raw(ret))
- }
- }
-
- /// Load all submodules for this repository and return them.
- pub fn submodules(&self) -> Result<Vec<Submodule<'_>>, Error> {
- struct Data<'a, 'b> {
- repo: &'b Repository,
- ret: &'a mut Vec<Submodule<'b>>,
- }
- let mut ret = Vec::new();
-
- unsafe {
- let mut data = Data {
- repo: self,
- ret: &mut ret,
- };
- let cb: raw::git_submodule_cb = Some(append);
- try_call!(raw::git_submodule_foreach(
- self.raw,
- cb,
- &mut data as *mut _ as *mut c_void
- ));
- }
-
- return Ok(ret);
-
- extern "C" fn append(
- _repo: *mut raw::git_submodule,
- name: *const c_char,
- data: *mut c_void,
- ) -> c_int {
- unsafe {
- let data = &mut *(data as *mut Data<'_, '_>);
- let mut raw = ptr::null_mut();
- let rc = raw::git_submodule_lookup(&mut raw, data.repo.raw(), name);
- assert_eq!(rc, 0);
- data.ret.push(Binding::from_raw(raw));
- }
- 0
- }
- }
-
- /// Gather file status information and populate the returned structure.
- ///
- /// Note that if a pathspec is given in the options to filter the
- /// status, then the results from rename detection (if you enable it) may
- /// not be accurate. To do rename detection properly, this must be called
- /// with no pathspec so that all files can be considered.
- pub fn statuses(&self, options: Option<&mut StatusOptions>) -> Result<Statuses<'_>, Error> {
- let mut ret = ptr::null_mut();
- unsafe {
- try_call!(raw::git_status_list_new(
- &mut ret,
- self.raw,
- options.map(|s| s.raw()).unwrap_or(ptr::null())
- ));
- Ok(Binding::from_raw(ret))
- }
- }
-
- /// Test if the ignore rules apply to a given file.
- ///
- /// This function checks the ignore rules to see if they would apply to the
- /// given file. This indicates if the file would be ignored regardless of
- /// whether the file is already in the index or committed to the repository.
- ///
- /// One way to think of this is if you were to do "git add ." on the
- /// directory containing the file, would it be added or not?
- pub fn status_should_ignore(&self, path: &Path) -> Result<bool, Error> {
- let mut ret = 0 as c_int;
- let path = util::cstring_to_repo_path(path)?;
- unsafe {
- try_call!(raw::git_status_should_ignore(&mut ret, self.raw, path));
- }
- Ok(ret != 0)
- }
-
- /// Get file status for a single file.
- ///
- /// This tries to get status for the filename that you give. If no files
- /// match that name (in either the HEAD, index, or working directory), this
- /// returns NotFound.
- ///
- /// If the name matches multiple files (for example, if the path names a
- /// directory or if running on a case- insensitive filesystem and yet the
- /// HEAD has two entries that both match the path), then this returns
- /// Ambiguous because it cannot give correct results.
- ///
- /// This does not do any sort of rename detection. Renames require a set of
- /// targets and because of the path filtering, there is not enough
- /// information to check renames correctly. To check file status with rename
- /// detection, there is no choice but to do a full `statuses` and scan
- /// through looking for the path that you are interested in.
- pub fn status_file(&self, path: &Path) -> Result<Status, Error> {
- let mut ret = 0 as c_uint;
- let path = path_to_repo_path(path)?;
- unsafe {
- try_call!(raw::git_status_file(&mut ret, self.raw, path));
- }
- Ok(Status::from_bits_truncate(ret as u32))
- }
-
- /// Create an iterator which loops over the requested branches.
- pub fn branches(&self, filter: Option<BranchType>) -> Result<Branches<'_>, Error> {
- let mut raw = ptr::null_mut();
- unsafe {
- try_call!(raw::git_branch_iterator_new(&mut raw, self.raw(), filter));
- Ok(Branches::from_raw(raw))
- }
- }
-
- /// Get the Index file for this repository.
- ///
- /// If a custom index has not been set, the default index for the repository
- /// will be returned (the one located in .git/index).
- ///
- /// **Caution**: If the [`Repository`] of this index is dropped, then this
- /// [`Index`] will become detached, and most methods on it will fail. See
- /// [`Index::open`]. Be sure the repository has a binding such as a local
- /// variable to keep it alive at least as long as the index.
- pub fn index(&self) -> Result<Index, Error> {
- let mut raw = ptr::null_mut();
- unsafe {
- try_call!(raw::git_repository_index(&mut raw, self.raw()));
- Ok(Binding::from_raw(raw))
- }
- }
-
- /// Set the Index file for this repository.
- pub fn set_index(&self, index: &mut Index) -> Result<(), Error> {
- unsafe {
- try_call!(raw::git_repository_set_index(self.raw(), index.raw()));
- }
- Ok(())
- }
-
- /// Get the configuration file for this repository.
- ///
- /// If a configuration file has not been set, the default config set for the
- /// repository will be returned, including global and system configurations
- /// (if they are available).
- pub fn config(&self) -> Result<Config, Error> {
- let mut raw = ptr::null_mut();
- unsafe {
- try_call!(raw::git_repository_config(&mut raw, self.raw()));
- Ok(Binding::from_raw(raw))
- }
- }
-
- /// Get the value of a git attribute for a path as a string.
- ///
- /// This function will return a special string if the attribute is set to a special value.
- /// Interpreting the special string is discouraged. You should always use
- /// [`AttrValue::from_string`](crate::AttrValue::from_string) to interpret the return value
- /// and avoid the special string.
- ///
- /// As such, the return type of this function will probably be changed in the next major version
- /// to prevent interpreting the returned string without checking whether it's special.
- pub fn get_attr(
- &self,
- path: &Path,
- name: &str,
- flags: AttrCheckFlags,
- ) -> Result<Option<&str>, Error> {
- Ok(self
- .get_attr_bytes(path, name, flags)?
- .and_then(|a| str::from_utf8(a).ok()))
- }
-
- /// Get the value of a git attribute for a path as a byte slice.
- ///
- /// This function will return a special byte slice if the attribute is set to a special value.
- /// Interpreting the special byte slice is discouraged. You should always use
- /// [`AttrValue::from_bytes`](crate::AttrValue::from_bytes) to interpret the return value and
- /// avoid the special string.
- ///
- /// As such, the return type of this function will probably be changed in the next major version
- /// to prevent interpreting the returned byte slice without checking whether it's special.
- pub fn get_attr_bytes(
- &self,
- path: &Path,
- name: &str,
- flags: AttrCheckFlags,
- ) -> Result<Option<&[u8]>, Error> {
- let mut ret = ptr::null();
- let path = util::cstring_to_repo_path(path)?;
- let name = CString::new(name)?;
- unsafe {
- try_call!(raw::git_attr_get(
- &mut ret,
- self.raw(),
- flags.bits(),
- path,
- name
- ));
- Ok(crate::opt_bytes(self, ret))
- }
- }
-
- /// Write an in-memory buffer to the ODB as a blob.
- ///
- /// The Oid returned can in turn be passed to `find_blob` to get a handle to
- /// the blob.
- pub fn blob(&self, data: &[u8]) -> Result<Oid, Error> {
- let mut raw = raw::git_oid {
- id: [0; raw::GIT_OID_RAWSZ],
- };
- unsafe {
- let ptr = data.as_ptr() as *const c_void;
- let len = data.len() as size_t;
- try_call!(raw::git_blob_create_frombuffer(
- &mut raw,
- self.raw(),
- ptr,
- len
- ));
- Ok(Binding::from_raw(&raw as *const _))
- }
- }
-
- /// Read a file from the filesystem and write its content to the Object
- /// Database as a loose blob
- ///
- /// The Oid returned can in turn be passed to `find_blob` to get a handle to
- /// the blob.
- pub fn blob_path(&self, path: &Path) -> Result<Oid, Error> {
- // Normal file path OK (does not need Windows conversion).
- let path = path.into_c_string()?;
- let mut raw = raw::git_oid {
- id: [0; raw::GIT_OID_RAWSZ],
- };
- unsafe {
- try_call!(raw::git_blob_create_fromdisk(&mut raw, self.raw(), path));
- Ok(Binding::from_raw(&raw as *const _))
- }
- }
-
- /// Create a stream to write blob
- ///
- /// This function may need to buffer the data on disk and will in general
- /// not be the right choice if you know the size of the data to write.
- ///
- /// Use `BlobWriter::commit()` to commit the write to the object db
- /// and get the object id.
- ///
- /// If the `hintpath` parameter is filled, it will be used to determine
- /// what git filters should be applied to the object before it is written
- /// to the object database.
- pub fn blob_writer(&self, hintpath: Option<&Path>) -> Result<BlobWriter<'_>, Error> {
- let path_str = match hintpath {
- Some(path) => Some(path.into_c_string()?),
- None => None,
- };
- let path = match path_str {
- Some(ref path) => path.as_ptr(),
- None => ptr::null(),
- };
- let mut out = ptr::null_mut();
- unsafe {
- try_call!(raw::git_blob_create_fromstream(&mut out, self.raw(), path));
- Ok(BlobWriter::from_raw(out))
- }
- }
-
- /// Lookup a reference to one of the objects in a repository.
- pub fn find_blob(&self, oid: Oid) -> Result<Blob<'_>, Error> {
- let mut raw = ptr::null_mut();
- unsafe {
- try_call!(raw::git_blob_lookup(&mut raw, self.raw(), oid.raw()));
- Ok(Binding::from_raw(raw))
- }
- }
-
- /// Get the object database for this repository
- pub fn odb(&self) -> Result<Odb<'_>, Error> {
- let mut odb = ptr::null_mut();
- unsafe {
- try_call!(raw::git_repository_odb(&mut odb, self.raw()));
- Ok(Odb::from_raw(odb))
- }
- }
-
- /// Override the object database for this repository
- pub fn set_odb(&self, odb: &Odb<'_>) -> Result<(), Error> {
- unsafe {
- try_call!(raw::git_repository_set_odb(self.raw(), odb.raw()));
- }
- Ok(())
- }
-
- /// Create a new branch pointing at a target commit
- ///
- /// A new direct reference will be created pointing to this target commit.
- /// If `force` is true and a reference already exists with the given name,
- /// it'll be replaced.
- pub fn branch(
- &self,
- branch_name: &str,
- target: &Commit<'_>,
- force: bool,
- ) -> Result<Branch<'_>, Error> {
- let branch_name = CString::new(branch_name)?;
- let mut raw = ptr::null_mut();
- unsafe {
- try_call!(raw::git_branch_create(
- &mut raw,
- self.raw(),
- branch_name,
- target.raw(),
- force
- ));
- Ok(Branch::wrap(Binding::from_raw(raw)))
- }
- }
-
- /// Create a new branch pointing at a target commit
- ///
- /// This behaves like `Repository::branch()` but takes
- /// an annotated commit, which lets you specify which
- /// extended SHA syntax string was specified by a user,
- /// allowing for more exact reflog messages.
- ///
- /// See the documentation for `Repository::branch()`
- pub fn branch_from_annotated_commit(
- &self,
- branch_name: &str,
- target: &AnnotatedCommit<'_>,
- force: bool,
- ) -> Result<Branch<'_>, Error> {
- let branch_name = CString::new(branch_name)?;
- let mut raw = ptr::null_mut();
- unsafe {
- try_call!(raw::git_branch_create_from_annotated(
- &mut raw,
- self.raw(),
- branch_name,
- target.raw(),
- force
- ));
- Ok(Branch::wrap(Binding::from_raw(raw)))
- }
- }
-
- /// Lookup a branch by its name in a repository.
- pub fn find_branch(&self, name: &str, branch_type: BranchType) -> Result<Branch<'_>, Error> {
- let name = CString::new(name)?;
- let mut ret = ptr::null_mut();
- unsafe {
- try_call!(raw::git_branch_lookup(
- &mut ret,
- self.raw(),
- name,
- branch_type
- ));
- Ok(Branch::wrap(Binding::from_raw(ret)))
- }
- }
-
- /// Create new commit in the repository
- ///
- /// If the `update_ref` is not `None`, name of the reference that will be
- /// updated to point to this commit. If the reference is not direct, it will
- /// be resolved to a direct reference. Use "HEAD" to update the HEAD of the
- /// current branch and make it point to this commit. If the reference
- /// doesn't exist yet, it will be created. If it does exist, the first
- /// parent must be the tip of this branch.
- pub fn commit(
- &self,
- update_ref: Option<&str>,
- author: &Signature<'_>,
- committer: &Signature<'_>,
- message: &str,
- tree: &Tree<'_>,
- parents: &[&Commit<'_>],
- ) -> Result<Oid, Error> {
- let update_ref = crate::opt_cstr(update_ref)?;
- let mut parent_ptrs = parents
- .iter()
- .map(|p| p.raw() as *const raw::git_commit)
- .collect::<Vec<_>>();
- let message = CString::new(message)?;
- let mut raw = raw::git_oid {
- id: [0; raw::GIT_OID_RAWSZ],
- };
- unsafe {
- try_call!(raw::git_commit_create(
- &mut raw,
- self.raw(),
- update_ref,
- author.raw(),
- committer.raw(),
- ptr::null(),
- message,
- tree.raw(),
- parents.len() as size_t,
- parent_ptrs.as_mut_ptr()
- ));
- Ok(Binding::from_raw(&raw as *const _))
- }
- }
-
- /// Create a commit object and return that as a Buf.
- ///
- /// That can be converted to a string like this `str::from_utf8(&buf).unwrap().to_string()`.
- /// And that string can be passed to the `commit_signed` function,
- /// the arguments behave the same as in the `commit` function.
- pub fn commit_create_buffer(
- &self,
- author: &Signature<'_>,
- committer: &Signature<'_>,
- message: &str,
- tree: &Tree<'_>,
- parents: &[&Commit<'_>],
- ) -> Result<Buf, Error> {
- let mut parent_ptrs = parents
- .iter()
- .map(|p| p.raw() as *const raw::git_commit)
- .collect::<Vec<_>>();
- let message = CString::new(message)?;
- let buf = Buf::new();
- unsafe {
- try_call!(raw::git_commit_create_buffer(
- buf.raw(),
- self.raw(),
- author.raw(),
- committer.raw(),
- ptr::null(),
- message,
- tree.raw(),
- parents.len() as size_t,
- parent_ptrs.as_mut_ptr()
- ));
- Ok(buf)
- }
- }
-
- /// Create a commit object from the given buffer and signature
- ///
- /// Given the unsigned commit object's contents, its signature and the
- /// header field in which to store the signature, attach the signature to
- /// the commit and write it into the given repository.
- ///
- /// Use `None` in `signature_field` to use the default of `gpgsig`, which is
- /// almost certainly what you want.
- ///
- /// Returns the resulting (signed) commit id.
- pub fn commit_signed(
- &self,
- commit_content: &str,
- signature: &str,
- signature_field: Option<&str>,
- ) -> Result<Oid, Error> {
- let commit_content = CString::new(commit_content)?;
- let signature = CString::new(signature)?;
- let signature_field = crate::opt_cstr(signature_field)?;
- let mut raw = raw::git_oid {
- id: [0; raw::GIT_OID_RAWSZ],
- };
- unsafe {
- try_call!(raw::git_commit_create_with_signature(
- &mut raw,
- self.raw(),
- commit_content,
- signature,
- signature_field
- ));
- Ok(Binding::from_raw(&raw as *const _))
- }
- }
-
- /// Extract the signature from a commit
- ///
- /// Returns a tuple containing the signature in the first value and the
- /// signed data in the second.
- pub fn extract_signature(
- &self,
- commit_id: &Oid,
- signature_field: Option<&str>,
- ) -> Result<(Buf, Buf), Error> {
- let signature_field = crate::opt_cstr(signature_field)?;
- let signature = Buf::new();
- let content = Buf::new();
- unsafe {
- try_call!(raw::git_commit_extract_signature(
- signature.raw(),
- content.raw(),
- self.raw(),
- commit_id.raw() as *mut _,
- signature_field
- ));
- Ok((signature, content))
- }
- }
-
- /// Lookup a reference to one of the commits in a repository.
- pub fn find_commit(&self, oid: Oid) -> Result<Commit<'_>, Error> {
- let mut raw = ptr::null_mut();
- unsafe {
- try_call!(raw::git_commit_lookup(&mut raw, self.raw(), oid.raw()));
- Ok(Binding::from_raw(raw))
- }
- }
-
- /// Creates an `AnnotatedCommit` from the given commit id.
- pub fn find_annotated_commit(&self, id: Oid) -> Result<AnnotatedCommit<'_>, Error> {
- unsafe {
- let mut raw = ptr::null_mut();
- try_call!(raw::git_annotated_commit_lookup(
- &mut raw,
- self.raw(),
- id.raw()
- ));
- Ok(Binding::from_raw(raw))
- }
- }
-
- /// Lookup a reference to one of the objects in a repository.
- pub fn find_object(&self, oid: Oid, kind: Option<ObjectType>) -> Result<Object<'_>, Error> {
- let mut raw = ptr::null_mut();
- unsafe {
- try_call!(raw::git_object_lookup(
- &mut raw,
- self.raw(),
- oid.raw(),
- kind
- ));
- Ok(Binding::from_raw(raw))
- }
- }
-
- /// Create a new direct reference.
- ///
- /// This function will return an error if a reference already exists with
- /// the given name unless force is true, in which case it will be
- /// overwritten.
- pub fn reference(
- &self,
- name: &str,
- id: Oid,
- force: bool,
- log_message: &str,
- ) -> Result<Reference<'_>, Error> {
- let name = CString::new(name)?;
- let log_message = CString::new(log_message)?;
- let mut raw = ptr::null_mut();
- unsafe {
- try_call!(raw::git_reference_create(
- &mut raw,
- self.raw(),
- name,
- id.raw(),
- force,
- log_message
- ));
- Ok(Binding::from_raw(raw))
- }
- }
-
- /// Conditionally create new direct reference.
- ///
- /// A direct reference (also called an object id reference) refers directly
- /// to a specific object id (a.k.a. OID or SHA) in the repository. The id
- /// permanently refers to the object (although the reference itself can be
- /// moved). For example, in libgit2 the direct ref "refs/tags/v0.17.0"
- /// refers to OID 5b9fac39d8a76b9139667c26a63e6b3f204b3977.
- ///
- /// The direct reference will be created in the repository and written to
- /// the disk.
- ///
- /// Valid reference names must follow one of two patterns:
- ///
- /// 1. Top-level names must contain only capital letters and underscores,
- /// and must begin and end with a letter. (e.g. "HEAD", "ORIG_HEAD").
- /// 2. Names prefixed with "refs/" can be almost anything. You must avoid
- /// the characters `~`, `^`, `:`, `\\`, `?`, `[`, and `*`, and the
- /// sequences ".." and "@{" which have special meaning to revparse.
- ///
- /// This function will return an error if a reference already exists with
- /// the given name unless `force` is true, in which case it will be
- /// overwritten.
- ///
- /// The message for the reflog will be ignored if the reference does not
- /// belong in the standard set (HEAD, branches and remote-tracking
- /// branches) and it does not have a reflog.
- ///
- /// It will return GIT_EMODIFIED if the reference's value at the time of
- /// updating does not match the one passed through `current_id` (i.e. if the
- /// ref has changed since the user read it).
- pub fn reference_matching(
- &self,
- name: &str,
- id: Oid,
- force: bool,
- current_id: Oid,
- log_message: &str,
- ) -> Result<Reference<'_>, Error> {
- let name = CString::new(name)?;
- let log_message = CString::new(log_message)?;
- let mut raw = ptr::null_mut();
- unsafe {
- try_call!(raw::git_reference_create_matching(
- &mut raw,
- self.raw(),
- name,
- id.raw(),
- force,
- current_id.raw(),
- log_message
- ));
- Ok(Binding::from_raw(raw))
- }
- }
-
- /// Create a new symbolic reference.
- ///
- /// A symbolic reference is a reference name that refers to another
- /// reference name. If the other name moves, the symbolic name will move,
- /// too. As a simple example, the "HEAD" reference might refer to
- /// "refs/heads/master" while on the "master" branch of a repository.
- ///
- /// Valid reference names must follow one of two patterns:
- ///
- /// 1. Top-level names must contain only capital letters and underscores,
- /// and must begin and end with a letter. (e.g. "HEAD", "ORIG_HEAD").
- /// 2. Names prefixed with "refs/" can be almost anything. You must avoid
- /// the characters '~', '^', ':', '\\', '?', '[', and '*', and the
- /// sequences ".." and "@{" which have special meaning to revparse.
- ///
- /// This function will return an error if a reference already exists with
- /// the given name unless force is true, in which case it will be
- /// overwritten.
- pub fn reference_symbolic(
- &self,
- name: &str,
- target: &str,
- force: bool,
- log_message: &str,
- ) -> Result<Reference<'_>, Error> {
- let name = CString::new(name)?;
- let target = CString::new(target)?;
- let log_message = CString::new(log_message)?;
- let mut raw = ptr::null_mut();
- unsafe {
- try_call!(raw::git_reference_symbolic_create(
- &mut raw,
- self.raw(),
- name,
- target,
- force,
- log_message
- ));
- Ok(Binding::from_raw(raw))
- }
- }
-
- /// Create a new symbolic reference.
- ///
- /// This function will return an error if a reference already exists with
- /// the given name unless force is true, in which case it will be
- /// overwritten.
- ///
- /// It will return GIT_EMODIFIED if the reference's value at the time of
- /// updating does not match the one passed through current_value (i.e. if
- /// the ref has changed since the user read it).
- pub fn reference_symbolic_matching(
- &self,
- name: &str,
- target: &str,
- force: bool,
- current_value: &str,
- log_message: &str,
- ) -> Result<Reference<'_>, Error> {
- let name = CString::new(name)?;
- let target = CString::new(target)?;
- let current_value = CString::new(current_value)?;
- let log_message = CString::new(log_message)?;
- let mut raw = ptr::null_mut();
- unsafe {
- try_call!(raw::git_reference_symbolic_create_matching(
- &mut raw,
- self.raw(),
- name,
- target,
- force,
- current_value,
- log_message
- ));
- Ok(Binding::from_raw(raw))
- }
- }
-
- /// Lookup a reference to one of the objects in a repository.
- pub fn find_reference(&self, name: &str) -> Result<Reference<'_>, Error> {
- let name = CString::new(name)?;
- let mut raw = ptr::null_mut();
- unsafe {
- try_call!(raw::git_reference_lookup(&mut raw, self.raw(), name));
- Ok(Binding::from_raw(raw))
- }
- }
-
- /// Lookup a reference to one of the objects in a repository.
- /// `Repository::find_reference` with teeth; give the method your reference in
- /// human-readable format e.g. 'main' instead of 'refs/heads/main', and it
- /// will do-what-you-mean, returning the `Reference`.
- pub fn resolve_reference_from_short_name(&self, refname: &str) -> Result<Reference<'_>, Error> {
- let refname = CString::new(refname)?;
- let mut raw = ptr::null_mut();
- unsafe {
- try_call!(raw::git_reference_dwim(&mut raw, self.raw(), refname));
- Ok(Binding::from_raw(raw))
- }
- }
-
- /// Lookup a reference by name and resolve immediately to OID.
- ///
- /// This function provides a quick way to resolve a reference name straight
- /// through to the object id that it refers to. This avoids having to
- /// allocate or free any `Reference` objects for simple situations.
- pub fn refname_to_id(&self, name: &str) -> Result<Oid, Error> {
- let name = CString::new(name)?;
- let mut ret = raw::git_oid {
- id: [0; raw::GIT_OID_RAWSZ],
- };
- unsafe {
- try_call!(raw::git_reference_name_to_id(&mut ret, self.raw(), name));
- Ok(Binding::from_raw(&ret as *const _))
- }
- }
-
- /// Creates a git_annotated_commit from the given reference.
- pub fn reference_to_annotated_commit(
- &self,
- reference: &Reference<'_>,
- ) -> Result<AnnotatedCommit<'_>, Error> {
- let mut ret = ptr::null_mut();
- unsafe {
- try_call!(raw::git_annotated_commit_from_ref(
- &mut ret,
- self.raw(),
- reference.raw()
- ));
- Ok(AnnotatedCommit::from_raw(ret))
- }
- }
-
- /// Creates a git_annotated_commit from FETCH_HEAD.
- pub fn annotated_commit_from_fetchhead(
- &self,
- branch_name: &str,
- remote_url: &str,
- id: &Oid,
- ) -> Result<AnnotatedCommit<'_>, Error> {
- let branch_name = CString::new(branch_name)?;
- let remote_url = CString::new(remote_url)?;
-
- let mut ret = ptr::null_mut();
- unsafe {
- try_call!(raw::git_annotated_commit_from_fetchhead(
- &mut ret,
- self.raw(),
- branch_name,
- remote_url,
- id.raw()
- ));
- Ok(AnnotatedCommit::from_raw(ret))
- }
- }
-
- /// Create a new action signature with default user and now timestamp.
- ///
- /// This looks up the user.name and user.email from the configuration and
- /// uses the current time as the timestamp, and creates a new signature
- /// based on that information. It will return `NotFound` if either the
- /// user.name or user.email are not set.
- pub fn signature(&self) -> Result<Signature<'static>, Error> {
- let mut ret = ptr::null_mut();
- unsafe {
- try_call!(raw::git_signature_default(&mut ret, self.raw()));
- Ok(Binding::from_raw(ret))
- }
- }
-
- /// Set up a new git submodule for checkout.
- ///
- /// This does "git submodule add" up to the fetch and checkout of the
- /// submodule contents. It preps a new submodule, creates an entry in
- /// `.gitmodules` and creates an empty initialized repository either at the
- /// given path in the working directory or in `.git/modules` with a gitlink
- /// from the working directory to the new repo.
- ///
- /// To fully emulate "git submodule add" call this function, then `open()`
- /// the submodule repo and perform the clone step as needed. Lastly, call
- /// `add_finalize()` to wrap up adding the new submodule and `.gitmodules`
- /// to the index to be ready to commit.
- pub fn submodule(
- &self,
- url: &str,
- path: &Path,
- use_gitlink: bool,
- ) -> Result<Submodule<'_>, Error> {
- let url = CString::new(url)?;
- let path = path_to_repo_path(path)?;
- let mut raw = ptr::null_mut();
- unsafe {
- try_call!(raw::git_submodule_add_setup(
- &mut raw,
- self.raw(),
- url,
- path,
- use_gitlink
- ));
- Ok(Binding::from_raw(raw))
- }
- }
-
- /// Lookup submodule information by name or path.
- ///
- /// Given either the submodule name or path (they are usually the same),
- /// this returns a structure describing the submodule.
- pub fn find_submodule(&self, name: &str) -> Result<Submodule<'_>, Error> {
- let name = CString::new(name)?;
- let mut raw = ptr::null_mut();
- unsafe {
- try_call!(raw::git_submodule_lookup(&mut raw, self.raw(), name));
- Ok(Binding::from_raw(raw))
- }
- }
-
- /// Get the status for a submodule.
- ///
- /// This looks at a submodule and tries to determine the status. It
- /// will return a combination of the `SubmoduleStatus` values.
- pub fn submodule_status(
- &self,
- name: &str,
- ignore: SubmoduleIgnore,
- ) -> Result<SubmoduleStatus, Error> {
- let mut ret = 0;
- let name = CString::new(name)?;
- unsafe {
- try_call!(raw::git_submodule_status(&mut ret, self.raw, name, ignore));
- }
- Ok(SubmoduleStatus::from_bits_truncate(ret as u32))
- }
-
- /// Set the ignore rule for the submodule in the configuration
- ///
- /// This does not affect any currently-loaded instances.
- pub fn submodule_set_ignore(
- &mut self,
- name: &str,
- ignore: SubmoduleIgnore,
- ) -> Result<(), Error> {
- let name = CString::new(name)?;
- unsafe {
- try_call!(raw::git_submodule_set_ignore(self.raw(), name, ignore));
- }
- Ok(())
- }
-
- /// Set the update rule for the submodule in the configuration
- ///
- /// This setting won't affect any existing instances.
- pub fn submodule_set_update(
- &mut self,
- name: &str,
- update: SubmoduleUpdate,
- ) -> Result<(), Error> {
- let name = CString::new(name)?;
- unsafe {
- try_call!(raw::git_submodule_set_update(self.raw(), name, update));
- }
- Ok(())
- }
-
- /// Set the URL for the submodule in the configuration
- ///
- /// After calling this, you may wish to call [`Submodule::sync`] to write
- /// the changes to the checked out submodule repository.
- pub fn submodule_set_url(&mut self, name: &str, url: &str) -> Result<(), Error> {
- let name = CString::new(name)?;
- let url = CString::new(url)?;
- unsafe {
- try_call!(raw::git_submodule_set_url(self.raw(), name, url));
- }
- Ok(())
- }
-
- /// Set the branch for the submodule in the configuration
- ///
- /// After calling this, you may wish to call [`Submodule::sync`] to write
- /// the changes to the checked out submodule repository.
- pub fn submodule_set_branch(&mut self, name: &str, branch_name: &str) -> Result<(), Error> {
- let name = CString::new(name)?;
- let branch_name = CString::new(branch_name)?;
- unsafe {
- try_call!(raw::git_submodule_set_branch(self.raw(), name, branch_name));
- }
- Ok(())
- }
-
- /// Lookup a reference to one of the objects in a repository.
- pub fn find_tree(&self, oid: Oid) -> Result<Tree<'_>, Error> {
- let mut raw = ptr::null_mut();
- unsafe {
- try_call!(raw::git_tree_lookup(&mut raw, self.raw(), oid.raw()));
- Ok(Binding::from_raw(raw))
- }
- }
-
- /// Create a new TreeBuilder, optionally initialized with the
- /// entries of the given Tree.
- ///
- /// The tree builder can be used to create or modify trees in memory and
- /// write them as tree objects to the database.
- pub fn treebuilder(&self, tree: Option<&Tree<'_>>) -> Result<TreeBuilder<'_>, Error> {
- unsafe {
- let mut ret = ptr::null_mut();
- let tree = match tree {
- Some(tree) => tree.raw(),
- None => ptr::null_mut(),
- };
- try_call!(raw::git_treebuilder_new(&mut ret, self.raw, tree));
- Ok(Binding::from_raw(ret))
- }
- }
-
- /// Create a new tag in the repository from an object
- ///
- /// A new reference will also be created pointing to this tag object. If
- /// `force` is true and a reference already exists with the given name,
- /// it'll be replaced.
- ///
- /// The message will not be cleaned up.
- ///
- /// The tag name will be checked for validity. You must avoid the characters
- /// '~', '^', ':', ' \ ', '?', '[', and '*', and the sequences ".." and " @
- /// {" which have special meaning to revparse.
- pub fn tag(
- &self,
- name: &str,
- target: &Object<'_>,
- tagger: &Signature<'_>,
- message: &str,
- force: bool,
- ) -> Result<Oid, Error> {
- let name = CString::new(name)?;
- let message = CString::new(message)?;
- let mut raw = raw::git_oid {
- id: [0; raw::GIT_OID_RAWSZ],
- };
- unsafe {
- try_call!(raw::git_tag_create(
- &mut raw,
- self.raw,
- name,
- target.raw(),
- tagger.raw(),
- message,
- force
- ));
- Ok(Binding::from_raw(&raw as *const _))
- }
- }
-
- /// Create a new tag in the repository from an object without creating a reference.
- ///
- /// The message will not be cleaned up.
- ///
- /// The tag name will be checked for validity. You must avoid the characters
- /// '~', '^', ':', ' \ ', '?', '[', and '*', and the sequences ".." and " @
- /// {" which have special meaning to revparse.
- pub fn tag_annotation_create(
- &self,
- name: &str,
- target: &Object<'_>,
- tagger: &Signature<'_>,
- message: &str,
- ) -> Result<Oid, Error> {
- let name = CString::new(name)?;
- let message = CString::new(message)?;
- let mut raw_oid = raw::git_oid {
- id: [0; raw::GIT_OID_RAWSZ],
- };
- unsafe {
- try_call!(raw::git_tag_annotation_create(
- &mut raw_oid,
- self.raw,
- name,
- target.raw(),
- tagger.raw(),
- message
- ));
- Ok(Binding::from_raw(&raw_oid as *const _))
- }
- }
-
- /// Create a new lightweight tag pointing at a target object
- ///
- /// A new direct reference will be created pointing to this target object.
- /// If force is true and a reference already exists with the given name,
- /// it'll be replaced.
- pub fn tag_lightweight(
- &self,
- name: &str,
- target: &Object<'_>,
- force: bool,
- ) -> Result<Oid, Error> {
- let name = CString::new(name)?;
- let mut raw = raw::git_oid {
- id: [0; raw::GIT_OID_RAWSZ],
- };
- unsafe {
- try_call!(raw::git_tag_create_lightweight(
- &mut raw,
- self.raw,
- name,
- target.raw(),
- force
- ));
- Ok(Binding::from_raw(&raw as *const _))
- }
- }
-
- /// Lookup a tag object from the repository.
- pub fn find_tag(&self, id: Oid) -> Result<Tag<'_>, Error> {
- let mut raw = ptr::null_mut();
- unsafe {
- try_call!(raw::git_tag_lookup(&mut raw, self.raw, id.raw()));
- Ok(Binding::from_raw(raw))
- }
- }
-
- /// Delete an existing tag reference.
- ///
- /// The tag name will be checked for validity, see `tag` for some rules
- /// about valid names.
- pub fn tag_delete(&self, name: &str) -> Result<(), Error> {
- let name = CString::new(name)?;
- unsafe {
- try_call!(raw::git_tag_delete(self.raw, name));
- Ok(())
- }
- }
-
- /// Get a list with all the tags in the repository.
- ///
- /// An optional fnmatch pattern can also be specified.
- pub fn tag_names(&self, pattern: Option<&str>) -> Result<StringArray, Error> {
- let mut arr = raw::git_strarray {
- strings: ptr::null_mut(),
- count: 0,
- };
- unsafe {
- match pattern {
- Some(s) => {
- let s = CString::new(s)?;
- try_call!(raw::git_tag_list_match(&mut arr, s, self.raw));
- }
- None => {
- try_call!(raw::git_tag_list(&mut arr, self.raw));
- }
- }
- Ok(Binding::from_raw(arr))
- }
- }
-
- /// iterate over all tags calling `cb` on each.
- /// the callback is provided the tag id and name
- pub fn tag_foreach<T>(&self, cb: T) -> Result<(), Error>
- where
- T: FnMut(Oid, &[u8]) -> bool,
- {
- let mut data = TagForeachData {
- cb: Box::new(cb) as TagForeachCB<'_>,
- };
-
- unsafe {
- raw::git_tag_foreach(
- self.raw,
- Some(tag_foreach_cb),
- (&mut data) as *mut _ as *mut _,
- );
- }
- Ok(())
- }
-
- /// Updates files in the index and the working tree to match the content of
- /// the commit pointed at by HEAD.
- pub fn checkout_head(&self, opts: Option<&mut CheckoutBuilder<'_>>) -> Result<(), Error> {
- unsafe {
- let mut raw_opts = mem::zeroed();
- try_call!(raw::git_checkout_init_options(
- &mut raw_opts,
- raw::GIT_CHECKOUT_OPTIONS_VERSION
- ));
- if let Some(c) = opts {
- c.configure(&mut raw_opts);
- }
-
- try_call!(raw::git_checkout_head(self.raw, &raw_opts));
- }
- Ok(())
- }
-
- /// Updates files in the working tree to match the content of the index.
- ///
- /// If the index is `None`, the repository's index will be used.
- pub fn checkout_index(
- &self,
- index: Option<&mut Index>,
- opts: Option<&mut CheckoutBuilder<'_>>,
- ) -> Result<(), Error> {
- unsafe {
- let mut raw_opts = mem::zeroed();
- try_call!(raw::git_checkout_init_options(
- &mut raw_opts,
- raw::GIT_CHECKOUT_OPTIONS_VERSION
- ));
- if let Some(c) = opts {
- c.configure(&mut raw_opts);
- }
-
- try_call!(raw::git_checkout_index(
- self.raw,
- index.map(|i| &mut *i.raw()),
- &raw_opts
- ));
- }
- Ok(())
- }
-
- /// Updates files in the index and working tree to match the content of the
- /// tree pointed at by the treeish.
- pub fn checkout_tree(
- &self,
- treeish: &Object<'_>,
- opts: Option<&mut CheckoutBuilder<'_>>,
- ) -> Result<(), Error> {
- unsafe {
- let mut raw_opts = mem::zeroed();
- try_call!(raw::git_checkout_init_options(
- &mut raw_opts,
- raw::GIT_CHECKOUT_OPTIONS_VERSION
- ));
- if let Some(c) = opts {
- c.configure(&mut raw_opts);
- }
-
- try_call!(raw::git_checkout_tree(self.raw, &*treeish.raw(), &raw_opts));
- }
- Ok(())
- }
-
- /// Merges the given commit(s) into HEAD, writing the results into the
- /// working directory. Any changes are staged for commit and any conflicts
- /// are written to the index. Callers should inspect the repository's index
- /// after this completes, resolve any conflicts and prepare a commit.
- ///
- /// For compatibility with git, the repository is put into a merging state.
- /// Once the commit is done (or if the user wishes to abort), you should
- /// clear this state by calling cleanup_state().
- pub fn merge(
- &self,
- annotated_commits: &[&AnnotatedCommit<'_>],
- merge_opts: Option<&mut MergeOptions>,
- checkout_opts: Option<&mut CheckoutBuilder<'_>>,
- ) -> Result<(), Error> {
- unsafe {
- let mut raw_checkout_opts = mem::zeroed();
- try_call!(raw::git_checkout_init_options(
- &mut raw_checkout_opts,
- raw::GIT_CHECKOUT_OPTIONS_VERSION
- ));
- if let Some(c) = checkout_opts {
- c.configure(&mut raw_checkout_opts);
- }
-
- let mut commit_ptrs = annotated_commits
- .iter()
- .map(|c| c.raw() as *const raw::git_annotated_commit)
- .collect::<Vec<_>>();
-
- try_call!(raw::git_merge(
- self.raw,
- commit_ptrs.as_mut_ptr(),
- annotated_commits.len() as size_t,
- merge_opts.map(|o| o.raw()).unwrap_or(ptr::null()),
- &raw_checkout_opts
- ));
- }
- Ok(())
- }
-
- /// Merge two commits, producing an index that reflects the result of
- /// the merge. The index may be written as-is to the working directory or
- /// checked out. If the index is to be converted to a tree, the caller
- /// should resolve any conflicts that arose as part of the merge.
- pub fn merge_commits(
- &self,
- our_commit: &Commit<'_>,
- their_commit: &Commit<'_>,
- opts: Option<&MergeOptions>,
- ) -> Result<Index, Error> {
- let mut raw = ptr::null_mut();
- unsafe {
- try_call!(raw::git_merge_commits(
- &mut raw,
- self.raw,
- our_commit.raw(),
- their_commit.raw(),
- opts.map(|o| o.raw())
- ));
- Ok(Binding::from_raw(raw))
- }
- }
-
- /// Merge two trees, producing an index that reflects the result of
- /// the merge. The index may be written as-is to the working directory or
- /// checked out. If the index is to be converted to a tree, the caller
- /// should resolve any conflicts that arose as part of the merge.
- pub fn merge_trees(
- &self,
- ancestor_tree: &Tree<'_>,
- our_tree: &Tree<'_>,
- their_tree: &Tree<'_>,
- opts: Option<&MergeOptions>,
- ) -> Result<Index, Error> {
- let mut raw = ptr::null_mut();
- unsafe {
- try_call!(raw::git_merge_trees(
- &mut raw,
- self.raw,
- ancestor_tree.raw(),
- our_tree.raw(),
- their_tree.raw(),
- opts.map(|o| o.raw())
- ));
- Ok(Binding::from_raw(raw))
- }
- }
-
- /// Remove all the metadata associated with an ongoing command like merge,
- /// revert, cherry-pick, etc. For example: MERGE_HEAD, MERGE_MSG, etc.
- pub fn cleanup_state(&self) -> Result<(), Error> {
- unsafe {
- try_call!(raw::git_repository_state_cleanup(self.raw));
- }
- Ok(())
- }
-
- /// Analyzes the given branch(es) and determines the opportunities for
- /// merging them into the HEAD of the repository.
- pub fn merge_analysis(
- &self,
- their_heads: &[&AnnotatedCommit<'_>],
- ) -> Result<(MergeAnalysis, MergePreference), Error> {
- unsafe {
- let mut raw_merge_analysis = 0 as raw::git_merge_analysis_t;
- let mut raw_merge_preference = 0 as raw::git_merge_preference_t;
- let mut their_heads = their_heads
- .iter()
- .map(|v| v.raw() as *const _)
- .collect::<Vec<_>>();
- try_call!(raw::git_merge_analysis(
- &mut raw_merge_analysis,
- &mut raw_merge_preference,
- self.raw,
- their_heads.as_mut_ptr() as *mut _,
- their_heads.len()
- ));
- Ok((
- MergeAnalysis::from_bits_truncate(raw_merge_analysis as u32),
- MergePreference::from_bits_truncate(raw_merge_preference as u32),
- ))
- }
- }
-
- /// Analyzes the given branch(es) and determines the opportunities for
- /// merging them into a reference.
- pub fn merge_analysis_for_ref(
- &self,
- our_ref: &Reference<'_>,
- their_heads: &[&AnnotatedCommit<'_>],
- ) -> Result<(MergeAnalysis, MergePreference), Error> {
- unsafe {
- let mut raw_merge_analysis = 0 as raw::git_merge_analysis_t;
- let mut raw_merge_preference = 0 as raw::git_merge_preference_t;
- let mut their_heads = their_heads
- .iter()
- .map(|v| v.raw() as *const _)
- .collect::<Vec<_>>();
- try_call!(raw::git_merge_analysis_for_ref(
- &mut raw_merge_analysis,
- &mut raw_merge_preference,
- self.raw,
- our_ref.raw(),
- their_heads.as_mut_ptr() as *mut _,
- their_heads.len()
- ));
- Ok((
- MergeAnalysis::from_bits_truncate(raw_merge_analysis as u32),
- MergePreference::from_bits_truncate(raw_merge_preference as u32),
- ))
- }
- }
-
- /// Initializes a rebase operation to rebase the changes in `branch`
- /// relative to `upstream` onto another branch. To begin the rebase process,
- /// call `next()`.
- pub fn rebase(
- &self,
- branch: Option<&AnnotatedCommit<'_>>,
- upstream: Option<&AnnotatedCommit<'_>>,
- onto: Option<&AnnotatedCommit<'_>>,
- opts: Option<&mut RebaseOptions<'_>>,
- ) -> Result<Rebase<'_>, Error> {
- let mut rebase: *mut raw::git_rebase = ptr::null_mut();
- unsafe {
- try_call!(raw::git_rebase_init(
- &mut rebase,
- self.raw(),
- branch.map(|c| c.raw()),
- upstream.map(|c| c.raw()),
- onto.map(|c| c.raw()),
- opts.map(|o| o.raw()).unwrap_or(ptr::null())
- ));
-
- Ok(Rebase::from_raw(rebase))
- }
- }
-
- /// Opens an existing rebase that was previously started by either an
- /// invocation of `rebase()` or by another client.
- pub fn open_rebase(&self, opts: Option<&mut RebaseOptions<'_>>) -> Result<Rebase<'_>, Error> {
- let mut rebase: *mut raw::git_rebase = ptr::null_mut();
- unsafe {
- try_call!(raw::git_rebase_open(
- &mut rebase,
- self.raw(),
- opts.map(|o| o.raw()).unwrap_or(ptr::null())
- ));
- Ok(Rebase::from_raw(rebase))
- }
- }
-
- /// Add a note for an object
- ///
- /// The `notes_ref` argument is the canonical name of the reference to use,
- /// defaulting to "refs/notes/commits". If `force` is specified then
- /// previous notes are overwritten.
- pub fn note(
- &self,
- author: &Signature<'_>,
- committer: &Signature<'_>,
- notes_ref: Option<&str>,
- oid: Oid,
- note: &str,
- force: bool,
- ) -> Result<Oid, Error> {
- let notes_ref = crate::opt_cstr(notes_ref)?;
- let note = CString::new(note)?;
- let mut ret = raw::git_oid {
- id: [0; raw::GIT_OID_RAWSZ],
- };
- unsafe {
- try_call!(raw::git_note_create(
- &mut ret,
- self.raw,
- notes_ref,
- author.raw(),
- committer.raw(),
- oid.raw(),
- note,
- force
- ));
- Ok(Binding::from_raw(&ret as *const _))
- }
- }
-
- /// Get the default notes reference for this repository
- pub fn note_default_ref(&self) -> Result<String, Error> {
- let ret = Buf::new();
- unsafe {
- try_call!(raw::git_note_default_ref(ret.raw(), self.raw));
- }
- Ok(str::from_utf8(&ret).unwrap().to_string())
- }
-
- /// Creates a new iterator for notes in this repository.
- ///
- /// The `notes_ref` argument is the canonical name of the reference to use,
- /// defaulting to "refs/notes/commits".
- ///
- /// The iterator returned yields pairs of (Oid, Oid) where the first element
- /// is the id of the note and the second id is the id the note is
- /// annotating.
- pub fn notes(&self, notes_ref: Option<&str>) -> Result<Notes<'_>, Error> {
- let notes_ref = crate::opt_cstr(notes_ref)?;
- let mut ret = ptr::null_mut();
- unsafe {
- try_call!(raw::git_note_iterator_new(&mut ret, self.raw, notes_ref));
- Ok(Binding::from_raw(ret))
- }
- }
-
- /// Read the note for an object.
- ///
- /// The `notes_ref` argument is the canonical name of the reference to use,
- /// defaulting to "refs/notes/commits".
- ///
- /// The id specified is the Oid of the git object to read the note from.
- pub fn find_note(&self, notes_ref: Option<&str>, id: Oid) -> Result<Note<'_>, Error> {
- let notes_ref = crate::opt_cstr(notes_ref)?;
- let mut ret = ptr::null_mut();
- unsafe {
- try_call!(raw::git_note_read(&mut ret, self.raw, notes_ref, id.raw()));
- Ok(Binding::from_raw(ret))
- }
- }
-
- /// Remove the note for an object.
- ///
- /// The `notes_ref` argument is the canonical name of the reference to use,
- /// defaulting to "refs/notes/commits".
- ///
- /// The id specified is the Oid of the git object to remove the note from.
- pub fn note_delete(
- &self,
- id: Oid,
- notes_ref: Option<&str>,
- author: &Signature<'_>,
- committer: &Signature<'_>,
- ) -> Result<(), Error> {
- let notes_ref = crate::opt_cstr(notes_ref)?;
- unsafe {
- try_call!(raw::git_note_remove(
- self.raw,
- notes_ref,
- author.raw(),
- committer.raw(),
- id.raw()
- ));
- Ok(())
- }
- }
-
- /// Create a revwalk that can be used to traverse the commit graph.
- pub fn revwalk(&self) -> Result<Revwalk<'_>, Error> {
- let mut raw = ptr::null_mut();
- unsafe {
- try_call!(raw::git_revwalk_new(&mut raw, self.raw()));
- Ok(Binding::from_raw(raw))
- }
- }
-
- /// Get the blame for a single file.
- pub fn blame_file(
- &self,
- path: &Path,
- opts: Option<&mut BlameOptions>,
- ) -> Result<Blame<'_>, Error> {
- let path = path_to_repo_path(path)?;
- let mut raw = ptr::null_mut();
-
- unsafe {
- try_call!(raw::git_blame_file(
- &mut raw,
- self.raw(),
- path,
- opts.map(|s| s.raw())
- ));
- Ok(Binding::from_raw(raw))
- }
- }
-
- /// Find a merge base between two commits
- pub fn merge_base(&self, one: Oid, two: Oid) -> Result<Oid, Error> {
- let mut raw = raw::git_oid {
- id: [0; raw::GIT_OID_RAWSZ],
- };
- unsafe {
- try_call!(raw::git_merge_base(
- &mut raw,
- self.raw,
- one.raw(),
- two.raw()
- ));
- Ok(Binding::from_raw(&raw as *const _))
- }
- }
-
- /// Find a merge base given a list of commits
- pub fn merge_base_many(&self, oids: &[Oid]) -> Result<Oid, Error> {
- let mut raw = raw::git_oid {
- id: [0; raw::GIT_OID_RAWSZ],
- };
-
- unsafe {
- try_call!(raw::git_merge_base_many(
- &mut raw,
- self.raw,
- oids.len() as size_t,
- oids.as_ptr() as *const raw::git_oid
- ));
- Ok(Binding::from_raw(&raw as *const _))
- }
- }
-
- /// Find all merge bases between two commits
- pub fn merge_bases(&self, one: Oid, two: Oid) -> Result<OidArray, Error> {
- let mut arr = raw::git_oidarray {
- ids: ptr::null_mut(),
- count: 0,
- };
- unsafe {
- try_call!(raw::git_merge_bases(
- &mut arr,
- self.raw,
- one.raw(),
- two.raw()
- ));
- Ok(Binding::from_raw(arr))
- }
- }
-
- /// Find all merge bases given a list of commits
- pub fn merge_bases_many(&self, oids: &[Oid]) -> Result<OidArray, Error> {
- let mut arr = raw::git_oidarray {
- ids: ptr::null_mut(),
- count: 0,
- };
- unsafe {
- try_call!(raw::git_merge_bases_many(
- &mut arr,
- self.raw,
- oids.len() as size_t,
- oids.as_ptr() as *const raw::git_oid
- ));
- Ok(Binding::from_raw(arr))
- }
- }
-
- /// Count the number of unique commits between two commit objects
- ///
- /// There is no need for branches containing the commits to have any
- /// upstream relationship, but it helps to think of one as a branch and the
- /// other as its upstream, the ahead and behind values will be what git
- /// would report for the branches.
- pub fn graph_ahead_behind(&self, local: Oid, upstream: Oid) -> Result<(usize, usize), Error> {
- unsafe {
- let mut ahead: size_t = 0;
- let mut behind: size_t = 0;
- try_call!(raw::git_graph_ahead_behind(
- &mut ahead,
- &mut behind,
- self.raw(),
- local.raw(),
- upstream.raw()
- ));
- Ok((ahead as usize, behind as usize))
- }
- }
-
- /// Determine if a commit is the descendant of another commit
- ///
- /// Note that a commit is not considered a descendant of itself, in contrast
- /// to `git merge-base --is-ancestor`.
- pub fn graph_descendant_of(&self, commit: Oid, ancestor: Oid) -> Result<bool, Error> {
- unsafe {
- let rv = try_call!(raw::git_graph_descendant_of(
- self.raw(),
- commit.raw(),
- ancestor.raw()
- ));
- Ok(rv != 0)
- }
- }
-
- /// Read the reflog for the given reference
- ///
- /// If there is no reflog file for the given reference yet, an empty reflog
- /// object will be returned.
- pub fn reflog(&self, name: &str) -> Result<Reflog, Error> {
- let name = CString::new(name)?;
- let mut ret = ptr::null_mut();
- unsafe {
- try_call!(raw::git_reflog_read(&mut ret, self.raw, name));
- Ok(Binding::from_raw(ret))
- }
- }
-
- /// Delete the reflog for the given reference
- pub fn reflog_delete(&self, name: &str) -> Result<(), Error> {
- let name = CString::new(name)?;
- unsafe {
- try_call!(raw::git_reflog_delete(self.raw, name));
- }
- Ok(())
- }
-
- /// Rename a reflog
- ///
- /// The reflog to be renamed is expected to already exist.
- pub fn reflog_rename(&self, old_name: &str, new_name: &str) -> Result<(), Error> {
- let old_name = CString::new(old_name)?;
- let new_name = CString::new(new_name)?;
- unsafe {
- try_call!(raw::git_reflog_rename(self.raw, old_name, new_name));
- }
- Ok(())
- }
-
- /// Check if the given reference has a reflog.
- pub fn reference_has_log(&self, name: &str) -> Result<bool, Error> {
- let name = CString::new(name)?;
- let ret = unsafe { try_call!(raw::git_reference_has_log(self.raw, name)) };
- Ok(ret != 0)
- }
-
- /// Ensure that the given reference has a reflog.
- pub fn reference_ensure_log(&self, name: &str) -> Result<(), Error> {
- let name = CString::new(name)?;
- unsafe {
- try_call!(raw::git_reference_ensure_log(self.raw, name));
- }
- Ok(())
- }
-
- /// Describes a commit
- ///
- /// Performs a describe operation on the current commit and the worktree.
- /// After performing a describe on HEAD, a status is run and description is
- /// considered to be dirty if there are.
- pub fn describe(&self, opts: &DescribeOptions) -> Result<Describe<'_>, Error> {
- let mut ret = ptr::null_mut();
- unsafe {
- try_call!(raw::git_describe_workdir(&mut ret, self.raw, opts.raw()));
- Ok(Binding::from_raw(ret))
- }
- }
-
- /// Directly run a diff on two blobs.
- ///
- /// Compared to a file, a blob lacks some contextual information. As such, the
- /// `DiffFile` given to the callback will have some fake data; i.e. mode will be
- /// 0 and path will be `None`.
- ///
- /// `None` is allowed for either `old_blob` or `new_blob` and will be treated
- /// as an empty blob, with the oid set to zero in the `DiffFile`. Passing `None`
- /// for both blobs is a noop; no callbacks will be made at all.
- ///
- /// We do run a binary content check on the blob content and if either blob looks
- /// like binary data, the `DiffFile` binary attribute will be set to 1 and no call to
- /// the `hunk_cb` nor `line_cb` will be made (unless you set the `force_text`
- /// option).
- pub fn diff_blobs(
- &self,
- old_blob: Option<&Blob<'_>>,
- old_as_path: Option<&str>,
- new_blob: Option<&Blob<'_>>,
- new_as_path: Option<&str>,
- opts: Option<&mut DiffOptions>,
- file_cb: Option<&mut FileCb<'_>>,
- binary_cb: Option<&mut BinaryCb<'_>>,
- hunk_cb: Option<&mut HunkCb<'_>>,
- line_cb: Option<&mut LineCb<'_>>,
- ) -> Result<(), Error> {
- let old_as_path = crate::opt_cstr(old_as_path)?;
- let new_as_path = crate::opt_cstr(new_as_path)?;
- let mut cbs = DiffCallbacks {
- file: file_cb,
- binary: binary_cb,
- hunk: hunk_cb,
- line: line_cb,
- };
- let ptr = &mut cbs as *mut _;
- unsafe {
- let file_cb_c: raw::git_diff_file_cb = if cbs.file.is_some() {
- Some(file_cb_c)
- } else {
- None
- };
- let binary_cb_c: raw::git_diff_binary_cb = if cbs.binary.is_some() {
- Some(binary_cb_c)
- } else {
- None
- };
- let hunk_cb_c: raw::git_diff_hunk_cb = if cbs.hunk.is_some() {
- Some(hunk_cb_c)
- } else {
- None
- };
- let line_cb_c: raw::git_diff_line_cb = if cbs.line.is_some() {
- Some(line_cb_c)
- } else {
- None
- };
- try_call!(raw::git_diff_blobs(
- old_blob.map(|s| s.raw()),
- old_as_path,
- new_blob.map(|s| s.raw()),
- new_as_path,
- opts.map(|s| s.raw()),
- file_cb_c,
- binary_cb_c,
- hunk_cb_c,
- line_cb_c,
- ptr as *mut _
- ));
- Ok(())
- }
- }
-
- /// Create a diff with the difference between two tree objects.
- ///
- /// This is equivalent to `git diff <old-tree> <new-tree>`
- ///
- /// The first tree will be used for the "old_file" side of the delta and the
- /// second tree will be used for the "new_file" side of the delta. You can
- /// pass `None` to indicate an empty tree, although it is an error to pass
- /// `None` for both the `old_tree` and `new_tree`.
- pub fn diff_tree_to_tree(
- &self,
- old_tree: Option<&Tree<'_>>,
- new_tree: Option<&Tree<'_>>,
- opts: Option<&mut DiffOptions>,
- ) -> Result<Diff<'_>, Error> {
- let mut ret = ptr::null_mut();
- unsafe {
- try_call!(raw::git_diff_tree_to_tree(
- &mut ret,
- self.raw(),
- old_tree.map(|s| s.raw()),
- new_tree.map(|s| s.raw()),
- opts.map(|s| s.raw())
- ));
- Ok(Binding::from_raw(ret))
- }
- }
-
- /// Create a diff between a tree and repository index.
- ///
- /// This is equivalent to `git diff --cached <treeish>` or if you pass
- /// the HEAD tree, then like `git diff --cached`.
- ///
- /// The tree you pass will be used for the "old_file" side of the delta, and
- /// the index will be used for the "new_file" side of the delta.
- ///
- /// If you pass `None` for the index, then the existing index of the `repo`
- /// will be used. In this case, the index will be refreshed from disk
- /// (if it has changed) before the diff is generated.
- ///
- /// If the tree is `None`, then it is considered an empty tree.
- pub fn diff_tree_to_index(
- &self,
- old_tree: Option<&Tree<'_>>,
- index: Option<&Index>,
- opts: Option<&mut DiffOptions>,
- ) -> Result<Diff<'_>, Error> {
- let mut ret = ptr::null_mut();
- unsafe {
- try_call!(raw::git_diff_tree_to_index(
- &mut ret,
- self.raw(),
- old_tree.map(|s| s.raw()),
- index.map(|s| s.raw()),
- opts.map(|s| s.raw())
- ));
- Ok(Binding::from_raw(ret))
- }
- }
-
- /// Create a diff between two index objects.
- ///
- /// The first index will be used for the "old_file" side of the delta, and
- /// the second index will be used for the "new_file" side of the delta.
- pub fn diff_index_to_index(
- &self,
- old_index: &Index,
- new_index: &Index,
- opts: Option<&mut DiffOptions>,
- ) -> Result<Diff<'_>, Error> {
- let mut ret = ptr::null_mut();
- unsafe {
- try_call!(raw::git_diff_index_to_index(
- &mut ret,
- self.raw(),
- old_index.raw(),
- new_index.raw(),
- opts.map(|s| s.raw())
- ));
- Ok(Binding::from_raw(ret))
- }
- }
-
- /// Create a diff between the repository index and the workdir directory.
- ///
- /// This matches the `git diff` command. See the note below on
- /// `tree_to_workdir` for a discussion of the difference between
- /// `git diff` and `git diff HEAD` and how to emulate a `git diff <treeish>`
- /// using libgit2.
- ///
- /// The index will be used for the "old_file" side of the delta, and the
- /// working directory will be used for the "new_file" side of the delta.
- ///
- /// If you pass `None` for the index, then the existing index of the `repo`
- /// will be used. In this case, the index will be refreshed from disk
- /// (if it has changed) before the diff is generated.
- pub fn diff_index_to_workdir(
- &self,
- index: Option<&Index>,
- opts: Option<&mut DiffOptions>,
- ) -> Result<Diff<'_>, Error> {
- let mut ret = ptr::null_mut();
- unsafe {
- try_call!(raw::git_diff_index_to_workdir(
- &mut ret,
- self.raw(),
- index.map(|s| s.raw()),
- opts.map(|s| s.raw())
- ));
- Ok(Binding::from_raw(ret))
- }
- }
-
- /// Create a diff between a tree and the working directory.
- ///
- /// The tree you provide will be used for the "old_file" side of the delta,
- /// and the working directory will be used for the "new_file" side.
- ///
- /// This is not the same as `git diff <treeish>` or `git diff-index
- /// <treeish>`. Those commands use information from the index, whereas this
- /// function strictly returns the differences between the tree and the files
- /// in the working directory, regardless of the state of the index. Use
- /// `tree_to_workdir_with_index` to emulate those commands.
- ///
- /// To see difference between this and `tree_to_workdir_with_index`,
- /// consider the example of a staged file deletion where the file has then
- /// been put back into the working dir and further modified. The
- /// tree-to-workdir diff for that file is 'modified', but `git diff` would
- /// show status 'deleted' since there is a staged delete.
- ///
- /// If `None` is passed for `tree`, then an empty tree is used.
- pub fn diff_tree_to_workdir(
- &self,
- old_tree: Option<&Tree<'_>>,
- opts: Option<&mut DiffOptions>,
- ) -> Result<Diff<'_>, Error> {
- let mut ret = ptr::null_mut();
- unsafe {
- try_call!(raw::git_diff_tree_to_workdir(
- &mut ret,
- self.raw(),
- old_tree.map(|s| s.raw()),
- opts.map(|s| s.raw())
- ));
- Ok(Binding::from_raw(ret))
- }
- }
-
- /// Create a diff between a tree and the working directory using index data
- /// to account for staged deletes, tracked files, etc.
- ///
- /// This emulates `git diff <tree>` by diffing the tree to the index and
- /// the index to the working directory and blending the results into a
- /// single diff that includes staged deleted, etc.
- pub fn diff_tree_to_workdir_with_index(
- &self,
- old_tree: Option<&Tree<'_>>,
- opts: Option<&mut DiffOptions>,
- ) -> Result<Diff<'_>, Error> {
- let mut ret = ptr::null_mut();
- unsafe {
- try_call!(raw::git_diff_tree_to_workdir_with_index(
- &mut ret,
- self.raw(),
- old_tree.map(|s| s.raw()),
- opts.map(|s| s.raw())
- ));
- Ok(Binding::from_raw(ret))
- }
- }
-
- /// Create a PackBuilder
- pub fn packbuilder(&self) -> Result<PackBuilder<'_>, Error> {
- let mut ret = ptr::null_mut();
- unsafe {
- try_call!(raw::git_packbuilder_new(&mut ret, self.raw()));
- Ok(Binding::from_raw(ret))
- }
- }
-
- /// Save the local modifications to a new stash.
- pub fn stash_save(
- &mut self,
- stasher: &Signature<'_>,
- message: &str,
- flags: Option<StashFlags>,
- ) -> Result<Oid, Error> {
- self.stash_save2(stasher, Some(message), flags)
- }
-
- /// Save the local modifications to a new stash.
- /// unlike `stash_save` it allows to pass a null `message`
- pub fn stash_save2(
- &mut self,
- stasher: &Signature<'_>,
- message: Option<&str>,
- flags: Option<StashFlags>,
- ) -> Result<Oid, Error> {
- unsafe {
- let mut raw_oid = raw::git_oid {
- id: [0; raw::GIT_OID_RAWSZ],
- };
- let message = crate::opt_cstr(message)?;
- let flags = flags.unwrap_or_else(StashFlags::empty);
- try_call!(raw::git_stash_save(
- &mut raw_oid,
- self.raw(),
- stasher.raw(),
- message,
- flags.bits() as c_uint
- ));
- Ok(Binding::from_raw(&raw_oid as *const _))
- }
- }
-
- /// Like `stash_save` but with more options like selective statshing via path patterns.
- pub fn stash_save_ext(
- &mut self,
- opts: Option<&mut StashSaveOptions<'_>>,
- ) -> Result<Oid, Error> {
- unsafe {
- let mut raw_oid = raw::git_oid {
- id: [0; raw::GIT_OID_RAWSZ],
- };
- let opts = opts.map(|opts| opts.raw());
- try_call!(raw::git_stash_save_with_opts(
- &mut raw_oid,
- self.raw(),
- opts
- ));
- Ok(Binding::from_raw(&raw_oid as *const _))
- }
- }
-
- /// Apply a single stashed state from the stash list.
- pub fn stash_apply(
- &mut self,
- index: usize,
- opts: Option<&mut StashApplyOptions<'_>>,
- ) -> Result<(), Error> {
- unsafe {
- let opts = opts.map(|opts| opts.raw());
- try_call!(raw::git_stash_apply(self.raw(), index, opts));
- Ok(())
- }
- }
-
- /// Loop over all the stashed states and issue a callback for each one.
- ///
- /// Return `true` to continue iterating or `false` to stop.
- pub fn stash_foreach<C>(&mut self, mut callback: C) -> Result<(), Error>
- where
- C: FnMut(usize, &str, &Oid) -> bool,
- {
- unsafe {
- let mut data = StashCbData {
- callback: &mut callback,
- };
- let cb: raw::git_stash_cb = Some(stash_cb);
- try_call!(raw::git_stash_foreach(
- self.raw(),
- cb,
- &mut data as *mut _ as *mut _
- ));
- Ok(())
- }
- }
-
- /// Remove a single stashed state from the stash list.
- pub fn stash_drop(&mut self, index: usize) -> Result<(), Error> {
- unsafe {
- try_call!(raw::git_stash_drop(self.raw(), index));
- Ok(())
- }
- }
-
- /// Apply a single stashed state from the stash list and remove it from the list if successful.
- pub fn stash_pop(
- &mut self,
- index: usize,
- opts: Option<&mut StashApplyOptions<'_>>,
- ) -> Result<(), Error> {
- unsafe {
- let opts = opts.map(|opts| opts.raw());
- try_call!(raw::git_stash_pop(self.raw(), index, opts));
- Ok(())
- }
- }
-
- /// Add ignore rules for a repository.
- ///
- /// The format of the rules is the same one of the .gitignore file.
- pub fn add_ignore_rule(&self, rules: &str) -> Result<(), Error> {
- let rules = CString::new(rules)?;
- unsafe {
- try_call!(raw::git_ignore_add_rule(self.raw, rules));
- }
- Ok(())
- }
-
- /// Clear ignore rules that were explicitly added.
- pub fn clear_ignore_rules(&self) -> Result<(), Error> {
- unsafe {
- try_call!(raw::git_ignore_clear_internal_rules(self.raw));
- }
- Ok(())
- }
-
- /// Test if the ignore rules apply to a given path.
- pub fn is_path_ignored<P: AsRef<Path>>(&self, path: P) -> Result<bool, Error> {
- let path = util::cstring_to_repo_path(path.as_ref())?;
- let mut ignored: c_int = 0;
- unsafe {
- try_call!(raw::git_ignore_path_is_ignored(
- &mut ignored,
- self.raw,
- path
- ));
- }
- Ok(ignored == 1)
- }
-
- /// Perform a cherrypick
- pub fn cherrypick(
- &self,
- commit: &Commit<'_>,
- options: Option<&mut CherrypickOptions<'_>>,
- ) -> Result<(), Error> {
- let raw_opts = options.map(|o| o.raw());
- let ptr_raw_opts = match raw_opts.as_ref() {
- Some(v) => v,
- None => std::ptr::null(),
- };
- unsafe {
- try_call!(raw::git_cherrypick(self.raw(), commit.raw(), ptr_raw_opts));
-
- Ok(())
- }
- }
-
- /// Create an index of uncommitted changes, representing the result of
- /// cherry-picking.
- pub fn cherrypick_commit(
- &self,
- cherrypick_commit: &Commit<'_>,
- our_commit: &Commit<'_>,
- mainline: u32,
- options: Option<&MergeOptions>,
- ) -> Result<Index, Error> {
- let mut ret = ptr::null_mut();
- unsafe {
- try_call!(raw::git_cherrypick_commit(
- &mut ret,
- self.raw(),
- cherrypick_commit.raw(),
- our_commit.raw(),
- mainline,
- options.map(|o| o.raw())
- ));
- Ok(Binding::from_raw(ret))
- }
- }
-
- /// Find the remote name of a remote-tracking branch
- pub fn branch_remote_name(&self, refname: &str) -> Result<Buf, Error> {
- let refname = CString::new(refname)?;
- unsafe {
- let buf = Buf::new();
- try_call!(raw::git_branch_remote_name(buf.raw(), self.raw, refname));
- Ok(buf)
- }
- }
-
- /// Retrieves the name of the reference supporting the remote tracking branch,
- /// given the name of a local branch reference.
- pub fn branch_upstream_name(&self, refname: &str) -> Result<Buf, Error> {
- let refname = CString::new(refname)?;
- unsafe {
- let buf = Buf::new();
- try_call!(raw::git_branch_upstream_name(buf.raw(), self.raw, refname));
- Ok(buf)
- }
- }
-
- /// Retrieve the name of the upstream remote of a local branch.
- pub fn branch_upstream_remote(&self, refname: &str) -> Result<Buf, Error> {
- let refname = CString::new(refname)?;
- unsafe {
- let buf = Buf::new();
- try_call!(raw::git_branch_upstream_remote(
- buf.raw(),
- self.raw,
- refname
- ));
- Ok(buf)
- }
- }
-
- /// Apply a Diff to the given repo, making changes directly in the working directory, the index, or both.
- pub fn apply(
- &self,
- diff: &Diff<'_>,
- location: ApplyLocation,
- options: Option<&mut ApplyOptions<'_>>,
- ) -> Result<(), Error> {
- unsafe {
- try_call!(raw::git_apply(
- self.raw,
- diff.raw(),
- location.raw(),
- options.map(|s| s.raw()).unwrap_or(ptr::null())
- ));
-
- Ok(())
- }
- }
-
- /// Apply a Diff to the provided tree, and return the resulting Index.
- pub fn apply_to_tree(
- &self,
- tree: &Tree<'_>,
- diff: &Diff<'_>,
- options: Option<&mut ApplyOptions<'_>>,
- ) -> Result<Index, Error> {
- let mut ret = ptr::null_mut();
- unsafe {
- try_call!(raw::git_apply_to_tree(
- &mut ret,
- self.raw,
- tree.raw(),
- diff.raw(),
- options.map(|s| s.raw()).unwrap_or(ptr::null())
- ));
- Ok(Binding::from_raw(ret))
- }
- }
-
- /// Reverts the given commit, producing changes in the index and working directory.
- pub fn revert(
- &self,
- commit: &Commit<'_>,
- options: Option<&mut RevertOptions<'_>>,
- ) -> Result<(), Error> {
- let raw_opts = options.map(|o| o.raw());
- let ptr_raw_opts = match raw_opts.as_ref() {
- Some(v) => v,
- None => 0 as *const _,
- };
- unsafe {
- try_call!(raw::git_revert(self.raw(), commit.raw(), ptr_raw_opts));
- Ok(())
- }
- }
-
- /// Reverts the given commit against the given "our" commit,
- /// producing an index that reflects the result of the revert.
- pub fn revert_commit(
- &self,
- revert_commit: &Commit<'_>,
- our_commit: &Commit<'_>,
- mainline: u32,
- options: Option<&MergeOptions>,
- ) -> Result<Index, Error> {
- let mut ret = ptr::null_mut();
- unsafe {
- try_call!(raw::git_revert_commit(
- &mut ret,
- self.raw(),
- revert_commit.raw(),
- our_commit.raw(),
- mainline,
- options.map(|o| o.raw())
- ));
- Ok(Binding::from_raw(ret))
- }
- }
-
- /// Lists all the worktrees for the repository
- pub fn worktrees(&self) -> Result<StringArray, Error> {
- let mut arr = raw::git_strarray {
- strings: ptr::null_mut(),
- count: 0,
- };
- unsafe {
- try_call!(raw::git_worktree_list(&mut arr, self.raw));
- Ok(Binding::from_raw(arr))
- }
- }
-
- /// Opens a worktree by name for the given repository
- ///
- /// This can open any worktree that the worktrees method returns.
- pub fn find_worktree(&self, name: &str) -> Result<Worktree, Error> {
- let mut raw = ptr::null_mut();
- let raw_name = CString::new(name)?;
- unsafe {
- try_call!(raw::git_worktree_lookup(&mut raw, self.raw, raw_name));
- Ok(Binding::from_raw(raw))
- }
- }
-
- /// Creates a new worktree for the repository
- pub fn worktree<'a>(
- &'a self,
- name: &str,
- path: &Path,
- opts: Option<&WorktreeAddOptions<'a>>,
- ) -> Result<Worktree, Error> {
- let mut raw = ptr::null_mut();
- let raw_name = CString::new(name)?;
- let raw_path = path.into_c_string()?;
-
- unsafe {
- try_call!(raw::git_worktree_add(
- &mut raw,
- self.raw,
- raw_name,
- raw_path,
- opts.map(|o| o.raw())
- ));
- Ok(Binding::from_raw(raw))
- }
- }
-
- /// Create a new transaction
- pub fn transaction<'a>(&'a self) -> Result<Transaction<'a>, Error> {
- let mut raw = ptr::null_mut();
- unsafe {
- try_call!(raw::git_transaction_new(&mut raw, self.raw));
- Ok(Binding::from_raw(raw))
- }
- }
-
- /// Gets this repository's mailmap.
- pub fn mailmap(&self) -> Result<Mailmap, Error> {
- let mut ret = ptr::null_mut();
- unsafe {
- try_call!(raw::git_mailmap_from_repository(&mut ret, self.raw));
- Ok(Binding::from_raw(ret))
- }
- }
-
- /// If a merge is in progress, invoke 'callback' for each commit ID in the
- /// MERGE_HEAD file.
- pub fn mergehead_foreach<C>(&mut self, mut callback: C) -> Result<(), Error>
- where
- C: FnMut(&Oid) -> bool,
- {
- unsafe {
- let mut data = MergeheadForeachCbData {
- callback: &mut callback,
- };
- let cb: raw::git_repository_mergehead_foreach_cb = Some(mergehead_foreach_cb);
- try_call!(raw::git_repository_mergehead_foreach(
- self.raw(),
- cb,
- &mut data as *mut _ as *mut _
- ));
- Ok(())
- }
- }
-
- /// Invoke 'callback' for each entry in the given FETCH_HEAD file.
- ///
- /// `callback` will be called with with following arguments:
- ///
- /// - `&str`: the reference name
- /// - `&[u8]`: the remote URL
- /// - `&Oid`: the reference target OID
- /// - `bool`: was the reference the result of a merge
- pub fn fetchhead_foreach<C>(&self, mut callback: C) -> Result<(), Error>
- where
- C: FnMut(&str, &[u8], &Oid, bool) -> bool,
- {
- unsafe {
- let mut data = FetchheadForeachCbData {
- callback: &mut callback,
- };
- let cb: raw::git_repository_fetchhead_foreach_cb = Some(fetchhead_foreach_cb);
- try_call!(raw::git_repository_fetchhead_foreach(
- self.raw(),
- cb,
- &mut data as *mut _ as *mut _
- ));
- Ok(())
- }
- }
-}
-
-impl Binding for Repository {
- type Raw = *mut raw::git_repository;
- unsafe fn from_raw(ptr: *mut raw::git_repository) -> Repository {
- Repository { raw: ptr }
- }
- fn raw(&self) -> *mut raw::git_repository {
- self.raw
- }
-}
-
-impl Drop for Repository {
- fn drop(&mut self) {
- unsafe { raw::git_repository_free(self.raw) }
- }
-}
-
-impl RepositoryInitOptions {
- /// Creates a default set of initialization options.
- ///
- /// By default this will set flags for creating all necessary directories
- /// and initializing a directory from the user-configured templates path.
- pub fn new() -> RepositoryInitOptions {
- RepositoryInitOptions {
- flags: raw::GIT_REPOSITORY_INIT_MKDIR as u32
- | raw::GIT_REPOSITORY_INIT_MKPATH as u32
- | raw::GIT_REPOSITORY_INIT_EXTERNAL_TEMPLATE as u32,
- mode: 0,
- workdir_path: None,
- description: None,
- template_path: None,
- initial_head: None,
- origin_url: None,
- }
- }
-
- /// Create a bare repository with no working directory.
- ///
- /// Defaults to false.
- pub fn bare(&mut self, bare: bool) -> &mut RepositoryInitOptions {
- self.flag(raw::GIT_REPOSITORY_INIT_BARE, bare)
- }
-
- /// Return an error if the repository path appears to already be a git
- /// repository.
- ///
- /// Defaults to false.
- pub fn no_reinit(&mut self, enabled: bool) -> &mut RepositoryInitOptions {
- self.flag(raw::GIT_REPOSITORY_INIT_NO_REINIT, enabled)
- }
-
- /// Normally a '/.git/' will be appended to the repo path for non-bare repos
- /// (if it is not already there), but passing this flag prevents that
- /// behavior.
- ///
- /// Defaults to false.
- pub fn no_dotgit_dir(&mut self, enabled: bool) -> &mut RepositoryInitOptions {
- self.flag(raw::GIT_REPOSITORY_INIT_NO_DOTGIT_DIR, enabled)
- }
-
- /// Make the repo path (and workdir path) as needed. The ".git" directory
- /// will always be created regardless of this flag.
- ///
- /// Defaults to true.
- pub fn mkdir(&mut self, enabled: bool) -> &mut RepositoryInitOptions {
- self.flag(raw::GIT_REPOSITORY_INIT_MKDIR, enabled)
- }
-
- /// Recursively make all components of the repo and workdir path as
- /// necessary.
- ///
- /// Defaults to true.
- pub fn mkpath(&mut self, enabled: bool) -> &mut RepositoryInitOptions {
- self.flag(raw::GIT_REPOSITORY_INIT_MKPATH, enabled)
- }
-
- /// Set to one of the `RepositoryInit` constants, or a custom value.
- pub fn mode(&mut self, mode: RepositoryInitMode) -> &mut RepositoryInitOptions {
- self.mode = mode.bits();
- self
- }
-
- /// Enable or disable using external templates.
- ///
- /// If enabled, then the `template_path` option will be queried first, then
- /// `init.templatedir` from the global config, and finally
- /// `/usr/share/git-core-templates` will be used (if it exists).
- ///
- /// Defaults to true.
- pub fn external_template(&mut self, enabled: bool) -> &mut RepositoryInitOptions {
- self.flag(raw::GIT_REPOSITORY_INIT_EXTERNAL_TEMPLATE, enabled)
- }
-
- fn flag(
- &mut self,
- flag: raw::git_repository_init_flag_t,
- on: bool,
- ) -> &mut RepositoryInitOptions {
- if on {
- self.flags |= flag as u32;
- } else {
- self.flags &= !(flag as u32);
- }
- self
- }
-
- /// The path to the working directory.
- ///
- /// If this is a relative path it will be evaluated relative to the repo
- /// path. If this is not the "natural" working directory, a .git gitlink
- /// file will be created here linking to the repo path.
- pub fn workdir_path(&mut self, path: &Path) -> &mut RepositoryInitOptions {
- // Normal file path OK (does not need Windows conversion).
- self.workdir_path = Some(path.into_c_string().unwrap());
- self
- }
-
- /// If set, this will be used to initialize the "description" file in the
- /// repository instead of using the template content.
- pub fn description(&mut self, desc: &str) -> &mut RepositoryInitOptions {
- self.description = Some(CString::new(desc).unwrap());
- self
- }
-
- /// When the `external_template` option is set, this is the first location
- /// to check for the template directory.
- ///
- /// If this is not configured, then the default locations will be searched
- /// instead.
- pub fn template_path(&mut self, path: &Path) -> &mut RepositoryInitOptions {
- // Normal file path OK (does not need Windows conversion).
- self.template_path = Some(path.into_c_string().unwrap());
- self
- }
-
- /// The name of the head to point HEAD at.
- ///
- /// If not configured, this will be taken from your git configuration.
- /// If this begins with `refs/` it will be used verbatim;
- /// otherwise `refs/heads/` will be prefixed
- pub fn initial_head(&mut self, head: &str) -> &mut RepositoryInitOptions {
- self.initial_head = Some(CString::new(head).unwrap());
- self
- }
-
- /// If set, then after the rest of the repository initialization is
- /// completed an `origin` remote will be added pointing to this URL.
- pub fn origin_url(&mut self, url: &str) -> &mut RepositoryInitOptions {
- self.origin_url = Some(CString::new(url).unwrap());
- self
- }
-
- /// Creates a set of raw init options to be used with
- /// `git_repository_init_ext`.
- ///
- /// This method is unsafe as the returned value may have pointers to the
- /// interior of this structure.
- pub unsafe fn raw(&self) -> raw::git_repository_init_options {
- let mut opts = mem::zeroed();
- assert_eq!(
- raw::git_repository_init_init_options(
- &mut opts,
- raw::GIT_REPOSITORY_INIT_OPTIONS_VERSION
- ),
- 0
- );
- opts.flags = self.flags;
- opts.mode = self.mode;
- opts.workdir_path = crate::call::convert(&self.workdir_path);
- opts.description = crate::call::convert(&self.description);
- opts.template_path = crate::call::convert(&self.template_path);
- opts.initial_head = crate::call::convert(&self.initial_head);
- opts.origin_url = crate::call::convert(&self.origin_url);
- opts
- }
-}
-
-#[cfg(test)]
-mod tests {
- use crate::build::CheckoutBuilder;
- use crate::CherrypickOptions;
- use crate::{
- ObjectType, Oid, Repository, ResetType, Signature, SubmoduleIgnore, SubmoduleUpdate,
- };
- use std::ffi::OsStr;
- use std::fs;
- use std::path::Path;
- use tempfile::TempDir;
-
- #[test]
- fn smoke_init() {
- let td = TempDir::new().unwrap();
- let path = td.path();
-
- let repo = Repository::init(path).unwrap();
- assert!(!repo.is_bare());
- }
-
- #[test]
- fn smoke_init_bare() {
- let td = TempDir::new().unwrap();
- let path = td.path();
-
- let repo = Repository::init_bare(path).unwrap();
- assert!(repo.is_bare());
- assert!(repo.namespace().is_none());
- }
-
- #[test]
- fn smoke_open() {
- let td = TempDir::new().unwrap();
- let path = td.path();
- Repository::init(td.path()).unwrap();
- let repo = Repository::open(path).unwrap();
- assert!(!repo.is_bare());
- assert!(!repo.is_shallow());
- assert!(repo.is_empty().unwrap());
- assert_eq!(
- crate::test::realpath(&repo.path()).unwrap(),
- crate::test::realpath(&td.path().join(".git/")).unwrap()
- );
- assert_eq!(repo.state(), crate::RepositoryState::Clean);
- }
-
- #[test]
- fn smoke_open_bare() {
- let td = TempDir::new().unwrap();
- let path = td.path();
- Repository::init_bare(td.path()).unwrap();
-
- let repo = Repository::open(path).unwrap();
- assert!(repo.is_bare());
- assert_eq!(
- crate::test::realpath(&repo.path()).unwrap(),
- crate::test::realpath(&td.path().join("")).unwrap()
- );
- }
-
- #[test]
- fn smoke_checkout() {
- let (_td, repo) = crate::test::repo_init();
- repo.checkout_head(None).unwrap();
- }
-
- #[test]
- fn smoke_revparse() {
- let (_td, repo) = crate::test::repo_init();
- let rev = repo.revparse("HEAD").unwrap();
- assert!(rev.to().is_none());
- let from = rev.from().unwrap();
- assert!(rev.from().is_some());
-
- assert_eq!(repo.revparse_single("HEAD").unwrap().id(), from.id());
- let obj = repo.find_object(from.id(), None).unwrap().clone();
- obj.peel(ObjectType::Any).unwrap();
- obj.short_id().unwrap();
- repo.reset(&obj, ResetType::Hard, None).unwrap();
- let mut opts = CheckoutBuilder::new();
- t!(repo.reset(&obj, ResetType::Soft, Some(&mut opts)));
- }
-
- #[test]
- fn makes_dirs() {
- let td = TempDir::new().unwrap();
- Repository::init(&td.path().join("a/b/c/d")).unwrap();
- }
-
- #[test]
- fn smoke_discover() {
- let td = TempDir::new().unwrap();
- let subdir = td.path().join("subdi");
- fs::create_dir(&subdir).unwrap();
- Repository::init_bare(td.path()).unwrap();
- let repo = Repository::discover(&subdir).unwrap();
- assert_eq!(
- crate::test::realpath(&repo.path()).unwrap(),
- crate::test::realpath(&td.path().join("")).unwrap()
- );
- }
-
- #[test]
- fn smoke_discover_path() {
- let td = TempDir::new().unwrap();
- let subdir = td.path().join("subdi");
- fs::create_dir(&subdir).unwrap();
- Repository::init_bare(td.path()).unwrap();
- let path = Repository::discover_path(&subdir, &[] as &[&OsStr]).unwrap();
- assert_eq!(
- crate::test::realpath(&path).unwrap(),
- crate::test::realpath(&td.path().join("")).unwrap()
- );
- }
-
- #[test]
- fn smoke_discover_path_ceiling_dir() {
- let td = TempDir::new().unwrap();
- let subdir = td.path().join("subdi");
- fs::create_dir(&subdir).unwrap();
- let ceilingdir = subdir.join("ceiling");
- fs::create_dir(&ceilingdir).unwrap();
- let testdir = ceilingdir.join("testdi");
- fs::create_dir(&testdir).unwrap();
- Repository::init_bare(td.path()).unwrap();
- let path = Repository::discover_path(&testdir, &[ceilingdir.as_os_str()]);
-
- assert!(path.is_err());
- }
-
- #[test]
- fn smoke_open_ext() {
- let td = TempDir::new().unwrap();
- let subdir = td.path().join("subdir");
- fs::create_dir(&subdir).unwrap();
- Repository::init(td.path()).unwrap();
-
- let repo = Repository::open_ext(
- &subdir,
- crate::RepositoryOpenFlags::empty(),
- &[] as &[&OsStr],
- )
- .unwrap();
- assert!(!repo.is_bare());
- assert_eq!(
- crate::test::realpath(&repo.path()).unwrap(),
- crate::test::realpath(&td.path().join(".git")).unwrap()
- );
-
- let repo =
- Repository::open_ext(&subdir, crate::RepositoryOpenFlags::BARE, &[] as &[&OsStr])
- .unwrap();
- assert!(repo.is_bare());
- assert_eq!(
- crate::test::realpath(&repo.path()).unwrap(),
- crate::test::realpath(&td.path().join(".git")).unwrap()
- );
-
- let err = Repository::open_ext(
- &subdir,
- crate::RepositoryOpenFlags::NO_SEARCH,
- &[] as &[&OsStr],
- )
- .err()
- .unwrap();
- assert_eq!(err.code(), crate::ErrorCode::NotFound);
-
- assert!(
- Repository::open_ext(&subdir, crate::RepositoryOpenFlags::empty(), &[&subdir]).is_ok()
- );
- }
-
- fn graph_repo_init() -> (TempDir, Repository) {
- let (_td, repo) = crate::test::repo_init();
- {
- let head = repo.head().unwrap().target().unwrap();
- let head = repo.find_commit(head).unwrap();
-
- let mut index = repo.index().unwrap();
- let id = index.write_tree().unwrap();
-
- let tree = repo.find_tree(id).unwrap();
- let sig = repo.signature().unwrap();
- repo.commit(Some("HEAD"), &sig, &sig, "second", &tree, &[&head])
- .unwrap();
- }
- (_td, repo)
- }
-
- #[test]
- fn smoke_graph_ahead_behind() {
- let (_td, repo) = graph_repo_init();
- let head = repo.head().unwrap().target().unwrap();
- let head = repo.find_commit(head).unwrap();
- let head_id = head.id();
- let head_parent_id = head.parent(0).unwrap().id();
- let (ahead, behind) = repo.graph_ahead_behind(head_id, head_parent_id).unwrap();
- assert_eq!(ahead, 1);
- assert_eq!(behind, 0);
- let (ahead, behind) = repo.graph_ahead_behind(head_parent_id, head_id).unwrap();
- assert_eq!(ahead, 0);
- assert_eq!(behind, 1);
- }
-
- #[test]
- fn smoke_graph_descendant_of() {
- let (_td, repo) = graph_repo_init();
- let head = repo.head().unwrap().target().unwrap();
- let head = repo.find_commit(head).unwrap();
- let head_id = head.id();
- let head_parent_id = head.parent(0).unwrap().id();
- assert!(repo.graph_descendant_of(head_id, head_parent_id).unwrap());
- assert!(!repo.graph_descendant_of(head_parent_id, head_id).unwrap());
- }
-
- #[test]
- fn smoke_reference_has_log_ensure_log() {
- let (_td, repo) = crate::test::repo_init();
-
- assert_eq!(repo.reference_has_log("HEAD").unwrap(), true);
- assert_eq!(repo.reference_has_log("refs/heads/main").unwrap(), true);
- assert_eq!(repo.reference_has_log("NOT_HEAD").unwrap(), false);
- let main_oid = repo.revparse_single("main").unwrap().id();
- assert!(repo
- .reference("NOT_HEAD", main_oid, false, "creating a new branch")
- .is_ok());
- assert_eq!(repo.reference_has_log("NOT_HEAD").unwrap(), false);
- assert!(repo.reference_ensure_log("NOT_HEAD").is_ok());
- assert_eq!(repo.reference_has_log("NOT_HEAD").unwrap(), true);
- }
-
- #[test]
- fn smoke_set_head() {
- let (_td, repo) = crate::test::repo_init();
-
- assert!(repo.set_head("refs/heads/does-not-exist").is_ok());
- assert!(repo.head().is_err());
-
- assert!(repo.set_head("refs/heads/main").is_ok());
- assert!(repo.head().is_ok());
-
- assert!(repo.set_head("*").is_err());
- }
-
- #[test]
- fn smoke_set_head_bytes() {
- let (_td, repo) = crate::test::repo_init();
-
- assert!(repo.set_head_bytes(b"refs/heads/does-not-exist").is_ok());
- assert!(repo.head().is_err());
-
- assert!(repo.set_head_bytes(b"refs/heads/main").is_ok());
- assert!(repo.head().is_ok());
-
- assert!(repo.set_head_bytes(b"*").is_err());
- }
-
- #[test]
- fn smoke_set_head_detached() {
- let (_td, repo) = crate::test::repo_init();
-
- let void_oid = Oid::from_bytes(b"00000000000000000000").unwrap();
- assert!(repo.set_head_detached(void_oid).is_err());
-
- let main_oid = repo.revparse_single("main").unwrap().id();
- assert!(repo.set_head_detached(main_oid).is_ok());
- assert_eq!(repo.head().unwrap().target().unwrap(), main_oid);
- }
-
- /// create the following:
- /// /---o4
- /// /---o3
- /// o1---o2
- #[test]
- fn smoke_merge_base() {
- let (_td, repo) = graph_repo_init();
- let sig = repo.signature().unwrap();
-
- // let oid1 = head
- let oid1 = repo.head().unwrap().target().unwrap();
- let commit1 = repo.find_commit(oid1).unwrap();
- println!("created oid1 {:?}", oid1);
-
- repo.branch("branch_a", &commit1, true).unwrap();
- repo.branch("branch_b", &commit1, true).unwrap();
- repo.branch("branch_c", &commit1, true).unwrap();
-
- // create commit oid2 on branch_a
- let mut index = repo.index().unwrap();
- let p = Path::new(repo.workdir().unwrap()).join("file_a");
- println!("using path {:?}", p);
- fs::File::create(&p).unwrap();
- index.add_path(Path::new("file_a")).unwrap();
- let id_a = index.write_tree().unwrap();
- let tree_a = repo.find_tree(id_a).unwrap();
- let oid2 = repo
- .commit(
- Some("refs/heads/branch_a"),
- &sig,
- &sig,
- "commit 2",
- &tree_a,
- &[&commit1],
- )
- .unwrap();
- repo.find_commit(oid2).unwrap();
- println!("created oid2 {:?}", oid2);
-
- t!(repo.reset(commit1.as_object(), ResetType::Hard, None));
-
- // create commit oid3 on branch_b
- let mut index = repo.index().unwrap();
- let p = Path::new(repo.workdir().unwrap()).join("file_b");
- fs::File::create(&p).unwrap();
- index.add_path(Path::new("file_b")).unwrap();
- let id_b = index.write_tree().unwrap();
- let tree_b = repo.find_tree(id_b).unwrap();
- let oid3 = repo
- .commit(
- Some("refs/heads/branch_b"),
- &sig,
- &sig,
- "commit 3",
- &tree_b,
- &[&commit1],
- )
- .unwrap();
- repo.find_commit(oid3).unwrap();
- println!("created oid3 {:?}", oid3);
-
- t!(repo.reset(commit1.as_object(), ResetType::Hard, None));
-
- // create commit oid4 on branch_c
- let mut index = repo.index().unwrap();
- let p = Path::new(repo.workdir().unwrap()).join("file_c");
- fs::File::create(&p).unwrap();
- index.add_path(Path::new("file_c")).unwrap();
- let id_c = index.write_tree().unwrap();
- let tree_c = repo.find_tree(id_c).unwrap();
- let oid4 = repo
- .commit(
- Some("refs/heads/branch_c"),
- &sig,
- &sig,
- "commit 3",
- &tree_c,
- &[&commit1],
- )
- .unwrap();
- repo.find_commit(oid4).unwrap();
- println!("created oid4 {:?}", oid4);
-
- // the merge base of (oid2,oid3) should be oid1
- let merge_base = repo.merge_base(oid2, oid3).unwrap();
- assert_eq!(merge_base, oid1);
-
- // the merge base of (oid2,oid3,oid4) should be oid1
- let merge_base = repo.merge_base_many(&[oid2, oid3, oid4]).unwrap();
- assert_eq!(merge_base, oid1);
- }
-
- /// create an octopus:
- /// /---o2-o4
- /// o1 X
- /// \---o3-o5
- /// and checks that the merge bases of (o4,o5) are (o2,o3)
- #[test]
- fn smoke_merge_bases() {
- let (_td, repo) = graph_repo_init();
- let sig = repo.signature().unwrap();
-
- // let oid1 = head
- let oid1 = repo.head().unwrap().target().unwrap();
- let commit1 = repo.find_commit(oid1).unwrap();
- println!("created oid1 {:?}", oid1);
-
- repo.branch("branch_a", &commit1, true).unwrap();
- repo.branch("branch_b", &commit1, true).unwrap();
-
- // create commit oid2 on branchA
- let mut index = repo.index().unwrap();
- let p = Path::new(repo.workdir().unwrap()).join("file_a");
- println!("using path {:?}", p);
- fs::File::create(&p).unwrap();
- index.add_path(Path::new("file_a")).unwrap();
- let id_a = index.write_tree().unwrap();
- let tree_a = repo.find_tree(id_a).unwrap();
- let oid2 = repo
- .commit(
- Some("refs/heads/branch_a"),
- &sig,
- &sig,
- "commit 2",
- &tree_a,
- &[&commit1],
- )
- .unwrap();
- let commit2 = repo.find_commit(oid2).unwrap();
- println!("created oid2 {:?}", oid2);
-
- t!(repo.reset(commit1.as_object(), ResetType::Hard, None));
-
- // create commit oid3 on branchB
- let mut index = repo.index().unwrap();
- let p = Path::new(repo.workdir().unwrap()).join("file_b");
- fs::File::create(&p).unwrap();
- index.add_path(Path::new("file_b")).unwrap();
- let id_b = index.write_tree().unwrap();
- let tree_b = repo.find_tree(id_b).unwrap();
- let oid3 = repo
- .commit(
- Some("refs/heads/branch_b"),
- &sig,
- &sig,
- "commit 3",
- &tree_b,
- &[&commit1],
- )
- .unwrap();
- let commit3 = repo.find_commit(oid3).unwrap();
- println!("created oid3 {:?}", oid3);
-
- // create merge commit oid4 on branchA with parents oid2 and oid3
- //let mut index4 = repo.merge_commits(&commit2, &commit3, None).unwrap();
- repo.set_head("refs/heads/branch_a").unwrap();
- repo.checkout_head(None).unwrap();
- let oid4 = repo
- .commit(
- Some("refs/heads/branch_a"),
- &sig,
- &sig,
- "commit 4",
- &tree_a,
- &[&commit2, &commit3],
- )
- .unwrap();
- //index4.write_tree_to(&repo).unwrap();
- println!("created oid4 {:?}", oid4);
-
- // create merge commit oid5 on branchB with parents oid2 and oid3
- //let mut index5 = repo.merge_commits(&commit3, &commit2, None).unwrap();
- repo.set_head("refs/heads/branch_b").unwrap();
- repo.checkout_head(None).unwrap();
- let oid5 = repo
- .commit(
- Some("refs/heads/branch_b"),
- &sig,
- &sig,
- "commit 5",
- &tree_a,
- &[&commit3, &commit2],
- )
- .unwrap();
- //index5.write_tree_to(&repo).unwrap();
- println!("created oid5 {:?}", oid5);
-
- // merge bases of (oid4,oid5) should be (oid2,oid3)
- let merge_bases = repo.merge_bases(oid4, oid5).unwrap();
- let mut found_oid2 = false;
- let mut found_oid3 = false;
- for mg in merge_bases.iter() {
- println!("found merge base {:?}", mg);
- if mg == &oid2 {
- found_oid2 = true;
- } else if mg == &oid3 {
- found_oid3 = true;
- } else {
- assert!(false);
- }
- }
- assert!(found_oid2);
- assert!(found_oid3);
- assert_eq!(merge_bases.len(), 2);
-
- // merge bases of (oid4,oid5) should be (oid2,oid3)
- let merge_bases = repo.merge_bases_many(&[oid4, oid5]).unwrap();
- let mut found_oid2 = false;
- let mut found_oid3 = false;
- for mg in merge_bases.iter() {
- println!("found merge base {:?}", mg);
- if mg == &oid2 {
- found_oid2 = true;
- } else if mg == &oid3 {
- found_oid3 = true;
- } else {
- assert!(false);
- }
- }
- assert!(found_oid2);
- assert!(found_oid3);
- assert_eq!(merge_bases.len(), 2);
- }
-
- #[test]
- fn smoke_revparse_ext() {
- let (_td, repo) = graph_repo_init();
-
- {
- let short_refname = "main";
- let expected_refname = "refs/heads/main";
- let (obj, reference) = repo.revparse_ext(short_refname).unwrap();
- let expected_obj = repo.revparse_single(expected_refname).unwrap();
- assert_eq!(obj.id(), expected_obj.id());
- assert_eq!(reference.unwrap().name().unwrap(), expected_refname);
- }
- {
- let missing_refname = "refs/heads/does-not-exist";
- assert!(repo.revparse_ext(missing_refname).is_err());
- }
- {
- let (_obj, reference) = repo.revparse_ext("HEAD^").unwrap();
- assert!(reference.is_none());
- }
- }
-
- #[test]
- fn smoke_is_path_ignored() {
- let (_td, repo) = graph_repo_init();
-
- assert!(!repo.is_path_ignored(Path::new("foo")).unwrap());
-
- let _ = repo.add_ignore_rule("/foo");
- assert!(repo.is_path_ignored(Path::new("foo")).unwrap());
- if cfg!(windows) {
- assert!(repo.is_path_ignored(Path::new("foo\\thing")).unwrap());
- }
-
- let _ = repo.clear_ignore_rules();
- assert!(!repo.is_path_ignored(Path::new("foo")).unwrap());
- if cfg!(windows) {
- assert!(!repo.is_path_ignored(Path::new("foo\\thing")).unwrap());
- }
- }
-
- #[test]
- fn smoke_cherrypick() {
- let (_td, repo) = crate::test::repo_init();
- let sig = repo.signature().unwrap();
-
- let oid1 = repo.head().unwrap().target().unwrap();
- let commit1 = repo.find_commit(oid1).unwrap();
-
- repo.branch("branch_a", &commit1, true).unwrap();
-
- // Add 2 commits on top of the initial one in branch_a
- let mut index = repo.index().unwrap();
- let p1 = Path::new(repo.workdir().unwrap()).join("file_c");
- fs::File::create(&p1).unwrap();
- index.add_path(Path::new("file_c")).unwrap();
- let id = index.write_tree().unwrap();
- let tree_c = repo.find_tree(id).unwrap();
- let oid2 = repo
- .commit(
- Some("refs/heads/branch_a"),
- &sig,
- &sig,
- "commit 2",
- &tree_c,
- &[&commit1],
- )
- .unwrap();
- let commit2 = repo.find_commit(oid2).unwrap();
- println!("created oid2 {:?}", oid2);
- assert!(p1.exists());
-
- let mut index = repo.index().unwrap();
- let p2 = Path::new(repo.workdir().unwrap()).join("file_d");
- fs::File::create(&p2).unwrap();
- index.add_path(Path::new("file_d")).unwrap();
- let id = index.write_tree().unwrap();
- let tree_d = repo.find_tree(id).unwrap();
- let oid3 = repo
- .commit(
- Some("refs/heads/branch_a"),
- &sig,
- &sig,
- "commit 3",
- &tree_d,
- &[&commit2],
- )
- .unwrap();
- let commit3 = repo.find_commit(oid3).unwrap();
- println!("created oid3 {:?}", oid3);
- assert!(p1.exists());
- assert!(p2.exists());
-
- // cherry-pick commit3 on top of commit1 in branch b
- repo.reset(commit1.as_object(), ResetType::Hard, None)
- .unwrap();
- let mut cherrypick_opts = CherrypickOptions::new();
- repo.cherrypick(&commit3, Some(&mut cherrypick_opts))
- .unwrap();
- let id = repo.index().unwrap().write_tree().unwrap();
- let tree_d = repo.find_tree(id).unwrap();
- let oid4 = repo
- .commit(Some("HEAD"), &sig, &sig, "commit 4", &tree_d, &[&commit1])
- .unwrap();
- let commit4 = repo.find_commit(oid4).unwrap();
- // should have file from commit3, but not the file from commit2
- assert_eq!(commit4.parent(0).unwrap().id(), commit1.id());
- assert!(!p1.exists());
- assert!(p2.exists());
- }
-
- #[test]
- fn smoke_revert() {
- let (_td, repo) = crate::test::repo_init();
- let foo_file = Path::new(repo.workdir().unwrap()).join("foo");
- assert!(!foo_file.exists());
-
- let (oid1, _id) = crate::test::commit(&repo);
- let commit1 = repo.find_commit(oid1).unwrap();
- t!(repo.reset(commit1.as_object(), ResetType::Hard, None));
- assert!(foo_file.exists());
-
- repo.revert(&commit1, None).unwrap();
- let id = repo.index().unwrap().write_tree().unwrap();
- let tree2 = repo.find_tree(id).unwrap();
- let sig = repo.signature().unwrap();
- repo.commit(Some("HEAD"), &sig, &sig, "commit 1", &tree2, &[&commit1])
- .unwrap();
- // reverting once removes `foo` file
- assert!(!foo_file.exists());
-
- let oid2 = repo.head().unwrap().target().unwrap();
- let commit2 = repo.find_commit(oid2).unwrap();
- repo.revert(&commit2, None).unwrap();
- let id = repo.index().unwrap().write_tree().unwrap();
- let tree3 = repo.find_tree(id).unwrap();
- repo.commit(Some("HEAD"), &sig, &sig, "commit 2", &tree3, &[&commit2])
- .unwrap();
- // reverting twice restores `foo` file
- assert!(foo_file.exists());
- }
-
- #[test]
- fn smoke_config_write_and_read() {
- let (td, repo) = crate::test::repo_init();
-
- let mut config = repo.config().unwrap();
-
- config.set_bool("commit.gpgsign", false).unwrap();
-
- let c = fs::read_to_string(td.path().join(".git").join("config")).unwrap();
-
- assert!(c.contains("[commit]"));
- assert!(c.contains("gpgsign = false"));
-
- let config = repo.config().unwrap();
-
- assert!(!config.get_bool("commit.gpgsign").unwrap());
- }
-
- #[test]
- fn smoke_merge_analysis_for_ref() -> Result<(), crate::Error> {
- let (_td, repo) = graph_repo_init();
-
- // Set up this repo state:
- // * second (their-branch)
- // * initial (HEAD -> main)
- //
- // We expect that their-branch can be fast-forward merged into main.
-
- // git checkout --detach HEAD
- let head_commit = repo.head()?.peel_to_commit()?;
- repo.set_head_detached(head_commit.id())?;
-
- // git branch their-branch HEAD
- let their_branch = repo.branch("their-branch", &head_commit, false)?;
-
- // git branch -f main HEAD~
- let mut parents_iter = head_commit.parents();
- let parent = parents_iter.next().unwrap();
- assert!(parents_iter.next().is_none());
-
- let main = repo.branch("main", &parent, true)?;
-
- // git checkout main
- repo.set_head(main.get().name().expect("should be utf-8"))?;
-
- let (merge_analysis, _merge_preference) = repo.merge_analysis_for_ref(
- main.get(),
- &[&repo.reference_to_annotated_commit(their_branch.get())?],
- )?;
-
- assert!(merge_analysis.contains(crate::MergeAnalysis::ANALYSIS_FASTFORWARD));
-
- Ok(())
- }
-
- #[test]
- fn smoke_submodule_set() -> Result<(), crate::Error> {
- let (td1, _repo) = crate::test::repo_init();
- let (td2, mut repo2) = crate::test::repo_init();
- let url = crate::test::path2url(td1.path());
- let name = "bar";
- {
- let mut s = repo2.submodule(&url, Path::new(name), true)?;
- fs::remove_dir_all(td2.path().join("bar")).unwrap();
- Repository::clone(&url, td2.path().join("bar"))?;
- s.add_to_index(false)?;
- s.add_finalize()?;
- }
-
- // update strategy
- repo2.submodule_set_update(name, SubmoduleUpdate::None)?;
- assert!(matches!(
- repo2.find_submodule(name)?.update_strategy(),
- SubmoduleUpdate::None
- ));
- repo2.submodule_set_update(name, SubmoduleUpdate::Rebase)?;
- assert!(matches!(
- repo2.find_submodule(name)?.update_strategy(),
- SubmoduleUpdate::Rebase
- ));
-
- // ignore rule
- repo2.submodule_set_ignore(name, SubmoduleIgnore::Untracked)?;
- assert!(matches!(
- repo2.find_submodule(name)?.ignore_rule(),
- SubmoduleIgnore::Untracked
- ));
- repo2.submodule_set_ignore(name, SubmoduleIgnore::Dirty)?;
- assert!(matches!(
- repo2.find_submodule(name)?.ignore_rule(),
- SubmoduleIgnore::Dirty
- ));
-
- // url
- repo2.submodule_set_url(name, "fake-url")?;
- assert_eq!(repo2.find_submodule(name)?.url(), Some("fake-url"));
-
- // branch
- repo2.submodule_set_branch(name, "fake-branch")?;
- assert_eq!(repo2.find_submodule(name)?.branch(), Some("fake-branch"));
-
- Ok(())
- }
-
- #[test]
- fn smoke_mailmap_from_repository() {
- let (_td, repo) = crate::test::repo_init();
-
- let commit = {
- let head = t!(repo.head()).target().unwrap();
- t!(repo.find_commit(head))
- };
-
- // This is our baseline for HEAD.
- let author = commit.author();
- let committer = commit.committer();
- assert_eq!(author.name(), Some("name"));
- assert_eq!(author.email(), Some("email"));
- assert_eq!(committer.name(), Some("name"));
- assert_eq!(committer.email(), Some("email"));
-
- // There is no .mailmap file in the test repo so all signature identities are equal.
- let mailmap = t!(repo.mailmap());
- let mailmapped_author = t!(commit.author_with_mailmap(&mailmap));
- let mailmapped_committer = t!(commit.committer_with_mailmap(&mailmap));
- assert_eq!(mailmapped_author.name(), author.name());
- assert_eq!(mailmapped_author.email(), author.email());
- assert_eq!(mailmapped_committer.name(), committer.name());
- assert_eq!(mailmapped_committer.email(), committer.email());
-
- let commit = {
- // - Add a .mailmap file to the repository.
- // - Commit with a signature identity different from the author's.
- // - Include entries for both author and committer to prove we call
- // the right raw functions.
- let mailmap_file = Path::new(".mailmap");
- let p = Path::new(repo.workdir().unwrap()).join(&mailmap_file);
- t!(fs::write(
- p,
- r#"
-Author Name <author.proper@email> name <email>
-Committer Name <committer.proper@email> <committer@email>"#,
- ));
- let mut index = t!(repo.index());
- t!(index.add_path(&mailmap_file));
- let id_mailmap = t!(index.write_tree());
- let tree_mailmap = t!(repo.find_tree(id_mailmap));
-
- let head = t!(repo.commit(
- Some("HEAD"),
- &author,
- t!(&Signature::now("committer", "committer@email")),
- "Add mailmap",
- &tree_mailmap,
- &[&commit],
- ));
- t!(repo.find_commit(head))
- };
-
- // Sanity check that we're working with the right commit and that its
- // author and committer identities differ.
- let author = commit.author();
- let committer = commit.committer();
- assert_ne!(author.name(), committer.name());
- assert_ne!(author.email(), committer.email());
- assert_eq!(author.name(), Some("name"));
- assert_eq!(author.email(), Some("email"));
- assert_eq!(committer.name(), Some("committer"));
- assert_eq!(committer.email(), Some("committer@email"));
-
- // Fetch the newly added .mailmap from the repository.
- let mailmap = t!(repo.mailmap());
- let mailmapped_author = t!(commit.author_with_mailmap(&mailmap));
- let mailmapped_committer = t!(commit.committer_with_mailmap(&mailmap));
-
- let mm_resolve_author = t!(mailmap.resolve_signature(&author));
- let mm_resolve_committer = t!(mailmap.resolve_signature(&committer));
-
- // Mailmap Signature lifetime is independent of Commit lifetime.
- drop(author);
- drop(committer);
- drop(commit);
-
- // author_with_mailmap() + committer_with_mailmap() work
- assert_eq!(mailmapped_author.name(), Some("Author Name"));
- assert_eq!(mailmapped_author.email(), Some("author.proper@email"));
- assert_eq!(mailmapped_committer.name(), Some("Committer Name"));
- assert_eq!(mailmapped_committer.email(), Some("committer.proper@email"));
-
- // resolve_signature() works
- assert_eq!(mm_resolve_author.email(), mailmapped_author.email());
- assert_eq!(mm_resolve_committer.email(), mailmapped_committer.email());
- }
-}
diff --git a/extra/git2/src/revert.rs b/extra/git2/src/revert.rs
deleted file mode 100644
index 55d702600..000000000
--- a/extra/git2/src/revert.rs
+++ /dev/null
@@ -1,69 +0,0 @@
-use std::mem;
-
-use crate::build::CheckoutBuilder;
-use crate::merge::MergeOptions;
-use crate::raw;
-use std::ptr;
-
-/// Options to specify when reverting
-pub struct RevertOptions<'cb> {
- mainline: u32,
- checkout_builder: Option<CheckoutBuilder<'cb>>,
- merge_opts: Option<MergeOptions>,
-}
-
-impl<'cb> RevertOptions<'cb> {
- /// Creates a default set of revert options
- pub fn new() -> RevertOptions<'cb> {
- RevertOptions {
- mainline: 0,
- checkout_builder: None,
- merge_opts: None,
- }
- }
-
- /// Set the mainline value
- ///
- /// For merge commits, the "mainline" is treated as the parent.
- pub fn mainline(&mut self, mainline: u32) -> &mut Self {
- self.mainline = mainline;
- self
- }
-
- /// Set the checkout builder
- pub fn checkout_builder(&mut self, cb: CheckoutBuilder<'cb>) -> &mut Self {
- self.checkout_builder = Some(cb);
- self
- }
-
- /// Set the merge options
- pub fn merge_opts(&mut self, merge_opts: MergeOptions) -> &mut Self {
- self.merge_opts = Some(merge_opts);
- self
- }
-
- /// Obtain the raw struct
- pub fn raw(&mut self) -> raw::git_revert_options {
- unsafe {
- let mut checkout_opts: raw::git_checkout_options = mem::zeroed();
- raw::git_checkout_init_options(&mut checkout_opts, raw::GIT_CHECKOUT_OPTIONS_VERSION);
- if let Some(ref mut cb) = self.checkout_builder {
- cb.configure(&mut checkout_opts);
- }
-
- let mut merge_opts: raw::git_merge_options = mem::zeroed();
- raw::git_merge_init_options(&mut merge_opts, raw::GIT_MERGE_OPTIONS_VERSION);
- if let Some(ref opts) = self.merge_opts {
- ptr::copy(opts.raw(), &mut merge_opts, 1);
- }
-
- let mut revert_opts: raw::git_revert_options = mem::zeroed();
- raw::git_revert_options_init(&mut revert_opts, raw::GIT_REVERT_OPTIONS_VERSION);
- revert_opts.mainline = self.mainline;
- revert_opts.checkout_opts = checkout_opts;
- revert_opts.merge_opts = merge_opts;
-
- revert_opts
- }
- }
-}
diff --git a/extra/git2/src/revspec.rs b/extra/git2/src/revspec.rs
deleted file mode 100644
index d2e08670a..000000000
--- a/extra/git2/src/revspec.rs
+++ /dev/null
@@ -1,34 +0,0 @@
-use crate::{Object, RevparseMode};
-
-/// A revspec represents a range of revisions within a repository.
-pub struct Revspec<'repo> {
- from: Option<Object<'repo>>,
- to: Option<Object<'repo>>,
- mode: RevparseMode,
-}
-
-impl<'repo> Revspec<'repo> {
- /// Assembles a new revspec from the from/to components.
- pub fn from_objects(
- from: Option<Object<'repo>>,
- to: Option<Object<'repo>>,
- mode: RevparseMode,
- ) -> Revspec<'repo> {
- Revspec { from, to, mode }
- }
-
- /// Access the `from` range of this revspec.
- pub fn from(&self) -> Option<&Object<'repo>> {
- self.from.as_ref()
- }
-
- /// Access the `to` range of this revspec.
- pub fn to(&self) -> Option<&Object<'repo>> {
- self.to.as_ref()
- }
-
- /// Returns the intent of the revspec.
- pub fn mode(&self) -> RevparseMode {
- self.mode
- }
-}
diff --git a/extra/git2/src/revwalk.rs b/extra/git2/src/revwalk.rs
deleted file mode 100644
index 7837f00d6..000000000
--- a/extra/git2/src/revwalk.rs
+++ /dev/null
@@ -1,316 +0,0 @@
-use libc::{c_int, c_uint, c_void};
-use std::ffi::CString;
-use std::marker;
-
-use crate::util::Binding;
-use crate::{panic, raw, Error, Oid, Repository, Sort};
-
-/// A revwalk allows traversal of the commit graph defined by including one or
-/// more leaves and excluding one or more roots.
-pub struct Revwalk<'repo> {
- raw: *mut raw::git_revwalk,
- _marker: marker::PhantomData<&'repo Repository>,
-}
-
-/// A `Revwalk` with an associated "hide callback", see `with_hide_callback`
-pub struct RevwalkWithHideCb<'repo, 'cb, C>
-where
- C: FnMut(Oid) -> bool,
-{
- revwalk: Revwalk<'repo>,
- _marker: marker::PhantomData<&'cb C>,
-}
-
-extern "C" fn revwalk_hide_cb<C>(commit_id: *const raw::git_oid, payload: *mut c_void) -> c_int
-where
- C: FnMut(Oid) -> bool,
-{
- panic::wrap(|| unsafe {
- let hide_cb = payload as *mut C;
- if (*hide_cb)(Oid::from_raw(commit_id)) {
- 1
- } else {
- 0
- }
- })
- .unwrap_or(-1)
-}
-
-impl<'repo, 'cb, C: FnMut(Oid) -> bool> RevwalkWithHideCb<'repo, 'cb, C> {
- /// Consumes the `RevwalkWithHideCb` and returns the contained `Revwalk`.
- ///
- /// Note that this will reset the `Revwalk`.
- pub fn into_inner(mut self) -> Result<Revwalk<'repo>, Error> {
- self.revwalk.reset()?;
- Ok(self.revwalk)
- }
-}
-
-impl<'repo> Revwalk<'repo> {
- /// Reset a revwalk to allow re-configuring it.
- ///
- /// The revwalk is automatically reset when iteration of its commits
- /// completes.
- pub fn reset(&mut self) -> Result<(), Error> {
- unsafe {
- try_call!(raw::git_revwalk_reset(self.raw()));
- }
- Ok(())
- }
-
- /// Set the order in which commits are visited.
- pub fn set_sorting(&mut self, sort_mode: Sort) -> Result<(), Error> {
- unsafe {
- try_call!(raw::git_revwalk_sorting(
- self.raw(),
- sort_mode.bits() as c_uint
- ));
- }
- Ok(())
- }
-
- /// Simplify the history by first-parent
- ///
- /// No parents other than the first for each commit will be enqueued.
- pub fn simplify_first_parent(&mut self) -> Result<(), Error> {
- unsafe {
- try_call!(raw::git_revwalk_simplify_first_parent(self.raw));
- }
- Ok(())
- }
-
- /// Mark a commit to start traversal from.
- ///
- /// The given OID must belong to a commitish on the walked repository.
- ///
- /// The given commit will be used as one of the roots when starting the
- /// revision walk. At least one commit must be pushed onto the walker before
- /// a walk can be started.
- pub fn push(&mut self, oid: Oid) -> Result<(), Error> {
- unsafe {
- try_call!(raw::git_revwalk_push(self.raw(), oid.raw()));
- }
- Ok(())
- }
-
- /// Push the repository's HEAD
- ///
- /// For more information, see `push`.
- pub fn push_head(&mut self) -> Result<(), Error> {
- unsafe {
- try_call!(raw::git_revwalk_push_head(self.raw()));
- }
- Ok(())
- }
-
- /// Push matching references
- ///
- /// The OIDs pointed to by the references that match the given glob pattern
- /// will be pushed to the revision walker.
- ///
- /// A leading 'refs/' is implied if not present as well as a trailing `/ \
- /// *` if the glob lacks '?', ' \ *' or '['.
- ///
- /// Any references matching this glob which do not point to a commitish
- /// will be ignored.
- pub fn push_glob(&mut self, glob: &str) -> Result<(), Error> {
- let glob = CString::new(glob)?;
- unsafe {
- try_call!(raw::git_revwalk_push_glob(self.raw, glob));
- }
- Ok(())
- }
-
- /// Push and hide the respective endpoints of the given range.
- ///
- /// The range should be of the form `<commit>..<commit>` where each
- /// `<commit>` is in the form accepted by `revparse_single`. The left-hand
- /// commit will be hidden and the right-hand commit pushed.
- pub fn push_range(&mut self, range: &str) -> Result<(), Error> {
- let range = CString::new(range)?;
- unsafe {
- try_call!(raw::git_revwalk_push_range(self.raw, range));
- }
- Ok(())
- }
-
- /// Push the OID pointed to by a reference
- ///
- /// The reference must point to a commitish.
- pub fn push_ref(&mut self, reference: &str) -> Result<(), Error> {
- let reference = CString::new(reference)?;
- unsafe {
- try_call!(raw::git_revwalk_push_ref(self.raw, reference));
- }
- Ok(())
- }
-
- /// Mark a commit as not of interest to this revwalk.
- pub fn hide(&mut self, oid: Oid) -> Result<(), Error> {
- unsafe {
- try_call!(raw::git_revwalk_hide(self.raw(), oid.raw()));
- }
- Ok(())
- }
-
- /// Hide all commits for which the callback returns true from
- /// the walk.
- pub fn with_hide_callback<'cb, C>(
- self,
- callback: &'cb mut C,
- ) -> Result<RevwalkWithHideCb<'repo, 'cb, C>, Error>
- where
- C: FnMut(Oid) -> bool,
- {
- let r = RevwalkWithHideCb {
- revwalk: self,
- _marker: marker::PhantomData,
- };
- unsafe {
- raw::git_revwalk_add_hide_cb(
- r.revwalk.raw(),
- Some(revwalk_hide_cb::<C>),
- callback as *mut _ as *mut c_void,
- );
- };
- Ok(r)
- }
-
- /// Hide the repository's HEAD
- ///
- /// For more information, see `hide`.
- pub fn hide_head(&mut self) -> Result<(), Error> {
- unsafe {
- try_call!(raw::git_revwalk_hide_head(self.raw()));
- }
- Ok(())
- }
-
- /// Hide matching references.
- ///
- /// The OIDs pointed to by the references that match the given glob pattern
- /// and their ancestors will be hidden from the output on the revision walk.
- ///
- /// A leading 'refs/' is implied if not present as well as a trailing `/ \
- /// *` if the glob lacks '?', ' \ *' or '['.
- ///
- /// Any references matching this glob which do not point to a commitish
- /// will be ignored.
- pub fn hide_glob(&mut self, glob: &str) -> Result<(), Error> {
- let glob = CString::new(glob)?;
- unsafe {
- try_call!(raw::git_revwalk_hide_glob(self.raw, glob));
- }
- Ok(())
- }
-
- /// Hide the OID pointed to by a reference.
- ///
- /// The reference must point to a commitish.
- pub fn hide_ref(&mut self, reference: &str) -> Result<(), Error> {
- let reference = CString::new(reference)?;
- unsafe {
- try_call!(raw::git_revwalk_hide_ref(self.raw, reference));
- }
- Ok(())
- }
-}
-
-impl<'repo> Binding for Revwalk<'repo> {
- type Raw = *mut raw::git_revwalk;
- unsafe fn from_raw(raw: *mut raw::git_revwalk) -> Revwalk<'repo> {
- Revwalk {
- raw,
- _marker: marker::PhantomData,
- }
- }
- fn raw(&self) -> *mut raw::git_revwalk {
- self.raw
- }
-}
-
-impl<'repo> Drop for Revwalk<'repo> {
- fn drop(&mut self) {
- unsafe { raw::git_revwalk_free(self.raw) }
- }
-}
-
-impl<'repo> Iterator for Revwalk<'repo> {
- type Item = Result<Oid, Error>;
- fn next(&mut self) -> Option<Result<Oid, Error>> {
- let mut out: raw::git_oid = raw::git_oid {
- id: [0; raw::GIT_OID_RAWSZ],
- };
- unsafe {
- try_call_iter!(raw::git_revwalk_next(&mut out, self.raw()));
- Some(Ok(Binding::from_raw(&out as *const _)))
- }
- }
-}
-
-impl<'repo, 'cb, C: FnMut(Oid) -> bool> Iterator for RevwalkWithHideCb<'repo, 'cb, C> {
- type Item = Result<Oid, Error>;
- fn next(&mut self) -> Option<Result<Oid, Error>> {
- let out = self.revwalk.next();
- crate::panic::check();
- out
- }
-}
-
-#[cfg(test)]
-mod tests {
- #[test]
- fn smoke() {
- let (_td, repo) = crate::test::repo_init();
- let head = repo.head().unwrap();
- let target = head.target().unwrap();
-
- let mut walk = repo.revwalk().unwrap();
- walk.push(target).unwrap();
-
- let oids: Vec<crate::Oid> = walk.by_ref().collect::<Result<Vec<_>, _>>().unwrap();
-
- assert_eq!(oids.len(), 1);
- assert_eq!(oids[0], target);
-
- walk.reset().unwrap();
- walk.push_head().unwrap();
- assert_eq!(walk.by_ref().count(), 1);
-
- walk.reset().unwrap();
- walk.push_head().unwrap();
- walk.hide_head().unwrap();
- assert_eq!(walk.by_ref().count(), 0);
- }
-
- #[test]
- fn smoke_hide_cb() {
- let (_td, repo) = crate::test::repo_init();
- let head = repo.head().unwrap();
- let target = head.target().unwrap();
-
- let mut walk = repo.revwalk().unwrap();
- walk.push(target).unwrap();
-
- let oids: Vec<crate::Oid> = walk.by_ref().collect::<Result<Vec<_>, _>>().unwrap();
-
- assert_eq!(oids.len(), 1);
- assert_eq!(oids[0], target);
-
- walk.reset().unwrap();
- walk.push_head().unwrap();
- assert_eq!(walk.by_ref().count(), 1);
-
- walk.reset().unwrap();
- walk.push_head().unwrap();
-
- let mut hide_cb = |oid| oid == target;
- let mut walk = walk.with_hide_callback(&mut hide_cb).unwrap();
-
- assert_eq!(walk.by_ref().count(), 0);
-
- let mut walk = walk.into_inner().unwrap();
- walk.push_head().unwrap();
- assert_eq!(walk.by_ref().count(), 1);
- }
-}
diff --git a/extra/git2/src/signature.rs b/extra/git2/src/signature.rs
deleted file mode 100644
index 83fbbf593..000000000
--- a/extra/git2/src/signature.rs
+++ /dev/null
@@ -1,189 +0,0 @@
-use libc;
-use std::ffi::CString;
-use std::fmt;
-use std::marker;
-use std::mem;
-use std::ptr;
-use std::str;
-
-use crate::util::Binding;
-use crate::{raw, Error, Time};
-
-/// A Signature is used to indicate authorship of various actions throughout the
-/// library.
-///
-/// Signatures contain a name, email, and timestamp. All fields can be specified
-/// with `new` while the `now` constructor omits the timestamp. The
-/// [`Repository::signature`] method can be used to create a default signature
-/// with name and email values read from the configuration.
-///
-/// [`Repository::signature`]: struct.Repository.html#method.signature
-pub struct Signature<'a> {
- raw: *mut raw::git_signature,
- _marker: marker::PhantomData<&'a str>,
- owned: bool,
-}
-
-impl<'a> Signature<'a> {
- /// Create a new action signature with a timestamp of 'now'.
- ///
- /// See `new` for more information
- pub fn now(name: &str, email: &str) -> Result<Signature<'static>, Error> {
- crate::init();
- let mut ret = ptr::null_mut();
- let name = CString::new(name)?;
- let email = CString::new(email)?;
- unsafe {
- try_call!(raw::git_signature_now(&mut ret, name, email));
- Ok(Binding::from_raw(ret))
- }
- }
-
- /// Create a new action signature.
- ///
- /// The `time` specified is in seconds since the epoch, and the `offset` is
- /// the time zone offset in minutes.
- ///
- /// Returns error if either `name` or `email` contain angle brackets.
- pub fn new(name: &str, email: &str, time: &Time) -> Result<Signature<'static>, Error> {
- crate::init();
- let mut ret = ptr::null_mut();
- let name = CString::new(name)?;
- let email = CString::new(email)?;
- unsafe {
- try_call!(raw::git_signature_new(
- &mut ret,
- name,
- email,
- time.seconds() as raw::git_time_t,
- time.offset_minutes() as libc::c_int
- ));
- Ok(Binding::from_raw(ret))
- }
- }
-
- /// Gets the name on the signature.
- ///
- /// Returns `None` if the name is not valid utf-8
- pub fn name(&self) -> Option<&str> {
- str::from_utf8(self.name_bytes()).ok()
- }
-
- /// Gets the name on the signature as a byte slice.
- pub fn name_bytes(&self) -> &[u8] {
- unsafe { crate::opt_bytes(self, (*self.raw).name).unwrap() }
- }
-
- /// Gets the email on the signature.
- ///
- /// Returns `None` if the email is not valid utf-8
- pub fn email(&self) -> Option<&str> {
- str::from_utf8(self.email_bytes()).ok()
- }
-
- /// Gets the email on the signature as a byte slice.
- pub fn email_bytes(&self) -> &[u8] {
- unsafe { crate::opt_bytes(self, (*self.raw).email).unwrap() }
- }
-
- /// Get the `when` of this signature.
- pub fn when(&self) -> Time {
- unsafe { Binding::from_raw((*self.raw).when) }
- }
-
- /// Convert a signature of any lifetime into an owned signature with a
- /// static lifetime.
- pub fn to_owned(&self) -> Signature<'static> {
- unsafe {
- let me = mem::transmute::<&Signature<'a>, &Signature<'static>>(self);
- me.clone()
- }
- }
-}
-
-impl<'a> Binding for Signature<'a> {
- type Raw = *mut raw::git_signature;
- unsafe fn from_raw(raw: *mut raw::git_signature) -> Signature<'a> {
- Signature {
- raw,
- _marker: marker::PhantomData,
- owned: true,
- }
- }
- fn raw(&self) -> *mut raw::git_signature {
- self.raw
- }
-}
-
-/// Creates a new signature from the give raw pointer, tied to the lifetime
-/// of the given object.
-///
-/// This function is unsafe as there is no guarantee that `raw` is valid for
-/// `'a` nor if it's a valid pointer.
-pub unsafe fn from_raw_const<'b, T>(_lt: &'b T, raw: *const raw::git_signature) -> Signature<'b> {
- Signature {
- raw: raw as *mut raw::git_signature,
- _marker: marker::PhantomData,
- owned: false,
- }
-}
-
-impl Clone for Signature<'static> {
- fn clone(&self) -> Signature<'static> {
- // TODO: can this be defined for 'a and just do a plain old copy if the
- // lifetime isn't static?
- let mut raw = ptr::null_mut();
- let rc = unsafe { raw::git_signature_dup(&mut raw, &*self.raw) };
- assert_eq!(rc, 0);
- unsafe { Binding::from_raw(raw) }
- }
-}
-
-impl<'a> Drop for Signature<'a> {
- fn drop(&mut self) {
- if self.owned {
- unsafe { raw::git_signature_free(self.raw) }
- }
- }
-}
-
-impl<'a> fmt::Display for Signature<'a> {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- write!(
- f,
- "{} <{}>",
- String::from_utf8_lossy(self.name_bytes()),
- String::from_utf8_lossy(self.email_bytes())
- )
- }
-}
-
-impl PartialEq for Signature<'_> {
- fn eq(&self, other: &Self) -> bool {
- self.when() == other.when()
- && self.email_bytes() == other.email_bytes()
- && self.name_bytes() == other.name_bytes()
- }
-}
-
-impl Eq for Signature<'_> {}
-
-#[cfg(test)]
-mod tests {
- use crate::{Signature, Time};
-
- #[test]
- fn smoke() {
- Signature::new("foo", "bar", &Time::new(89, 0)).unwrap();
- Signature::now("foo", "bar").unwrap();
- assert!(Signature::new("<foo>", "bar", &Time::new(89, 0)).is_err());
- assert!(Signature::now("<foo>", "bar").is_err());
-
- let s = Signature::now("foo", "bar").unwrap();
- assert_eq!(s.name(), Some("foo"));
- assert_eq!(s.email(), Some("bar"));
-
- drop(s.clone());
- drop(s.to_owned());
- }
-}
diff --git a/extra/git2/src/stash.rs b/extra/git2/src/stash.rs
deleted file mode 100644
index ea898e46b..000000000
--- a/extra/git2/src/stash.rs
+++ /dev/null
@@ -1,348 +0,0 @@
-use crate::build::CheckoutBuilder;
-use crate::util::{self, Binding};
-use crate::{panic, raw, IntoCString, Oid, Signature, StashApplyProgress, StashFlags};
-use libc::{c_char, c_int, c_void, size_t};
-use std::ffi::{c_uint, CStr, CString};
-use std::mem;
-
-/// Stash application options structure
-pub struct StashSaveOptions<'a> {
- message: Option<CString>,
- flags: Option<StashFlags>,
- stasher: Signature<'a>,
- pathspec: Vec<CString>,
- pathspec_ptrs: Vec<*const c_char>,
- raw_opts: raw::git_stash_save_options,
-}
-
-impl<'a> StashSaveOptions<'a> {
- /// Creates a default
- pub fn new(stasher: Signature<'a>) -> Self {
- let mut opts = Self {
- message: None,
- flags: None,
- stasher,
- pathspec: Vec::new(),
- pathspec_ptrs: Vec::new(),
- raw_opts: unsafe { mem::zeroed() },
- };
- assert_eq!(
- unsafe {
- raw::git_stash_save_options_init(
- &mut opts.raw_opts,
- raw::GIT_STASH_SAVE_OPTIONS_VERSION,
- )
- },
- 0
- );
- opts
- }
-
- /// Customize optional `flags` field
- pub fn flags(&mut self, flags: Option<StashFlags>) -> &mut Self {
- self.flags = flags;
- self
- }
-
- /// Add to the array of paths patterns to build the stash.
- pub fn pathspec<T: IntoCString>(&mut self, pathspec: T) -> &mut Self {
- let s = util::cstring_to_repo_path(pathspec).unwrap();
- self.pathspec_ptrs.push(s.as_ptr());
- self.pathspec.push(s);
- self
- }
-
- /// Acquire a pointer to the underlying raw options.
- ///
- /// This function is unsafe as the pointer is only valid so long as this
- /// structure is not moved, modified, or used elsewhere.
- pub unsafe fn raw(&mut self) -> *const raw::git_stash_save_options {
- self.raw_opts.flags = self.flags.unwrap_or_else(StashFlags::empty).bits() as c_uint;
- self.raw_opts.message = crate::call::convert(&self.message);
- self.raw_opts.paths.count = self.pathspec_ptrs.len() as size_t;
- self.raw_opts.paths.strings = self.pathspec_ptrs.as_ptr() as *mut _;
- self.raw_opts.stasher = self.stasher.raw();
-
- &self.raw_opts as *const _
- }
-}
-
-/// Stash application progress notification function.
-///
-/// Return `true` to continue processing, or `false` to
-/// abort the stash application.
-// FIXME: This probably should have been pub(crate) since it is not used anywhere.
-pub type StashApplyProgressCb<'a> = dyn FnMut(StashApplyProgress) -> bool + 'a;
-
-/// This is a callback function you can provide to iterate over all the
-/// stashed states that will be invoked per entry.
-// FIXME: This probably should have been pub(crate) since it is not used anywhere.
-pub type StashCb<'a> = dyn FnMut(usize, &str, &Oid) -> bool + 'a;
-
-/// Stash application options structure
-pub struct StashApplyOptions<'cb> {
- progress: Option<Box<StashApplyProgressCb<'cb>>>,
- checkout_options: Option<CheckoutBuilder<'cb>>,
- raw_opts: raw::git_stash_apply_options,
-}
-
-impl<'cb> Default for StashApplyOptions<'cb> {
- fn default() -> Self {
- Self::new()
- }
-}
-
-impl<'cb> StashApplyOptions<'cb> {
- /// Creates a default set of merge options.
- pub fn new() -> StashApplyOptions<'cb> {
- let mut opts = StashApplyOptions {
- progress: None,
- checkout_options: None,
- raw_opts: unsafe { mem::zeroed() },
- };
- assert_eq!(
- unsafe { raw::git_stash_apply_init_options(&mut opts.raw_opts, 1) },
- 0
- );
- opts
- }
-
- /// Set stash application flag to GIT_STASH_APPLY_REINSTATE_INDEX
- pub fn reinstantiate_index(&mut self) -> &mut StashApplyOptions<'cb> {
- self.raw_opts.flags = raw::GIT_STASH_APPLY_REINSTATE_INDEX as u32;
- self
- }
-
- /// Options to use when writing files to the working directory
- pub fn checkout_options(&mut self, opts: CheckoutBuilder<'cb>) -> &mut StashApplyOptions<'cb> {
- self.checkout_options = Some(opts);
- self
- }
-
- /// Optional callback to notify the consumer of application progress.
- ///
- /// Return `true` to continue processing, or `false` to
- /// abort the stash application.
- pub fn progress_cb<C>(&mut self, callback: C) -> &mut StashApplyOptions<'cb>
- where
- C: FnMut(StashApplyProgress) -> bool + 'cb,
- {
- self.progress = Some(Box::new(callback) as Box<StashApplyProgressCb<'cb>>);
- self.raw_opts.progress_cb = Some(stash_apply_progress_cb);
- self.raw_opts.progress_payload = self as *mut _ as *mut _;
- self
- }
-
- /// Pointer to a raw git_stash_apply_options
- pub fn raw(&mut self) -> &raw::git_stash_apply_options {
- unsafe {
- if let Some(opts) = self.checkout_options.as_mut() {
- opts.configure(&mut self.raw_opts.checkout_options);
- }
- }
- &self.raw_opts
- }
-}
-
-pub(crate) struct StashCbData<'a> {
- pub callback: &'a mut StashCb<'a>,
-}
-
-pub(crate) extern "C" fn stash_cb(
- index: size_t,
- message: *const c_char,
- stash_id: *const raw::git_oid,
- payload: *mut c_void,
-) -> c_int {
- panic::wrap(|| unsafe {
- let data = &mut *(payload as *mut StashCbData<'_>);
- let res = {
- let callback = &mut data.callback;
- callback(
- index,
- CStr::from_ptr(message).to_str().unwrap(),
- &Binding::from_raw(stash_id),
- )
- };
-
- if res {
- 0
- } else {
- 1
- }
- })
- .unwrap_or(1)
-}
-
-fn convert_progress(progress: raw::git_stash_apply_progress_t) -> StashApplyProgress {
- match progress {
- raw::GIT_STASH_APPLY_PROGRESS_NONE => StashApplyProgress::None,
- raw::GIT_STASH_APPLY_PROGRESS_LOADING_STASH => StashApplyProgress::LoadingStash,
- raw::GIT_STASH_APPLY_PROGRESS_ANALYZE_INDEX => StashApplyProgress::AnalyzeIndex,
- raw::GIT_STASH_APPLY_PROGRESS_ANALYZE_MODIFIED => StashApplyProgress::AnalyzeModified,
- raw::GIT_STASH_APPLY_PROGRESS_ANALYZE_UNTRACKED => StashApplyProgress::AnalyzeUntracked,
- raw::GIT_STASH_APPLY_PROGRESS_CHECKOUT_UNTRACKED => StashApplyProgress::CheckoutUntracked,
- raw::GIT_STASH_APPLY_PROGRESS_CHECKOUT_MODIFIED => StashApplyProgress::CheckoutModified,
- raw::GIT_STASH_APPLY_PROGRESS_DONE => StashApplyProgress::Done,
-
- _ => StashApplyProgress::None,
- }
-}
-
-extern "C" fn stash_apply_progress_cb(
- progress: raw::git_stash_apply_progress_t,
- payload: *mut c_void,
-) -> c_int {
- panic::wrap(|| unsafe {
- let options = &mut *(payload as *mut StashApplyOptions<'_>);
- let res = {
- let callback = options.progress.as_mut().unwrap();
- callback(convert_progress(progress))
- };
-
- if res {
- 0
- } else {
- -1
- }
- })
- .unwrap_or(-1)
-}
-
-#[cfg(test)]
-mod tests {
- use crate::stash::{StashApplyOptions, StashSaveOptions};
- use crate::test::repo_init;
- use crate::{IndexAddOption, Repository, StashFlags, Status};
- use std::fs;
- use std::path::{Path, PathBuf};
-
- fn make_stash<C>(next: C)
- where
- C: FnOnce(&mut Repository),
- {
- let (_td, mut repo) = repo_init();
- let signature = repo.signature().unwrap();
-
- let p = Path::new(repo.workdir().unwrap()).join("file_b.txt");
- println!("using path {:?}", p);
-
- fs::write(&p, "data".as_bytes()).unwrap();
-
- let rel_p = Path::new("file_b.txt");
- assert!(repo.status_file(&rel_p).unwrap() == Status::WT_NEW);
-
- repo.stash_save(&signature, "msg1", Some(StashFlags::INCLUDE_UNTRACKED))
- .unwrap();
-
- assert!(repo.status_file(&rel_p).is_err());
-
- let mut count = 0;
- repo.stash_foreach(|index, name, _oid| {
- count += 1;
- assert!(index == 0);
- assert!(name == "On main: msg1");
- true
- })
- .unwrap();
-
- assert!(count == 1);
- next(&mut repo);
- }
-
- fn count_stash(repo: &mut Repository) -> usize {
- let mut count = 0;
- repo.stash_foreach(|_, _, _| {
- count += 1;
- true
- })
- .unwrap();
- count
- }
-
- #[test]
- fn smoke_stash_save_drop() {
- make_stash(|repo| {
- repo.stash_drop(0).unwrap();
- assert!(count_stash(repo) == 0)
- })
- }
-
- #[test]
- fn smoke_stash_save_pop() {
- make_stash(|repo| {
- repo.stash_pop(0, None).unwrap();
- assert!(count_stash(repo) == 0)
- })
- }
-
- #[test]
- fn smoke_stash_save_apply() {
- make_stash(|repo| {
- let mut options = StashApplyOptions::new();
- options.progress_cb(|progress| {
- println!("{:?}", progress);
- true
- });
-
- repo.stash_apply(0, Some(&mut options)).unwrap();
- assert!(count_stash(repo) == 1)
- })
- }
-
- #[test]
- fn test_stash_save2_msg_none() {
- let (_td, mut repo) = repo_init();
- let signature = repo.signature().unwrap();
-
- let p = Path::new(repo.workdir().unwrap()).join("file_b.txt");
-
- fs::write(&p, "data".as_bytes()).unwrap();
-
- repo.stash_save2(&signature, None, Some(StashFlags::INCLUDE_UNTRACKED))
- .unwrap();
-
- let mut stash_name = String::new();
- repo.stash_foreach(|index, name, _oid| {
- assert_eq!(index, 0);
- stash_name = name.to_string();
- true
- })
- .unwrap();
-
- assert!(stash_name.starts_with("WIP on main:"));
- }
-
- fn create_file(r: &Repository, name: &str, data: &str) -> PathBuf {
- let p = Path::new(r.workdir().unwrap()).join(name);
- fs::write(&p, data).unwrap();
- p
- }
-
- #[test]
- fn test_stash_save_ext() {
- let (_td, mut repo) = repo_init();
- let signature = repo.signature().unwrap();
-
- create_file(&repo, "file_a", "foo");
- create_file(&repo, "file_b", "foo");
-
- let mut index = repo.index().unwrap();
- index
- .add_all(["*"].iter(), IndexAddOption::DEFAULT, None)
- .unwrap();
- index.write().unwrap();
-
- assert_eq!(repo.statuses(None).unwrap().len(), 2);
-
- let mut opt = StashSaveOptions::new(signature);
- opt.pathspec("file_a");
- repo.stash_save_ext(Some(&mut opt)).unwrap();
-
- assert_eq!(repo.statuses(None).unwrap().len(), 0);
-
- repo.stash_pop(0, None).unwrap();
-
- assert_eq!(repo.statuses(None).unwrap().len(), 1);
- }
-}
diff --git a/extra/git2/src/status.rs b/extra/git2/src/status.rs
deleted file mode 100644
index a5a8cffd3..000000000
--- a/extra/git2/src/status.rs
+++ /dev/null
@@ -1,435 +0,0 @@
-use libc::{c_char, c_uint, size_t};
-use std::ffi::CString;
-use std::iter::FusedIterator;
-use std::marker;
-use std::mem;
-use std::ops::Range;
-use std::str;
-
-use crate::util::{self, Binding};
-use crate::{raw, DiffDelta, IntoCString, Repository, Status};
-
-/// Options that can be provided to `repo.statuses()` to control how the status
-/// information is gathered.
-pub struct StatusOptions {
- raw: raw::git_status_options,
- pathspec: Vec<CString>,
- ptrs: Vec<*const c_char>,
-}
-
-/// Enumeration of possible methods of what can be shown through a status
-/// operation.
-#[derive(Copy, Clone)]
-pub enum StatusShow {
- /// Only gives status based on HEAD to index comparison, not looking at
- /// working directory changes.
- Index,
-
- /// Only gives status based on index to working directory comparison, not
- /// comparing the index to the HEAD.
- Workdir,
-
- /// The default, this roughly matches `git status --porcelain` regarding
- /// which files are included and in what order.
- IndexAndWorkdir,
-}
-
-/// A container for a list of status information about a repository.
-///
-/// Each instance appears as if it were a collection, having a length and
-/// allowing indexing, as well as providing an iterator.
-pub struct Statuses<'repo> {
- raw: *mut raw::git_status_list,
-
- // Hm, not currently present, but can't hurt?
- _marker: marker::PhantomData<&'repo Repository>,
-}
-
-/// An iterator over the statuses in a `Statuses` instance.
-pub struct StatusIter<'statuses> {
- statuses: &'statuses Statuses<'statuses>,
- range: Range<usize>,
-}
-
-/// A structure representing an entry in the `Statuses` structure.
-///
-/// Instances are created through the `.iter()` method or the `.get()` method.
-pub struct StatusEntry<'statuses> {
- raw: *const raw::git_status_entry,
- _marker: marker::PhantomData<&'statuses DiffDelta<'statuses>>,
-}
-
-impl Default for StatusOptions {
- fn default() -> Self {
- Self::new()
- }
-}
-
-impl StatusOptions {
- /// Creates a new blank set of status options.
- pub fn new() -> StatusOptions {
- unsafe {
- let mut raw = mem::zeroed();
- let r = raw::git_status_init_options(&mut raw, raw::GIT_STATUS_OPTIONS_VERSION);
- assert_eq!(r, 0);
- StatusOptions {
- raw,
- pathspec: Vec::new(),
- ptrs: Vec::new(),
- }
- }
- }
-
- /// Select the files on which to report status.
- ///
- /// The default, if unspecified, is to show the index and the working
- /// directory.
- pub fn show(&mut self, show: StatusShow) -> &mut StatusOptions {
- self.raw.show = match show {
- StatusShow::Index => raw::GIT_STATUS_SHOW_INDEX_ONLY,
- StatusShow::Workdir => raw::GIT_STATUS_SHOW_WORKDIR_ONLY,
- StatusShow::IndexAndWorkdir => raw::GIT_STATUS_SHOW_INDEX_AND_WORKDIR,
- };
- self
- }
-
- /// Add a path pattern to match (using fnmatch-style matching).
- ///
- /// If the `disable_pathspec_match` option is given, then this is a literal
- /// path to match. If this is not called, then there will be no patterns to
- /// match and the entire directory will be used.
- pub fn pathspec<T: IntoCString>(&mut self, pathspec: T) -> &mut StatusOptions {
- let s = util::cstring_to_repo_path(pathspec).unwrap();
- self.ptrs.push(s.as_ptr());
- self.pathspec.push(s);
- self
- }
-
- fn flag(&mut self, flag: raw::git_status_opt_t, val: bool) -> &mut StatusOptions {
- if val {
- self.raw.flags |= flag as c_uint;
- } else {
- self.raw.flags &= !(flag as c_uint);
- }
- self
- }
-
- /// Flag whether untracked files will be included.
- ///
- /// Untracked files will only be included if the workdir files are included
- /// in the status "show" option.
- pub fn include_untracked(&mut self, include: bool) -> &mut StatusOptions {
- self.flag(raw::GIT_STATUS_OPT_INCLUDE_UNTRACKED, include)
- }
-
- /// Flag whether ignored files will be included.
- ///
- /// The files will only be included if the workdir files are included
- /// in the status "show" option.
- pub fn include_ignored(&mut self, include: bool) -> &mut StatusOptions {
- self.flag(raw::GIT_STATUS_OPT_INCLUDE_IGNORED, include)
- }
-
- /// Flag to include unmodified files.
- pub fn include_unmodified(&mut self, include: bool) -> &mut StatusOptions {
- self.flag(raw::GIT_STATUS_OPT_INCLUDE_UNMODIFIED, include)
- }
-
- /// Flag that submodules should be skipped.
- ///
- /// This only applies if there are no pending typechanges to the submodule
- /// (either from or to another type).
- pub fn exclude_submodules(&mut self, exclude: bool) -> &mut StatusOptions {
- self.flag(raw::GIT_STATUS_OPT_EXCLUDE_SUBMODULES, exclude)
- }
-
- /// Flag that all files in untracked directories should be included.
- ///
- /// Normally if an entire directory is new then just the top-level directory
- /// is included (with a trailing slash on the entry name).
- pub fn recurse_untracked_dirs(&mut self, include: bool) -> &mut StatusOptions {
- self.flag(raw::GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS, include)
- }
-
- /// Indicates that the given paths should be treated as literals paths, note
- /// patterns.
- pub fn disable_pathspec_match(&mut self, include: bool) -> &mut StatusOptions {
- self.flag(raw::GIT_STATUS_OPT_DISABLE_PATHSPEC_MATCH, include)
- }
-
- /// Indicates that the contents of ignored directories should be included in
- /// the status.
- pub fn recurse_ignored_dirs(&mut self, include: bool) -> &mut StatusOptions {
- self.flag(raw::GIT_STATUS_OPT_RECURSE_IGNORED_DIRS, include)
- }
-
- /// Indicates that rename detection should be processed between the head.
- pub fn renames_head_to_index(&mut self, include: bool) -> &mut StatusOptions {
- self.flag(raw::GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX, include)
- }
-
- /// Indicates that rename detection should be run between the index and the
- /// working directory.
- pub fn renames_index_to_workdir(&mut self, include: bool) -> &mut StatusOptions {
- self.flag(raw::GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR, include)
- }
-
- /// Override the native case sensitivity for the file system and force the
- /// output to be in case sensitive order.
- pub fn sort_case_sensitively(&mut self, include: bool) -> &mut StatusOptions {
- self.flag(raw::GIT_STATUS_OPT_SORT_CASE_SENSITIVELY, include)
- }
-
- /// Override the native case sensitivity for the file system and force the
- /// output to be in case-insensitive order.
- pub fn sort_case_insensitively(&mut self, include: bool) -> &mut StatusOptions {
- self.flag(raw::GIT_STATUS_OPT_SORT_CASE_INSENSITIVELY, include)
- }
-
- /// Indicates that rename detection should include rewritten files.
- pub fn renames_from_rewrites(&mut self, include: bool) -> &mut StatusOptions {
- self.flag(raw::GIT_STATUS_OPT_RENAMES_FROM_REWRITES, include)
- }
-
- /// Bypasses the default status behavior of doing a "soft" index reload.
- pub fn no_refresh(&mut self, include: bool) -> &mut StatusOptions {
- self.flag(raw::GIT_STATUS_OPT_NO_REFRESH, include)
- }
-
- /// Refresh the stat cache in the index for files are unchanged but have
- /// out of date stat information in the index.
- ///
- /// This will result in less work being done on subsequent calls to fetching
- /// the status.
- pub fn update_index(&mut self, include: bool) -> &mut StatusOptions {
- self.flag(raw::GIT_STATUS_OPT_UPDATE_INDEX, include)
- }
-
- // erm...
- #[allow(missing_docs)]
- pub fn include_unreadable(&mut self, include: bool) -> &mut StatusOptions {
- self.flag(raw::GIT_STATUS_OPT_INCLUDE_UNREADABLE, include)
- }
-
- // erm...
- #[allow(missing_docs)]
- pub fn include_unreadable_as_untracked(&mut self, include: bool) -> &mut StatusOptions {
- self.flag(raw::GIT_STATUS_OPT_INCLUDE_UNREADABLE_AS_UNTRACKED, include)
- }
-
- /// Set threshold above which similar files will be considered renames.
- ///
- /// This is equivalent to the `-M` option. Defaults to 50.
- pub fn rename_threshold(&mut self, threshold: u16) -> &mut StatusOptions {
- self.raw.rename_threshold = threshold;
- self
- }
-
- /// Get a pointer to the inner list of status options.
- ///
- /// This function is unsafe as the returned structure has interior pointers
- /// and may no longer be valid if these options continue to be mutated.
- pub unsafe fn raw(&mut self) -> *const raw::git_status_options {
- self.raw.pathspec.strings = self.ptrs.as_ptr() as *mut _;
- self.raw.pathspec.count = self.ptrs.len() as size_t;
- &self.raw
- }
-}
-
-impl<'repo> Statuses<'repo> {
- /// Gets a status entry from this list at the specified index.
- ///
- /// Returns `None` if the index is out of bounds.
- pub fn get(&self, index: usize) -> Option<StatusEntry<'_>> {
- unsafe {
- let p = raw::git_status_byindex(self.raw, index as size_t);
- Binding::from_raw_opt(p)
- }
- }
-
- /// Gets the count of status entries in this list.
- ///
- /// If there are no changes in status (according to the options given
- /// when the status list was created), this should return 0.
- pub fn len(&self) -> usize {
- unsafe { raw::git_status_list_entrycount(self.raw) as usize }
- }
-
- /// Return `true` if there is no status entry in this list.
- pub fn is_empty(&self) -> bool {
- self.len() == 0
- }
-
- /// Returns an iterator over the statuses in this list.
- pub fn iter(&self) -> StatusIter<'_> {
- StatusIter {
- statuses: self,
- range: 0..self.len(),
- }
- }
-}
-
-impl<'repo> Binding for Statuses<'repo> {
- type Raw = *mut raw::git_status_list;
- unsafe fn from_raw(raw: *mut raw::git_status_list) -> Statuses<'repo> {
- Statuses {
- raw,
- _marker: marker::PhantomData,
- }
- }
- fn raw(&self) -> *mut raw::git_status_list {
- self.raw
- }
-}
-
-impl<'repo> Drop for Statuses<'repo> {
- fn drop(&mut self) {
- unsafe {
- raw::git_status_list_free(self.raw);
- }
- }
-}
-
-impl<'a> Iterator for StatusIter<'a> {
- type Item = StatusEntry<'a>;
- fn next(&mut self) -> Option<StatusEntry<'a>> {
- self.range.next().and_then(|i| self.statuses.get(i))
- }
- fn size_hint(&self) -> (usize, Option<usize>) {
- self.range.size_hint()
- }
-}
-impl<'a> DoubleEndedIterator for StatusIter<'a> {
- fn next_back(&mut self) -> Option<StatusEntry<'a>> {
- self.range.next_back().and_then(|i| self.statuses.get(i))
- }
-}
-impl<'a> FusedIterator for StatusIter<'a> {}
-impl<'a> ExactSizeIterator for StatusIter<'a> {}
-
-impl<'a> IntoIterator for &'a Statuses<'a> {
- type Item = StatusEntry<'a>;
- type IntoIter = StatusIter<'a>;
- fn into_iter(self) -> Self::IntoIter {
- self.iter()
- }
-}
-
-impl<'statuses> StatusEntry<'statuses> {
- /// Access the bytes for this entry's corresponding pathname
- pub fn path_bytes(&self) -> &[u8] {
- unsafe {
- if (*self.raw).head_to_index.is_null() {
- crate::opt_bytes(self, (*(*self.raw).index_to_workdir).old_file.path)
- } else {
- crate::opt_bytes(self, (*(*self.raw).head_to_index).old_file.path)
- }
- .unwrap()
- }
- }
-
- /// Access this entry's path name as a string.
- ///
- /// Returns `None` if the path is not valid utf-8.
- pub fn path(&self) -> Option<&str> {
- str::from_utf8(self.path_bytes()).ok()
- }
-
- /// Access the status flags for this file
- pub fn status(&self) -> Status {
- Status::from_bits_truncate(unsafe { (*self.raw).status as u32 })
- }
-
- /// Access detailed information about the differences between the file in
- /// HEAD and the file in the index.
- pub fn head_to_index(&self) -> Option<DiffDelta<'statuses>> {
- unsafe { Binding::from_raw_opt((*self.raw).head_to_index) }
- }
-
- /// Access detailed information about the differences between the file in
- /// the index and the file in the working directory.
- pub fn index_to_workdir(&self) -> Option<DiffDelta<'statuses>> {
- unsafe { Binding::from_raw_opt((*self.raw).index_to_workdir) }
- }
-}
-
-impl<'statuses> Binding for StatusEntry<'statuses> {
- type Raw = *const raw::git_status_entry;
-
- unsafe fn from_raw(raw: *const raw::git_status_entry) -> StatusEntry<'statuses> {
- StatusEntry {
- raw,
- _marker: marker::PhantomData,
- }
- }
- fn raw(&self) -> *const raw::git_status_entry {
- self.raw
- }
-}
-
-#[cfg(test)]
-mod tests {
- use super::StatusOptions;
- use std::fs::File;
- use std::io::prelude::*;
- use std::path::Path;
-
- #[test]
- fn smoke() {
- let (td, repo) = crate::test::repo_init();
- assert_eq!(repo.statuses(None).unwrap().len(), 0);
- File::create(&td.path().join("foo")).unwrap();
- let statuses = repo.statuses(None).unwrap();
- assert_eq!(statuses.iter().count(), 1);
- let status = statuses.iter().next().unwrap();
- assert_eq!(status.path(), Some("foo"));
- assert!(status.status().contains(crate::Status::WT_NEW));
- assert!(!status.status().contains(crate::Status::INDEX_NEW));
- assert!(status.head_to_index().is_none());
- let diff = status.index_to_workdir().unwrap();
- assert_eq!(diff.old_file().path_bytes().unwrap(), b"foo");
- assert_eq!(diff.new_file().path_bytes().unwrap(), b"foo");
- }
-
- #[test]
- fn filter() {
- let (td, repo) = crate::test::repo_init();
- t!(File::create(&td.path().join("foo")));
- t!(File::create(&td.path().join("bar")));
- let mut opts = StatusOptions::new();
- opts.include_untracked(true).pathspec("foo");
-
- let statuses = t!(repo.statuses(Some(&mut opts)));
- assert_eq!(statuses.iter().count(), 1);
- let status = statuses.iter().next().unwrap();
- assert_eq!(status.path(), Some("foo"));
- }
-
- #[test]
- fn gitignore() {
- let (td, repo) = crate::test::repo_init();
- t!(t!(File::create(td.path().join(".gitignore"))).write_all(b"foo\n"));
- assert!(!t!(repo.status_should_ignore(Path::new("bar"))));
- assert!(t!(repo.status_should_ignore(Path::new("foo"))));
- }
-
- #[test]
- fn status_file() {
- let (td, repo) = crate::test::repo_init();
- assert!(repo.status_file(Path::new("foo")).is_err());
- if cfg!(windows) {
- assert!(repo.status_file(Path::new("bar\\foo.txt")).is_err());
- }
- t!(File::create(td.path().join("foo")));
- if cfg!(windows) {
- t!(::std::fs::create_dir_all(td.path().join("bar")));
- t!(File::create(td.path().join("bar").join("foo.txt")));
- }
- let status = t!(repo.status_file(Path::new("foo")));
- assert!(status.contains(crate::Status::WT_NEW));
- if cfg!(windows) {
- let status = t!(repo.status_file(Path::new("bar\\foo.txt")));
- assert!(status.contains(crate::Status::WT_NEW));
- }
- }
-}
diff --git a/extra/git2/src/string_array.rs b/extra/git2/src/string_array.rs
deleted file mode 100644
index c77ccdab9..000000000
--- a/extra/git2/src/string_array.rs
+++ /dev/null
@@ -1,136 +0,0 @@
-//! Bindings to libgit2's raw `git_strarray` type
-
-use std::iter::FusedIterator;
-use std::ops::Range;
-use std::str;
-
-use crate::raw;
-use crate::util::Binding;
-
-/// A string array structure used by libgit2
-///
-/// Some APIs return arrays of strings which originate from libgit2. This
-/// wrapper type behaves a little like `Vec<&str>` but does so without copying
-/// the underlying strings until necessary.
-pub struct StringArray {
- raw: raw::git_strarray,
-}
-
-/// A forward iterator over the strings of an array, casted to `&str`.
-pub struct Iter<'a> {
- range: Range<usize>,
- arr: &'a StringArray,
-}
-
-/// A forward iterator over the strings of an array, casted to `&[u8]`.
-pub struct IterBytes<'a> {
- range: Range<usize>,
- arr: &'a StringArray,
-}
-
-impl StringArray {
- /// Returns None if the i'th string is not utf8 or if i is out of bounds.
- pub fn get(&self, i: usize) -> Option<&str> {
- self.get_bytes(i).and_then(|s| str::from_utf8(s).ok())
- }
-
- /// Returns None if `i` is out of bounds.
- pub fn get_bytes(&self, i: usize) -> Option<&[u8]> {
- if i < self.raw.count as usize {
- unsafe {
- let ptr = *self.raw.strings.add(i) as *const _;
- Some(crate::opt_bytes(self, ptr).unwrap())
- }
- } else {
- None
- }
- }
-
- /// Returns an iterator over the strings contained within this array.
- ///
- /// The iterator yields `Option<&str>` as it is unknown whether the contents
- /// are utf-8 or not.
- pub fn iter(&self) -> Iter<'_> {
- Iter {
- range: 0..self.len(),
- arr: self,
- }
- }
-
- /// Returns an iterator over the strings contained within this array,
- /// yielding byte slices.
- pub fn iter_bytes(&self) -> IterBytes<'_> {
- IterBytes {
- range: 0..self.len(),
- arr: self,
- }
- }
-
- /// Returns the number of strings in this array.
- pub fn len(&self) -> usize {
- self.raw.count as usize
- }
-
- /// Return `true` if this array is empty.
- pub fn is_empty(&self) -> bool {
- self.len() == 0
- }
-}
-
-impl Binding for StringArray {
- type Raw = raw::git_strarray;
- unsafe fn from_raw(raw: raw::git_strarray) -> StringArray {
- StringArray { raw }
- }
- fn raw(&self) -> raw::git_strarray {
- self.raw
- }
-}
-
-impl<'a> IntoIterator for &'a StringArray {
- type Item = Option<&'a str>;
- type IntoIter = Iter<'a>;
- fn into_iter(self) -> Self::IntoIter {
- self.iter()
- }
-}
-
-impl<'a> Iterator for Iter<'a> {
- type Item = Option<&'a str>;
- fn next(&mut self) -> Option<Option<&'a str>> {
- self.range.next().map(|i| self.arr.get(i))
- }
- fn size_hint(&self) -> (usize, Option<usize>) {
- self.range.size_hint()
- }
-}
-impl<'a> DoubleEndedIterator for Iter<'a> {
- fn next_back(&mut self) -> Option<Option<&'a str>> {
- self.range.next_back().map(|i| self.arr.get(i))
- }
-}
-impl<'a> FusedIterator for Iter<'a> {}
-impl<'a> ExactSizeIterator for Iter<'a> {}
-
-impl<'a> Iterator for IterBytes<'a> {
- type Item = &'a [u8];
- fn next(&mut self) -> Option<&'a [u8]> {
- self.range.next().and_then(|i| self.arr.get_bytes(i))
- }
- fn size_hint(&self) -> (usize, Option<usize>) {
- self.range.size_hint()
- }
-}
-impl<'a> DoubleEndedIterator for IterBytes<'a> {
- fn next_back(&mut self) -> Option<&'a [u8]> {
- self.range.next_back().and_then(|i| self.arr.get_bytes(i))
- }
-}
-impl<'a> FusedIterator for IterBytes<'a> {}
-impl<'a> ExactSizeIterator for IterBytes<'a> {}
-
-impl Drop for StringArray {
- fn drop(&mut self) {
- unsafe { raw::git_strarray_free(&mut self.raw) }
- }
-}
diff --git a/extra/git2/src/submodule.rs b/extra/git2/src/submodule.rs
deleted file mode 100644
index 06a635940..000000000
--- a/extra/git2/src/submodule.rs
+++ /dev/null
@@ -1,471 +0,0 @@
-use std::marker;
-use std::mem;
-use std::os::raw::c_int;
-use std::path::Path;
-use std::ptr;
-use std::str;
-
-use crate::util::{self, Binding};
-use crate::{build::CheckoutBuilder, SubmoduleIgnore, SubmoduleUpdate};
-use crate::{raw, Error, FetchOptions, Oid, Repository};
-
-/// A structure to represent a git [submodule][1]
-///
-/// [1]: http://git-scm.com/book/en/Git-Tools-Submodules
-pub struct Submodule<'repo> {
- raw: *mut raw::git_submodule,
- _marker: marker::PhantomData<&'repo Repository>,
-}
-
-impl<'repo> Submodule<'repo> {
- /// Get the submodule's branch.
- ///
- /// Returns `None` if the branch is not valid utf-8 or if the branch is not
- /// yet available.
- pub fn branch(&self) -> Option<&str> {
- self.branch_bytes().and_then(|s| str::from_utf8(s).ok())
- }
-
- /// Get the branch for the submodule.
- ///
- /// Returns `None` if the branch is not yet available.
- pub fn branch_bytes(&self) -> Option<&[u8]> {
- unsafe { crate::opt_bytes(self, raw::git_submodule_branch(self.raw)) }
- }
-
- /// Perform the clone step for a newly created submodule.
- ///
- /// This performs the necessary `git_clone` to setup a newly-created submodule.
- pub fn clone(
- &mut self,
- opts: Option<&mut SubmoduleUpdateOptions<'_>>,
- ) -> Result<Repository, Error> {
- unsafe {
- let raw_opts = opts.map(|o| o.raw());
- let mut raw_repo = ptr::null_mut();
- try_call!(raw::git_submodule_clone(
- &mut raw_repo,
- self.raw,
- raw_opts.as_ref()
- ));
- Ok(Binding::from_raw(raw_repo))
- }
- }
-
- /// Get the submodule's URL.
- ///
- /// Returns `None` if the URL is not valid utf-8 or if the URL isn't present
- pub fn url(&self) -> Option<&str> {
- self.opt_url_bytes().and_then(|b| str::from_utf8(b).ok())
- }
-
- /// Get the URL for the submodule.
- #[doc(hidden)]
- #[deprecated(note = "renamed to `opt_url_bytes`")]
- pub fn url_bytes(&self) -> &[u8] {
- self.opt_url_bytes().unwrap()
- }
-
- /// Get the URL for the submodule.
- ///
- /// Returns `None` if the URL isn't present
- // TODO: delete this method and fix the signature of `url_bytes` on next
- // major version bump
- pub fn opt_url_bytes(&self) -> Option<&[u8]> {
- unsafe { crate::opt_bytes(self, raw::git_submodule_url(self.raw)) }
- }
-
- /// Get the submodule's name.
- ///
- /// Returns `None` if the name is not valid utf-8
- pub fn name(&self) -> Option<&str> {
- str::from_utf8(self.name_bytes()).ok()
- }
-
- /// Get the name for the submodule.
- pub fn name_bytes(&self) -> &[u8] {
- unsafe { crate::opt_bytes(self, raw::git_submodule_name(self.raw)).unwrap() }
- }
-
- /// Get the path for the submodule.
- pub fn path(&self) -> &Path {
- util::bytes2path(unsafe {
- crate::opt_bytes(self, raw::git_submodule_path(self.raw)).unwrap()
- })
- }
-
- /// Get the OID for the submodule in the current HEAD tree.
- pub fn head_id(&self) -> Option<Oid> {
- unsafe { Binding::from_raw_opt(raw::git_submodule_head_id(self.raw)) }
- }
-
- /// Get the OID for the submodule in the index.
- pub fn index_id(&self) -> Option<Oid> {
- unsafe { Binding::from_raw_opt(raw::git_submodule_index_id(self.raw)) }
- }
-
- /// Get the OID for the submodule in the current working directory.
- ///
- /// This returns the OID that corresponds to looking up 'HEAD' in the
- /// checked out submodule. If there are pending changes in the index or
- /// anything else, this won't notice that.
- pub fn workdir_id(&self) -> Option<Oid> {
- unsafe { Binding::from_raw_opt(raw::git_submodule_wd_id(self.raw)) }
- }
-
- /// Get the ignore rule that will be used for the submodule.
- pub fn ignore_rule(&self) -> SubmoduleIgnore {
- SubmoduleIgnore::from_raw(unsafe { raw::git_submodule_ignore(self.raw) })
- }
-
- /// Get the update rule that will be used for the submodule.
- pub fn update_strategy(&self) -> SubmoduleUpdate {
- SubmoduleUpdate::from_raw(unsafe { raw::git_submodule_update_strategy(self.raw) })
- }
-
- /// Copy submodule info into ".git/config" file.
- ///
- /// Just like "git submodule init", this copies information about the
- /// submodule into ".git/config". You can use the accessor functions above
- /// to alter the in-memory git_submodule object and control what is written
- /// to the config, overriding what is in .gitmodules.
- ///
- /// By default, existing entries will not be overwritten, but passing `true`
- /// for `overwrite` forces them to be updated.
- pub fn init(&mut self, overwrite: bool) -> Result<(), Error> {
- unsafe {
- try_call!(raw::git_submodule_init(self.raw, overwrite));
- }
- Ok(())
- }
-
- /// Set up the subrepository for a submodule in preparation for clone.
- ///
- /// This function can be called to init and set up a submodule repository
- /// from a submodule in preparation to clone it from its remote.
-
- /// use_gitlink: Should the workdir contain a gitlink to the repo in
- /// .git/modules vs. repo directly in workdir.
- pub fn repo_init(&mut self, use_gitlink: bool) -> Result<Repository, Error> {
- unsafe {
- let mut raw_repo = ptr::null_mut();
- try_call!(raw::git_submodule_repo_init(
- &mut raw_repo,
- self.raw,
- use_gitlink
- ));
- Ok(Binding::from_raw(raw_repo))
- }
- }
-
- /// Open the repository for a submodule.
- ///
- /// This will only work if the submodule is checked out into the working
- /// directory.
- pub fn open(&self) -> Result<Repository, Error> {
- let mut raw = ptr::null_mut();
- unsafe {
- try_call!(raw::git_submodule_open(&mut raw, self.raw));
- Ok(Binding::from_raw(raw))
- }
- }
-
- /// Reread submodule info from config, index, and HEAD.
- ///
- /// Call this to reread cached submodule information for this submodule if
- /// you have reason to believe that it has changed.
- ///
- /// If `force` is `true`, then data will be reloaded even if it doesn't seem
- /// out of date
- pub fn reload(&mut self, force: bool) -> Result<(), Error> {
- unsafe {
- try_call!(raw::git_submodule_reload(self.raw, force));
- }
- Ok(())
- }
-
- /// Copy submodule remote info into submodule repo.
- ///
- /// This copies the information about the submodules URL into the checked
- /// out submodule config, acting like "git submodule sync". This is useful
- /// if you have altered the URL for the submodule (or it has been altered
- /// by a fetch of upstream changes) and you need to update your local repo.
- pub fn sync(&mut self) -> Result<(), Error> {
- unsafe {
- try_call!(raw::git_submodule_sync(self.raw));
- }
- Ok(())
- }
-
- /// Add current submodule HEAD commit to index of superproject.
- ///
- /// If `write_index` is true, then the index file will be immediately
- /// written. Otherwise you must explicitly call `write()` on an `Index`
- /// later on.
- pub fn add_to_index(&mut self, write_index: bool) -> Result<(), Error> {
- unsafe {
- try_call!(raw::git_submodule_add_to_index(self.raw, write_index));
- }
- Ok(())
- }
-
- /// Resolve the setup of a new git submodule.
- ///
- /// This should be called on a submodule once you have called add setup and
- /// done the clone of the submodule. This adds the .gitmodules file and the
- /// newly cloned submodule to the index to be ready to be committed (but
- /// doesn't actually do the commit).
- pub fn add_finalize(&mut self) -> Result<(), Error> {
- unsafe {
- try_call!(raw::git_submodule_add_finalize(self.raw));
- }
- Ok(())
- }
-
- /// Update submodule.
- ///
- /// This will clone a missing submodule and check out the subrepository to
- /// the commit specified in the index of the containing repository. If
- /// the submodule repository doesn't contain the target commit, then the
- /// submodule is fetched using the fetch options supplied in `opts`.
- ///
- /// `init` indicates if the submodule should be initialized first if it has
- /// not been initialized yet.
- pub fn update(
- &mut self,
- init: bool,
- opts: Option<&mut SubmoduleUpdateOptions<'_>>,
- ) -> Result<(), Error> {
- unsafe {
- let mut raw_opts = opts.map(|o| o.raw());
- try_call!(raw::git_submodule_update(
- self.raw,
- init as c_int,
- raw_opts.as_mut().map_or(ptr::null_mut(), |o| o)
- ));
- }
- Ok(())
- }
-}
-
-impl<'repo> Binding for Submodule<'repo> {
- type Raw = *mut raw::git_submodule;
- unsafe fn from_raw(raw: *mut raw::git_submodule) -> Submodule<'repo> {
- Submodule {
- raw,
- _marker: marker::PhantomData,
- }
- }
- fn raw(&self) -> *mut raw::git_submodule {
- self.raw
- }
-}
-
-impl<'repo> Drop for Submodule<'repo> {
- fn drop(&mut self) {
- unsafe { raw::git_submodule_free(self.raw) }
- }
-}
-
-/// Options to update a submodule.
-pub struct SubmoduleUpdateOptions<'cb> {
- checkout_builder: CheckoutBuilder<'cb>,
- fetch_opts: FetchOptions<'cb>,
- allow_fetch: bool,
-}
-
-impl<'cb> SubmoduleUpdateOptions<'cb> {
- /// Return default options.
- pub fn new() -> Self {
- SubmoduleUpdateOptions {
- checkout_builder: CheckoutBuilder::new(),
- fetch_opts: FetchOptions::new(),
- allow_fetch: true,
- }
- }
-
- unsafe fn raw(&mut self) -> raw::git_submodule_update_options {
- let mut checkout_opts: raw::git_checkout_options = mem::zeroed();
- let init_res =
- raw::git_checkout_init_options(&mut checkout_opts, raw::GIT_CHECKOUT_OPTIONS_VERSION);
- assert_eq!(0, init_res);
- self.checkout_builder.configure(&mut checkout_opts);
- let opts = raw::git_submodule_update_options {
- version: raw::GIT_SUBMODULE_UPDATE_OPTIONS_VERSION,
- checkout_opts,
- fetch_opts: self.fetch_opts.raw(),
- allow_fetch: self.allow_fetch as c_int,
- };
- opts
- }
-
- /// Set checkout options.
- pub fn checkout(&mut self, opts: CheckoutBuilder<'cb>) -> &mut Self {
- self.checkout_builder = opts;
- self
- }
-
- /// Set fetch options and allow fetching.
- pub fn fetch(&mut self, opts: FetchOptions<'cb>) -> &mut Self {
- self.fetch_opts = opts;
- self.allow_fetch = true;
- self
- }
-
- /// Allow or disallow fetching.
- pub fn allow_fetch(&mut self, b: bool) -> &mut Self {
- self.allow_fetch = b;
- self
- }
-}
-
-impl<'cb> Default for SubmoduleUpdateOptions<'cb> {
- fn default() -> Self {
- Self::new()
- }
-}
-
-#[cfg(test)]
-mod tests {
- use std::fs;
- use std::path::Path;
- use tempfile::TempDir;
- use url::Url;
-
- use crate::Repository;
- use crate::SubmoduleUpdateOptions;
-
- #[test]
- fn smoke() {
- let td = TempDir::new().unwrap();
- let repo = Repository::init(td.path()).unwrap();
- let mut s1 = repo
- .submodule("/path/to/nowhere", Path::new("foo"), true)
- .unwrap();
- s1.init(false).unwrap();
- s1.sync().unwrap();
-
- let s2 = repo
- .submodule("/path/to/nowhere", Path::new("bar"), true)
- .unwrap();
- drop((s1, s2));
-
- let mut submodules = repo.submodules().unwrap();
- assert_eq!(submodules.len(), 2);
- let mut s = submodules.remove(0);
- assert_eq!(s.name(), Some("bar"));
- assert_eq!(s.url(), Some("/path/to/nowhere"));
- assert_eq!(s.branch(), None);
- assert!(s.head_id().is_none());
- assert!(s.index_id().is_none());
- assert!(s.workdir_id().is_none());
-
- repo.find_submodule("bar").unwrap();
- s.open().unwrap();
- assert!(s.path() == Path::new("bar"));
- s.reload(true).unwrap();
- }
-
- #[test]
- fn add_a_submodule() {
- let (_td, repo1) = crate::test::repo_init();
- let (td, repo2) = crate::test::repo_init();
-
- let url = Url::from_file_path(&repo1.workdir().unwrap()).unwrap();
- let mut s = repo2
- .submodule(&url.to_string(), Path::new("bar"), true)
- .unwrap();
- t!(fs::remove_dir_all(td.path().join("bar")));
- t!(Repository::clone(&url.to_string(), td.path().join("bar")));
- t!(s.add_to_index(false));
- t!(s.add_finalize());
- }
-
- #[test]
- fn update_submodule() {
- // -----------------------------------
- // Same as `add_a_submodule()`
- let (_td, repo1) = crate::test::repo_init();
- let (td, repo2) = crate::test::repo_init();
-
- let url = Url::from_file_path(&repo1.workdir().unwrap()).unwrap();
- let mut s = repo2
- .submodule(&url.to_string(), Path::new("bar"), true)
- .unwrap();
- t!(fs::remove_dir_all(td.path().join("bar")));
- t!(Repository::clone(&url.to_string(), td.path().join("bar")));
- t!(s.add_to_index(false));
- t!(s.add_finalize());
- // -----------------------------------
-
- // Attempt to update submodule
- let submodules = t!(repo1.submodules());
- for mut submodule in submodules {
- let mut submodule_options = SubmoduleUpdateOptions::new();
- let init = true;
- let opts = Some(&mut submodule_options);
-
- t!(submodule.update(init, opts));
- }
- }
-
- #[test]
- fn clone_submodule() {
- // -----------------------------------
- // Same as `add_a_submodule()`
- let (_td, repo1) = crate::test::repo_init();
- let (_td, repo2) = crate::test::repo_init();
- let (_td, parent) = crate::test::repo_init();
-
- let url1 = Url::from_file_path(&repo1.workdir().unwrap()).unwrap();
- let url2 = Url::from_file_path(&repo2.workdir().unwrap()).unwrap();
- let mut s1 = parent
- .submodule(&url1.to_string(), Path::new("bar"), true)
- .unwrap();
- let mut s2 = parent
- .submodule(&url2.to_string(), Path::new("bar2"), true)
- .unwrap();
- // -----------------------------------
-
- t!(s1.clone(Some(&mut SubmoduleUpdateOptions::default())));
- t!(s2.clone(None));
- }
-
- #[test]
- fn repo_init_submodule() {
- // -----------------------------------
- // Same as `clone_submodule()`
- let (_td, child) = crate::test::repo_init();
- let (_td, parent) = crate::test::repo_init();
-
- let url_child = Url::from_file_path(&child.workdir().unwrap()).unwrap();
- let url_parent = Url::from_file_path(&parent.workdir().unwrap()).unwrap();
- let mut sub = parent
- .submodule(&url_child.to_string(), Path::new("bar"), true)
- .unwrap();
-
- // -----------------------------------
- // Let's commit the submodule for later clone
- t!(sub.clone(None));
- t!(sub.add_to_index(true));
- t!(sub.add_finalize());
-
- crate::test::commit(&parent);
-
- // Clone the parent to init its submodules
- let td = TempDir::new().unwrap();
- let new_parent = Repository::clone(&url_parent.to_string(), &td).unwrap();
-
- let mut submodules = new_parent.submodules().unwrap();
- let child = submodules.first_mut().unwrap();
-
- // First init child
- t!(child.init(false));
- assert_eq!(child.url().unwrap(), url_child.as_str());
-
- // open() is not possible before initializing the repo
- assert!(child.open().is_err());
- t!(child.repo_init(true));
- assert!(child.open().is_ok());
- }
-}
diff --git a/extra/git2/src/tag.rs b/extra/git2/src/tag.rs
deleted file mode 100644
index 6986c7c16..000000000
--- a/extra/git2/src/tag.rs
+++ /dev/null
@@ -1,234 +0,0 @@
-use std::ffi::CString;
-use std::marker;
-use std::mem;
-use std::ptr;
-use std::str;
-
-use crate::util::Binding;
-use crate::{call, raw, signature, Error, Object, ObjectType, Oid, Signature};
-
-/// A structure to represent a git [tag][1]
-///
-/// [1]: http://git-scm.com/book/en/Git-Basics-Tagging
-pub struct Tag<'repo> {
- raw: *mut raw::git_tag,
- _marker: marker::PhantomData<Object<'repo>>,
-}
-
-impl<'repo> Tag<'repo> {
- /// Determine whether a tag name is valid, meaning that (when prefixed with refs/tags/) that
- /// it is a valid reference name, and that any additional tag name restrictions are imposed
- /// (eg, it cannot start with a -).
- pub fn is_valid_name(tag_name: &str) -> bool {
- crate::init();
- let tag_name = CString::new(tag_name).unwrap();
- let mut valid: libc::c_int = 0;
- unsafe {
- call::c_try(raw::git_tag_name_is_valid(&mut valid, tag_name.as_ptr())).unwrap();
- }
- valid == 1
- }
-
- /// Get the id (SHA1) of a repository tag
- pub fn id(&self) -> Oid {
- unsafe { Binding::from_raw(raw::git_tag_id(&*self.raw)) }
- }
-
- /// Get the message of a tag
- ///
- /// Returns None if there is no message or if it is not valid utf8
- pub fn message(&self) -> Option<&str> {
- self.message_bytes().and_then(|s| str::from_utf8(s).ok())
- }
-
- /// Get the message of a tag
- ///
- /// Returns None if there is no message
- pub fn message_bytes(&self) -> Option<&[u8]> {
- unsafe { crate::opt_bytes(self, raw::git_tag_message(&*self.raw)) }
- }
-
- /// Get the name of a tag
- ///
- /// Returns None if it is not valid utf8
- pub fn name(&self) -> Option<&str> {
- str::from_utf8(self.name_bytes()).ok()
- }
-
- /// Get the name of a tag
- pub fn name_bytes(&self) -> &[u8] {
- unsafe { crate::opt_bytes(self, raw::git_tag_name(&*self.raw)).unwrap() }
- }
-
- /// Recursively peel a tag until a non tag git_object is found
- pub fn peel(&self) -> Result<Object<'repo>, Error> {
- let mut ret = ptr::null_mut();
- unsafe {
- try_call!(raw::git_tag_peel(&mut ret, &*self.raw));
- Ok(Binding::from_raw(ret))
- }
- }
-
- /// Get the tagger (author) of a tag
- ///
- /// If the author is unspecified, then `None` is returned.
- pub fn tagger(&self) -> Option<Signature<'_>> {
- unsafe {
- let ptr = raw::git_tag_tagger(&*self.raw);
- if ptr.is_null() {
- None
- } else {
- Some(signature::from_raw_const(self, ptr))
- }
- }
- }
-
- /// Get the tagged object of a tag
- ///
- /// This method performs a repository lookup for the given object and
- /// returns it
- pub fn target(&self) -> Result<Object<'repo>, Error> {
- let mut ret = ptr::null_mut();
- unsafe {
- try_call!(raw::git_tag_target(&mut ret, &*self.raw));
- Ok(Binding::from_raw(ret))
- }
- }
-
- /// Get the OID of the tagged object of a tag
- pub fn target_id(&self) -> Oid {
- unsafe { Binding::from_raw(raw::git_tag_target_id(&*self.raw)) }
- }
-
- /// Get the ObjectType of the tagged object of a tag
- pub fn target_type(&self) -> Option<ObjectType> {
- unsafe { ObjectType::from_raw(raw::git_tag_target_type(&*self.raw)) }
- }
-
- /// Casts this Tag to be usable as an `Object`
- pub fn as_object(&self) -> &Object<'repo> {
- unsafe { &*(self as *const _ as *const Object<'repo>) }
- }
-
- /// Consumes Tag to be returned as an `Object`
- pub fn into_object(self) -> Object<'repo> {
- assert_eq!(mem::size_of_val(&self), mem::size_of::<Object<'_>>());
- unsafe { mem::transmute(self) }
- }
-}
-
-impl<'repo> std::fmt::Debug for Tag<'repo> {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
- let mut ds = f.debug_struct("Tag");
- if let Some(name) = self.name() {
- ds.field("name", &name);
- }
- ds.field("id", &self.id());
- ds.finish()
- }
-}
-
-impl<'repo> Binding for Tag<'repo> {
- type Raw = *mut raw::git_tag;
- unsafe fn from_raw(raw: *mut raw::git_tag) -> Tag<'repo> {
- Tag {
- raw,
- _marker: marker::PhantomData,
- }
- }
- fn raw(&self) -> *mut raw::git_tag {
- self.raw
- }
-}
-
-impl<'repo> Clone for Tag<'repo> {
- fn clone(&self) -> Self {
- self.as_object().clone().into_tag().ok().unwrap()
- }
-}
-
-impl<'repo> Drop for Tag<'repo> {
- fn drop(&mut self) {
- unsafe { raw::git_tag_free(self.raw) }
- }
-}
-
-#[cfg(test)]
-mod tests {
- use crate::Tag;
-
- // Reference -- https://git-scm.com/docs/git-check-ref-format
- #[test]
- fn name_is_valid() {
- assert_eq!(Tag::is_valid_name("blah_blah"), true);
- assert_eq!(Tag::is_valid_name("v1.2.3"), true);
- assert_eq!(Tag::is_valid_name("my/tag"), true);
- assert_eq!(Tag::is_valid_name("@"), true);
-
- assert_eq!(Tag::is_valid_name("-foo"), false);
- assert_eq!(Tag::is_valid_name("foo:bar"), false);
- assert_eq!(Tag::is_valid_name("foo^bar"), false);
- assert_eq!(Tag::is_valid_name("foo."), false);
- assert_eq!(Tag::is_valid_name("@{"), false);
- assert_eq!(Tag::is_valid_name("as\\cd"), false);
- }
-
- #[test]
- #[should_panic]
- fn is_valid_name_for_invalid_tag() {
- Tag::is_valid_name("ab\012");
- }
-
- #[test]
- fn smoke() {
- let (_td, repo) = crate::test::repo_init();
- let head = repo.head().unwrap();
- let id = head.target().unwrap();
- assert!(repo.find_tag(id).is_err());
-
- let obj = repo.find_object(id, None).unwrap();
- let sig = repo.signature().unwrap();
- let tag_id = repo.tag("foo", &obj, &sig, "msg", false).unwrap();
- let tag = repo.find_tag(tag_id).unwrap();
- assert_eq!(tag.id(), tag_id);
-
- let tags = repo.tag_names(None).unwrap();
- assert_eq!(tags.len(), 1);
- assert_eq!(tags.get(0), Some("foo"));
-
- assert_eq!(tag.name(), Some("foo"));
- assert_eq!(tag.message(), Some("msg"));
- assert_eq!(tag.peel().unwrap().id(), obj.id());
- assert_eq!(tag.target_id(), obj.id());
- assert_eq!(tag.target_type(), Some(crate::ObjectType::Commit));
-
- assert_eq!(tag.tagger().unwrap().name(), sig.name());
- tag.target().unwrap();
- tag.into_object();
-
- repo.find_object(tag_id, None).unwrap().as_tag().unwrap();
- repo.find_object(tag_id, None)
- .unwrap()
- .into_tag()
- .ok()
- .unwrap();
-
- repo.tag_delete("foo").unwrap();
- }
-
- #[test]
- fn lite() {
- let (_td, repo) = crate::test::repo_init();
- let head = t!(repo.head());
- let id = head.target().unwrap();
- let obj = t!(repo.find_object(id, None));
- let tag_id = t!(repo.tag_lightweight("foo", &obj, false));
- assert!(repo.find_tag(tag_id).is_err());
- assert_eq!(t!(repo.refname_to_id("refs/tags/foo")), id);
-
- let tags = t!(repo.tag_names(Some("f*")));
- assert_eq!(tags.len(), 1);
- let tags = t!(repo.tag_names(Some("b*")));
- assert_eq!(tags.len(), 0);
- }
-}
diff --git a/extra/git2/src/tagforeach.rs b/extra/git2/src/tagforeach.rs
deleted file mode 100644
index 425eea5a4..000000000
--- a/extra/git2/src/tagforeach.rs
+++ /dev/null
@@ -1,69 +0,0 @@
-//! git_tag_foreach support
-//! see original: <https://libgit2.org/libgit2/#HEAD/group/tag/git_tag_foreach>
-
-use crate::{panic, raw, util::Binding, Oid};
-use libc::{c_char, c_int};
-use raw::git_oid;
-use std::ffi::{c_void, CStr};
-
-/// boxed callback type
-pub(crate) type TagForeachCB<'a> = Box<dyn FnMut(Oid, &[u8]) -> bool + 'a>;
-
-/// helper type to be able to pass callback to payload
-pub(crate) struct TagForeachData<'a> {
- /// callback
- pub(crate) cb: TagForeachCB<'a>,
-}
-
-/// c callback forwarding to rust callback inside `TagForeachData`
-/// see original: <https://libgit2.org/libgit2/#HEAD/group/callback/git_tag_foreach_cb>
-pub(crate) extern "C" fn tag_foreach_cb(
- name: *const c_char,
- oid: *mut git_oid,
- payload: *mut c_void,
-) -> c_int {
- panic::wrap(|| unsafe {
- let id: Oid = Binding::from_raw(oid as *const _);
-
- let name = CStr::from_ptr(name);
- let name = name.to_bytes();
-
- let payload = &mut *(payload as *mut TagForeachData<'_>);
- let cb = &mut payload.cb;
-
- let res = cb(id, name);
-
- if res {
- 0
- } else {
- -1
- }
- })
- .unwrap_or(-1)
-}
-
-#[cfg(test)]
-mod tests {
-
- #[test]
- fn smoke() {
- let (_td, repo) = crate::test::repo_init();
- let head = repo.head().unwrap();
- let id = head.target().unwrap();
- assert!(repo.find_tag(id).is_err());
-
- let obj = repo.find_object(id, None).unwrap();
- let sig = repo.signature().unwrap();
- let tag_id = repo.tag("foo", &obj, &sig, "msg", false).unwrap();
-
- let mut tags = Vec::new();
- repo.tag_foreach(|id, name| {
- tags.push((id, String::from_utf8(name.into()).unwrap()));
- true
- })
- .unwrap();
-
- assert_eq!(tags[0].0, tag_id);
- assert_eq!(tags[0].1, "refs/tags/foo");
- }
-}
diff --git a/extra/git2/src/test.rs b/extra/git2/src/test.rs
deleted file mode 100644
index c1ff1de21..000000000
--- a/extra/git2/src/test.rs
+++ /dev/null
@@ -1,89 +0,0 @@
-use std::fs::File;
-use std::io;
-use std::path::{Path, PathBuf};
-#[cfg(unix)]
-use std::ptr;
-use tempfile::TempDir;
-use url::Url;
-
-use crate::{Branch, Oid, Repository, RepositoryInitOptions};
-
-macro_rules! t {
- ($e:expr) => {
- match $e {
- Ok(e) => e,
- Err(e) => panic!("{} failed with {}", stringify!($e), e),
- }
- };
-}
-
-pub fn repo_init() -> (TempDir, Repository) {
- let td = TempDir::new().unwrap();
- let mut opts = RepositoryInitOptions::new();
- opts.initial_head("main");
- let repo = Repository::init_opts(td.path(), &opts).unwrap();
- {
- let mut config = repo.config().unwrap();
- config.set_str("user.name", "name").unwrap();
- config.set_str("user.email", "email").unwrap();
- let mut index = repo.index().unwrap();
- let id = index.write_tree().unwrap();
-
- let tree = repo.find_tree(id).unwrap();
- let sig = repo.signature().unwrap();
- repo.commit(Some("HEAD"), &sig, &sig, "initial\n\nbody", &tree, &[])
- .unwrap();
- }
- (td, repo)
-}
-
-pub fn commit(repo: &Repository) -> (Oid, Oid) {
- let mut index = t!(repo.index());
- let root = repo.path().parent().unwrap();
- t!(File::create(&root.join("foo")));
- t!(index.add_path(Path::new("foo")));
-
- let tree_id = t!(index.write_tree());
- let tree = t!(repo.find_tree(tree_id));
- let sig = t!(repo.signature());
- let head_id = t!(repo.refname_to_id("HEAD"));
- let parent = t!(repo.find_commit(head_id));
- let commit = t!(repo.commit(Some("HEAD"), &sig, &sig, "commit", &tree, &[&parent]));
- (commit, tree_id)
-}
-
-pub fn path2url(path: &Path) -> String {
- Url::from_file_path(path).unwrap().to_string()
-}
-
-pub fn worktrees_env_init(repo: &Repository) -> (TempDir, Branch<'_>) {
- let oid = repo.head().unwrap().target().unwrap();
- let commit = repo.find_commit(oid).unwrap();
- let branch = repo.branch("wt-branch", &commit, true).unwrap();
- let wtdir = TempDir::new().unwrap();
- (wtdir, branch)
-}
-
-#[cfg(windows)]
-pub fn realpath(original: &Path) -> io::Result<PathBuf> {
- Ok(original.to_path_buf())
-}
-#[cfg(unix)]
-pub fn realpath(original: &Path) -> io::Result<PathBuf> {
- use libc::c_char;
- use std::ffi::{CStr, CString, OsString};
- use std::os::unix::prelude::*;
- extern "C" {
- fn realpath(name: *const c_char, resolved: *mut c_char) -> *mut c_char;
- }
- unsafe {
- let cstr = CString::new(original.as_os_str().as_bytes())?;
- let ptr = realpath(cstr.as_ptr(), ptr::null_mut());
- if ptr.is_null() {
- return Err(io::Error::last_os_error());
- }
- let bytes = CStr::from_ptr(ptr).to_bytes().to_vec();
- libc::free(ptr as *mut _);
- Ok(PathBuf::from(OsString::from_vec(bytes)))
- }
-}
diff --git a/extra/git2/src/time.rs b/extra/git2/src/time.rs
deleted file mode 100644
index 46b5bd3f9..000000000
--- a/extra/git2/src/time.rs
+++ /dev/null
@@ -1,127 +0,0 @@
-use std::cmp::Ordering;
-
-use libc::{c_char, c_int};
-
-use crate::raw;
-use crate::util::Binding;
-
-/// Time in a signature
-#[derive(Copy, Clone, Debug, Eq, PartialEq)]
-pub struct Time {
- raw: raw::git_time,
-}
-
-/// Time structure used in a git index entry.
-#[derive(Copy, Clone, Debug, Eq, PartialEq)]
-pub struct IndexTime {
- raw: raw::git_index_time,
-}
-
-impl Time {
- /// Creates a new time structure from its components.
- pub fn new(time: i64, offset: i32) -> Time {
- unsafe {
- Binding::from_raw(raw::git_time {
- time: time as raw::git_time_t,
- offset: offset as c_int,
- sign: if offset < 0 { '-' } else { '+' } as c_char,
- })
- }
- }
-
- /// Return the time, in seconds, from epoch
- pub fn seconds(&self) -> i64 {
- self.raw.time as i64
- }
-
- /// Return the timezone offset, in minutes
- pub fn offset_minutes(&self) -> i32 {
- self.raw.offset as i32
- }
-
- /// Return whether the offset was positive or negative. Primarily useful
- /// in case the offset is specified as a negative zero.
- pub fn sign(&self) -> char {
- self.raw.sign as u8 as char
- }
-}
-
-impl PartialOrd for Time {
- fn partial_cmp(&self, other: &Time) -> Option<Ordering> {
- Some(self.cmp(other))
- }
-}
-
-impl Ord for Time {
- fn cmp(&self, other: &Time) -> Ordering {
- (self.raw.time, self.raw.offset).cmp(&(other.raw.time, other.raw.offset))
- }
-}
-
-impl Binding for Time {
- type Raw = raw::git_time;
- unsafe fn from_raw(raw: raw::git_time) -> Time {
- Time { raw }
- }
- fn raw(&self) -> raw::git_time {
- self.raw
- }
-}
-
-impl IndexTime {
- /// Creates a new time structure from its components.
- pub fn new(seconds: i32, nanoseconds: u32) -> IndexTime {
- unsafe {
- Binding::from_raw(raw::git_index_time {
- seconds,
- nanoseconds,
- })
- }
- }
-
- /// Returns the number of seconds in the second component of this time.
- pub fn seconds(&self) -> i32 {
- self.raw.seconds
- }
- /// Returns the nanosecond component of this time.
- pub fn nanoseconds(&self) -> u32 {
- self.raw.nanoseconds
- }
-}
-
-impl Binding for IndexTime {
- type Raw = raw::git_index_time;
- unsafe fn from_raw(raw: raw::git_index_time) -> IndexTime {
- IndexTime { raw }
- }
- fn raw(&self) -> raw::git_index_time {
- self.raw
- }
-}
-
-impl PartialOrd for IndexTime {
- fn partial_cmp(&self, other: &IndexTime) -> Option<Ordering> {
- Some(self.cmp(other))
- }
-}
-
-impl Ord for IndexTime {
- fn cmp(&self, other: &IndexTime) -> Ordering {
- let me = (self.raw.seconds, self.raw.nanoseconds);
- let other = (other.raw.seconds, other.raw.nanoseconds);
- me.cmp(&other)
- }
-}
-
-#[cfg(test)]
-mod tests {
- use crate::Time;
-
- #[test]
- fn smoke() {
- assert_eq!(Time::new(1608839587, -300).seconds(), 1608839587);
- assert_eq!(Time::new(1608839587, -300).offset_minutes(), -300);
- assert_eq!(Time::new(1608839587, -300).sign(), '-');
- assert_eq!(Time::new(1608839587, 300).sign(), '+');
- }
-}
diff --git a/extra/git2/src/tracing.rs b/extra/git2/src/tracing.rs
deleted file mode 100644
index 5acae8a85..000000000
--- a/extra/git2/src/tracing.rs
+++ /dev/null
@@ -1,85 +0,0 @@
-use std::sync::atomic::{AtomicUsize, Ordering};
-
-use libc::c_char;
-
-use crate::{panic, raw, util::Binding};
-
-/// Available tracing levels. When tracing is set to a particular level,
-/// callers will be provided tracing at the given level and all lower levels.
-#[derive(Copy, Clone, Debug)]
-pub enum TraceLevel {
- /// No tracing will be performed.
- None,
-
- /// Severe errors that may impact the program's execution
- Fatal,
-
- /// Errors that do not impact the program's execution
- Error,
-
- /// Warnings that suggest abnormal data
- Warn,
-
- /// Informational messages about program execution
- Info,
-
- /// Detailed data that allows for debugging
- Debug,
-
- /// Exceptionally detailed debugging data
- Trace,
-}
-
-impl Binding for TraceLevel {
- type Raw = raw::git_trace_level_t;
- unsafe fn from_raw(raw: raw::git_trace_level_t) -> Self {
- match raw {
- raw::GIT_TRACE_NONE => Self::None,
- raw::GIT_TRACE_FATAL => Self::Fatal,
- raw::GIT_TRACE_ERROR => Self::Error,
- raw::GIT_TRACE_WARN => Self::Warn,
- raw::GIT_TRACE_INFO => Self::Info,
- raw::GIT_TRACE_DEBUG => Self::Debug,
- raw::GIT_TRACE_TRACE => Self::Trace,
- _ => panic!("Unknown git trace level"),
- }
- }
- fn raw(&self) -> raw::git_trace_level_t {
- match *self {
- Self::None => raw::GIT_TRACE_NONE,
- Self::Fatal => raw::GIT_TRACE_FATAL,
- Self::Error => raw::GIT_TRACE_ERROR,
- Self::Warn => raw::GIT_TRACE_WARN,
- Self::Info => raw::GIT_TRACE_INFO,
- Self::Debug => raw::GIT_TRACE_DEBUG,
- Self::Trace => raw::GIT_TRACE_TRACE,
- }
- }
-}
-
-//TODO: pass raw &[u8] and leave conversion to consumer (breaking API)
-/// Callback type used to pass tracing events to the subscriber.
-/// see `trace_set` to register a subscriber.
-pub type TracingCb = fn(TraceLevel, &str);
-
-static CALLBACK: AtomicUsize = AtomicUsize::new(0);
-
-///
-pub fn trace_set(level: TraceLevel, cb: TracingCb) -> bool {
- CALLBACK.store(cb as usize, Ordering::SeqCst);
-
- unsafe {
- raw::git_trace_set(level.raw(), Some(tracing_cb_c));
- }
-
- return true;
-}
-
-extern "C" fn tracing_cb_c(level: raw::git_trace_level_t, msg: *const c_char) {
- let cb = CALLBACK.load(Ordering::SeqCst);
- panic::wrap(|| unsafe {
- let cb: TracingCb = std::mem::transmute(cb);
- let msg = std::ffi::CStr::from_ptr(msg).to_string_lossy();
- cb(Binding::from_raw(level), msg.as_ref());
- });
-}
diff --git a/extra/git2/src/transaction.rs b/extra/git2/src/transaction.rs
deleted file mode 100644
index 4f661f1d4..000000000
--- a/extra/git2/src/transaction.rs
+++ /dev/null
@@ -1,285 +0,0 @@
-use std::ffi::CString;
-use std::marker;
-
-use crate::{raw, util::Binding, Error, Oid, Reflog, Repository, Signature};
-
-/// A structure representing a transactional update of a repository's references.
-///
-/// Transactions work by locking loose refs for as long as the [`Transaction`]
-/// is held, and committing all changes to disk when [`Transaction::commit`] is
-/// called. Note that committing is not atomic: if an operation fails, the
-/// transaction aborts, but previous successful operations are not rolled back.
-pub struct Transaction<'repo> {
- raw: *mut raw::git_transaction,
- _marker: marker::PhantomData<&'repo Repository>,
-}
-
-impl Drop for Transaction<'_> {
- fn drop(&mut self) {
- unsafe { raw::git_transaction_free(self.raw) }
- }
-}
-
-impl<'repo> Binding for Transaction<'repo> {
- type Raw = *mut raw::git_transaction;
-
- unsafe fn from_raw(ptr: *mut raw::git_transaction) -> Transaction<'repo> {
- Transaction {
- raw: ptr,
- _marker: marker::PhantomData,
- }
- }
-
- fn raw(&self) -> *mut raw::git_transaction {
- self.raw
- }
-}
-
-impl<'repo> Transaction<'repo> {
- /// Lock the specified reference by name.
- pub fn lock_ref(&mut self, refname: &str) -> Result<(), Error> {
- let refname = CString::new(refname).unwrap();
- unsafe {
- try_call!(raw::git_transaction_lock_ref(self.raw, refname));
- }
-
- Ok(())
- }
-
- /// Set the target of the specified reference.
- ///
- /// The reference must have been locked via `lock_ref`.
- ///
- /// If `reflog_signature` is `None`, the [`Signature`] is read from the
- /// repository config.
- pub fn set_target(
- &mut self,
- refname: &str,
- target: Oid,
- reflog_signature: Option<&Signature<'_>>,
- reflog_message: &str,
- ) -> Result<(), Error> {
- let refname = CString::new(refname).unwrap();
- let reflog_message = CString::new(reflog_message).unwrap();
- unsafe {
- try_call!(raw::git_transaction_set_target(
- self.raw,
- refname,
- target.raw(),
- reflog_signature.map(|s| s.raw()),
- reflog_message
- ));
- }
-
- Ok(())
- }
-
- /// Set the target of the specified symbolic reference.
- ///
- /// The reference must have been locked via `lock_ref`.
- ///
- /// If `reflog_signature` is `None`, the [`Signature`] is read from the
- /// repository config.
- pub fn set_symbolic_target(
- &mut self,
- refname: &str,
- target: &str,
- reflog_signature: Option<&Signature<'_>>,
- reflog_message: &str,
- ) -> Result<(), Error> {
- let refname = CString::new(refname).unwrap();
- let target = CString::new(target).unwrap();
- let reflog_message = CString::new(reflog_message).unwrap();
- unsafe {
- try_call!(raw::git_transaction_set_symbolic_target(
- self.raw,
- refname,
- target,
- reflog_signature.map(|s| s.raw()),
- reflog_message
- ));
- }
-
- Ok(())
- }
-
- /// Add a [`Reflog`] to the transaction.
- ///
- /// This commit the in-memory [`Reflog`] to disk when the transaction commits.
- /// Note that atomicity is **not* guaranteed: if the transaction fails to
- /// modify `refname`, the reflog may still have been committed to disk.
- ///
- /// If this is combined with setting the target, that update won't be
- /// written to the log (i.e. the `reflog_signature` and `reflog_message`
- /// parameters will be ignored).
- pub fn set_reflog(&mut self, refname: &str, reflog: Reflog) -> Result<(), Error> {
- let refname = CString::new(refname).unwrap();
- unsafe {
- try_call!(raw::git_transaction_set_reflog(
- self.raw,
- refname,
- reflog.raw()
- ));
- }
-
- Ok(())
- }
-
- /// Remove a reference.
- ///
- /// The reference must have been locked via `lock_ref`.
- pub fn remove(&mut self, refname: &str) -> Result<(), Error> {
- let refname = CString::new(refname).unwrap();
- unsafe {
- try_call!(raw::git_transaction_remove(self.raw, refname));
- }
-
- Ok(())
- }
-
- /// Commit the changes from the transaction.
- ///
- /// The updates will be made one by one, and the first failure will stop the
- /// processing.
- pub fn commit(self) -> Result<(), Error> {
- unsafe {
- try_call!(raw::git_transaction_commit(self.raw));
- }
- Ok(())
- }
-}
-
-#[cfg(test)]
-mod tests {
- use crate::{Error, ErrorClass, ErrorCode, Oid, Repository};
-
- #[test]
- fn smoke() {
- let (_td, repo) = crate::test::repo_init();
-
- let mut tx = t!(repo.transaction());
-
- t!(tx.lock_ref("refs/heads/main"));
- t!(tx.lock_ref("refs/heads/next"));
-
- t!(tx.set_target("refs/heads/main", Oid::zero(), None, "set main to zero"));
- t!(tx.set_symbolic_target(
- "refs/heads/next",
- "refs/heads/main",
- None,
- "set next to main",
- ));
-
- t!(tx.commit());
-
- assert_eq!(repo.refname_to_id("refs/heads/main").unwrap(), Oid::zero());
- assert_eq!(
- repo.find_reference("refs/heads/next")
- .unwrap()
- .symbolic_target()
- .unwrap(),
- "refs/heads/main"
- );
- }
-
- #[test]
- fn locks_same_repo_handle() {
- let (_td, repo) = crate::test::repo_init();
-
- let mut tx1 = t!(repo.transaction());
- t!(tx1.lock_ref("refs/heads/seen"));
-
- let mut tx2 = t!(repo.transaction());
- assert!(matches!(tx2.lock_ref("refs/heads/seen"), Err(e) if e.code() == ErrorCode::Locked))
- }
-
- #[test]
- fn locks_across_repo_handles() {
- let (td, repo1) = crate::test::repo_init();
- let repo2 = t!(Repository::open(&td));
-
- let mut tx1 = t!(repo1.transaction());
- t!(tx1.lock_ref("refs/heads/seen"));
-
- let mut tx2 = t!(repo2.transaction());
- assert!(matches!(tx2.lock_ref("refs/heads/seen"), Err(e) if e.code() == ErrorCode::Locked))
- }
-
- #[test]
- fn drop_unlocks() {
- let (_td, repo) = crate::test::repo_init();
-
- let mut tx = t!(repo.transaction());
- t!(tx.lock_ref("refs/heads/seen"));
- drop(tx);
-
- let mut tx2 = t!(repo.transaction());
- t!(tx2.lock_ref("refs/heads/seen"))
- }
-
- #[test]
- fn commit_unlocks() {
- let (_td, repo) = crate::test::repo_init();
-
- let mut tx = t!(repo.transaction());
- t!(tx.lock_ref("refs/heads/seen"));
- t!(tx.commit());
-
- let mut tx2 = t!(repo.transaction());
- t!(tx2.lock_ref("refs/heads/seen"));
- }
-
- #[test]
- fn prevents_non_transactional_updates() {
- let (_td, repo) = crate::test::repo_init();
- let head = t!(repo.refname_to_id("HEAD"));
-
- let mut tx = t!(repo.transaction());
- t!(tx.lock_ref("refs/heads/seen"));
-
- assert!(matches!(
- repo.reference("refs/heads/seen", head, true, "competing with lock"),
- Err(e) if e.code() == ErrorCode::Locked
- ));
- }
-
- #[test]
- fn remove() {
- let (_td, repo) = crate::test::repo_init();
- let head = t!(repo.refname_to_id("HEAD"));
- let next = "refs/heads/next";
-
- t!(repo.reference(
- next,
- head,
- true,
- "refs/heads/next@{0}: branch: Created from HEAD"
- ));
-
- {
- let mut tx = t!(repo.transaction());
- t!(tx.lock_ref(next));
- t!(tx.remove(next));
- t!(tx.commit());
- }
- assert!(matches!(repo.refname_to_id(next), Err(e) if e.code() == ErrorCode::NotFound))
- }
-
- #[test]
- fn must_lock_ref() {
- let (_td, repo) = crate::test::repo_init();
-
- // 🤷
- fn is_not_locked_err(e: &Error) -> bool {
- e.code() == ErrorCode::NotFound
- && e.class() == ErrorClass::Reference
- && e.message() == "the specified reference is not locked"
- }
-
- let mut tx = t!(repo.transaction());
- assert!(matches!(
- tx.set_target("refs/heads/main", Oid::zero(), None, "set main to zero"),
- Err(e) if is_not_locked_err(&e)
- ))
- }
-}
diff --git a/extra/git2/src/transport.rs b/extra/git2/src/transport.rs
deleted file mode 100644
index 74446d0ca..000000000
--- a/extra/git2/src/transport.rs
+++ /dev/null
@@ -1,429 +0,0 @@
-//! Interfaces for adding custom transports to libgit2
-
-use libc::{c_char, c_int, c_uint, c_void, size_t};
-use std::ffi::{CStr, CString};
-use std::io;
-use std::io::prelude::*;
-use std::mem;
-use std::ptr;
-use std::slice;
-use std::str;
-
-use crate::util::Binding;
-use crate::{panic, raw, Error, Remote};
-
-/// A transport is a structure which knows how to transfer data to and from a
-/// remote.
-///
-/// This transport is a representation of the raw transport underneath it, which
-/// is similar to a trait object in Rust.
-#[allow(missing_copy_implementations)]
-pub struct Transport {
- raw: *mut raw::git_transport,
- owned: bool,
-}
-
-/// Interface used by smart transports.
-///
-/// The full-fledged definition of transports has to deal with lots of
-/// nitty-gritty details of the git protocol, but "smart transports" largely
-/// only need to deal with read() and write() of data over a channel.
-///
-/// A smart subtransport is contained within an instance of a smart transport
-/// and is delegated to in order to actually conduct network activity to push or
-/// pull data from a remote.
-pub trait SmartSubtransport: Send + 'static {
- /// Indicates that this subtransport will be performing the specified action
- /// on the specified URL.
- ///
- /// This function is responsible for making any network connections and
- /// returns a stream which can be read and written from in order to
- /// negotiate the git protocol.
- fn action(&self, url: &str, action: Service)
- -> Result<Box<dyn SmartSubtransportStream>, Error>;
-
- /// Terminates a connection with the remote.
- ///
- /// Each subtransport is guaranteed a call to close() between calls to
- /// action(), except for the following two natural progressions of actions
- /// against a constant URL.
- ///
- /// 1. UploadPackLs -> UploadPack
- /// 2. ReceivePackLs -> ReceivePack
- fn close(&self) -> Result<(), Error>;
-}
-
-/// Actions that a smart transport can ask a subtransport to perform
-#[derive(Copy, Clone, PartialEq)]
-#[allow(missing_docs)]
-pub enum Service {
- UploadPackLs,
- UploadPack,
- ReceivePackLs,
- ReceivePack,
-}
-
-/// An instance of a stream over which a smart transport will communicate with a
-/// remote.
-///
-/// Currently this only requires the standard `Read` and `Write` traits. This
-/// trait also does not need to be implemented manually as long as the `Read`
-/// and `Write` traits are implemented.
-pub trait SmartSubtransportStream: Read + Write + Send + 'static {}
-
-impl<T: Read + Write + Send + 'static> SmartSubtransportStream for T {}
-
-type TransportFactory = dyn Fn(&Remote<'_>) -> Result<Transport, Error> + Send + Sync + 'static;
-
-/// Boxed data payload used for registering new transports.
-///
-/// Currently only contains a field which knows how to create transports.
-struct TransportData {
- factory: Box<TransportFactory>,
-}
-
-/// Instance of a `git_smart_subtransport`, must use `#[repr(C)]` to ensure that
-/// the C fields come first.
-#[repr(C)]
-struct RawSmartSubtransport {
- raw: raw::git_smart_subtransport,
- stream: Option<*mut raw::git_smart_subtransport_stream>,
- rpc: bool,
- obj: Box<dyn SmartSubtransport>,
-}
-
-/// Instance of a `git_smart_subtransport_stream`, must use `#[repr(C)]` to
-/// ensure that the C fields come first.
-#[repr(C)]
-struct RawSmartSubtransportStream {
- raw: raw::git_smart_subtransport_stream,
- obj: Box<dyn SmartSubtransportStream>,
-}
-
-/// Add a custom transport definition, to be used in addition to the built-in
-/// set of transports that come with libgit2.
-///
-/// This function is unsafe as it needs to be externally synchronized with calls
-/// to creation of other transports.
-pub unsafe fn register<F>(prefix: &str, factory: F) -> Result<(), Error>
-where
- F: Fn(&Remote<'_>) -> Result<Transport, Error> + Send + Sync + 'static,
-{
- crate::init();
- let mut data = Box::new(TransportData {
- factory: Box::new(factory),
- });
- let prefix = CString::new(prefix)?;
- let datap = (&mut *data) as *mut TransportData as *mut c_void;
- let factory: raw::git_transport_cb = Some(transport_factory);
- try_call!(raw::git_transport_register(prefix, factory, datap));
- mem::forget(data);
- Ok(())
-}
-
-impl Transport {
- /// Creates a new transport which will use the "smart" transport protocol
- /// for transferring data.
- ///
- /// A smart transport requires a *subtransport* over which data is actually
- /// communicated, but this subtransport largely just needs to be able to
- /// read() and write(). The subtransport provided will be used to make
- /// connections which can then be read/written from.
- ///
- /// The `rpc` argument is `true` if the protocol is stateless, false
- /// otherwise. For example `http://` is stateless but `git://` is not.
- pub fn smart<S>(remote: &Remote<'_>, rpc: bool, subtransport: S) -> Result<Transport, Error>
- where
- S: SmartSubtransport,
- {
- let mut ret = ptr::null_mut();
-
- let mut raw = Box::new(RawSmartSubtransport {
- raw: raw::git_smart_subtransport {
- action: Some(subtransport_action),
- close: Some(subtransport_close),
- free: Some(subtransport_free),
- },
- stream: None,
- rpc,
- obj: Box::new(subtransport),
- });
- let mut defn = raw::git_smart_subtransport_definition {
- callback: Some(smart_factory),
- rpc: rpc as c_uint,
- param: &mut *raw as *mut _ as *mut _,
- };
-
- // Currently there's no way to pass a payload via the
- // git_smart_subtransport_definition structure, but it's only used as a
- // configuration for the initial creation of the smart transport (verified
- // by reading the current code, hopefully it doesn't change!).
- //
- // We, however, need some state (gotta pass in our
- // `RawSmartSubtransport`). This also means that this block must be
- // entirely synchronized with a lock (boo!)
- unsafe {
- try_call!(raw::git_transport_smart(
- &mut ret,
- remote.raw(),
- &mut defn as *mut _ as *mut _
- ));
- mem::forget(raw); // ownership transport to `ret`
- }
- return Ok(Transport {
- raw: ret,
- owned: true,
- });
-
- extern "C" fn smart_factory(
- out: *mut *mut raw::git_smart_subtransport,
- _owner: *mut raw::git_transport,
- ptr: *mut c_void,
- ) -> c_int {
- unsafe {
- *out = ptr as *mut raw::git_smart_subtransport;
- 0
- }
- }
- }
-}
-
-impl Drop for Transport {
- fn drop(&mut self) {
- if self.owned {
- unsafe { (*self.raw).free.unwrap()(self.raw) }
- }
- }
-}
-
-// callback used by register() to create new transports
-extern "C" fn transport_factory(
- out: *mut *mut raw::git_transport,
- owner: *mut raw::git_remote,
- param: *mut c_void,
-) -> c_int {
- struct Bomb<'a> {
- remote: Option<Remote<'a>>,
- }
- impl<'a> Drop for Bomb<'a> {
- fn drop(&mut self) {
- // TODO: maybe a method instead?
- mem::forget(self.remote.take());
- }
- }
-
- panic::wrap(|| unsafe {
- let remote = Bomb {
- remote: Some(Binding::from_raw(owner)),
- };
- let data = &mut *(param as *mut TransportData);
- match (data.factory)(remote.remote.as_ref().unwrap()) {
- Ok(mut transport) => {
- *out = transport.raw;
- transport.owned = false;
- 0
- }
- Err(e) => e.raw_code() as c_int,
- }
- })
- .unwrap_or(-1)
-}
-
-// callback used by smart transports to delegate an action to a
-// `SmartSubtransport` trait object.
-extern "C" fn subtransport_action(
- stream: *mut *mut raw::git_smart_subtransport_stream,
- raw_transport: *mut raw::git_smart_subtransport,
- url: *const c_char,
- action: raw::git_smart_service_t,
-) -> c_int {
- panic::wrap(|| unsafe {
- let url = CStr::from_ptr(url).to_bytes();
- let url = match str::from_utf8(url).ok() {
- Some(s) => s,
- None => return -1,
- };
- let action = match action {
- raw::GIT_SERVICE_UPLOADPACK_LS => Service::UploadPackLs,
- raw::GIT_SERVICE_UPLOADPACK => Service::UploadPack,
- raw::GIT_SERVICE_RECEIVEPACK_LS => Service::ReceivePackLs,
- raw::GIT_SERVICE_RECEIVEPACK => Service::ReceivePack,
- n => panic!("unknown action: {}", n),
- };
-
- let transport = &mut *(raw_transport as *mut RawSmartSubtransport);
- // Note: we only need to generate if rpc is on. Else, for receive-pack and upload-pack
- // libgit2 reuses the stream generated for receive-pack-ls or upload-pack-ls.
- let generate_stream =
- transport.rpc || action == Service::UploadPackLs || action == Service::ReceivePackLs;
- if generate_stream {
- let obj = match transport.obj.action(url, action) {
- Ok(s) => s,
- Err(e) => {
- set_err(&e);
- return e.raw_code() as c_int;
- }
- };
- *stream = mem::transmute(Box::new(RawSmartSubtransportStream {
- raw: raw::git_smart_subtransport_stream {
- subtransport: raw_transport,
- read: Some(stream_read),
- write: Some(stream_write),
- free: Some(stream_free),
- },
- obj,
- }));
- transport.stream = Some(*stream);
- } else {
- if transport.stream.is_none() {
- return -1;
- }
- *stream = transport.stream.unwrap();
- }
- 0
- })
- .unwrap_or(-1)
-}
-
-// callback used by smart transports to close a `SmartSubtransport` trait
-// object.
-extern "C" fn subtransport_close(transport: *mut raw::git_smart_subtransport) -> c_int {
- let ret = panic::wrap(|| unsafe {
- let transport = &mut *(transport as *mut RawSmartSubtransport);
- transport.obj.close()
- });
- match ret {
- Some(Ok(())) => 0,
- Some(Err(e)) => e.raw_code() as c_int,
- None => -1,
- }
-}
-
-// callback used by smart transports to free a `SmartSubtransport` trait
-// object.
-extern "C" fn subtransport_free(transport: *mut raw::git_smart_subtransport) {
- let _ = panic::wrap(|| unsafe {
- mem::transmute::<_, Box<RawSmartSubtransport>>(transport);
- });
-}
-
-// callback used by smart transports to read from a `SmartSubtransportStream`
-// object.
-extern "C" fn stream_read(
- stream: *mut raw::git_smart_subtransport_stream,
- buffer: *mut c_char,
- buf_size: size_t,
- bytes_read: *mut size_t,
-) -> c_int {
- let ret = panic::wrap(|| unsafe {
- let transport = &mut *(stream as *mut RawSmartSubtransportStream);
- let buf = slice::from_raw_parts_mut(buffer as *mut u8, buf_size as usize);
- match transport.obj.read(buf) {
- Ok(n) => {
- *bytes_read = n as size_t;
- Ok(n)
- }
- e => e,
- }
- });
- match ret {
- Some(Ok(_)) => 0,
- Some(Err(e)) => unsafe {
- set_err_io(&e);
- -2
- },
- None => -1,
- }
-}
-
-// callback used by smart transports to write to a `SmartSubtransportStream`
-// object.
-extern "C" fn stream_write(
- stream: *mut raw::git_smart_subtransport_stream,
- buffer: *const c_char,
- len: size_t,
-) -> c_int {
- let ret = panic::wrap(|| unsafe {
- let transport = &mut *(stream as *mut RawSmartSubtransportStream);
- let buf = slice::from_raw_parts(buffer as *const u8, len as usize);
- transport.obj.write_all(buf)
- });
- match ret {
- Some(Ok(())) => 0,
- Some(Err(e)) => unsafe {
- set_err_io(&e);
- -2
- },
- None => -1,
- }
-}
-
-unsafe fn set_err_io(e: &io::Error) {
- let s = CString::new(e.to_string()).unwrap();
- raw::git_error_set_str(raw::GIT_ERROR_NET as c_int, s.as_ptr());
-}
-
-unsafe fn set_err(e: &Error) {
- let s = CString::new(e.message()).unwrap();
- raw::git_error_set_str(e.raw_class() as c_int, s.as_ptr());
-}
-
-// callback used by smart transports to free a `SmartSubtransportStream`
-// object.
-extern "C" fn stream_free(stream: *mut raw::git_smart_subtransport_stream) {
- let _ = panic::wrap(|| unsafe {
- mem::transmute::<_, Box<RawSmartSubtransportStream>>(stream);
- });
-}
-
-#[cfg(test)]
-mod tests {
- use super::*;
- use crate::{ErrorClass, ErrorCode};
- use std::sync::Once;
-
- struct DummyTransport;
-
- // in lieu of lazy_static
- fn dummy_error() -> Error {
- Error::new(ErrorCode::Ambiguous, ErrorClass::Net, "bleh")
- }
-
- impl SmartSubtransport for DummyTransport {
- fn action(
- &self,
- _url: &str,
- _service: Service,
- ) -> Result<Box<dyn SmartSubtransportStream>, Error> {
- Err(dummy_error())
- }
-
- fn close(&self) -> Result<(), Error> {
- Ok(())
- }
- }
-
- #[test]
- fn transport_error_propagates() {
- static INIT: Once = Once::new();
-
- unsafe {
- INIT.call_once(|| {
- register("dummy", move |remote| {
- Transport::smart(&remote, true, DummyTransport)
- })
- .unwrap();
- })
- }
-
- let (_td, repo) = crate::test::repo_init();
- t!(repo.remote("origin", "dummy://ball"));
-
- let mut origin = t!(repo.find_remote("origin"));
-
- match origin.fetch(&["main"], None, None) {
- Ok(()) => unreachable!(),
- Err(e) => assert_eq!(e, dummy_error()),
- }
- }
-}
diff --git a/extra/git2/src/tree.rs b/extra/git2/src/tree.rs
deleted file mode 100644
index 9a38244cf..000000000
--- a/extra/git2/src/tree.rs
+++ /dev/null
@@ -1,570 +0,0 @@
-use libc::{self, c_char, c_int, c_void};
-use std::cmp::Ordering;
-use std::ffi::{CStr, CString};
-use std::iter::FusedIterator;
-use std::marker;
-use std::mem;
-use std::ops::Range;
-use std::path::Path;
-use std::ptr;
-use std::str;
-
-use crate::util::{c_cmp_to_ordering, path_to_repo_path, Binding};
-use crate::{panic, raw, Error, Object, ObjectType, Oid, Repository};
-
-/// A structure to represent a git [tree][1]
-///
-/// [1]: http://git-scm.com/book/en/Git-Internals-Git-Objects
-pub struct Tree<'repo> {
- raw: *mut raw::git_tree,
- _marker: marker::PhantomData<Object<'repo>>,
-}
-
-/// A structure representing an entry inside of a tree. An entry is borrowed
-/// from a tree.
-pub struct TreeEntry<'tree> {
- raw: *mut raw::git_tree_entry,
- owned: bool,
- _marker: marker::PhantomData<&'tree raw::git_tree_entry>,
-}
-
-/// An iterator over the entries in a tree.
-pub struct TreeIter<'tree> {
- range: Range<usize>,
- tree: &'tree Tree<'tree>,
-}
-
-/// A binary indicator of whether a tree walk should be performed in pre-order
-/// or post-order.
-pub enum TreeWalkMode {
- /// Runs the traversal in pre-order.
- PreOrder = 0,
- /// Runs the traversal in post-order.
- PostOrder = 1,
-}
-
-/// Possible return codes for tree walking callback functions.
-#[repr(i32)]
-pub enum TreeWalkResult {
- /// Continue with the traversal as normal.
- Ok = 0,
- /// Skip the current node (in pre-order mode).
- Skip = 1,
- /// Completely stop the traversal.
- Abort = raw::GIT_EUSER,
-}
-
-impl Into<i32> for TreeWalkResult {
- fn into(self) -> i32 {
- self as i32
- }
-}
-
-impl Into<raw::git_treewalk_mode> for TreeWalkMode {
- #[cfg(target_env = "msvc")]
- fn into(self) -> raw::git_treewalk_mode {
- self as i32
- }
- #[cfg(not(target_env = "msvc"))]
- fn into(self) -> raw::git_treewalk_mode {
- self as u32
- }
-}
-
-impl<'repo> Tree<'repo> {
- /// Get the id (SHA1) of a repository object
- pub fn id(&self) -> Oid {
- unsafe { Binding::from_raw(raw::git_tree_id(&*self.raw)) }
- }
-
- /// Get the number of entries listed in this tree.
- pub fn len(&self) -> usize {
- unsafe { raw::git_tree_entrycount(&*self.raw) as usize }
- }
-
- /// Return `true` if there is not entry
- pub fn is_empty(&self) -> bool {
- self.len() == 0
- }
-
- /// Returns an iterator over the entries in this tree.
- pub fn iter(&self) -> TreeIter<'_> {
- TreeIter {
- range: 0..self.len(),
- tree: self,
- }
- }
-
- /// Traverse the entries in a tree and its subtrees in post or pre-order.
- /// The callback function will be run on each node of the tree that's
- /// walked. The return code of this function will determine how the walk
- /// continues.
- ///
- /// libgit2 requires that the callback be an integer, where 0 indicates a
- /// successful visit, 1 skips the node, and -1 aborts the traversal completely.
- /// You may opt to use the enum [`TreeWalkResult`](TreeWalkResult) instead.
- ///
- /// ```ignore
- /// let mut ct = 0;
- /// tree.walk(TreeWalkMode::PreOrder, |_, entry| {
- /// assert_eq!(entry.name(), Some("foo"));
- /// ct += 1;
- /// TreeWalkResult::Ok
- /// }).unwrap();
- /// assert_eq!(ct, 1);
- /// ```
- ///
- /// See [libgit2 documentation][1] for more information.
- ///
- /// [1]: https://libgit2.org/libgit2/#HEAD/group/tree/git_tree_walk
- pub fn walk<C, T>(&self, mode: TreeWalkMode, mut callback: C) -> Result<(), Error>
- where
- C: FnMut(&str, &TreeEntry<'_>) -> T,
- T: Into<i32>,
- {
- unsafe {
- let mut data = TreeWalkCbData {
- callback: &mut callback,
- };
- raw::git_tree_walk(
- self.raw(),
- mode.into(),
- Some(treewalk_cb::<T>),
- &mut data as *mut _ as *mut c_void,
- );
- Ok(())
- }
- }
-
- /// Lookup a tree entry by SHA value.
- pub fn get_id(&self, id: Oid) -> Option<TreeEntry<'_>> {
- unsafe {
- let ptr = raw::git_tree_entry_byid(&*self.raw(), &*id.raw());
- if ptr.is_null() {
- None
- } else {
- Some(entry_from_raw_const(ptr))
- }
- }
- }
-
- /// Lookup a tree entry by its position in the tree
- pub fn get(&self, n: usize) -> Option<TreeEntry<'_>> {
- unsafe {
- let ptr = raw::git_tree_entry_byindex(&*self.raw(), n as libc::size_t);
- if ptr.is_null() {
- None
- } else {
- Some(entry_from_raw_const(ptr))
- }
- }
- }
-
- /// Lookup a tree entry by its filename
- pub fn get_name(&self, filename: &str) -> Option<TreeEntry<'_>> {
- self.get_name_bytes(filename.as_bytes())
- }
-
- /// Lookup a tree entry by its filename, specified as bytes.
- ///
- /// This allows for non-UTF-8 filenames.
- pub fn get_name_bytes(&self, filename: &[u8]) -> Option<TreeEntry<'_>> {
- let filename = CString::new(filename).unwrap();
- unsafe {
- let ptr = call!(raw::git_tree_entry_byname(&*self.raw(), filename));
- if ptr.is_null() {
- None
- } else {
- Some(entry_from_raw_const(ptr))
- }
- }
- }
-
- /// Retrieve a tree entry contained in a tree or in any of its subtrees,
- /// given its relative path.
- pub fn get_path(&self, path: &Path) -> Result<TreeEntry<'static>, Error> {
- let path = path_to_repo_path(path)?;
- let mut ret = ptr::null_mut();
- unsafe {
- try_call!(raw::git_tree_entry_bypath(&mut ret, &*self.raw(), path));
- Ok(Binding::from_raw(ret))
- }
- }
-
- /// Casts this Tree to be usable as an `Object`
- pub fn as_object(&self) -> &Object<'repo> {
- unsafe { &*(self as *const _ as *const Object<'repo>) }
- }
-
- /// Consumes this Tree to be returned as an `Object`
- pub fn into_object(self) -> Object<'repo> {
- assert_eq!(mem::size_of_val(&self), mem::size_of::<Object<'_>>());
- unsafe { mem::transmute(self) }
- }
-}
-
-type TreeWalkCb<'a, T> = dyn FnMut(&str, &TreeEntry<'_>) -> T + 'a;
-
-struct TreeWalkCbData<'a, T> {
- callback: &'a mut TreeWalkCb<'a, T>,
-}
-
-extern "C" fn treewalk_cb<T: Into<i32>>(
- root: *const c_char,
- entry: *const raw::git_tree_entry,
- payload: *mut c_void,
-) -> c_int {
- match panic::wrap(|| unsafe {
- let root = match CStr::from_ptr(root).to_str() {
- Ok(value) => value,
- _ => return -1,
- };
- let entry = entry_from_raw_const(entry);
- let payload = &mut *(payload as *mut TreeWalkCbData<'_, T>);
- let callback = &mut payload.callback;
- callback(root, &entry).into()
- }) {
- Some(value) => value,
- None => -1,
- }
-}
-
-impl<'repo> Binding for Tree<'repo> {
- type Raw = *mut raw::git_tree;
-
- unsafe fn from_raw(raw: *mut raw::git_tree) -> Tree<'repo> {
- Tree {
- raw,
- _marker: marker::PhantomData,
- }
- }
- fn raw(&self) -> *mut raw::git_tree {
- self.raw
- }
-}
-
-impl<'repo> std::fmt::Debug for Tree<'repo> {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
- f.debug_struct("Tree").field("id", &self.id()).finish()
- }
-}
-
-impl<'repo> Clone for Tree<'repo> {
- fn clone(&self) -> Self {
- self.as_object().clone().into_tree().ok().unwrap()
- }
-}
-
-impl<'repo> Drop for Tree<'repo> {
- fn drop(&mut self) {
- unsafe { raw::git_tree_free(self.raw) }
- }
-}
-
-impl<'repo, 'iter> IntoIterator for &'iter Tree<'repo> {
- type Item = TreeEntry<'iter>;
- type IntoIter = TreeIter<'iter>;
- fn into_iter(self) -> Self::IntoIter {
- self.iter()
- }
-}
-
-/// Create a new tree entry from the raw pointer provided.
-///
-/// The lifetime of the entry is tied to the tree provided and the function
-/// is unsafe because the validity of the pointer cannot be guaranteed.
-pub unsafe fn entry_from_raw_const<'tree>(raw: *const raw::git_tree_entry) -> TreeEntry<'tree> {
- TreeEntry {
- raw: raw as *mut raw::git_tree_entry,
- owned: false,
- _marker: marker::PhantomData,
- }
-}
-
-impl<'tree> TreeEntry<'tree> {
- /// Get the id of the object pointed by the entry
- pub fn id(&self) -> Oid {
- unsafe { Binding::from_raw(raw::git_tree_entry_id(&*self.raw)) }
- }
-
- /// Get the filename of a tree entry
- ///
- /// Returns `None` if the name is not valid utf-8
- pub fn name(&self) -> Option<&str> {
- str::from_utf8(self.name_bytes()).ok()
- }
-
- /// Get the filename of a tree entry
- pub fn name_bytes(&self) -> &[u8] {
- unsafe { crate::opt_bytes(self, raw::git_tree_entry_name(&*self.raw())).unwrap() }
- }
-
- /// Convert a tree entry to the object it points to.
- pub fn to_object<'a>(&self, repo: &'a Repository) -> Result<Object<'a>, Error> {
- let mut ret = ptr::null_mut();
- unsafe {
- try_call!(raw::git_tree_entry_to_object(
- &mut ret,
- repo.raw(),
- &*self.raw()
- ));
- Ok(Binding::from_raw(ret))
- }
- }
-
- /// Get the type of the object pointed by the entry
- pub fn kind(&self) -> Option<ObjectType> {
- ObjectType::from_raw(unsafe { raw::git_tree_entry_type(&*self.raw) })
- }
-
- /// Get the UNIX file attributes of a tree entry
- pub fn filemode(&self) -> i32 {
- unsafe { raw::git_tree_entry_filemode(&*self.raw) as i32 }
- }
-
- /// Get the raw UNIX file attributes of a tree entry
- pub fn filemode_raw(&self) -> i32 {
- unsafe { raw::git_tree_entry_filemode_raw(&*self.raw) as i32 }
- }
-
- /// Convert this entry of any lifetime into an owned signature with a static
- /// lifetime.
- ///
- /// This will use the `Clone::clone` implementation under the hood.
- pub fn to_owned(&self) -> TreeEntry<'static> {
- unsafe {
- let me = mem::transmute::<&TreeEntry<'tree>, &TreeEntry<'static>>(self);
- me.clone()
- }
- }
-}
-
-impl<'a> Binding for TreeEntry<'a> {
- type Raw = *mut raw::git_tree_entry;
- unsafe fn from_raw(raw: *mut raw::git_tree_entry) -> TreeEntry<'a> {
- TreeEntry {
- raw,
- owned: true,
- _marker: marker::PhantomData,
- }
- }
- fn raw(&self) -> *mut raw::git_tree_entry {
- self.raw
- }
-}
-
-impl<'a> Clone for TreeEntry<'a> {
- fn clone(&self) -> TreeEntry<'a> {
- let mut ret = ptr::null_mut();
- unsafe {
- assert_eq!(raw::git_tree_entry_dup(&mut ret, &*self.raw()), 0);
- Binding::from_raw(ret)
- }
- }
-}
-
-impl<'a> PartialOrd for TreeEntry<'a> {
- fn partial_cmp(&self, other: &TreeEntry<'a>) -> Option<Ordering> {
- Some(self.cmp(other))
- }
-}
-impl<'a> Ord for TreeEntry<'a> {
- fn cmp(&self, other: &TreeEntry<'a>) -> Ordering {
- c_cmp_to_ordering(unsafe { raw::git_tree_entry_cmp(&*self.raw(), &*other.raw()) })
- }
-}
-
-impl<'a> PartialEq for TreeEntry<'a> {
- fn eq(&self, other: &TreeEntry<'a>) -> bool {
- self.cmp(other) == Ordering::Equal
- }
-}
-impl<'a> Eq for TreeEntry<'a> {}
-
-impl<'a> Drop for TreeEntry<'a> {
- fn drop(&mut self) {
- if self.owned {
- unsafe { raw::git_tree_entry_free(self.raw) }
- }
- }
-}
-
-impl<'tree> Iterator for TreeIter<'tree> {
- type Item = TreeEntry<'tree>;
- fn next(&mut self) -> Option<TreeEntry<'tree>> {
- self.range.next().and_then(|i| self.tree.get(i))
- }
- fn size_hint(&self) -> (usize, Option<usize>) {
- self.range.size_hint()
- }
-}
-impl<'tree> DoubleEndedIterator for TreeIter<'tree> {
- fn next_back(&mut self) -> Option<TreeEntry<'tree>> {
- self.range.next_back().and_then(|i| self.tree.get(i))
- }
-}
-impl<'tree> FusedIterator for TreeIter<'tree> {}
-impl<'tree> ExactSizeIterator for TreeIter<'tree> {}
-
-#[cfg(test)]
-mod tests {
- use super::{TreeWalkMode, TreeWalkResult};
- use crate::{Object, ObjectType, Repository, Tree, TreeEntry};
- use std::fs::File;
- use std::io::prelude::*;
- use std::path::Path;
- use tempfile::TempDir;
-
- pub struct TestTreeIter<'a> {
- entries: Vec<TreeEntry<'a>>,
- repo: &'a Repository,
- }
-
- impl<'a> Iterator for TestTreeIter<'a> {
- type Item = TreeEntry<'a>;
-
- fn next(&mut self) -> Option<TreeEntry<'a>> {
- if self.entries.is_empty() {
- None
- } else {
- let entry = self.entries.remove(0);
-
- match entry.kind() {
- Some(ObjectType::Tree) => {
- let obj: Object<'a> = entry.to_object(self.repo).unwrap();
-
- let tree: &Tree<'a> = obj.as_tree().unwrap();
-
- for entry in tree.iter() {
- self.entries.push(entry.to_owned());
- }
- }
- _ => {}
- }
-
- Some(entry)
- }
- }
- }
-
- fn tree_iter<'repo>(tree: &Tree<'repo>, repo: &'repo Repository) -> TestTreeIter<'repo> {
- let mut initial = vec![];
-
- for entry in tree.iter() {
- initial.push(entry.to_owned());
- }
-
- TestTreeIter {
- entries: initial,
- repo: repo,
- }
- }
-
- #[test]
- fn smoke_tree_iter() {
- let (td, repo) = crate::test::repo_init();
-
- setup_repo(&td, &repo);
-
- let head = repo.head().unwrap();
- let target = head.target().unwrap();
- let commit = repo.find_commit(target).unwrap();
-
- let tree = repo.find_tree(commit.tree_id()).unwrap();
- assert_eq!(tree.id(), commit.tree_id());
- assert_eq!(tree.len(), 1);
-
- for entry in tree_iter(&tree, &repo) {
- println!("iter entry {:?}", entry.name());
- }
- }
-
- fn setup_repo(td: &TempDir, repo: &Repository) {
- let mut index = repo.index().unwrap();
- File::create(&td.path().join("foo"))
- .unwrap()
- .write_all(b"foo")
- .unwrap();
- index.add_path(Path::new("foo")).unwrap();
- let id = index.write_tree().unwrap();
- let sig = repo.signature().unwrap();
- let tree = repo.find_tree(id).unwrap();
- let parent = repo
- .find_commit(repo.head().unwrap().target().unwrap())
- .unwrap();
- repo.commit(
- Some("HEAD"),
- &sig,
- &sig,
- "another commit",
- &tree,
- &[&parent],
- )
- .unwrap();
- }
-
- #[test]
- fn smoke() {
- let (td, repo) = crate::test::repo_init();
-
- setup_repo(&td, &repo);
-
- let head = repo.head().unwrap();
- let target = head.target().unwrap();
- let commit = repo.find_commit(target).unwrap();
-
- let tree = repo.find_tree(commit.tree_id()).unwrap();
- assert_eq!(tree.id(), commit.tree_id());
- assert_eq!(tree.len(), 1);
- {
- let e1 = tree.get(0).unwrap();
- assert!(e1 == tree.get_id(e1.id()).unwrap());
- assert!(e1 == tree.get_name("foo").unwrap());
- assert!(e1 == tree.get_name_bytes(b"foo").unwrap());
- assert!(e1 == tree.get_path(Path::new("foo")).unwrap());
- assert_eq!(e1.name(), Some("foo"));
- e1.to_object(&repo).unwrap();
- }
- tree.into_object();
-
- repo.find_object(commit.tree_id(), None)
- .unwrap()
- .as_tree()
- .unwrap();
- repo.find_object(commit.tree_id(), None)
- .unwrap()
- .into_tree()
- .ok()
- .unwrap();
- }
-
- #[test]
- fn tree_walk() {
- let (td, repo) = crate::test::repo_init();
-
- setup_repo(&td, &repo);
-
- let head = repo.head().unwrap();
- let target = head.target().unwrap();
- let commit = repo.find_commit(target).unwrap();
- let tree = repo.find_tree(commit.tree_id()).unwrap();
-
- let mut ct = 0;
- tree.walk(TreeWalkMode::PreOrder, |_, entry| {
- assert_eq!(entry.name(), Some("foo"));
- ct += 1;
- 0
- })
- .unwrap();
- assert_eq!(ct, 1);
-
- let mut ct = 0;
- tree.walk(TreeWalkMode::PreOrder, |_, entry| {
- assert_eq!(entry.name(), Some("foo"));
- ct += 1;
- TreeWalkResult::Ok
- })
- .unwrap();
- assert_eq!(ct, 1);
- }
-}
diff --git a/extra/git2/src/treebuilder.rs b/extra/git2/src/treebuilder.rs
deleted file mode 100644
index 1548a048c..000000000
--- a/extra/git2/src/treebuilder.rs
+++ /dev/null
@@ -1,234 +0,0 @@
-use std::marker;
-use std::ptr;
-
-use libc::{c_int, c_void};
-
-use crate::util::{Binding, IntoCString};
-use crate::{panic, raw, tree, Error, Oid, Repository, TreeEntry};
-
-/// Constructor for in-memory trees (low-level)
-///
-/// You probably want to use [`build::TreeUpdateBuilder`] instead.
-///
-/// This is the more raw of the two tree update facilities. It
-/// handles only one level of a nested tree structure at a time. Each
-/// path passed to `insert` etc. must be a single component.
-///
-/// [`build::TreeUpdateBuilder`]: crate::build::TreeUpdateBuilder
-pub struct TreeBuilder<'repo> {
- raw: *mut raw::git_treebuilder,
- _marker: marker::PhantomData<&'repo Repository>,
-}
-
-impl<'repo> TreeBuilder<'repo> {
- /// Clear all the entries in the builder
- pub fn clear(&mut self) -> Result<(), Error> {
- unsafe {
- try_call!(raw::git_treebuilder_clear(self.raw));
- }
- Ok(())
- }
-
- /// Get the number of entries
- pub fn len(&self) -> usize {
- unsafe { raw::git_treebuilder_entrycount(self.raw) as usize }
- }
-
- /// Return `true` if there is no entry
- pub fn is_empty(&self) -> bool {
- self.len() == 0
- }
-
- /// Get en entry from the builder from its filename
- pub fn get<P>(&self, filename: P) -> Result<Option<TreeEntry<'_>>, Error>
- where
- P: IntoCString,
- {
- let filename = filename.into_c_string()?;
- unsafe {
- let ret = raw::git_treebuilder_get(self.raw, filename.as_ptr());
- if ret.is_null() {
- Ok(None)
- } else {
- Ok(Some(tree::entry_from_raw_const(ret)))
- }
- }
- }
-
- /// Add or update an entry in the builder
- ///
- /// No attempt is made to ensure that the provided Oid points to
- /// an object of a reasonable type (or any object at all).
- ///
- /// The mode given must be one of 0o040000, 0o100644, 0o100755, 0o120000 or
- /// 0o160000 currently.
- pub fn insert<P: IntoCString>(
- &mut self,
- filename: P,
- oid: Oid,
- filemode: i32,
- ) -> Result<TreeEntry<'_>, Error> {
- let filename = filename.into_c_string()?;
- let filemode = filemode as raw::git_filemode_t;
-
- let mut ret = ptr::null();
- unsafe {
- try_call!(raw::git_treebuilder_insert(
- &mut ret,
- self.raw,
- filename,
- oid.raw(),
- filemode
- ));
- Ok(tree::entry_from_raw_const(ret))
- }
- }
-
- /// Remove an entry from the builder by its filename
- pub fn remove<P: IntoCString>(&mut self, filename: P) -> Result<(), Error> {
- let filename = filename.into_c_string()?;
- unsafe {
- try_call!(raw::git_treebuilder_remove(self.raw, filename));
- }
- Ok(())
- }
-
- /// Selectively remove entries from the tree
- ///
- /// Values for which the filter returns `true` will be kept. Note
- /// that this behavior is different from the libgit2 C interface.
- pub fn filter<F>(&mut self, mut filter: F) -> Result<(), Error>
- where
- F: FnMut(&TreeEntry<'_>) -> bool,
- {
- let mut cb: &mut FilterCb<'_> = &mut filter;
- let ptr = &mut cb as *mut _;
- let cb: raw::git_treebuilder_filter_cb = Some(filter_cb);
- unsafe {
- try_call!(raw::git_treebuilder_filter(self.raw, cb, ptr as *mut _));
- panic::check();
- }
- Ok(())
- }
-
- /// Write the contents of the TreeBuilder as a Tree object and
- /// return its Oid
- pub fn write(&self) -> Result<Oid, Error> {
- let mut raw = raw::git_oid {
- id: [0; raw::GIT_OID_RAWSZ],
- };
- unsafe {
- try_call!(raw::git_treebuilder_write(&mut raw, self.raw()));
- Ok(Binding::from_raw(&raw as *const _))
- }
- }
-}
-
-type FilterCb<'a> = dyn FnMut(&TreeEntry<'_>) -> bool + 'a;
-
-extern "C" fn filter_cb(entry: *const raw::git_tree_entry, payload: *mut c_void) -> c_int {
- let ret = panic::wrap(|| unsafe {
- // There's no way to return early from git_treebuilder_filter.
- if panic::panicked() {
- true
- } else {
- let entry = tree::entry_from_raw_const(entry);
- let payload = payload as *mut &mut FilterCb<'_>;
- (*payload)(&entry)
- }
- });
- if ret == Some(false) {
- 1
- } else {
- 0
- }
-}
-
-impl<'repo> Binding for TreeBuilder<'repo> {
- type Raw = *mut raw::git_treebuilder;
-
- unsafe fn from_raw(raw: *mut raw::git_treebuilder) -> TreeBuilder<'repo> {
- TreeBuilder {
- raw,
- _marker: marker::PhantomData,
- }
- }
- fn raw(&self) -> *mut raw::git_treebuilder {
- self.raw
- }
-}
-
-impl<'repo> Drop for TreeBuilder<'repo> {
- fn drop(&mut self) {
- unsafe { raw::git_treebuilder_free(self.raw) }
- }
-}
-
-#[cfg(test)]
-mod tests {
- use crate::ObjectType;
-
- #[test]
- fn smoke() {
- let (_td, repo) = crate::test::repo_init();
-
- let mut builder = repo.treebuilder(None).unwrap();
- assert_eq!(builder.len(), 0);
- let blob = repo.blob(b"data").unwrap();
- {
- let entry = builder.insert("a", blob, 0o100644).unwrap();
- assert_eq!(entry.kind(), Some(ObjectType::Blob));
- }
- builder.insert("b", blob, 0o100644).unwrap();
- assert_eq!(builder.len(), 2);
- builder.remove("a").unwrap();
- assert_eq!(builder.len(), 1);
- assert_eq!(builder.get("b").unwrap().unwrap().id(), blob);
- builder.clear().unwrap();
- assert_eq!(builder.len(), 0);
- }
-
- #[test]
- fn write() {
- let (_td, repo) = crate::test::repo_init();
-
- let mut builder = repo.treebuilder(None).unwrap();
- let data = repo.blob(b"data").unwrap();
- builder.insert("name", data, 0o100644).unwrap();
- let tree = builder.write().unwrap();
- let tree = repo.find_tree(tree).unwrap();
- let entry = tree.get(0).unwrap();
- assert_eq!(entry.name(), Some("name"));
- let blob = entry.to_object(&repo).unwrap();
- let blob = blob.as_blob().unwrap();
- assert_eq!(blob.content(), b"data");
-
- let builder = repo.treebuilder(Some(&tree)).unwrap();
- assert_eq!(builder.len(), 1);
- }
-
- #[test]
- fn filter() {
- let (_td, repo) = crate::test::repo_init();
-
- let mut builder = repo.treebuilder(None).unwrap();
- let blob = repo.blob(b"data").unwrap();
- let tree = {
- let head = repo.head().unwrap().peel(ObjectType::Commit).unwrap();
- let head = head.as_commit().unwrap();
- head.tree_id()
- };
- builder.insert("blob", blob, 0o100644).unwrap();
- builder.insert("dir", tree, 0o040000).unwrap();
- builder.insert("dir2", tree, 0o040000).unwrap();
-
- builder.filter(|_| true).unwrap();
- assert_eq!(builder.len(), 3);
- builder
- .filter(|e| e.kind().unwrap() != ObjectType::Blob)
- .unwrap();
- assert_eq!(builder.len(), 2);
- builder.filter(|_| false).unwrap();
- assert_eq!(builder.len(), 0);
- }
-}
diff --git a/extra/git2/src/util.rs b/extra/git2/src/util.rs
deleted file mode 100644
index 5f735bc00..000000000
--- a/extra/git2/src/util.rs
+++ /dev/null
@@ -1,342 +0,0 @@
-use libc::{c_char, c_int, size_t};
-use std::cmp::Ordering;
-use std::ffi::{CString, OsStr, OsString};
-use std::iter::IntoIterator;
-use std::path::{Component, Path, PathBuf};
-
-use crate::{raw, Error};
-
-#[doc(hidden)]
-pub trait IsNull {
- fn is_ptr_null(&self) -> bool;
-}
-impl<T> IsNull for *const T {
- fn is_ptr_null(&self) -> bool {
- self.is_null()
- }
-}
-impl<T> IsNull for *mut T {
- fn is_ptr_null(&self) -> bool {
- self.is_null()
- }
-}
-
-#[doc(hidden)]
-pub trait Binding: Sized {
- type Raw;
-
- unsafe fn from_raw(raw: Self::Raw) -> Self;
- fn raw(&self) -> Self::Raw;
-
- unsafe fn from_raw_opt<T>(raw: T) -> Option<Self>
- where
- T: Copy + IsNull,
- Self: Binding<Raw = T>,
- {
- if raw.is_ptr_null() {
- None
- } else {
- Some(Binding::from_raw(raw))
- }
- }
-}
-
-/// Converts an iterator of repo paths into a git2-compatible array of cstrings.
-///
-/// Only use this for repo-relative paths or pathspecs.
-///
-/// See `iter2cstrs` for more details.
-pub fn iter2cstrs_paths<T, I>(
- iter: I,
-) -> Result<(Vec<CString>, Vec<*const c_char>, raw::git_strarray), Error>
-where
- T: IntoCString,
- I: IntoIterator<Item = T>,
-{
- let cstrs = iter
- .into_iter()
- .map(|i| fixup_windows_path(i.into_c_string()?))
- .collect::<Result<Vec<CString>, _>>()?;
- iter2cstrs(cstrs)
-}
-
-/// Converts an iterator of things into a git array of c-strings.
-///
-/// Returns a tuple `(cstrings, pointers, git_strarray)`. The first two values
-/// should not be dropped before `git_strarray`.
-pub fn iter2cstrs<T, I>(
- iter: I,
-) -> Result<(Vec<CString>, Vec<*const c_char>, raw::git_strarray), Error>
-where
- T: IntoCString,
- I: IntoIterator<Item = T>,
-{
- let cstrs = iter
- .into_iter()
- .map(|i| i.into_c_string())
- .collect::<Result<Vec<CString>, _>>()?;
- let ptrs = cstrs.iter().map(|i| i.as_ptr()).collect::<Vec<_>>();
- let raw = raw::git_strarray {
- strings: ptrs.as_ptr() as *mut _,
- count: ptrs.len() as size_t,
- };
- Ok((cstrs, ptrs, raw))
-}
-
-#[cfg(unix)]
-pub fn bytes2path(b: &[u8]) -> &Path {
- use std::os::unix::prelude::*;
- Path::new(OsStr::from_bytes(b))
-}
-#[cfg(windows)]
-pub fn bytes2path(b: &[u8]) -> &Path {
- use std::str;
- Path::new(str::from_utf8(b).unwrap())
-}
-
-/// A class of types that can be converted to C strings.
-///
-/// These types are represented internally as byte slices and it is quite rare
-/// for them to contain an interior 0 byte.
-pub trait IntoCString {
- /// Consume this container, converting it into a CString
- fn into_c_string(self) -> Result<CString, Error>;
-}
-
-impl<'a, T: IntoCString + Clone> IntoCString for &'a T {
- fn into_c_string(self) -> Result<CString, Error> {
- self.clone().into_c_string()
- }
-}
-
-impl<'a> IntoCString for &'a str {
- fn into_c_string(self) -> Result<CString, Error> {
- Ok(CString::new(self)?)
- }
-}
-
-impl IntoCString for String {
- fn into_c_string(self) -> Result<CString, Error> {
- Ok(CString::new(self.into_bytes())?)
- }
-}
-
-impl IntoCString for CString {
- fn into_c_string(self) -> Result<CString, Error> {
- Ok(self)
- }
-}
-
-impl<'a> IntoCString for &'a Path {
- fn into_c_string(self) -> Result<CString, Error> {
- let s: &OsStr = self.as_ref();
- s.into_c_string()
- }
-}
-
-impl IntoCString for PathBuf {
- fn into_c_string(self) -> Result<CString, Error> {
- let s: OsString = self.into();
- s.into_c_string()
- }
-}
-
-impl<'a> IntoCString for &'a OsStr {
- fn into_c_string(self) -> Result<CString, Error> {
- self.to_os_string().into_c_string()
- }
-}
-
-impl IntoCString for OsString {
- #[cfg(unix)]
- fn into_c_string(self) -> Result<CString, Error> {
- use std::os::unix::prelude::*;
- let s: &OsStr = self.as_ref();
- Ok(CString::new(s.as_bytes())?)
- }
- #[cfg(windows)]
- fn into_c_string(self) -> Result<CString, Error> {
- match self.to_str() {
- Some(s) => s.into_c_string(),
- None => Err(Error::from_str(
- "only valid unicode paths are accepted on windows",
- )),
- }
- }
-}
-
-impl<'a> IntoCString for &'a [u8] {
- fn into_c_string(self) -> Result<CString, Error> {
- Ok(CString::new(self)?)
- }
-}
-
-impl IntoCString for Vec<u8> {
- fn into_c_string(self) -> Result<CString, Error> {
- Ok(CString::new(self)?)
- }
-}
-
-pub fn into_opt_c_string<S>(opt_s: Option<S>) -> Result<Option<CString>, Error>
-where
- S: IntoCString,
-{
- match opt_s {
- None => Ok(None),
- Some(s) => Ok(Some(s.into_c_string()?)),
- }
-}
-
-pub fn c_cmp_to_ordering(cmp: c_int) -> Ordering {
- match cmp {
- 0 => Ordering::Equal,
- n if n < 0 => Ordering::Less,
- _ => Ordering::Greater,
- }
-}
-
-/// Converts a path to a CString that is usable by the libgit2 API.
-///
-/// Checks if it is a relative path.
-///
-/// On Windows, this also requires the path to be valid Unicode, and translates
-/// back slashes to forward slashes.
-pub fn path_to_repo_path(path: &Path) -> Result<CString, Error> {
- macro_rules! err {
- ($msg:literal, $path:expr) => {
- return Err(Error::from_str(&format!($msg, $path.display())))
- };
- }
- match path.components().next() {
- None => return Err(Error::from_str("repo path should not be empty")),
- Some(Component::Prefix(_)) => err!(
- "repo path `{}` should be relative, not a windows prefix",
- path
- ),
- Some(Component::RootDir) => err!("repo path `{}` should be relative", path),
- Some(Component::CurDir) => err!("repo path `{}` should not start with `.`", path),
- Some(Component::ParentDir) => err!("repo path `{}` should not start with `..`", path),
- Some(Component::Normal(_)) => {}
- }
- #[cfg(windows)]
- {
- match path.to_str() {
- None => {
- return Err(Error::from_str(
- "only valid unicode paths are accepted on windows",
- ))
- }
- Some(s) => return fixup_windows_path(s),
- }
- }
- #[cfg(not(windows))]
- {
- path.into_c_string()
- }
-}
-
-pub fn cstring_to_repo_path<T: IntoCString>(path: T) -> Result<CString, Error> {
- fixup_windows_path(path.into_c_string()?)
-}
-
-#[cfg(windows)]
-fn fixup_windows_path<P: Into<Vec<u8>>>(path: P) -> Result<CString, Error> {
- let mut bytes: Vec<u8> = path.into();
- for i in 0..bytes.len() {
- if bytes[i] == b'\\' {
- bytes[i] = b'/';
- }
- }
- Ok(CString::new(bytes)?)
-}
-
-#[cfg(not(windows))]
-fn fixup_windows_path(path: CString) -> Result<CString, Error> {
- Ok(path)
-}
-
-#[cfg(test)]
-mod tests {
- use super::*;
-
- macro_rules! assert_err {
- ($path:expr, $msg:expr) => {
- match path_to_repo_path(Path::new($path)) {
- Ok(_) => panic!("expected `{}` to err", $path),
- Err(e) => assert_eq!(e.message(), $msg),
- }
- };
- }
-
- macro_rules! assert_repo_path_ok {
- ($path:expr) => {
- assert_repo_path_ok!($path, $path)
- };
- ($path:expr, $expect:expr) => {
- assert_eq!(
- path_to_repo_path(Path::new($path)),
- Ok(CString::new($expect).unwrap())
- );
- };
- }
-
- #[test]
- #[cfg(windows)]
- fn path_to_repo_path_translate() {
- assert_repo_path_ok!("foo");
- assert_repo_path_ok!("foo/bar");
- assert_repo_path_ok!(r"foo\bar", "foo/bar");
- assert_repo_path_ok!(r"foo\bar\", "foo/bar/");
- }
-
- #[test]
- fn path_to_repo_path_no_weird() {
- assert_err!("", "repo path should not be empty");
- assert_err!("./foo", "repo path `./foo` should not start with `.`");
- assert_err!("../foo", "repo path `../foo` should not start with `..`");
- }
-
- #[test]
- #[cfg(not(windows))]
- fn path_to_repo_path_no_absolute() {
- assert_err!("/", "repo path `/` should be relative");
- assert_repo_path_ok!("foo/bar");
- }
-
- #[test]
- #[cfg(windows)]
- fn path_to_repo_path_no_absolute() {
- assert_err!(
- r"c:",
- r"repo path `c:` should be relative, not a windows prefix"
- );
- assert_err!(
- r"c:\",
- r"repo path `c:\` should be relative, not a windows prefix"
- );
- assert_err!(
- r"c:temp",
- r"repo path `c:temp` should be relative, not a windows prefix"
- );
- assert_err!(
- r"\\?\UNC\a\b\c",
- r"repo path `\\?\UNC\a\b\c` should be relative, not a windows prefix"
- );
- assert_err!(
- r"\\?\c:\foo",
- r"repo path `\\?\c:\foo` should be relative, not a windows prefix"
- );
- assert_err!(
- r"\\.\COM42",
- r"repo path `\\.\COM42` should be relative, not a windows prefix"
- );
- assert_err!(
- r"\\a\b",
- r"repo path `\\a\b` should be relative, not a windows prefix"
- );
- assert_err!(r"\", r"repo path `\` should be relative");
- assert_err!(r"/", r"repo path `/` should be relative");
- assert_err!(r"\foo", r"repo path `\foo` should be relative");
- assert_err!(r"/foo", r"repo path `/foo` should be relative");
- }
-}
diff --git a/extra/git2/src/version.rs b/extra/git2/src/version.rs
deleted file mode 100644
index b5dd4fb12..000000000
--- a/extra/git2/src/version.rs
+++ /dev/null
@@ -1,95 +0,0 @@
-use crate::raw;
-use libc::c_int;
-use std::fmt;
-
-/// Version information about libgit2 and the capabilities it supports.
-pub struct Version {
- major: c_int,
- minor: c_int,
- rev: c_int,
- features: c_int,
-}
-
-macro_rules! flag_test {
- ($features:expr, $flag:expr) => {
- ($features as u32 & $flag as u32) != 0
- };
-}
-
-impl Version {
- /// Returns a [`Version`] which provides information about libgit2.
- pub fn get() -> Version {
- let mut v = Version {
- major: 0,
- minor: 0,
- rev: 0,
- features: 0,
- };
- unsafe {
- raw::git_libgit2_version(&mut v.major, &mut v.minor, &mut v.rev);
- v.features = raw::git_libgit2_features();
- }
- v
- }
-
- /// Returns the version of libgit2.
- ///
- /// The return value is a tuple of `(major, minor, rev)`
- pub fn libgit2_version(&self) -> (u32, u32, u32) {
- (self.major as u32, self.minor as u32, self.rev as u32)
- }
-
- /// Returns the version of the libgit2-sys crate.
- pub fn crate_version(&self) -> &'static str {
- env!("CARGO_PKG_VERSION")
- }
-
- /// Returns true if this was built with the vendored version of libgit2.
- pub fn vendored(&self) -> bool {
- raw::vendored()
- }
-
- /// Returns true if libgit2 was built thread-aware and can be safely used
- /// from multiple threads.
- pub fn threads(&self) -> bool {
- flag_test!(self.features, raw::GIT_FEATURE_THREADS)
- }
-
- /// Returns true if libgit2 was built with and linked against a TLS implementation.
- ///
- /// Custom TLS streams may still be added by the user to support HTTPS
- /// regardless of this.
- pub fn https(&self) -> bool {
- flag_test!(self.features, raw::GIT_FEATURE_HTTPS)
- }
-
- /// Returns true if libgit2 was built with and linked against libssh2.
- ///
- /// A custom transport may still be added by the user to support libssh2
- /// regardless of this.
- pub fn ssh(&self) -> bool {
- flag_test!(self.features, raw::GIT_FEATURE_SSH)
- }
-
- /// Returns true if libgit2 was built with support for sub-second
- /// resolution in file modification times.
- pub fn nsec(&self) -> bool {
- flag_test!(self.features, raw::GIT_FEATURE_NSEC)
- }
-}
-
-impl fmt::Debug for Version {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
- let mut f = f.debug_struct("Version");
- f.field("major", &self.major)
- .field("minor", &self.minor)
- .field("rev", &self.rev)
- .field("crate_version", &self.crate_version())
- .field("vendored", &self.vendored())
- .field("threads", &self.threads())
- .field("https", &self.https())
- .field("ssh", &self.ssh())
- .field("nsec", &self.nsec());
- f.finish()
- }
-}
diff --git a/extra/git2/src/worktree.rs b/extra/git2/src/worktree.rs
deleted file mode 100644
index 569b639cf..000000000
--- a/extra/git2/src/worktree.rs
+++ /dev/null
@@ -1,331 +0,0 @@
-use crate::buf::Buf;
-use crate::reference::Reference;
-use crate::repo::Repository;
-use crate::util::{self, Binding};
-use crate::{raw, Error};
-use std::os::raw::c_int;
-use std::path::Path;
-use std::ptr;
-use std::str;
-use std::{marker, mem};
-
-/// An owned git worktree
-///
-/// This structure corresponds to a `git_worktree` in libgit2.
-//
-pub struct Worktree {
- raw: *mut raw::git_worktree,
-}
-
-/// Options which can be used to configure how a worktree is initialized
-pub struct WorktreeAddOptions<'a> {
- raw: raw::git_worktree_add_options,
- _marker: marker::PhantomData<Reference<'a>>,
-}
-
-/// Options to configure how worktree pruning is performed
-pub struct WorktreePruneOptions {
- raw: raw::git_worktree_prune_options,
-}
-
-/// Lock Status of a worktree
-#[derive(PartialEq, Debug)]
-pub enum WorktreeLockStatus {
- /// Worktree is Unlocked
- Unlocked,
- /// Worktree is locked with the optional message
- Locked(Option<String>),
-}
-
-impl Worktree {
- /// Open a worktree of a the repository
- ///
- /// If a repository is not the main tree but a worktree, this
- /// function will look up the worktree inside the parent
- /// repository and create a new `git_worktree` structure.
- pub fn open_from_repository(repo: &Repository) -> Result<Worktree, Error> {
- let mut raw = ptr::null_mut();
- unsafe {
- try_call!(raw::git_worktree_open_from_repository(&mut raw, repo.raw()));
- Ok(Binding::from_raw(raw))
- }
- }
-
- /// Retrieves the name of the worktree
- ///
- /// This is the name that can be passed to repo::Repository::find_worktree
- /// to reopen the worktree. This is also the name that would appear in the
- /// list returned by repo::Repository::worktrees
- pub fn name(&self) -> Option<&str> {
- unsafe {
- crate::opt_bytes(self, raw::git_worktree_name(self.raw))
- .and_then(|s| str::from_utf8(s).ok())
- }
- }
-
- /// Retrieves the path to the worktree
- ///
- /// This is the path to the top-level of the source and not the path to the
- /// .git file within the worktree. This path can be passed to
- /// repo::Repository::open.
- pub fn path(&self) -> &Path {
- unsafe {
- util::bytes2path(crate::opt_bytes(self, raw::git_worktree_path(self.raw)).unwrap())
- }
- }
-
- /// Validates the worktree
- ///
- /// This checks that it still exists on the
- /// filesystem and that the metadata is correct
- pub fn validate(&self) -> Result<(), Error> {
- unsafe {
- try_call!(raw::git_worktree_validate(self.raw));
- }
- Ok(())
- }
-
- /// Locks the worktree
- pub fn lock(&self, reason: Option<&str>) -> Result<(), Error> {
- let reason = crate::opt_cstr(reason)?;
- unsafe {
- try_call!(raw::git_worktree_lock(self.raw, reason));
- }
- Ok(())
- }
-
- /// Unlocks the worktree
- pub fn unlock(&self) -> Result<(), Error> {
- unsafe {
- try_call!(raw::git_worktree_unlock(self.raw));
- }
- Ok(())
- }
-
- /// Checks if worktree is locked
- pub fn is_locked(&self) -> Result<WorktreeLockStatus, Error> {
- let buf = Buf::new();
- unsafe {
- match try_call!(raw::git_worktree_is_locked(buf.raw(), self.raw)) {
- 0 => Ok(WorktreeLockStatus::Unlocked),
- _ => {
- let v = buf.to_vec();
- Ok(WorktreeLockStatus::Locked(match v.len() {
- 0 => None,
- _ => Some(String::from_utf8(v).unwrap()),
- }))
- }
- }
- }
- }
-
- /// Prunes the worktree
- pub fn prune(&self, opts: Option<&mut WorktreePruneOptions>) -> Result<(), Error> {
- // When successful the worktree should be removed however the backing structure
- // of the git_worktree should still be valid.
- unsafe {
- try_call!(raw::git_worktree_prune(self.raw, opts.map(|o| o.raw())));
- }
- Ok(())
- }
-
- /// Checks if the worktree is prunable
- pub fn is_prunable(&self, opts: Option<&mut WorktreePruneOptions>) -> Result<bool, Error> {
- unsafe {
- let rv = try_call!(raw::git_worktree_is_prunable(
- self.raw,
- opts.map(|o| o.raw())
- ));
- Ok(rv != 0)
- }
- }
-}
-
-impl<'a> WorktreeAddOptions<'a> {
- /// Creates a default set of add options.
- ///
- /// By default this will not lock the worktree
- pub fn new() -> WorktreeAddOptions<'a> {
- unsafe {
- let mut raw = mem::zeroed();
- assert_eq!(
- raw::git_worktree_add_options_init(&mut raw, raw::GIT_WORKTREE_ADD_OPTIONS_VERSION),
- 0
- );
- WorktreeAddOptions {
- raw,
- _marker: marker::PhantomData,
- }
- }
- }
-
- /// If enabled, this will cause the newly added worktree to be locked
- pub fn lock(&mut self, enabled: bool) -> &mut WorktreeAddOptions<'a> {
- self.raw.lock = enabled as c_int;
- self
- }
-
- /// reference to use for the new worktree HEAD
- pub fn reference(
- &mut self,
- reference: Option<&'a Reference<'_>>,
- ) -> &mut WorktreeAddOptions<'a> {
- self.raw.reference = if let Some(reference) = reference {
- reference.raw()
- } else {
- ptr::null_mut()
- };
- self
- }
-
- /// Get a set of raw add options to be used with `git_worktree_add`
- pub fn raw(&self) -> *const raw::git_worktree_add_options {
- &self.raw
- }
-}
-
-impl WorktreePruneOptions {
- /// Creates a default set of pruning options
- ///
- /// By defaults this will prune only worktrees that are no longer valid
- /// unlocked and not checked out
- pub fn new() -> WorktreePruneOptions {
- unsafe {
- let mut raw = mem::zeroed();
- assert_eq!(
- raw::git_worktree_prune_options_init(
- &mut raw,
- raw::GIT_WORKTREE_PRUNE_OPTIONS_VERSION
- ),
- 0
- );
- WorktreePruneOptions { raw }
- }
- }
-
- /// Controls whether valid (still existing on the filesystem) worktrees
- /// will be pruned
- ///
- /// Defaults to false
- pub fn valid(&mut self, valid: bool) -> &mut WorktreePruneOptions {
- self.flag(raw::GIT_WORKTREE_PRUNE_VALID, valid)
- }
-
- /// Controls whether locked worktrees will be pruned
- ///
- /// Defaults to false
- pub fn locked(&mut self, locked: bool) -> &mut WorktreePruneOptions {
- self.flag(raw::GIT_WORKTREE_PRUNE_LOCKED, locked)
- }
-
- /// Controls whether the actual working tree on the filesystem is recursively removed
- ///
- /// Defaults to false
- pub fn working_tree(&mut self, working_tree: bool) -> &mut WorktreePruneOptions {
- self.flag(raw::GIT_WORKTREE_PRUNE_WORKING_TREE, working_tree)
- }
-
- fn flag(&mut self, flag: raw::git_worktree_prune_t, on: bool) -> &mut WorktreePruneOptions {
- if on {
- self.raw.flags |= flag as u32;
- } else {
- self.raw.flags &= !(flag as u32);
- }
- self
- }
-
- /// Get a set of raw prune options to be used with `git_worktree_prune`
- pub fn raw(&mut self) -> *mut raw::git_worktree_prune_options {
- &mut self.raw
- }
-}
-
-impl Binding for Worktree {
- type Raw = *mut raw::git_worktree;
- unsafe fn from_raw(ptr: *mut raw::git_worktree) -> Worktree {
- Worktree { raw: ptr }
- }
- fn raw(&self) -> *mut raw::git_worktree {
- self.raw
- }
-}
-
-impl Drop for Worktree {
- fn drop(&mut self) {
- unsafe { raw::git_worktree_free(self.raw) }
- }
-}
-
-#[cfg(test)]
-mod tests {
- use crate::WorktreeAddOptions;
- use crate::WorktreeLockStatus;
-
- use tempfile::TempDir;
-
- #[test]
- fn smoke_add_no_ref() {
- let (_td, repo) = crate::test::repo_init();
-
- let wtdir = TempDir::new().unwrap();
- let wt_path = wtdir.path().join("tree-no-ref-dir");
- let opts = WorktreeAddOptions::new();
-
- let wt = repo.worktree("tree-no-ref", &wt_path, Some(&opts)).unwrap();
- assert_eq!(wt.name(), Some("tree-no-ref"));
- assert_eq!(
- wt.path().canonicalize().unwrap(),
- wt_path.canonicalize().unwrap()
- );
- let status = wt.is_locked().unwrap();
- assert_eq!(status, WorktreeLockStatus::Unlocked);
- }
-
- #[test]
- fn smoke_add_locked() {
- let (_td, repo) = crate::test::repo_init();
-
- let wtdir = TempDir::new().unwrap();
- let wt_path = wtdir.path().join("locked-tree");
- let mut opts = WorktreeAddOptions::new();
- opts.lock(true);
-
- let wt = repo.worktree("locked-tree", &wt_path, Some(&opts)).unwrap();
- // shouldn't be able to lock a worktree that was created locked
- assert!(wt.lock(Some("my reason")).is_err());
- assert_eq!(wt.name(), Some("locked-tree"));
- assert_eq!(
- wt.path().canonicalize().unwrap(),
- wt_path.canonicalize().unwrap()
- );
- assert_eq!(wt.is_locked().unwrap(), WorktreeLockStatus::Locked(None));
- assert!(wt.unlock().is_ok());
- assert!(wt.lock(Some("my reason")).is_ok());
- assert_eq!(
- wt.is_locked().unwrap(),
- WorktreeLockStatus::Locked(Some("my reason".to_string()))
- );
- }
-
- #[test]
- fn smoke_add_from_branch() {
- let (_td, repo) = crate::test::repo_init();
-
- let (wt_top, branch) = crate::test::worktrees_env_init(&repo);
- let wt_path = wt_top.path().join("test");
- let mut opts = WorktreeAddOptions::new();
- let reference = branch.into_reference();
- opts.reference(Some(&reference));
-
- let wt = repo
- .worktree("test-worktree", &wt_path, Some(&opts))
- .unwrap();
- assert_eq!(wt.name(), Some("test-worktree"));
- assert_eq!(
- wt.path().canonicalize().unwrap(),
- wt_path.canonicalize().unwrap()
- );
- let status = wt.is_locked().unwrap();
- assert_eq!(status, WorktreeLockStatus::Unlocked);
- }
-}