Skip to content

Commit c55bd6d

Browse files
authored
Add support for custom git extensions (#791)
* Add support for custom git extensions This would allow one to use git worktrees together with sparse-checkout. Reference: libgit2/libgit2#6031 * Mark extension methods unsafe * Replace custom spin-lock in tests with serial-test crate * Rewrite tests as integration tests
1 parent 21f40b0 commit c55bd6d

File tree

5 files changed

+115
-0
lines changed

5 files changed

+115
-0
lines changed

libgit2-sys/lib.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1870,6 +1870,10 @@ git_enum! {
18701870
GIT_OPT_ENABLE_HTTP_EXPECT_CONTINUE,
18711871
GIT_OPT_GET_MWINDOW_FILE_LIMIT,
18721872
GIT_OPT_SET_MWINDOW_FILE_LIMIT,
1873+
GIT_OPT_SET_ODB_PACKED_PRIORITY,
1874+
GIT_OPT_SET_ODB_LOOSE_PRIORITY,
1875+
GIT_OPT_GET_EXTENSIONS,
1876+
GIT_OPT_SET_EXTENSIONS,
18731877
}
18741878
}
18751879

src/opts.rs

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
//! Bindings to libgit2's git_libgit2_opts function.
22
33
use std::ffi::CString;
4+
use std::ptr;
45

6+
use crate::string_array::StringArray;
57
use crate::util::Binding;
68
use crate::{raw, Buf, ConfigLevel, Error, IntoCString};
79

@@ -119,6 +121,63 @@ pub fn strict_hash_verification(enabled: bool) {
119121
debug_assert!(error >= 0);
120122
}
121123

124+
/// Returns the list of git extensions that are supported. This is the list of
125+
/// built-in extensions supported by libgit2 and custom extensions that have
126+
/// been added with [`set_extensions`]. Extensions that have been negated will
127+
/// not be returned.
128+
///
129+
/// # Safety
130+
///
131+
/// libgit2 stores user extensions in a static variable.
132+
/// This function is effectively reading a `static mut` and should be treated as such
133+
pub unsafe fn get_extensions() -> Result<StringArray, Error> {
134+
crate::init();
135+
136+
let mut extensions = raw::git_strarray {
137+
strings: ptr::null_mut(),
138+
count: 0,
139+
};
140+
141+
try_call!(raw::git_libgit2_opts(
142+
raw::GIT_OPT_GET_EXTENSIONS as libc::c_int,
143+
&mut extensions
144+
));
145+
146+
Ok(StringArray::from_raw(extensions))
147+
}
148+
149+
/// Set that the given git extensions are supported by the caller. Extensions
150+
/// supported by libgit2 may be negated by prefixing them with a `!`.
151+
/// For example: setting extensions to `[ "!noop", "newext" ]` indicates that
152+
/// the caller does not want to support repositories with the `noop` extension
153+
/// but does want to support repositories with the `newext` extension.
154+
///
155+
/// # Safety
156+
///
157+
/// libgit2 stores user extensions in a static variable.
158+
/// This function is effectively modifying a `static mut` and should be treated as such
159+
pub unsafe fn set_extensions<E>(extensions: &[E]) -> Result<(), Error>
160+
where
161+
for<'x> &'x E: IntoCString,
162+
{
163+
crate::init();
164+
165+
let extensions = extensions
166+
.iter()
167+
.map(|e| e.into_c_string())
168+
.collect::<Result<Vec<_>, _>>()?;
169+
170+
let extension_ptrs = extensions.iter().map(|e| e.as_ptr()).collect::<Vec<_>>();
171+
172+
try_call!(raw::git_libgit2_opts(
173+
raw::GIT_OPT_SET_EXTENSIONS as libc::c_int,
174+
extension_ptrs.as_ptr(),
175+
extension_ptrs.len() as libc::size_t
176+
));
177+
178+
Ok(())
179+
}
180+
122181
#[cfg(test)]
123182
mod test {
124183
use super::*;

tests/add_extensions.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
//! Test for `set_extensions`, which writes a global state maintained by libgit2
2+
3+
use git2::opts::{get_extensions, set_extensions};
4+
use git2::Error;
5+
6+
#[test]
7+
fn test_add_extensions() -> Result<(), Error> {
8+
unsafe {
9+
set_extensions(&["custom"])?;
10+
}
11+
12+
let extensions = unsafe { get_extensions() }?;
13+
14+
assert_eq!(extensions.len(), 2);
15+
assert_eq!(extensions.get(0), Some("noop"));
16+
assert_eq!(extensions.get(1), Some("custom"));
17+
18+
Ok(())
19+
}

tests/get_extensions.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
//! Test for `get_extensions`, which reads a global state maintained by libgit2
2+
3+
use git2::opts::get_extensions;
4+
use git2::Error;
5+
6+
#[test]
7+
fn test_get_extensions() -> Result<(), Error> {
8+
let extensions = unsafe { get_extensions() }?;
9+
10+
assert_eq!(extensions.len(), 1);
11+
assert_eq!(extensions.get(0), Some("noop"));
12+
13+
Ok(())
14+
}

tests/remove_extensions.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
//! Test for `set_extensions`, which writes a global state maintained by libgit2
2+
3+
use git2::opts::{get_extensions, set_extensions};
4+
use git2::Error;
5+
6+
#[test]
7+
fn test_remove_extensions() -> Result<(), Error> {
8+
unsafe {
9+
set_extensions(&["custom", "!ignore", "!noop", "other"])?;
10+
}
11+
12+
let extensions = unsafe { get_extensions() }?;
13+
14+
assert_eq!(extensions.len(), 2);
15+
assert_eq!(extensions.get(0), Some("custom"));
16+
assert_eq!(extensions.get(1), Some("other"));
17+
18+
Ok(())
19+
}

0 commit comments

Comments
 (0)