aboutsummaryrefslogtreecommitdiff
path: root/contrib
diff options
context:
space:
mode:
Diffstat (limited to 'contrib')
-rw-r--r--contrib/libgit-rs/Cargo.lock77
-rw-r--r--contrib/libgit-rs/Cargo.toml17
-rw-r--r--contrib/libgit-rs/README.md13
-rw-r--r--contrib/libgit-rs/build.rs4
-rw-r--r--contrib/libgit-rs/src/config.rs106
-rw-r--r--contrib/libgit-rs/src/lib.rs1
-rw-r--r--contrib/libgit-rs/testdata/config12
-rw-r--r--contrib/libgit-rs/testdata/config22
-rw-r--r--contrib/libgit-rs/testdata/config32
-rw-r--r--contrib/libgit-sys/Cargo.lock69
-rw-r--r--contrib/libgit-sys/Cargo.toml19
-rw-r--r--contrib/libgit-sys/README.md4
-rw-r--r--contrib/libgit-sys/build.rs35
-rw-r--r--contrib/libgit-sys/public_symbol_export.c59
-rw-r--r--contrib/libgit-sys/public_symbol_export.h18
-rw-r--r--contrib/libgit-sys/src/lib.rs79
16 files changed, 507 insertions, 0 deletions
diff --git a/contrib/libgit-rs/Cargo.lock b/contrib/libgit-rs/Cargo.lock
new file mode 100644
index 0000000000..a30c7c8d33
--- /dev/null
+++ b/contrib/libgit-rs/Cargo.lock
@@ -0,0 +1,77 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "autocfg"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
+
+[[package]]
+name = "cc"
+version = "1.1.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "57b6a275aa2903740dc87da01c62040406b8812552e97129a63ea8850a17c6e6"
+dependencies = [
+ "shlex",
+]
+
+[[package]]
+name = "libc"
+version = "0.2.158"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439"
+
+[[package]]
+name = "libgit"
+version = "0.1.0"
+dependencies = [
+ "autocfg",
+ "libgit-sys",
+]
+
+[[package]]
+name = "libgit-sys"
+version = "0.1.0"
+dependencies = [
+ "autocfg",
+ "libz-sys",
+ "make-cmd",
+]
+
+[[package]]
+name = "libz-sys"
+version = "1.1.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d2d16453e800a8cf6dd2fc3eb4bc99b786a9b90c663b8559a5b1a041bf89e472"
+dependencies = [
+ "cc",
+ "libc",
+ "pkg-config",
+ "vcpkg",
+]
+
+[[package]]
+name = "make-cmd"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a8ca8afbe8af1785e09636acb5a41e08a765f5f0340568716c18a8700ba3c0d3"
+
+[[package]]
+name = "pkg-config"
+version = "0.3.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec"
+
+[[package]]
+name = "shlex"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
+
+[[package]]
+name = "vcpkg"
+version = "0.2.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
diff --git a/contrib/libgit-rs/Cargo.toml b/contrib/libgit-rs/Cargo.toml
new file mode 100644
index 0000000000..c3289e69db
--- /dev/null
+++ b/contrib/libgit-rs/Cargo.toml
@@ -0,0 +1,17 @@
+[package]
+name = "libgit"
+version = "0.1.0"
+edition = "2021"
+build = "build.rs"
+rust-version = "1.63" # TODO: Once we hit 1.84 or newer, we may want to remove Cargo.lock from
+ # version control. See https://lore.kernel.org/git/Z47jgK-oMjFRSslr@tapette.crustytoothpaste.net/
+
+
+[lib]
+path = "src/lib.rs"
+
+[dependencies]
+libgit-sys = { version = "0.1.0", path = "../libgit-sys" }
+
+[build-dependencies]
+autocfg = "1.4.0"
diff --git a/contrib/libgit-rs/README.md b/contrib/libgit-rs/README.md
new file mode 100644
index 0000000000..ff945e1ce2
--- /dev/null
+++ b/contrib/libgit-rs/README.md
@@ -0,0 +1,13 @@
+# libgit-rs
+
+Proof-of-concept Git bindings for Rust.
+
+```toml
+[dependencies]
+libgit = "0.1.0"
+```
+
+## Rust version requirements
+
+libgit-rs should support Rust versions at least as old as the version included
+in Debian stable (currently 1.63).
diff --git a/contrib/libgit-rs/build.rs b/contrib/libgit-rs/build.rs
new file mode 100644
index 0000000000..f8bd01a690
--- /dev/null
+++ b/contrib/libgit-rs/build.rs
@@ -0,0 +1,4 @@
+pub fn main() {
+ let ac = autocfg::new();
+ ac.emit_has_path("std::ffi::c_char");
+}
diff --git a/contrib/libgit-rs/src/config.rs b/contrib/libgit-rs/src/config.rs
new file mode 100644
index 0000000000..6bf04845c8
--- /dev/null
+++ b/contrib/libgit-rs/src/config.rs
@@ -0,0 +1,106 @@
+use std::ffi::{c_void, CStr, CString};
+use std::path::Path;
+
+#[cfg(has_std__ffi__c_char)]
+use std::ffi::{c_char, c_int};
+
+#[cfg(not(has_std__ffi__c_char))]
+#[allow(non_camel_case_types)]
+type c_char = i8;
+
+#[cfg(not(has_std__ffi__c_char))]
+#[allow(non_camel_case_types)]
+type c_int = i32;
+
+use libgit_sys::*;
+
+/// A ConfigSet is an in-memory cache for config-like files such as `.gitmodules` or `.gitconfig`.
+/// It does not support all config directives; notably, it will not process `include` or
+/// `includeIf` directives (but it will store them so that callers can choose whether and how to
+/// handle them).
+pub struct ConfigSet(*mut libgit_config_set);
+impl ConfigSet {
+ /// Allocate a new ConfigSet
+ pub fn new() -> Self {
+ unsafe { ConfigSet(libgit_configset_alloc()) }
+ }
+
+ /// Load the given files into the ConfigSet; conflicting directives in later files will
+ /// override those given in earlier files.
+ pub fn add_files(&mut self, files: &[&Path]) {
+ for file in files {
+ let pstr = file.to_str().expect("Invalid UTF-8");
+ let rs = CString::new(pstr).expect("Couldn't convert to CString");
+ unsafe {
+ libgit_configset_add_file(self.0, rs.as_ptr());
+ }
+ }
+ }
+
+ /// Load the value for the given key and attempt to parse it as an i32. Dies with a fatal error
+ /// if the value cannot be parsed. Returns None if the key is not present.
+ pub fn get_int(&mut self, key: &str) -> Option<i32> {
+ let key = CString::new(key).expect("Couldn't convert to CString");
+ let mut val: c_int = 0;
+ unsafe {
+ if libgit_configset_get_int(self.0, key.as_ptr(), &mut val as *mut c_int) != 0 {
+ return None;
+ }
+ }
+
+ Some(val.into())
+ }
+
+ /// Clones the value for the given key. Dies with a fatal error if the value cannot be
+ /// converted to a String. Returns None if the key is not present.
+ pub fn get_string(&mut self, key: &str) -> Option<String> {
+ let key = CString::new(key).expect("Couldn't convert key to CString");
+ let mut val: *mut c_char = std::ptr::null_mut();
+ unsafe {
+ if libgit_configset_get_string(self.0, key.as_ptr(), &mut val as *mut *mut c_char) != 0
+ {
+ return None;
+ }
+ let borrowed_str = CStr::from_ptr(val);
+ let owned_str =
+ String::from(borrowed_str.to_str().expect("Couldn't convert val to str"));
+ free(val as *mut c_void); // Free the xstrdup()ed pointer from the C side
+ Some(owned_str)
+ }
+ }
+}
+
+impl Default for ConfigSet {
+ fn default() -> Self {
+ Self::new()
+ }
+}
+
+impl Drop for ConfigSet {
+ fn drop(&mut self) {
+ unsafe {
+ libgit_configset_free(self.0);
+ }
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn load_configs_via_configset() {
+ let mut cs = ConfigSet::new();
+ cs.add_files(&[
+ Path::new("testdata/config1"),
+ Path::new("testdata/config2"),
+ Path::new("testdata/config3"),
+ ]);
+ // ConfigSet retrieves correct value
+ assert_eq!(cs.get_int("trace2.eventTarget"), Some(1));
+ // ConfigSet respects last config value set
+ assert_eq!(cs.get_int("trace2.eventNesting"), Some(3));
+ // ConfigSet returns None for missing key
+ assert_eq!(cs.get_string("foo.bar"), None);
+ }
+}
diff --git a/contrib/libgit-rs/src/lib.rs b/contrib/libgit-rs/src/lib.rs
new file mode 100644
index 0000000000..ef68c36943
--- /dev/null
+++ b/contrib/libgit-rs/src/lib.rs
@@ -0,0 +1 @@
+pub mod config;
diff --git a/contrib/libgit-rs/testdata/config1 b/contrib/libgit-rs/testdata/config1
new file mode 100644
index 0000000000..4e9a9d25d1
--- /dev/null
+++ b/contrib/libgit-rs/testdata/config1
@@ -0,0 +1,2 @@
+[trace2]
+ eventNesting = 1
diff --git a/contrib/libgit-rs/testdata/config2 b/contrib/libgit-rs/testdata/config2
new file mode 100644
index 0000000000..b8d1eca423
--- /dev/null
+++ b/contrib/libgit-rs/testdata/config2
@@ -0,0 +1,2 @@
+[trace2]
+ eventTarget = 1
diff --git a/contrib/libgit-rs/testdata/config3 b/contrib/libgit-rs/testdata/config3
new file mode 100644
index 0000000000..ca7b9a7c38
--- /dev/null
+++ b/contrib/libgit-rs/testdata/config3
@@ -0,0 +1,2 @@
+[trace2]
+ eventNesting = 3
diff --git a/contrib/libgit-sys/Cargo.lock b/contrib/libgit-sys/Cargo.lock
new file mode 100644
index 0000000000..427a4c66b7
--- /dev/null
+++ b/contrib/libgit-sys/Cargo.lock
@@ -0,0 +1,69 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "autocfg"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
+
+[[package]]
+name = "cc"
+version = "1.1.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "57b6a275aa2903740dc87da01c62040406b8812552e97129a63ea8850a17c6e6"
+dependencies = [
+ "shlex",
+]
+
+[[package]]
+name = "libc"
+version = "0.2.158"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439"
+
+[[package]]
+name = "libgit-sys"
+version = "0.1.0"
+dependencies = [
+ "autocfg",
+ "libz-sys",
+ "make-cmd",
+]
+
+[[package]]
+name = "libz-sys"
+version = "1.1.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d2d16453e800a8cf6dd2fc3eb4bc99b786a9b90c663b8559a5b1a041bf89e472"
+dependencies = [
+ "cc",
+ "libc",
+ "pkg-config",
+ "vcpkg",
+]
+
+[[package]]
+name = "make-cmd"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a8ca8afbe8af1785e09636acb5a41e08a765f5f0340568716c18a8700ba3c0d3"
+
+[[package]]
+name = "pkg-config"
+version = "0.3.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec"
+
+[[package]]
+name = "shlex"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
+
+[[package]]
+name = "vcpkg"
+version = "0.2.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
diff --git a/contrib/libgit-sys/Cargo.toml b/contrib/libgit-sys/Cargo.toml
new file mode 100644
index 0000000000..e0623022c3
--- /dev/null
+++ b/contrib/libgit-sys/Cargo.toml
@@ -0,0 +1,19 @@
+[package]
+name = "libgit-sys"
+version = "0.1.0"
+edition = "2021"
+build = "build.rs"
+links = "gitpub"
+rust-version = "1.63" # TODO: Once we hit 1.84 or newer, we may want to remove Cargo.lock from
+ # version control. See https://lore.kernel.org/git/Z47jgK-oMjFRSslr@tapette.crustytoothpaste.net/
+description = "Native bindings to a portion of libgit"
+
+[lib]
+path = "src/lib.rs"
+
+[dependencies]
+libz-sys = "1.1.19"
+
+[build-dependencies]
+autocfg = "1.4.0"
+make-cmd = "0.1.0"
diff --git a/contrib/libgit-sys/README.md b/contrib/libgit-sys/README.md
new file mode 100644
index 0000000000..c061cfcaf5
--- /dev/null
+++ b/contrib/libgit-sys/README.md
@@ -0,0 +1,4 @@
+# libgit-sys
+
+A small proof-of-concept crate showing how to provide a Rust FFI to Git
+internals.
diff --git a/contrib/libgit-sys/build.rs b/contrib/libgit-sys/build.rs
new file mode 100644
index 0000000000..3ffd80ad91
--- /dev/null
+++ b/contrib/libgit-sys/build.rs
@@ -0,0 +1,35 @@
+use std::env;
+use std::path::PathBuf;
+
+pub fn main() -> std::io::Result<()> {
+ let ac = autocfg::new();
+ ac.emit_has_path("std::ffi::c_char");
+
+ let crate_root = PathBuf::from(env::var_os("CARGO_MANIFEST_DIR").unwrap());
+ let git_root = crate_root.join("../..");
+ let dst = PathBuf::from(env::var_os("OUT_DIR").unwrap());
+
+ let make_output = make_cmd::gnu_make()
+ .env("DEVELOPER", "1")
+ .env_remove("PROFILE")
+ .current_dir(git_root.clone())
+ .args([
+ "INCLUDE_LIBGIT_RS=YesPlease",
+ "contrib/libgit-sys/libgitpub.a",
+ ])
+ .output()
+ .expect("Make failed to run");
+ if !make_output.status.success() {
+ panic!(
+ "Make failed:\n stdout = {}\n stderr = {}\n",
+ String::from_utf8(make_output.stdout).unwrap(),
+ String::from_utf8(make_output.stderr).unwrap()
+ );
+ }
+ std::fs::copy(crate_root.join("libgitpub.a"), dst.join("libgitpub.a"))?;
+ println!("cargo:rustc-link-search=native={}", dst.display());
+ println!("cargo:rustc-link-lib=gitpub");
+ println!("cargo:rerun-if-changed={}", git_root.display());
+
+ Ok(())
+}
diff --git a/contrib/libgit-sys/public_symbol_export.c b/contrib/libgit-sys/public_symbol_export.c
new file mode 100644
index 0000000000..dfbb257115
--- /dev/null
+++ b/contrib/libgit-sys/public_symbol_export.c
@@ -0,0 +1,59 @@
+/*
+ * Shim to publicly export Git symbols. These must be renamed so that the
+ * original symbols can be hidden. Renaming these with a "libgit_" prefix also
+ * avoids conflicts with other libraries such as libgit2.
+ */
+
+#include "git-compat-util.h"
+#include "config.h"
+#include "contrib/libgit-sys/public_symbol_export.h"
+#include "version.h"
+
+#pragma GCC visibility push(default)
+
+struct libgit_config_set {
+ struct config_set cs;
+};
+
+struct libgit_config_set *libgit_configset_alloc(void)
+{
+ struct libgit_config_set *cs =
+ xmalloc(sizeof(struct libgit_config_set));
+ git_configset_init(&cs->cs);
+ return cs;
+}
+
+void libgit_configset_free(struct libgit_config_set *cs)
+{
+ git_configset_clear(&cs->cs);
+ free(cs);
+}
+
+int libgit_configset_add_file(struct libgit_config_set *cs, const char *filename)
+{
+ return git_configset_add_file(&cs->cs, filename);
+}
+
+int libgit_configset_get_int(struct libgit_config_set *cs, const char *key,
+ int *dest)
+{
+ return git_configset_get_int(&cs->cs, key, dest);
+}
+
+int libgit_configset_get_string(struct libgit_config_set *cs, const char *key,
+ char **dest)
+{
+ return git_configset_get_string(&cs->cs, key, dest);
+}
+
+const char *libgit_user_agent(void)
+{
+ return git_user_agent();
+}
+
+const char *libgit_user_agent_sanitized(void)
+{
+ return git_user_agent_sanitized();
+}
+
+#pragma GCC visibility pop
diff --git a/contrib/libgit-sys/public_symbol_export.h b/contrib/libgit-sys/public_symbol_export.h
new file mode 100644
index 0000000000..701db92d53
--- /dev/null
+++ b/contrib/libgit-sys/public_symbol_export.h
@@ -0,0 +1,18 @@
+#ifndef PUBLIC_SYMBOL_EXPORT_H
+#define PUBLIC_SYMBOL_EXPORT_H
+
+struct libgit_config_set *libgit_configset_alloc(void);
+
+void libgit_configset_free(struct libgit_config_set *cs);
+
+int libgit_configset_add_file(struct libgit_config_set *cs, const char *filename);
+
+int libgit_configset_get_int(struct libgit_config_set *cs, const char *key, int *dest);
+
+int libgit_configset_get_string(struct libgit_config_set *cs, const char *key, char **dest);
+
+const char *libgit_user_agent(void);
+
+const char *libgit_user_agent_sanitized(void);
+
+#endif /* PUBLIC_SYMBOL_EXPORT_H */
diff --git a/contrib/libgit-sys/src/lib.rs b/contrib/libgit-sys/src/lib.rs
new file mode 100644
index 0000000000..4bfc650450
--- /dev/null
+++ b/contrib/libgit-sys/src/lib.rs
@@ -0,0 +1,79 @@
+use std::ffi::c_void;
+
+#[cfg(has_std__ffi__c_char)]
+use std::ffi::{c_char, c_int};
+
+#[cfg(not(has_std__ffi__c_char))]
+#[allow(non_camel_case_types)]
+pub type c_char = i8;
+
+#[cfg(not(has_std__ffi__c_char))]
+#[allow(non_camel_case_types)]
+pub type c_int = i32;
+
+extern crate libz_sys;
+
+#[allow(non_camel_case_types)]
+#[repr(C)]
+pub struct libgit_config_set {
+ _data: [u8; 0],
+ _marker: core::marker::PhantomData<(*mut u8, core::marker::PhantomPinned)>,
+}
+
+extern "C" {
+ pub fn free(ptr: *mut c_void);
+
+ pub fn libgit_user_agent() -> *const c_char;
+ pub fn libgit_user_agent_sanitized() -> *const c_char;
+
+ pub fn libgit_configset_alloc() -> *mut libgit_config_set;
+ pub fn libgit_configset_free(cs: *mut libgit_config_set);
+
+ pub fn libgit_configset_add_file(cs: *mut libgit_config_set, filename: *const c_char) -> c_int;
+
+ pub fn libgit_configset_get_int(
+ cs: *mut libgit_config_set,
+ key: *const c_char,
+ int: *mut c_int,
+ ) -> c_int;
+
+ pub fn libgit_configset_get_string(
+ cs: *mut libgit_config_set,
+ key: *const c_char,
+ dest: *mut *mut c_char,
+ ) -> c_int;
+
+}
+
+#[cfg(test)]
+mod tests {
+ use std::ffi::CStr;
+
+ use super::*;
+
+ #[test]
+ fn user_agent_starts_with_git() {
+ let c_str = unsafe { CStr::from_ptr(libgit_user_agent()) };
+ let agent = c_str
+ .to_str()
+ .expect("User agent contains invalid UTF-8 data");
+ assert!(
+ agent.starts_with("git/"),
+ r#"Expected user agent to start with "git/", got: {}"#,
+ agent
+ );
+ }
+
+ #[test]
+ fn sanitized_user_agent_starts_with_git() {
+ let c_str = unsafe { CStr::from_ptr(libgit_user_agent_sanitized()) };
+ let agent = c_str
+ .to_str()
+ .expect("Sanitized user agent contains invalid UTF-8 data");
+ assert!(
+ agent.starts_with("git/"),
+ r#"Expected user agent to start with "git/", got: {}"#,
+ agent
+ );
+ }
+}