{"thread":{"id":"61917","subject":"[RFC PATCH 0/6] [RFC] Introduce cgit-rs, a Rust wrapper around libgit.a","startedAt":"2024-08-07T18:21:36Z","lastAt":"2025-01-30T19:32:24Z","messageCount":217,"participants":["Josh Steadmon","brian m. carlson","Junio C Hamano","rsbecker@nexbridge.com","Mike Hommey","Jason A. Donenfeld","Dragan Simic","Kyle Lippincott","Eric Sunshine","Phillip Wood","phillip.wood123@gmail.com","Patrick Steinhardt","Calvin Wan","Sean Allred"],"isPatch":true,"patchVersion":1,"patchTotal":6},"messages":[{"id":"500339","messageId":"cover.1723054623.git.steadmon@google.com","threadId":"61917","inReplyTo":null,"subject":"[RFC PATCH 0/6] [RFC] Introduce cgit-rs, a Rust wrapper around libgit.a","fromName":"Josh Steadmon","fromEmail":"steadmon@google.com","sentAt":"2024-08-07T18:21:25Z","receivedAt":"2024-08-07T18:21:36Z","isPatch":true,"sender":{"key":"steadmon@google.com","avatar":"https://avatars.githubusercontent.com/u/2654920?v=4"},"body":"When we, the Git team at Google, first embarked on the libification\njourney, we didn’t have a specific consumer to build a library for but\ninstead were interested in the various potential benefits of\nlibification for many use cases such as VFSes and submodules. Without a\nspecific consumer, it has been difficult to evaluate the scope of what\nis necessary or not even for the first library, git-std-lib. Attempting\nto solve problems such as error handling, symbol collisions, and\ninternal/external interfaces, in addition to separating out a library\nturns out to be both too complex of a task to both develop and review\nall at once. While we strive to eventually build an ideal library, we\nhave also realized in order to make meaningful and consistent progress,\nwe have to solve these problems iteratively in smaller pieces. That is\nwhy over the last month, we have been working with the jj project [1] to\nunderstand their current usage of libgit2-rs [2] and gitoxide [3] and\nfuture library functionality they would be interested in. In doing so,\nwe have built cgit-rs, a Rust wrapper around libgit.a that allows Rust\ncode to call various basic Git functions.\n\n[1] https://github.com/martinvonz/jj\n[2] https://github.com/rust-lang/git2-rs\n[3] https://github.com/Byron/gitoxide\n\nThis series provides a small Rust wrapper library around parts of\nlibgit.a, and a proof-of-concept Rust executable that uses the library\nto interface with Git. Additionally, we have tested building JJ with our\nlibrary and used it to replace some of the libgit2-rs uses.\n\nThis exercise has clarified a lot of things for us, and we believe that\ndeveloping this wrapper further provides benefits both for downstream\nconsumers and the Git project itself:\n* cgit-rs provides wrappers for Rust consumers of libraries (eg. jj)\n* cgit-rs suggests focus areas for libification\n  * shows us what potential challenges we face with library consumers\n* Git libification improves git interfaces\n* Libification improves cgit-rs FFI.\n\nWe are putting error handling on hold for now since it is too complex\nand we intend other CLIs to be our first customers, in which case\nprinting out errors is not the worst.\n\nWhile the wrapper itself lives in contrib/, there are a couple of\npatches that touch git.git code. These patches are necessary for the\nwrapper, but not for git.git itself, which may seem unnecessary to\nmerge. However, I would argue that other languages (not just limited to\nRust) have issues calling functions that require a pointer to\nnon-generic objects and essentially require a redefinition in their own\nlanguage.\n\nWe're sending this series as RFC because there is remaining work\nwe'd like to do, but we'd like to get early feedback on this approach,\nand particularly to ask for advice on a few topics:\n\n* alternative methods of exposing only a subset of symbols in our\n  library\n\n* bikeshedding on the name (yes, really). There is an active, unrelated\n  CGit project [4] that we only recently became aware of. We originally\n  took the name \"cgit\" because at $DAYJOB we sometimes refer to git.git\n  as \"cgit\" to distinguish it from jgit [5].\n\n* gauging the level of interest in calling Git code from Rust\n\n[4] https://git.zx2c4.com/cgit\n[5] https://www.eclipse.org/jgit\n\nRemaining work includes:\n\n* finding a better solution to the common-main split. We should probably\n  have a separate initialization function including all of main() up to\n  the call to cmd_main(), which can then be exposed in cgit-rs.\n\n* adding unit and integration tests\n\n* Makefile cleanup, particularly adding config.mak options that\n  developers can set to run Rust builds and tests by default\n\n* automating the process of exporting additional functions via cgit-rs\n  (possibly with a wrapper script around bindgen [6])\n\n[6] https://github.com/rust-lang/rust-bindgen\n\nFinally, a quick discussion about symbol collisions: if functions are\nnot prepended with “libgit_” or something similar, it leaves us open to\ncollision issues in the future – so this probably would’ve happened with\nlibification in general to begin with. Therefore it seems necessary to\nhave to wrap all the symbols we are looking to expose. While this seem\nnon-ideal, we couldn’t come up with a better method. Our next best\nalternative is to simply expose all symbols by default, but this leads\nto symbol collisions when library users link both cgit-rs and\nlibgit2-rs.\n\n\nCalvin Wan (2):\n  contrib/cgit-rs: add repo initialization and config access\n  contrib/cgit-rs: add a subset of configset wrappers\n\nJosh Steadmon (4):\n  common-main: split common_exit() into a new file\n  repository: add initialize_repo wrapper without pointer\n  contrib/cgit-rs: introduce Rust wrapper for libgit.a\n  config: add git_configset_alloc\n\n .gitignore                             |  1 +\n Makefile                               | 14 ++++\n common-exit.c                          | 26 +++++++\n common-main.c                          | 24 -------\n config.c                               |  5 ++\n config.h                               |  5 ++\n contrib/cgit-rs/Cargo.lock             | 99 ++++++++++++++++++++++++++\n contrib/cgit-rs/Cargo.toml             | 17 +++++\n contrib/cgit-rs/README.md              | 15 ++++\n contrib/cgit-rs/build.rs               | 33 +++++++++\n contrib/cgit-rs/public_symbol_export.c | 72 +++++++++++++++++++\n contrib/cgit-rs/public_symbol_export.h | 26 +++++++\n contrib/cgit-rs/src/lib.rs             | 81 +++++++++++++++++++++\n contrib/cgit-rs/src/main.rs            | 44 ++++++++++++\n repository.c                           |  9 +++\n repository.h                           |  1 +\n 16 files changed, 448 insertions(+), 24 deletions(-)\n create mode 100644 common-exit.c\n create mode 100644 contrib/cgit-rs/Cargo.lock\n create mode 100644 contrib/cgit-rs/Cargo.toml\n create mode 100644 contrib/cgit-rs/README.md\n create mode 100644 contrib/cgit-rs/build.rs\n create mode 100644 contrib/cgit-rs/public_symbol_export.c\n create mode 100644 contrib/cgit-rs/public_symbol_export.h\n create mode 100644 contrib/cgit-rs/src/lib.rs\n create mode 100644 contrib/cgit-rs/src/main.rs\n\n\nbase-commit: 557ae147e6cdc9db121269b058c757ac5092f9c9\n-- \n2.46.0.rc2.264.g509ed76dc8-goog\n\n"},{"id":"500340","messageId":"78c2aa2ef9351d977f31dbbb16b148baf254ad59.1723054623.git.steadmon@google.com","threadId":"61917","inReplyTo":"cover.1723054623.git.steadmon@google.com","subject":"[RFC PATCH 1/6] common-main: split common_exit() into a new file","fromName":"Josh Steadmon","fromEmail":"steadmon@google.com","sentAt":"2024-08-07T18:21:26Z","receivedAt":"2024-08-07T18:21:37Z","isPatch":true,"sender":{"key":"steadmon@google.com","avatar":"https://avatars.githubusercontent.com/u/2654920?v=4"},"body":"Currently, object files in libgit.a reference common_exit(), which is\ncontained in common-main.o. However, common-main.o also includes main(),\nwhich references cmd_main() in git.o, which in turn depends on all the\nbuiltin/*.o objects.\n\nWe would like to allow external users to link libgit.a without needing\nto include so many extra objects. Enable this by splitting common_exit()\nand check_bug_if_BUG() into a new file common-exit.c, and add\ncommon-exit.o to LIB_OBJS so that these are included in libgit.a.\n\nThis split has previously been proposed ([1], [2]) to support fuzz tests\nand unit tests by avoiding conflicting definitions for main(). However,\nboth of those issues were resolved by other methods of avoiding symbol\nconflicts. Now we are trying to make libgit.a more self-contained, so\nhopefully we can revisit this approach.\n\n[1] https://lore.kernel.org/git/Yp+wjCPhqieTku3X@google.com/\n[2] https://lore.kernel.org/git/20230517-unit-tests-v2-v2-1-21b5b60f4b32@google.com/\n\nSigned-off-by: Josh Steadmon <steadmon@google.com>\n---\n Makefile      |  1 +\n common-exit.c | 26 ++++++++++++++++++++++++++\n common-main.c | 24 ------------------------\n 3 files changed, 27 insertions(+), 24 deletions(-)\n create mode 100644 common-exit.c\n\ndiff --git a/Makefile b/Makefile\nindex 3eab701b10..1cac51a4f7 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -979,6 +979,7 @@ LIB_OBJS += combine-diff.o\n LIB_OBJS += commit-graph.o\n LIB_OBJS += commit-reach.o\n LIB_OBJS += commit.o\n+LIB_OBJS += common-exit.o\n LIB_OBJS += compat/nonblock.o\n LIB_OBJS += compat/obstack.o\n LIB_OBJS += compat/terminal.o\ndiff --git a/common-exit.c b/common-exit.c\nnew file mode 100644\nindex 0000000000..1aaa538be3\n--- /dev/null\n+++ b/common-exit.c\n@@ -0,0 +1,26 @@\n+#include \"git-compat-util.h\"\n+#include \"trace2.h\"\n+\n+static void check_bug_if_BUG(void)\n+{\n+\tif (!bug_called_must_BUG)\n+\t\treturn;\n+\tBUG(\"on exit(): had bug() call(s) in this process without explicit BUG_if_bug()\");\n+}\n+\n+/* We wrap exit() to call common_exit() in git-compat-util.h */\n+int common_exit(const char *file, int line, int code)\n+{\n+\t/*\n+\t * For non-POSIX systems: Take the lowest 8 bits of the \"code\"\n+\t * to e.g. turn -1 into 255. On a POSIX system this is\n+\t * redundant, see exit(3) and wait(2), but as it doesn't harm\n+\t * anything there we don't need to guard this with an \"ifdef\".\n+\t */\n+\tcode &= 0xff;\n+\n+\tcheck_bug_if_BUG();\n+\ttrace2_cmd_exit_fl(file, line, code);\n+\n+\treturn code;\n+}\ndiff --git a/common-main.c b/common-main.c\nindex 8e68ac9e42..af4dea049e 100644\n--- a/common-main.c\n+++ b/common-main.c\n@@ -66,27 +66,3 @@ int main(int argc, const char **argv)\n \t/* Not exit(3), but a wrapper calling our common_exit() */\n \texit(result);\n }\n-\n-static void check_bug_if_BUG(void)\n-{\n-\tif (!bug_called_must_BUG)\n-\t\treturn;\n-\tBUG(\"on exit(): had bug() call(s) in this process without explicit BUG_if_bug()\");\n-}\n-\n-/* We wrap exit() to call common_exit() in git-compat-util.h */\n-int common_exit(const char *file, int line, int code)\n-{\n-\t/*\n-\t * For non-POSIX systems: Take the lowest 8 bits of the \"code\"\n-\t * to e.g. turn -1 into 255. On a POSIX system this is\n-\t * redundant, see exit(3) and wait(2), but as it doesn't harm\n-\t * anything there we don't need to guard this with an \"ifdef\".\n-\t */\n-\tcode &= 0xff;\n-\n-\tcheck_bug_if_BUG();\n-\ttrace2_cmd_exit_fl(file, line, code);\n-\n-\treturn code;\n-}\n-- \n2.46.0.rc2.264.g509ed76dc8-goog\n\n"},{"id":"500341","messageId":"5f2e816cf6359725f2a86ce1d08e5e272fba4dac.1723054623.git.steadmon@google.com","threadId":"61917","inReplyTo":"cover.1723054623.git.steadmon@google.com","subject":"[RFC PATCH 2/6] repository: add initialize_repo wrapper without pointer","fromName":"Josh Steadmon","fromEmail":"steadmon@google.com","sentAt":"2024-08-07T18:21:27Z","receivedAt":"2024-08-07T18:21:39Z","isPatch":true,"sender":{"key":"steadmon@google.com","avatar":"https://avatars.githubusercontent.com/u/2654920?v=4"},"body":"Non-C external consumers of libgit.a have to redefine the `repository`\nobject in their own language if they want to call\ninitialize_repository() to ensure memory for the object is allocated\ncorrectly. This is not ideal for external consumers that have no need\nfor the entire `the_repository` object but need to call other functions\nfrom an initialized repository. Therefore, add a friendly\ninitialize_repository() wrapper without a `the_repository` pointer.\n\nCo-authored-by: Calvin Wan <calvinwan@google.com>\nSigned-off-by: Calvin Wan <calvinwan@google.com\nSigned-off-by: Josh Steadmon <steadmon@google.com>\n---\n repository.c | 9 +++++++++\n repository.h | 1 +\n 2 files changed, 10 insertions(+)\n\ndiff --git a/repository.c b/repository.c\nindex 9825a30899..15cb9ee735 100644\n--- a/repository.c\n+++ b/repository.c\n@@ -81,6 +81,15 @@ void initialize_repository(struct repository *repo)\n \t\tset_default_hash_algo(repo);\n }\n \n+/*\n+ * For non-C external consumers of libgit.a that do not need access\n+ * to the entire `the_repository` object.\n+ */\n+void initialize_the_repository(void)\n+{\n+\tinitialize_repository(the_repository);\n+}\n+\n static void expand_base_dir(char **out, const char *in,\n \t\t\t    const char *base_dir, const char *def_in)\n {\ndiff --git a/repository.h b/repository.h\nindex af6ea0a62c..65c9158866 100644\n--- a/repository.h\n+++ b/repository.h\n@@ -227,6 +227,7 @@ void repo_set_compat_hash_algo(struct repository *repo, int compat_algo);\n void repo_set_ref_storage_format(struct repository *repo,\n \t\t\t\t enum ref_storage_format format);\n void initialize_repository(struct repository *repo);\n+void initialize_the_repository(void);\n RESULT_MUST_BE_USED\n int repo_init(struct repository *r, const char *gitdir, const char *worktree);\n \n-- \n2.46.0.rc2.264.g509ed76dc8-goog\n\n"},{"id":"500342","messageId":"9a846c17c891e17566a9907b3627210a6a08ea76.1723054623.git.steadmon@google.com","threadId":"61917","inReplyTo":"cover.1723054623.git.steadmon@google.com","subject":"[RFC PATCH 3/6] contrib/cgit-rs: introduce Rust wrapper for libgit.a","fromName":"Josh Steadmon","fromEmail":"steadmon@google.com","sentAt":"2024-08-07T18:21:28Z","receivedAt":"2024-08-07T18:21:41Z","isPatch":true,"sender":{"key":"steadmon@google.com","avatar":"https://avatars.githubusercontent.com/u/2654920?v=4"},"body":"Introduce cgit-rs, a Rust wrapper crate that allows Rust code to call\nfunctions in libgit.a. This initial patch defines build rules and an\ninterface that exposes user agent string getter functions as a proof of\nconcept. A proof-of-concept library consumer is provided in\ncontrib/cgit-rs/src/main.rs. This executable can be run with `cargo run`\n\nSymbols in cgit can collide with symbols from other libraries such as\nlibgit2. We avoid this by first exposing library symbols in\npublic_symbol_export.[ch]. These symbols are prepended with \"libgit_\" to\navoid collisions and set to visible using a visibility pragma. In\nbuild.rs, Rust builds contrib/cgit-rs/libcgit.a, which also contains\nlibgit.a and other dependent libraries, with -fvisibility=hidden to hide\nall symbols within those libraries that haven't been exposed with a\nvisibility pragma.\n\nCo-authored-by: Kyle Lippincott <spectral@google.com>\nCo-authored-by: Calvin Wan <calvinwan@google.com>\nCo-authored-by: Josh Steadmon <steadmon@google.com>\nSigned-off-by: Calvin Wan <calvinwan@google.com>\nSigned-off-by: Kyle Lippincott <spectral@google.com>\nSigned-off-by: Josh Steadmon <steadmon@google.com>\n---\n .gitignore                             |  1 +\n Makefile                               | 13 ++++++++++\n contrib/cgit-rs/Cargo.lock             | 16 +++++++++++++\n contrib/cgit-rs/Cargo.toml             | 16 +++++++++++++\n contrib/cgit-rs/README.md              | 15 ++++++++++++\n contrib/cgit-rs/build.rs               | 33 ++++++++++++++++++++++++++\n contrib/cgit-rs/public_symbol_export.c | 20 ++++++++++++++++\n contrib/cgit-rs/public_symbol_export.h |  8 +++++++\n contrib/cgit-rs/src/lib.rs             |  7 ++++++\n contrib/cgit-rs/src/main.rs            | 10 ++++++++\n 10 files changed, 139 insertions(+)\n create mode 100644 contrib/cgit-rs/Cargo.lock\n create mode 100644 contrib/cgit-rs/Cargo.toml\n create mode 100644 contrib/cgit-rs/README.md\n create mode 100644 contrib/cgit-rs/build.rs\n create mode 100644 contrib/cgit-rs/public_symbol_export.c\n create mode 100644 contrib/cgit-rs/public_symbol_export.h\n create mode 100644 contrib/cgit-rs/src/lib.rs\n create mode 100644 contrib/cgit-rs/src/main.rs\n\ndiff --git a/.gitignore b/.gitignore\nindex 8caf3700c2..7031012330 100644\n--- a/.gitignore\n+++ b/.gitignore\n@@ -248,3 +248,4 @@ Release/\n /git.VC.db\n *.dSYM\n /contrib/buildsystems/out\n+/contrib/cgit-rs/target\ndiff --git a/Makefile b/Makefile\nindex 1cac51a4f7..fcd06af123 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -653,6 +653,8 @@ CURL_CONFIG = curl-config\n GCOV = gcov\n STRIP = strip\n SPATCH = spatch\n+LD = ld\n+OBJCOPY = objcopy\n \n export TCL_PATH TCLTK_PATH\n \n@@ -2712,6 +2714,7 @@ OBJECTS += $(XDIFF_OBJS)\n OBJECTS += $(FUZZ_OBJS)\n OBJECTS += $(REFTABLE_OBJS) $(REFTABLE_TEST_OBJS)\n OBJECTS += $(UNIT_TEST_OBJS)\n+OBJECTS += contrib/cgit-rs/public_symbol_export.o\n \n ifndef NO_CURL\n \tOBJECTS += http.o http-walker.o remote-curl.o\n@@ -3719,6 +3722,7 @@ clean: profile-clean coverage-clean cocciclean\n \t$(RM) $(htmldocs).tar.gz $(manpages).tar.gz\n \t$(MAKE) -C Documentation/ clean\n \t$(RM) Documentation/GIT-EXCLUDED-PROGRAMS\n+\t$(RM) -r contrib/cgit-rs/target\n ifndef NO_PERL\n \t$(RM) -r perl/build/\n endif\n@@ -3864,3 +3868,12 @@ $(UNIT_TEST_PROGS): $(UNIT_TEST_BIN)/%$X: $(UNIT_TEST_DIR)/%.o \\\n build-unit-tests: $(UNIT_TEST_PROGS)\n unit-tests: $(UNIT_TEST_PROGS) t/helper/test-tool$X\n \t$(MAKE) -C t/ unit-tests\n+\n+contrib/cgit-rs/partial_symbol_export.o: contrib/cgit-rs/public_symbol_export.o libgit.a reftable/libreftable.a xdiff/lib.a\n+\t$(LD) -r $^ -o $@\n+\n+contrib/cgit-rs/hidden_symbol_export.o: contrib/cgit-rs/partial_symbol_export.o\n+\t$(OBJCOPY) --localize-hidden $^ $@\n+\n+contrib/cgit-rs/libcgit.a: contrib/cgit-rs/hidden_symbol_export.o\n+\t$(AR) $(ARFLAGS) $@ $^\ndiff --git a/contrib/cgit-rs/Cargo.lock b/contrib/cgit-rs/Cargo.lock\nnew file mode 100644\nindex 0000000000..f50c593995\n--- /dev/null\n+++ b/contrib/cgit-rs/Cargo.lock\n@@ -0,0 +1,16 @@\n+# This file is automatically @generated by Cargo.\n+# It is not intended for manual editing.\n+version = 3\n+\n+[[package]]\n+name = \"cgit\"\n+version = \"0.1.0\"\n+dependencies = [\n+ \"libc\",\n+]\n+\n+[[package]]\n+name = \"libc\"\n+version = \"0.2.155\"\n+source = \"registry+https://github.com/rust-lang/crates.io-index\"\n+checksum = \"97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c\"\ndiff --git a/contrib/cgit-rs/Cargo.toml b/contrib/cgit-rs/Cargo.toml\nnew file mode 100644\nindex 0000000000..7b55e6f3e1\n--- /dev/null\n+++ b/contrib/cgit-rs/Cargo.toml\n@@ -0,0 +1,16 @@\n+[package]\n+name = \"cgit\"\n+version = \"0.1.0\"\n+edition = \"2021\"\n+build = \"build.rs\"\n+links = \"git\"\n+\n+[[bin]]\n+name = \"cgit-test\"\n+path = \"src/main.rs\"\n+\n+[lib]\n+path = \"src/lib.rs\"\n+\n+[dependencies]\n+libc = \"0.2.155\"\ndiff --git a/contrib/cgit-rs/README.md b/contrib/cgit-rs/README.md\nnew file mode 100644\nindex 0000000000..7a59602c30\n--- /dev/null\n+++ b/contrib/cgit-rs/README.md\n@@ -0,0 +1,15 @@\n+# cgit-info\n+\n+A small hacky proof-of-concept showing how to provide a Rust FFI for the Git\n+library.\n+\n+## Building\n+\n+`cargo build` automatically builds and picks up on changes made to both\n+the Rust wrapper and git.git code so there is no need to run `make`\n+beforehand.\n+\n+## Running\n+\n+Assuming you don't make any changes to the Git source, you can just work from\n+`contrib/cgit-rs` and use `cargo build` or `cargo run` as usual.\ndiff --git a/contrib/cgit-rs/build.rs b/contrib/cgit-rs/build.rs\nnew file mode 100644\nindex 0000000000..0c1ec06737\n--- /dev/null\n+++ b/contrib/cgit-rs/build.rs\n@@ -0,0 +1,33 @@\n+use std::env;\n+use std::path::PathBuf;\n+\n+pub fn main() -> std::io::Result<()> {\n+    let crate_root = PathBuf::from(env::var_os(\"CARGO_MANIFEST_DIR\").unwrap());\n+    let git_root = crate_root.join(\"../..\");\n+    let dst = PathBuf::from(env::var_os(\"OUT_DIR\").unwrap());\n+\n+    let make_output = std::process::Command::new(\"make\")\n+        .env_remove(\"PROFILE\")\n+        .current_dir(git_root.clone())\n+        .args(&[\n+            \"CC=clang\",\n+            \"CFLAGS=-fvisibility=hidden\",\n+            \"contrib/cgit-rs/libcgit.a\"\n+        ])\n+        .output()\n+        .expect(\"Make failed to run\");\n+    if !make_output.status.success() {\n+        panic!(\n+                \"Make failed:\\n  stdout = {}\\n  stderr = {}\\n\",\n+                String::from_utf8(make_output.stdout).unwrap(),\n+                String::from_utf8(make_output.stderr).unwrap()\n+        );\n+    }\n+    std::fs::copy(crate_root.join(\"libcgit.a\"), dst.join(\"libcgit.a\"))?;\n+    println!(\"cargo::rustc-link-search=native={}\", dst.into_os_string().into_string().unwrap());\n+    println!(\"cargo::rustc-link-lib=cgit\");\n+    println!(\"cargo::rustc-link-lib=z\");\n+    println!(\"cargo::rerun-if-changed={}\", git_root.into_os_string().into_string().unwrap());\n+\n+    Ok(())\n+}\ndiff --git a/contrib/cgit-rs/public_symbol_export.c b/contrib/cgit-rs/public_symbol_export.c\nnew file mode 100644\nindex 0000000000..3d1cd6cc4f\n--- /dev/null\n+++ b/contrib/cgit-rs/public_symbol_export.c\n@@ -0,0 +1,20 @@\n+// Shim to publicly export Git symbols. These must be renamed so that the\n+// original symbols can be hidden. Renaming these with a \"libgit_\" prefix also\n+// avoid conflicts with other libraries such as libgit2.\n+\n+#include \"contrib/cgit-rs/public_symbol_export.h\"\n+#include \"version.h\"\n+\n+#pragma GCC visibility push(default)\n+\n+const char *libgit_user_agent(void)\n+{\n+\treturn git_user_agent();\n+}\n+\n+const char *libgit_user_agent_sanitized(void)\n+{\n+\treturn git_user_agent_sanitized();\n+}\n+\n+#pragma GCC visibility pop\ndiff --git a/contrib/cgit-rs/public_symbol_export.h b/contrib/cgit-rs/public_symbol_export.h\nnew file mode 100644\nindex 0000000000..a3372f93fa\n--- /dev/null\n+++ b/contrib/cgit-rs/public_symbol_export.h\n@@ -0,0 +1,8 @@\n+#ifndef PUBLIC_SYMBOL_EXPORT_H\n+#define PUBLIC_SYMBOL_EXPORT_H\n+\n+const char *libgit_user_agent(void);\n+\n+const char *libgit_user_agent_sanitized(void);\n+\n+#endif /* PUBLIC_SYMBOL_EXPORT_H */\ndiff --git a/contrib/cgit-rs/src/lib.rs b/contrib/cgit-rs/src/lib.rs\nnew file mode 100644\nindex 0000000000..dc46e7ff42\n--- /dev/null\n+++ b/contrib/cgit-rs/src/lib.rs\n@@ -0,0 +1,7 @@\n+use libc::c_char;\n+\n+extern \"C\" {\n+    // From version.c\n+    pub fn libgit_user_agent() -> *const c_char;\n+    pub fn libgit_user_agent_sanitized() -> *const c_char;\n+}\ndiff --git a/contrib/cgit-rs/src/main.rs b/contrib/cgit-rs/src/main.rs\nnew file mode 100644\nindex 0000000000..1794e3f43e\n--- /dev/null\n+++ b/contrib/cgit-rs/src/main.rs\n@@ -0,0 +1,10 @@\n+use std::ffi::CStr;\n+\n+fn main() {\n+    println!(\"Let's print some strings provided by Git\");\n+    let c_buf = unsafe { cgit::libgit_user_agent() };\n+    let c_str = unsafe { CStr::from_ptr(c_buf) };\n+    println!(\"git_user_agent() = {:?}\", c_str);\n+    println!(\"git_user_agent_sanitized() = {:?}\",\n+        unsafe { CStr::from_ptr(cgit::libgit_user_agent_sanitized()) });\n+}\n-- \n2.46.0.rc2.264.g509ed76dc8-goog\n\n"},{"id":"500343","messageId":"b84a8210a05c2358dc29f24a56adcbeaa90c8654.1723054623.git.steadmon@google.com","threadId":"61917","inReplyTo":"cover.1723054623.git.steadmon@google.com","subject":"[RFC PATCH 4/6] contrib/cgit-rs: add repo initialization and config access","fromName":"Josh Steadmon","fromEmail":"steadmon@google.com","sentAt":"2024-08-07T18:21:29Z","receivedAt":"2024-08-07T18:21:43Z","isPatch":true,"sender":{"key":"steadmon@google.com","avatar":"https://avatars.githubusercontent.com/u/2654920?v=4"},"body":"From: Calvin Wan <calvinwan@google.com>\n\nCo-authored-by: Josh Steadmon <steadmon@google.com>\nSigned-off-by: Calvin Wan <calvinwan@google.com>\nSigned-off-by: Josh Steadmon <steadmon@google.com>\n---\n contrib/cgit-rs/public_symbol_export.c | 27 ++++++++++++++++++++++++++\n contrib/cgit-rs/public_symbol_export.h |  8 ++++++++\n contrib/cgit-rs/src/lib.rs             | 13 ++++++++++++-\n contrib/cgit-rs/src/main.rs            | 23 +++++++++++++++++++++-\n 4 files changed, 69 insertions(+), 2 deletions(-)\n\ndiff --git a/contrib/cgit-rs/public_symbol_export.c b/contrib/cgit-rs/public_symbol_export.c\nindex 3d1cd6cc4f..ab3401ac40 100644\n--- a/contrib/cgit-rs/public_symbol_export.c\n+++ b/contrib/cgit-rs/public_symbol_export.c\n@@ -2,11 +2,35 @@\n // original symbols can be hidden. Renaming these with a \"libgit_\" prefix also\n // avoid conflicts with other libraries such as libgit2.\n \n+#include \"git-compat-util.h\"\n #include \"contrib/cgit-rs/public_symbol_export.h\"\n+#include \"attr.h\"\n+#include \"config.h\"\n+#include \"setup.h\"\n #include \"version.h\"\n \n #pragma GCC visibility push(default)\n \n+const char *libgit_setup_git_directory(void)\n+{\n+\treturn setup_git_directory();\n+}\n+\n+int libgit_config_get_int(const char *key, int *dest)\n+{\n+\treturn git_config_get_int(key, dest);\n+}\n+\n+void libgit_initialize_the_repository(void)\n+{\n+\tinitialize_the_repository();\n+}\n+\n+int libgit_parse_maybe_bool(const char *val)\n+{\n+\treturn git_parse_maybe_bool(val);\n+}\n+\n const char *libgit_user_agent(void)\n {\n \treturn git_user_agent();\n@@ -17,4 +41,7 @@ const char *libgit_user_agent_sanitized(void)\n \treturn git_user_agent_sanitized();\n }\n \n+const char *libgit_attr__true = git_attr__true;\n+const char *libgit_attr__false = git_attr__false;\n+\n #pragma GCC visibility pop\ndiff --git a/contrib/cgit-rs/public_symbol_export.h b/contrib/cgit-rs/public_symbol_export.h\nindex a3372f93fa..97e8e871ba 100644\n--- a/contrib/cgit-rs/public_symbol_export.h\n+++ b/contrib/cgit-rs/public_symbol_export.h\n@@ -1,6 +1,14 @@\n #ifndef PUBLIC_SYMBOL_EXPORT_H\n #define PUBLIC_SYMBOL_EXPORT_H\n \n+const char *libgit_setup_git_directory(void);\n+\n+int libgit_config_get_int(const char *key, int *dest);\n+\n+void libgit_initialize_the_repository(void);\n+\n+int libgit_parse_maybe_bool(const char *val);\n+\n const char *libgit_user_agent(void);\n \n const char *libgit_user_agent_sanitized(void);\ndiff --git a/contrib/cgit-rs/src/lib.rs b/contrib/cgit-rs/src/lib.rs\nindex dc46e7ff42..df350e758f 100644\n--- a/contrib/cgit-rs/src/lib.rs\n+++ b/contrib/cgit-rs/src/lib.rs\n@@ -1,6 +1,17 @@\n-use libc::c_char;\n+use libc::{c_char, c_int};\n \n extern \"C\" {\n+    pub fn libgit_setup_git_directory() -> *const c_char;\n+\n+    // From config.c\n+    pub fn libgit_config_get_int(key: *const c_char, dest: *mut c_int) ->c_int;\n+\n+    // From repository.c\n+    pub fn libgit_initialize_the_repository();\n+\n+    // From parse.c\n+    pub fn libgit_parse_maybe_bool(val: *const c_char) -> c_int;\n+\n     // From version.c\n     pub fn libgit_user_agent() -> *const c_char;\n     pub fn libgit_user_agent_sanitized() -> *const c_char;\ndiff --git a/contrib/cgit-rs/src/main.rs b/contrib/cgit-rs/src/main.rs\nindex 1794e3f43e..c5f8644fca 100644\n--- a/contrib/cgit-rs/src/main.rs\n+++ b/contrib/cgit-rs/src/main.rs\n@@ -1,4 +1,4 @@\n-use std::ffi::CStr;\n+use std::ffi::{CStr, CString};\n \n fn main() {\n     println!(\"Let's print some strings provided by Git\");\n@@ -7,4 +7,25 @@ fn main() {\n     println!(\"git_user_agent() = {:?}\", c_str);\n     println!(\"git_user_agent_sanitized() = {:?}\",\n         unsafe { CStr::from_ptr(cgit::libgit_user_agent_sanitized()) });\n+\n+    println!(\"\\nNow try passing args\");\n+    let test_arg = CString::new(\"test_arg\").unwrap();\n+    println!(\"git_parse_maybe_bool(...) = {:?}\",\n+        unsafe { cgit::libgit_parse_maybe_bool(test_arg.as_ptr()) });\n+\n+    println!(\"\\nCan we get an int out of our config??\");\n+    unsafe {\n+        cgit::libgit_initialize_the_repository();\n+        cgit::libgit_setup_git_directory();\n+        let mut val: libc::c_int = 0;\n+        let key = CString::new(\"trace2.eventNesting\").unwrap();\n+        cgit::libgit_config_get_int(\n+            key.as_ptr(),\n+            &mut val as *mut i32\n+        );\n+        println!(\n+            \"git_config_get_int(\\\"trace2.eventNesting\\\") -> {:?}\",\n+            val\n+        );\n+    };\n }\n-- \n2.46.0.rc2.264.g509ed76dc8-goog\n\n"},{"id":"500344","messageId":"c8befb680e938cff5c89af82a12dbf8406f9e917.1723054623.git.steadmon@google.com","threadId":"61917","inReplyTo":"cover.1723054623.git.steadmon@google.com","subject":"[RFC PATCH 5/6] config: add git_configset_alloc","fromName":"Josh Steadmon","fromEmail":"steadmon@google.com","sentAt":"2024-08-07T18:21:30Z","receivedAt":"2024-08-07T18:21:45Z","isPatch":true,"sender":{"key":"steadmon@google.com","avatar":"https://avatars.githubusercontent.com/u/2654920?v=4"},"body":"Add git_configset_alloc so that non-C external consumers can use\nconfigset functions without redefining config_set.\n\nCo-authored-by: Calvin Wan <calvinwan@google.com>\nSigned-off-by: Calvin Wan <calvinwan@google.com>\nSigned-off-by: Josh Steadmon <steadmon@google.com>\n---\n config.c | 5 +++++\n config.h | 5 +++++\n 2 files changed, 10 insertions(+)\n\ndiff --git a/config.c b/config.c\nindex 6421894614..fcfe7fcf46 100644\n--- a/config.c\n+++ b/config.c\n@@ -2324,6 +2324,11 @@ static int config_set_element_cmp(const void *cmp_data UNUSED,\n \treturn strcmp(e1->key, e2->key);\n }\n \n+struct config_set *git_configset_alloc(void)\n+{\n+\treturn xmalloc(sizeof(struct config_set));\n+}\n+\n void git_configset_init(struct config_set *set)\n {\n \thashmap_init(&set->config_hash, config_set_element_cmp, NULL, 0);\ndiff --git a/config.h b/config.h\nindex 54b47dec9e..abb4415b80 100644\n--- a/config.h\n+++ b/config.h\n@@ -472,6 +472,11 @@ struct config_set {\n \tstruct configset_list list;\n };\n \n+/**\n+ * Alloc a config_set\n+ */\n+struct config_set *git_configset_alloc(void);\n+\n /**\n  * Initializes the config_set `cs`.\n  */\n-- \n2.46.0.rc2.264.g509ed76dc8-goog\n\n"},{"id":"500345","messageId":"1e981a68802ac5aa7538381eb9469e524265ee40.1723054623.git.steadmon@google.com","threadId":"61917","inReplyTo":"cover.1723054623.git.steadmon@google.com","subject":"[RFC PATCH 6/6] contrib/cgit-rs: add a subset of configset wrappers","fromName":"Josh Steadmon","fromEmail":"steadmon@google.com","sentAt":"2024-08-07T18:21:31Z","receivedAt":"2024-08-07T18:21:47Z","isPatch":true,"sender":{"key":"steadmon@google.com","avatar":"https://avatars.githubusercontent.com/u/2654920?v=4"},"body":"From: Calvin Wan <calvinwan@google.com>\n\nSigned-off-by: Calvin Wan <calvinwan@google.com>\nSigned-off-by: Josh Steadmon <steadmon@google.com>\n---\n contrib/cgit-rs/Cargo.lock             | 83 ++++++++++++++++++++++++++\n contrib/cgit-rs/Cargo.toml             |  1 +\n contrib/cgit-rs/public_symbol_export.c | 25 ++++++++\n contrib/cgit-rs/public_symbol_export.h | 10 ++++\n contrib/cgit-rs/src/lib.rs             | 65 +++++++++++++++++++-\n contrib/cgit-rs/src/main.rs            | 13 ++++\n 6 files changed, 196 insertions(+), 1 deletion(-)\n\ndiff --git a/contrib/cgit-rs/Cargo.lock b/contrib/cgit-rs/Cargo.lock\nindex f50c593995..1d40ac5faa 100644\n--- a/contrib/cgit-rs/Cargo.lock\n+++ b/contrib/cgit-rs/Cargo.lock\n@@ -6,11 +6,94 @@ version = 3\n name = \"cgit\"\n version = \"0.1.0\"\n dependencies = [\n+ \"home\",\n  \"libc\",\n ]\n \n+[[package]]\n+name = \"home\"\n+version = \"0.5.9\"\n+source = \"registry+https://github.com/rust-lang/crates.io-index\"\n+checksum = \"e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5\"\n+dependencies = [\n+ \"windows-sys\",\n+]\n+\n [[package]]\n name = \"libc\"\n version = \"0.2.155\"\n source = \"registry+https://github.com/rust-lang/crates.io-index\"\n checksum = \"97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c\"\n+\n+[[package]]\n+name = \"windows-sys\"\n+version = \"0.52.0\"\n+source = \"registry+https://github.com/rust-lang/crates.io-index\"\n+checksum = \"282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d\"\n+dependencies = [\n+ \"windows-targets\",\n+]\n+\n+[[package]]\n+name = \"windows-targets\"\n+version = \"0.52.6\"\n+source = \"registry+https://github.com/rust-lang/crates.io-index\"\n+checksum = \"9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973\"\n+dependencies = [\n+ \"windows_aarch64_gnullvm\",\n+ \"windows_aarch64_msvc\",\n+ \"windows_i686_gnu\",\n+ \"windows_i686_gnullvm\",\n+ \"windows_i686_msvc\",\n+ \"windows_x86_64_gnu\",\n+ \"windows_x86_64_gnullvm\",\n+ \"windows_x86_64_msvc\",\n+]\n+\n+[[package]]\n+name = \"windows_aarch64_gnullvm\"\n+version = \"0.52.6\"\n+source = \"registry+https://github.com/rust-lang/crates.io-index\"\n+checksum = \"32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3\"\n+\n+[[package]]\n+name = \"windows_aarch64_msvc\"\n+version = \"0.52.6\"\n+source = \"registry+https://github.com/rust-lang/crates.io-index\"\n+checksum = \"09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469\"\n+\n+[[package]]\n+name = \"windows_i686_gnu\"\n+version = \"0.52.6\"\n+source = \"registry+https://github.com/rust-lang/crates.io-index\"\n+checksum = \"8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b\"\n+\n+[[package]]\n+name = \"windows_i686_gnullvm\"\n+version = \"0.52.6\"\n+source = \"registry+https://github.com/rust-lang/crates.io-index\"\n+checksum = \"0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66\"\n+\n+[[package]]\n+name = \"windows_i686_msvc\"\n+version = \"0.52.6\"\n+source = \"registry+https://github.com/rust-lang/crates.io-index\"\n+checksum = \"240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66\"\n+\n+[[package]]\n+name = \"windows_x86_64_gnu\"\n+version = \"0.52.6\"\n+source = \"registry+https://github.com/rust-lang/crates.io-index\"\n+checksum = \"147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78\"\n+\n+[[package]]\n+name = \"windows_x86_64_gnullvm\"\n+version = \"0.52.6\"\n+source = \"registry+https://github.com/rust-lang/crates.io-index\"\n+checksum = \"24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d\"\n+\n+[[package]]\n+name = \"windows_x86_64_msvc\"\n+version = \"0.52.6\"\n+source = \"registry+https://github.com/rust-lang/crates.io-index\"\n+checksum = \"589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec\"\ndiff --git a/contrib/cgit-rs/Cargo.toml b/contrib/cgit-rs/Cargo.toml\nindex 7b55e6f3e1..5768fce9e5 100644\n--- a/contrib/cgit-rs/Cargo.toml\n+++ b/contrib/cgit-rs/Cargo.toml\n@@ -14,3 +14,4 @@ path = \"src/lib.rs\"\n \n [dependencies]\n libc = \"0.2.155\"\n+home = \"0.5.9\"\ndiff --git a/contrib/cgit-rs/public_symbol_export.c b/contrib/cgit-rs/public_symbol_export.c\nindex ab3401ac40..9641afca89 100644\n--- a/contrib/cgit-rs/public_symbol_export.c\n+++ b/contrib/cgit-rs/public_symbol_export.c\n@@ -31,6 +31,31 @@ int libgit_parse_maybe_bool(const char *val)\n \treturn git_parse_maybe_bool(val);\n }\n \n+struct config_set *libgit_configset_alloc(void)\n+{\n+\treturn git_configset_alloc();\n+}\n+\n+void libgit_configset_init(struct config_set *cs)\n+{\n+\tgit_configset_init(cs);\n+}\n+\n+int libgit_configset_add_file(struct config_set *cs, const char *filename)\n+{\n+\treturn git_configset_add_file(cs, filename);\n+}\n+\n+int libgit_configset_get_int(struct config_set *cs, const char *key, int *dest)\n+{\n+\treturn git_configset_get_int(cs, key, dest);\n+}\n+\n+int libgit_configset_get_string(struct config_set *cs, const char *key, char **dest)\n+{\n+\treturn git_configset_get_string(cs, key, dest);\n+}\n+\n const char *libgit_user_agent(void)\n {\n \treturn git_user_agent();\ndiff --git a/contrib/cgit-rs/public_symbol_export.h b/contrib/cgit-rs/public_symbol_export.h\nindex 97e8e871ba..f22937202a 100644\n--- a/contrib/cgit-rs/public_symbol_export.h\n+++ b/contrib/cgit-rs/public_symbol_export.h\n@@ -9,6 +9,16 @@ void libgit_initialize_the_repository(void);\n \n int libgit_parse_maybe_bool(const char *val);\n \n+struct config_set *libgit_configset_alloc(void);\n+\n+void libgit_configset_init(struct config_set *cs);\n+\n+int libgit_configset_add_file(struct config_set *cs, const char *filename);\n+\n+int libgit_configset_get_int(struct config_set *cs, const char *key, int *dest);\n+\n+int libgit_configset_get_string(struct config_set *cs, const char *key, char **dest);\n+\n const char *libgit_user_agent(void);\n \n const char *libgit_user_agent_sanitized(void);\ndiff --git a/contrib/cgit-rs/src/lib.rs b/contrib/cgit-rs/src/lib.rs\nindex df350e758f..acfd3659e8 100644\n--- a/contrib/cgit-rs/src/lib.rs\n+++ b/contrib/cgit-rs/src/lib.rs\n@@ -1,4 +1,57 @@\n-use libc::{c_char, c_int};\n+use std::ffi::{CStr, CString};\n+\n+use libc::{c_char, c_int, c_void};\n+\n+pub enum GitConfigSet {}\n+\n+pub struct ConfigSet(*mut GitConfigSet);\n+impl ConfigSet {\n+\n+    pub fn new() -> Self {\n+        unsafe {\n+            // TODO: we need to handle freeing this when the ConfigSet is dropped\n+            // git_configset_clear(ptr) and then libc::free(ptr)\n+            let ptr = libgit_configset_alloc();\n+            libgit_configset_init(ptr);\n+            ConfigSet(ptr)\n+        }\n+    }\n+\n+    pub fn add_files(&mut self, files: &[&str]) {\n+        for file in files {\n+            let rs = CString::new(*file).expect(\"Couldn't convert to CString\");\n+            unsafe {\n+                libgit_configset_add_file(self.0, rs.as_ptr());\n+            }\n+        }\n+    }\n+\n+    pub fn get_int(&mut self, key: &str) -> Option<c_int> {\n+        let key = CString::new(key).expect(\"Couldn't convert to CString\");\n+        let mut val: c_int = 0;\n+        unsafe {\n+            if libgit_configset_get_int(self.0, key.as_ptr(), &mut val as *mut c_int) != 0 {\n+                return None;\n+            }\n+        }\n+\n+        Some(val)\n+    }\n+\n+    pub fn get_str(&mut self, key: &str) -> Option<CString> {\n+        let key = CString::new(key).expect(\"Couldn't convert to CString\");\n+        let mut val: *mut c_char = std::ptr::null_mut();\n+        unsafe {\n+            if libgit_configset_get_string(self.0, key.as_ptr(), &mut val as *mut *mut c_char) != 0 {\n+                return None;\n+            }\n+            let borrowed_str = CStr::from_ptr(val);\n+            let owned_str = CString::from_vec_with_nul(borrowed_str.to_bytes_with_nul().to_vec());\n+            libc::free(val as *mut c_void); // Free the xstrdup()ed pointer from the C side\n+            Some(owned_str.unwrap())\n+        }\n+    }\n+}\n \n extern \"C\" {\n     pub fn libgit_setup_git_directory() -> *const c_char;\n@@ -15,4 +68,14 @@ extern \"C\" {\n     // From version.c\n     pub fn libgit_user_agent() -> *const c_char;\n     pub fn libgit_user_agent_sanitized() -> *const c_char;\n+\n+    fn libgit_configset_alloc() -> *mut GitConfigSet;\n+\n+    fn libgit_configset_init(cs: *mut GitConfigSet);\n+\n+    fn libgit_configset_add_file(cs: *mut GitConfigSet, filename: *const c_char) -> c_int;\n+\n+    pub fn libgit_configset_get_int(cs: *mut GitConfigSet, key: *const c_char, int: *mut c_int) -> c_int;\n+    pub fn libgit_configset_get_string(cs: *mut GitConfigSet, key: *const c_char, dest: *mut *mut c_char) -> c_int;\n+\n }\ndiff --git a/contrib/cgit-rs/src/main.rs b/contrib/cgit-rs/src/main.rs\nindex c5f8644fca..07c8a71a13 100644\n--- a/contrib/cgit-rs/src/main.rs\n+++ b/contrib/cgit-rs/src/main.rs\n@@ -1,4 +1,5 @@\n use std::ffi::{CStr, CString};\n+use home::home_dir;\n \n fn main() {\n     println!(\"Let's print some strings provided by Git\");\n@@ -28,4 +29,16 @@ fn main() {\n             val\n         );\n     };\n+\n+    println!(\"\\nTry out our configset wrappers\");\n+    let mut cs = cgit::ConfigSet::new();\n+    let mut path = home_dir().expect(\"cannot get home directory path\");\n+    path.push(\".gitconfig\");\n+    let path:String = path.into_os_string().into_string().unwrap();\n+    cs.add_files(&[\"/etc/gitconfig\", \".gitconfig\", &path]);\n+    /*\n+     * Returns Some(x) if defined in local config, otherwise None\n+     */\n+    println!(\"get_configset_get_int = {:?}\", cs.get_int(\"trace2.eventNesting\"));\n+    println!(\"cs.get_str(\\\"garbage\\\") = {:?}\", cs.get_str(\"this_string_does_not_exist\"));\n }\n-- \n2.46.0.rc2.264.g509ed76dc8-goog\n\n"},{"id":"500352","messageId":"ZrPlQRAGQDMnVGjo@tapette.crustytoothpaste.net","threadId":"61917","inReplyTo":"9a846c17c891e17566a9907b3627210a6a08ea76.1723054623.git.steadmon@google.com","subject":"Re: [RFC PATCH 3/6] contrib/cgit-rs: introduce Rust wrapper for libgit.a","fromName":"brian m. carlson","fromEmail":"sandals@crustytoothpaste.net","sentAt":"2024-08-07T21:21:05Z","receivedAt":"2024-08-07T21:21:13Z","isPatch":true,"sender":{"key":"sandals@crustytoothpaste.net","avatar":"https://avatars.githubusercontent.com/u/497054?v=4"},"body":"On 2024-08-07 at 18:21:28, Josh Steadmon wrote:\n> Introduce cgit-rs, a Rust wrapper crate that allows Rust code to call\n> functions in libgit.a. This initial patch defines build rules and an\n> interface that exposes user agent string getter functions as a proof of\n> concept. A proof-of-concept library consumer is provided in\n> contrib/cgit-rs/src/main.rs. This executable can be run with `cargo run`\n> \n> Symbols in cgit can collide with symbols from other libraries such as\n> libgit2. We avoid this by first exposing library symbols in\n> public_symbol_export.[ch]. These symbols are prepended with \"libgit_\" to\n> avoid collisions and set to visible using a visibility pragma. In\n> build.rs, Rust builds contrib/cgit-rs/libcgit.a, which also contains\n> libgit.a and other dependent libraries, with -fvisibility=hidden to hide\n> all symbols within those libraries that haven't been exposed with a\n> visibility pragma.\n\nI think this is a good idea.  It's optional and it allows us to add\nfunctionality as we go along.  Platforms that don't have Rust can just\nomit building it.\n\n> +[dependencies]\n> +libc = \"0.2.155\"\n\nI don't love that we're using libc here.  It would be better to use\nrustix because that provides safe APIs that are compatible with POSIX,\nbut I think for now we need this because rustix doesn't offer memory\nmanagement like free(3).  I'd really prefer that we didn't have to do\nmemory management in Rust, but maybe that can come in with a future\nseries.\n\nlibc also comes with the downside that it calls the actual libc\nfunctions, so you have to write things like stat64 on Linux if you want\na 64-bit stat, but that doesn't exist on some of the BSDs, so you have\nto write something else and compile it conditionally, and all of that\nmakes the portability of it worse than with C.\n\nIn any event, I have the intention to send a patch to replace libc with\nrustix in the future if this series lands.\n\n> diff --git a/contrib/cgit-rs/public_symbol_export.c b/contrib/cgit-rs/public_symbol_export.c\n> new file mode 100644\n> index 0000000000..3d1cd6cc4f\n> --- /dev/null\n> +++ b/contrib/cgit-rs/public_symbol_export.c\n> @@ -0,0 +1,20 @@\n> +// Shim to publicly export Git symbols. These must be renamed so that the\n> +// original symbols can be hidden. Renaming these with a \"libgit_\" prefix also\n> +// avoid conflicts with other libraries such as libgit2.\n> +\n> +#include \"contrib/cgit-rs/public_symbol_export.h\"\n> +#include \"version.h\"\n> +\n> +#pragma GCC visibility push(default)\n\nI assume this also works in clang?\n-- \nbrian m. carlson (they/them or he/him)\nToronto, Ontario, CA\n"},{"id":"500353","messageId":"xmqqr0b0ni5n.fsf@gitster.g","threadId":"61917","inReplyTo":"78c2aa2ef9351d977f31dbbb16b148baf254ad59.1723054623.git.steadmon@google.com","subject":"Re: [RFC PATCH 1/6] common-main: split common_exit() into a new file","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2024-08-07T21:21:24Z","receivedAt":"2024-08-07T21:21:27Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Josh Steadmon <steadmon@google.com> writes:\n\n> diff --git a/common-exit.c b/common-exit.c\n> new file mode 100644\n> index 0000000000..1aaa538be3\n> --- /dev/null\n> +++ b/common-exit.c\n> @@ -0,0 +1,26 @@\n> +#include \"git-compat-util.h\"\n> +#include \"trace2.h\"\n> +\n> +static void check_bug_if_BUG(void)\n> +{\n> +\tif (!bug_called_must_BUG)\n> +\t\treturn;\n> +\tBUG(\"on exit(): had bug() call(s) in this process without explicit BUG_if_bug()\");\n> +}\n\nNice that this can stay file-scope static.\n\n> +/* We wrap exit() to call common_exit() in git-compat-util.h */\n> +int common_exit(const char *file, int line, int code)\n> +{\n> +\t/*\n> +\t * For non-POSIX systems: Take the lowest 8 bits of the \"code\"\n> +\t * to e.g. turn -1 into 255. On a POSIX system this is\n> +\t * redundant, see exit(3) and wait(2), but as it doesn't harm\n> +\t * anything there we don't need to guard this with an \"ifdef\".\n> +\t */\n> +\tcode &= 0xff;\n> +\n> +\tcheck_bug_if_BUG();\n> +\ttrace2_cmd_exit_fl(file, line, code);\n> +\n> +\treturn code;\n> +}\n\nI wonder if at least the part that primes the trace2 system needs to\nalso be split out of the file that defines our own main(), though.\n\nAre libgit.a users now responsible for calling things like\ntrace2_initialize_clock(), trace2_initialize(), etc., themselves?  I\nam wondering if these calls are encapsulated into a simple helper\nfunction, say common_startup(), then the story we need to tell the\nlibgit.a users may become simpler, i.e. you call common_startup, do\nyour things, and then you somehow cause common_exit() to be called.\n\n"},{"id":"500354","messageId":"ZrPmmBqK3IIiQutV@tapette.crustytoothpaste.net","threadId":"61917","inReplyTo":"b84a8210a05c2358dc29f24a56adcbeaa90c8654.1723054623.git.steadmon@google.com","subject":"Re: [RFC PATCH 4/6] contrib/cgit-rs: add repo initialization and config access","fromName":"brian m. carlson","fromEmail":"sandals@crustytoothpaste.net","sentAt":"2024-08-07T21:26:48Z","receivedAt":"2024-08-07T21:26:51Z","isPatch":true,"sender":{"key":"sandals@crustytoothpaste.net","avatar":"https://avatars.githubusercontent.com/u/497054?v=4"},"body":"On 2024-08-07 at 18:21:29, Josh Steadmon wrote:\n> diff --git a/contrib/cgit-rs/src/lib.rs b/contrib/cgit-rs/src/lib.rs\n> index dc46e7ff42..df350e758f 100644\n> --- a/contrib/cgit-rs/src/lib.rs\n> +++ b/contrib/cgit-rs/src/lib.rs\n> @@ -1,6 +1,17 @@\n> -use libc::c_char;\n> +use libc::{c_char, c_int};\n>  \n>  extern \"C\" {\n> +    pub fn libgit_setup_git_directory() -> *const c_char;\n> +\n> +    // From config.c\n> +    pub fn libgit_config_get_int(key: *const c_char, dest: *mut c_int) ->c_int;\n> +\n> +    // From repository.c\n> +    pub fn libgit_initialize_the_repository();\n> +\n> +    // From parse.c\n> +    pub fn libgit_parse_maybe_bool(val: *const c_char) -> c_int;\n> +\n>      // From version.c\n>      pub fn libgit_user_agent() -> *const c_char;\n>      pub fn libgit_user_agent_sanitized() -> *const c_char;\n\nI think it would be helpful if we didn't expose the raw C API in Rust.\nNobody is going to want to write code that uses that in Rust.\n\nIf we _do_ expose that, it should be in a separate cgit-sys crate, which\nis the customary naming, that exposes only that and then we can offer\nnicer wrappers around it.\n\n> diff --git a/contrib/cgit-rs/src/main.rs b/contrib/cgit-rs/src/main.rs\n> index 1794e3f43e..c5f8644fca 100644\n> --- a/contrib/cgit-rs/src/main.rs\n> +++ b/contrib/cgit-rs/src/main.rs\n> @@ -1,4 +1,4 @@\n> -use std::ffi::CStr;\n> +use std::ffi::{CStr, CString};\n>  \n>  fn main() {\n>      println!(\"Let's print some strings provided by Git\");\n> @@ -7,4 +7,25 @@ fn main() {\n>      println!(\"git_user_agent() = {:?}\", c_str);\n>      println!(\"git_user_agent_sanitized() = {:?}\",\n>          unsafe { CStr::from_ptr(cgit::libgit_user_agent_sanitized()) });\n> +\n> +    println!(\"\\nNow try passing args\");\n> +    let test_arg = CString::new(\"test_arg\").unwrap();\n> +    println!(\"git_parse_maybe_bool(...) = {:?}\",\n> +        unsafe { cgit::libgit_parse_maybe_bool(test_arg.as_ptr()) });\n> +\n> +    println!(\"\\nCan we get an int out of our config??\");\n> +    unsafe {\n> +        cgit::libgit_initialize_the_repository();\n> +        cgit::libgit_setup_git_directory();\n> +        let mut val: libc::c_int = 0;\n> +        let key = CString::new(\"trace2.eventNesting\").unwrap();\n> +        cgit::libgit_config_get_int(\n> +            key.as_ptr(),\n> +            &mut val as *mut i32\n> +        );\n> +        println!(\n> +            \"git_config_get_int(\\\"trace2.eventNesting\\\") -> {:?}\",\n> +            val\n> +        );\n> +    };\n>  }\n\nIt seems like this code wants to be a set of tests and maybe it would\nbe helpful to write it as a few instead.  For example, we can assume\nthat our user-agent starts with `git/` assuming it hasn't been\noverridden, so maybe we could write that as a test, or at least that we\ngot a valid C string.\n-- \nbrian m. carlson (they/them or he/him)\nToronto, Ontario, CA\n"},{"id":"500356","messageId":"ZrPpxE7OZtqsbD81@tapette.crustytoothpaste.net","threadId":"61917","inReplyTo":"1e981a68802ac5aa7538381eb9469e524265ee40.1723054623.git.steadmon@google.com","subject":"Re: [RFC PATCH 6/6] contrib/cgit-rs: add a subset of configset wrappers","fromName":"brian m. carlson","fromEmail":"sandals@crustytoothpaste.net","sentAt":"2024-08-07T21:40:20Z","receivedAt":"2024-08-07T21:40:22Z","isPatch":true,"sender":{"key":"sandals@crustytoothpaste.net","avatar":"https://avatars.githubusercontent.com/u/497054?v=4"},"body":"On 2024-08-07 at 18:21:31, Josh Steadmon wrote:\n> diff --git a/contrib/cgit-rs/Cargo.toml b/contrib/cgit-rs/Cargo.toml\n> index 7b55e6f3e1..5768fce9e5 100644\n> --- a/contrib/cgit-rs/Cargo.toml\n> +++ b/contrib/cgit-rs/Cargo.toml\n> @@ -14,3 +14,4 @@ path = \"src/lib.rs\"\n>  \n>  [dependencies]\n>  libc = \"0.2.155\"\n> +home = \"0.5.9\"\n\nOkay, here's where we get to my previous mention of supported platforms.\nThis depends on Rust 1.70, and Debian stable has only 1.63.  Trying\n`cargo build --release` on that version returns this:\n\n  Downloaded home v0.5.9\n  Downloaded libc v0.2.155\n  Downloaded 2 crates (752.3 KB) in 0.17s\nerror: package `home v0.5.9` cannot be built because it requires rustc 1.70.0 or newer, while the currently active rustc version is 1.63.0\n\nMy recommended approach here is to support the version in Debian stable,\nplus the version in Debian oldstable for a year after the new stable\ncomes out, which is what I do.  That gives people a year to upgrade if\nthey want to use our code.  We _don't_ want to follow the\nlatest-stable-Rust approach because it isn't appropriate that software\nhas a six-week lifespan of support and that isn't going to work for\nsoftware like Git that people often compile locally on older versions.\n\nWe also need to be conscious that while Rust upstream provides some\nbinaries for some platforms, many platforms rely on the distro packages\nbecause Rust upstream doesn't ship binaries for their target.  Thus,\nsimply using rustup is not viable for many targets, which is another\nreason that latest-stable-Rust won't fly.\n\nDebian stable is the version that most projects who have defined\nlifespans track, so it's also what we should track.  According to my\nrecommended approach, that would be 1.63.\n\nIf the Rust project agrees to provide LTS versions, then we can switch\nto those.\n\nIn any event, whatever we decide is necessarily going to involve us very\ncarefully planning our dependencies since some crates depend on the\nlatest version whenever it comes out and we're not going to want to do\nthat.\n\nI'd also note that we don't actually want the home crate.  The\nREADME says this:\n\n  The definition of home_dir provided by the standard library is\n  incorrect because it considers the HOME environment variable on\n  Windows. This causes surprising situations where a Rust program will\n  behave differently depending on whether it is run under a Unix\n  emulation environment like Cygwin or MinGW. Neither Cargo nor rustup\n  use the standard library's definition - they use the definition here.\n\nExcept that in Git, we _do_ want to honour `HOME` at all times.  Thus,\nthe use of the `home` crate instead of the standard library provides\nexactly the wrong behaviour, and we should remove this dependency.\n-- \nbrian m. carlson (they/them or he/him)\nToronto, Ontario, CA\n"},{"id":"500357","messageId":"0a4601dae912$68d8e920$3a8abb60$@nexbridge.com","threadId":"61917","inReplyTo":"ZrPlQRAGQDMnVGjo@tapette.crustytoothpaste.net","subject":"RE: [RFC PATCH 3/6] contrib/cgit-rs: introduce Rust wrapper for libgit.a","fromName":"","fromEmail":"rsbecker@nexbridge.com","sentAt":"2024-08-07T21:40:17Z","receivedAt":"2024-08-07T21:40:36Z","isPatch":true,"sender":{"key":"randall.becker@nexbridge.ca","avatar":"https://avatars.githubusercontent.com/u/28956764?v=4"},"body":"On Wednesday, August 7, 2024 5:21 PM, brian m. carlson wrote:\n>On 2024-08-07 at 18:21:28, Josh Steadmon wrote:\n>> Introduce cgit-rs, a Rust wrapper crate that allows Rust code to call\n>> functions in libgit.a. This initial patch defines build rules and an\n>> interface that exposes user agent string getter functions as a proof\n>> of concept. A proof-of-concept library consumer is provided in\n>> contrib/cgit-rs/src/main.rs. This executable can be run with `cargo\n>> run`\n>>\n>> Symbols in cgit can collide with symbols from other libraries such as\n>> libgit2. We avoid this by first exposing library symbols in\n>> public_symbol_export.[ch]. These symbols are prepended with \"libgit_\"\n>> to avoid collisions and set to visible using a visibility pragma. In\n>> build.rs, Rust builds contrib/cgit-rs/libcgit.a, which also contains\n>> libgit.a and other dependent libraries, with -fvisibility=hidden to\n>> hide all symbols within those libraries that haven't been exposed with\n>> a visibility pragma.\n>\n>I think this is a good idea.  It's optional and it allows us to add functionality as we go\n>along.  Platforms that don't have Rust can just omit building it.\n>\n>> +[dependencies]\n>> +libc = \"0.2.155\"\n>\n>I don't love that we're using libc here.  It would be better to use rustix because that\n>provides safe APIs that are compatible with POSIX, but I think for now we need this\n>because rustix doesn't offer memory management like free(3).  I'd really prefer that\n>we didn't have to do memory management in Rust, but maybe that can come in\n>with a future series.\n\nThis is a good point. Libc is not portable, but because I can't build with RUST anyway,\nI hope that libc is restricted to this facility if used. It should not be included in the\ngit C build. It is probably moot for me anyway for this series, but I have to mention it\nin case anyone else gets the idea to include it as a dependency for git C.\n\n>libc also comes with the downside that it calls the actual libc functions, so you have\n>to write things like stat64 on Linux if you want a 64-bit stat, but that doesn't exist\n>on some of the BSDs, so you have to write something else and compile it\n>conditionally, and all of that makes the portability of it worse than with C.\n>\n>In any event, I have the intention to send a patch to replace libc with rustix in the\n>future if this series lands.\n>\n>> diff --git a/contrib/cgit-rs/public_symbol_export.c\n>> b/contrib/cgit-rs/public_symbol_export.c\n>> new file mode 100644\n>> index 0000000000..3d1cd6cc4f\n>> --- /dev/null\n>> +++ b/contrib/cgit-rs/public_symbol_export.c\n>> @@ -0,0 +1,20 @@\n>> +// Shim to publicly export Git symbols. These must be renamed so that\n>> +the // original symbols can be hidden. Renaming these with a\n>> +\"libgit_\" prefix also // avoid conflicts with other libraries such as libgit2.\n>> +\n>> +#include \"contrib/cgit-rs/public_symbol_export.h\"\n>> +#include \"version.h\"\n>> +\n>> +#pragma GCC visibility push(default)\n>\n>I assume this also works in clang?\n>--\n>brian m. carlson (they/them or he/him)\n>Toronto, Ontario, CA\n\n"},{"id":"500358","messageId":"xmqq4j7wngnz.fsf@gitster.g","threadId":"61917","inReplyTo":"ZrPpxE7OZtqsbD81@tapette.crustytoothpaste.net","subject":"Re: [RFC PATCH 6/6] contrib/cgit-rs: add a subset of configset wrappers","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2024-08-07T21:53:36Z","receivedAt":"2024-08-07T21:53:48Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"brian m. carlson\" <sandals@crustytoothpaste.net> writes:\n\n> My recommended approach here is to support the version in Debian stable,\n> plus the version in Debian oldstable for a year after the new stable\n> comes out, which is what I do.  That gives people a year to upgrade if\n> they want to use our code.  We _don't_ want to follow the\n> latest-stable-Rust approach because it isn't appropriate that software\n> has a six-week lifespan of support and that isn't going to work for\n> software like Git that people often compile locally on older versions.\n\nThis is sensible.  Conservative enough to avoid leaving folks on\nother major distros behind, but still not too old to be a burden.\n\n"},{"id":"500359","messageId":"ZrPvSWoUOEaUIjWq@tapette.crustytoothpaste.net","threadId":"61917","inReplyTo":"cover.1723054623.git.steadmon@google.com","subject":"Re: [RFC PATCH 0/6] [RFC] Introduce cgit-rs, a Rust wrapper around libgit.a","fromName":"brian m. carlson","fromEmail":"sandals@crustytoothpaste.net","sentAt":"2024-08-07T22:03:53Z","receivedAt":"2024-08-07T22:03:56Z","isPatch":true,"sender":{"key":"sandals@crustytoothpaste.net","avatar":"https://avatars.githubusercontent.com/u/497054?v=4"},"body":"On 2024-08-07 at 18:21:25, Josh Steadmon wrote:\n> We are putting error handling on hold for now since it is too complex\n> and we intend other CLIs to be our first customers, in which case\n> printing out errors is not the worst.\n\nWhile I think that's okay for now, that really needs to be one of the\nfirst priorities of things we fix.  I have some ideas about an approach\nwhich we can take, which I'll send out in the next few days in a\nseparate thread.\n\n> While the wrapper itself lives in contrib/, there are a couple of\n> patches that touch git.git code. These patches are necessary for the\n> wrapper, but not for git.git itself, which may seem unnecessary to\n> merge. However, I would argue that other languages (not just limited to\n> Rust) have issues calling functions that require a pointer to\n> non-generic objects and essentially require a redefinition in their own\n> language.\n\nI don't see a problem with this.  It seems very self contained.\n\n> We're sending this series as RFC because there is remaining work\n> we'd like to do, but we'd like to get early feedback on this approach,\n> and particularly to ask for advice on a few topics:\n> \n> * alternative methods of exposing only a subset of symbols in our\n>   library\n\nWe should probably support symbol versioning in the libgit_ code on\nsupported platforms if we're going to expose it.  Otherwise, we're\nsetting ourselves and distributors up for a world of pain when we change\nthe ABI, such as when we add error handling.\n\n> * bikeshedding on the name (yes, really). There is an active, unrelated\n>   CGit project [4] that we only recently became aware of. We originally\n>   took the name \"cgit\" because at $DAYJOB we sometimes refer to git.git\n>   as \"cgit\" to distinguish it from jgit [5].\n> \n> * gauging the level of interest in calling Git code from Rust\n\nI left some comments in the series.  I think this is a nice first step\nas a proof of concept, and I'm very pleased to see it.\n\nIf your goal is to simply expose C APIs, I suggest calling it something\nlike cgit-sys, since the -sys suffix is customary for wrappers of C\nlibraries.  If your goal is to provide actual wrappers that people will\nuse in real Rust code, then I think that we should _not_ expose the C\nAPI, since most Rust users will not want to use that.\n-- \nbrian m. carlson (they/them or he/him)\nToronto, Ontario, CA\n"},{"id":"500360","messageId":"20240807225246.7jhqioyqh4e2ibza@glandium.org","threadId":"61917","inReplyTo":"5f2e816cf6359725f2a86ce1d08e5e272fba4dac.1723054623.git.steadmon@google.com","subject":"Re: [RFC PATCH 2/6] repository: add initialize_repo wrapper without pointer","fromName":"Mike Hommey","fromEmail":"mh@glandium.org","sentAt":"2024-08-07T22:52:46Z","receivedAt":"2024-08-07T22:52:51Z","isPatch":true,"sender":{"key":"mh@glandium.org","avatar":"https://avatars.githubusercontent.com/u/1038527?v=4"},"body":"On Wed, Aug 07, 2024 at 11:21:27AM -0700, Josh Steadmon wrote:\n> Non-C external consumers of libgit.a have to redefine the `repository`\n> object in their own language if they want to call\n> initialize_repository() to ensure memory for the object is allocated\n> correctly. This is not ideal for external consumers that have no need\n> for the entire `the_repository` object but need to call other functions\n> from an initialized repository. Therefore, add a friendly\n> initialize_repository() wrapper without a `the_repository` pointer.\n\nTechnically speaking, you don't really need this.\n\nYou can define `repository` as an opaque type in Rust:\n```\n#[allow(non_camel_case_types)]\n#[repr(C)]\npub struct repository([u8; 0]);\n```\n\nAnd define `the_repository` as an extern symbol:\n```\nextern \"C\" {\n    pub static mut the_repository: *mut repository;\n}\n```\n\nMike\n"},{"id":"500361","messageId":"s3goovaobkxlq5kmhcycfxh7mt3bw7igrsyi7dqttkbcgpqtcn@wjixqesll5tx","threadId":"61917","inReplyTo":"xmqqr0b0ni5n.fsf@gitster.g","subject":"Re: [RFC PATCH 1/6] common-main: split common_exit() into a new file","fromName":"Josh Steadmon","fromEmail":"steadmon@google.com","sentAt":"2024-08-07T22:54:53Z","receivedAt":"2024-08-07T22:54:59Z","isPatch":true,"sender":{"key":"steadmon@google.com","avatar":"https://avatars.githubusercontent.com/u/2654920?v=4"},"body":"On 2024.08.07 14:21, Junio C Hamano wrote:\n> Josh Steadmon <steadmon@google.com> writes:\n> \n> > diff --git a/common-exit.c b/common-exit.c\n> > new file mode 100644\n> > index 0000000000..1aaa538be3\n> > --- /dev/null\n> > +++ b/common-exit.c\n> > @@ -0,0 +1,26 @@\n> > +#include \"git-compat-util.h\"\n> > +#include \"trace2.h\"\n> > +\n> > +static void check_bug_if_BUG(void)\n> > +{\n> > +\tif (!bug_called_must_BUG)\n> > +\t\treturn;\n> > +\tBUG(\"on exit(): had bug() call(s) in this process without explicit BUG_if_bug()\");\n> > +}\n> \n> Nice that this can stay file-scope static.\n> \n> > +/* We wrap exit() to call common_exit() in git-compat-util.h */\n> > +int common_exit(const char *file, int line, int code)\n> > +{\n> > +\t/*\n> > +\t * For non-POSIX systems: Take the lowest 8 bits of the \"code\"\n> > +\t * to e.g. turn -1 into 255. On a POSIX system this is\n> > +\t * redundant, see exit(3) and wait(2), but as it doesn't harm\n> > +\t * anything there we don't need to guard this with an \"ifdef\".\n> > +\t */\n> > +\tcode &= 0xff;\n> > +\n> > +\tcheck_bug_if_BUG();\n> > +\ttrace2_cmd_exit_fl(file, line, code);\n> > +\n> > +\treturn code;\n> > +}\n> \n> I wonder if at least the part that primes the trace2 system needs to\n> also be split out of the file that defines our own main(), though.\n> \n> Are libgit.a users now responsible for calling things like\n> trace2_initialize_clock(), trace2_initialize(), etc., themselves?  I\n> am wondering if these calls are encapsulated into a simple helper\n> function, say common_startup(), then the story we need to tell the\n> libgit.a users may become simpler, i.e. you call common_startup, do\n> your things, and then you somehow cause common_exit() to be called.\n\nYeah, I actually already have a fixup patch to move the initialization\ninto a separate object file as well. Will include in V2.\n\n"},{"id":"500363","messageId":"20240807224756.2zq5hkfq5j43b7jk@glandium.org","threadId":"61917","inReplyTo":"9a846c17c891e17566a9907b3627210a6a08ea76.1723054623.git.steadmon@google.com","subject":"Re: [RFC PATCH 3/6] contrib/cgit-rs: introduce Rust wrapper for libgit.a","fromName":"Mike Hommey","fromEmail":"mh@glandium.org","sentAt":"2024-08-07T22:47:56Z","receivedAt":"2024-08-07T23:01:11Z","isPatch":true,"sender":{"key":"mh@glandium.org","avatar":"https://avatars.githubusercontent.com/u/1038527?v=4"},"body":"On Wed, Aug 07, 2024 at 11:21:28AM -0700, Josh Steadmon wrote:\n> +contrib/cgit-rs/hidden_symbol_export.o: contrib/cgit-rs/partial_symbol_export.o\n> +\t$(OBJCOPY) --localize-hidden $^ $@\n\nThis is ELF-specific and won't work on Mac or Windows.\n\n> +    let make_output = std::process::Command::new(\"make\")\n> +        .env_remove(\"PROFILE\")\n> +        .current_dir(git_root.clone())\n> +        .args(&[\n> +            \"CC=clang\",\n\nYou should probably not set CC at all here.\n\n> +            \"CFLAGS=-fvisibility=hidden\",\n\nThis won't work for Windows targets.\n\n> +            \"contrib/cgit-rs/libcgit.a\"\n> +        ])\n> +        .output()\n> +        .expect(\"Make failed to run\");\n> +    if !make_output.status.success() {\n> +        panic!(\n> +                \"Make failed:\\n  stdout = {}\\n  stderr = {}\\n\",\n> +                String::from_utf8(make_output.stdout).unwrap(),\n> +                String::from_utf8(make_output.stderr).unwrap()\n> +        );\n> +    }\n> +    std::fs::copy(crate_root.join(\"libcgit.a\"), dst.join(\"libcgit.a\"))?;\n> +    println!(\"cargo::rustc-link-search=native={}\", dst.into_os_string().into_string().unwrap());\n\nYou might as well use `dst.display()`.\n\n> +    println!(\"cargo::rustc-link-lib=cgit\");\n> +    println!(\"cargo::rustc-link-lib=z\");\n> +    println!(\"cargo::rerun-if-changed={}\", git_root.into_os_string().into_string().unwrap());\n\nLikewise.\n\nMike\n"},{"id":"500364","messageId":"b5epjlsptw3punygmx2abmfnrkki6n6ta4fk3yse7iodlabr63@zss4z3575r7v","threadId":"61917","inReplyTo":"ZrPlQRAGQDMnVGjo@tapette.crustytoothpaste.net","subject":"Re: [RFC PATCH 3/6] contrib/cgit-rs: introduce Rust wrapper for libgit.a","fromName":"Josh Steadmon","fromEmail":"steadmon@google.com","sentAt":"2024-08-07T23:05:00Z","receivedAt":"2024-08-07T23:05:06Z","isPatch":true,"sender":{"key":"steadmon@google.com","avatar":"https://avatars.githubusercontent.com/u/2654920?v=4"},"body":"On 2024.08.07 21:21, brian m. carlson wrote:\n> On 2024-08-07 at 18:21:28, Josh Steadmon wrote:\n> > Introduce cgit-rs, a Rust wrapper crate that allows Rust code to call\n> > functions in libgit.a. This initial patch defines build rules and an\n> > interface that exposes user agent string getter functions as a proof of\n> > concept. A proof-of-concept library consumer is provided in\n> > contrib/cgit-rs/src/main.rs. This executable can be run with `cargo run`\n> > \n> > Symbols in cgit can collide with symbols from other libraries such as\n> > libgit2. We avoid this by first exposing library symbols in\n> > public_symbol_export.[ch]. These symbols are prepended with \"libgit_\" to\n> > avoid collisions and set to visible using a visibility pragma. In\n> > build.rs, Rust builds contrib/cgit-rs/libcgit.a, which also contains\n> > libgit.a and other dependent libraries, with -fvisibility=hidden to hide\n> > all symbols within those libraries that haven't been exposed with a\n> > visibility pragma.\n> \n> I think this is a good idea.  It's optional and it allows us to add\n> functionality as we go along.  Platforms that don't have Rust can just\n> omit building it.\n> \n> > +[dependencies]\n> > +libc = \"0.2.155\"\n> \n> I don't love that we're using libc here.  It would be better to use\n> rustix because that provides safe APIs that are compatible with POSIX,\n> but I think for now we need this because rustix doesn't offer memory\n> management like free(3).  I'd really prefer that we didn't have to do\n> memory management in Rust, but maybe that can come in with a future\n> series.\n\nYeah, needing to free() is the only thing we striclty need from libc\nright now. Please correct me if I'm wrong, but IIUC then any memory that\nis allocated on the C side and then passed to Rust needs one of:\n1) freed by libc::free() on the Rust side,\n2) passed back to the C side to be freed there, or\n3) leaked\n\nAm I correct in assuming that your opinion is that writing additional\n*_free() functions on the C side is worth it to avoid libc? If so, then\nI'm fine with including that in V2.\n\n> libc also comes with the downside that it calls the actual libc\n> functions, so you have to write things like stat64 on Linux if you want\n> a 64-bit stat, but that doesn't exist on some of the BSDs, so you have\n> to write something else and compile it conditionally, and all of that\n> makes the portability of it worse than with C.\n> \n> In any event, I have the intention to send a patch to replace libc with\n> rustix in the future if this series lands.\n\nEven if we can't fully eliminate libc in V2, I'll keep this in mind and\navoid adding additional usage of libc as much as possible.\n\n\n> > diff --git a/contrib/cgit-rs/public_symbol_export.c b/contrib/cgit-rs/public_symbol_export.c\n> > new file mode 100644\n> > index 0000000000..3d1cd6cc4f\n> > --- /dev/null\n> > +++ b/contrib/cgit-rs/public_symbol_export.c\n> > @@ -0,0 +1,20 @@\n> > +// Shim to publicly export Git symbols. These must be renamed so that the\n> > +// original symbols can be hidden. Renaming these with a \"libgit_\" prefix also\n> > +// avoid conflicts with other libraries such as libgit2.\n> > +\n> > +#include \"contrib/cgit-rs/public_symbol_export.h\"\n> > +#include \"version.h\"\n> > +\n> > +#pragma GCC visibility push(default)\n> \n> I assume this also works in clang?\n\nYep.\n\n\n> -- \n> brian m. carlson (they/them or he/him)\n> Toronto, Ontario, CA\n\n\n"},{"id":"500366","messageId":"zrbylipleb7fd7jrlnsampufeiuyiw2suwvd3tuxqoukhz6h52@dnjr7urrwzn3","threadId":"61917","inReplyTo":"0a4601dae912$68d8e920$3a8abb60$@nexbridge.com","subject":"Re: [RFC PATCH 3/6] contrib/cgit-rs: introduce Rust wrapper for libgit.a","fromName":"Josh Steadmon","fromEmail":"steadmon@google.com","sentAt":"2024-08-07T23:07:32Z","receivedAt":"2024-08-07T23:07:38Z","isPatch":true,"sender":{"key":"steadmon@google.com","avatar":"https://avatars.githubusercontent.com/u/2654920?v=4"},"body":"On 2024.08.07 17:40, rsbecker@nexbridge.com wrote:\n> On Wednesday, August 7, 2024 5:21 PM, brian m. carlson wrote:\n> >On 2024-08-07 at 18:21:28, Josh Steadmon wrote:\n> >> Introduce cgit-rs, a Rust wrapper crate that allows Rust code to call\n> >> functions in libgit.a. This initial patch defines build rules and an\n> >> interface that exposes user agent string getter functions as a proof\n> >> of concept. A proof-of-concept library consumer is provided in\n> >> contrib/cgit-rs/src/main.rs. This executable can be run with `cargo\n> >> run`\n> >>\n> >> Symbols in cgit can collide with symbols from other libraries such as\n> >> libgit2. We avoid this by first exposing library symbols in\n> >> public_symbol_export.[ch]. These symbols are prepended with \"libgit_\"\n> >> to avoid collisions and set to visible using a visibility pragma. In\n> >> build.rs, Rust builds contrib/cgit-rs/libcgit.a, which also contains\n> >> libgit.a and other dependent libraries, with -fvisibility=hidden to\n> >> hide all symbols within those libraries that haven't been exposed with\n> >> a visibility pragma.\n> >\n> >I think this is a good idea.  It's optional and it allows us to add functionality as we go\n> >along.  Platforms that don't have Rust can just omit building it.\n> >\n> >> +[dependencies]\n> >> +libc = \"0.2.155\"\n> >\n> >I don't love that we're using libc here.  It would be better to use rustix because that\n> >provides safe APIs that are compatible with POSIX, but I think for now we need this\n> >because rustix doesn't offer memory management like free(3).  I'd really prefer that\n> >we didn't have to do memory management in Rust, but maybe that can come in\n> >with a future series.\n> \n> This is a good point. Libc is not portable, but because I can't build with RUST anyway,\n> I hope that libc is restricted to this facility if used. It should not be included in the\n> git C build. It is probably moot for me anyway for this series, but I have to mention it\n> in case anyone else gets the idea to include it as a dependency for git C.\n\nI know you don't have access to Rust, but would you be able to test the\nsymbol visibility steps with `make contrib/cgit-rs/libcgit.a`?\n"},{"id":"500367","messageId":"2p3fkcw2iie53ojnqybhyxhzwsyhkm6qhreip7emeftkjprqrw@r6drczjhy7hu","threadId":"61917","inReplyTo":"ZrPmmBqK3IIiQutV@tapette.crustytoothpaste.net","subject":"Re: [RFC PATCH 4/6] contrib/cgit-rs: add repo initialization and config access","fromName":"Josh Steadmon","fromEmail":"steadmon@google.com","sentAt":"2024-08-07T23:14:43Z","receivedAt":"2024-08-07T23:14:49Z","isPatch":true,"sender":{"key":"steadmon@google.com","avatar":"https://avatars.githubusercontent.com/u/2654920?v=4"},"body":"On 2024.08.07 21:26, brian m. carlson wrote:\n> On 2024-08-07 at 18:21:29, Josh Steadmon wrote:\n> > diff --git a/contrib/cgit-rs/src/lib.rs b/contrib/cgit-rs/src/lib.rs\n> > index dc46e7ff42..df350e758f 100644\n> > --- a/contrib/cgit-rs/src/lib.rs\n> > +++ b/contrib/cgit-rs/src/lib.rs\n> > @@ -1,6 +1,17 @@\n> > -use libc::c_char;\n> > +use libc::{c_char, c_int};\n> >  \n> >  extern \"C\" {\n> > +    pub fn libgit_setup_git_directory() -> *const c_char;\n> > +\n> > +    // From config.c\n> > +    pub fn libgit_config_get_int(key: *const c_char, dest: *mut c_int) ->c_int;\n> > +\n> > +    // From repository.c\n> > +    pub fn libgit_initialize_the_repository();\n> > +\n> > +    // From parse.c\n> > +    pub fn libgit_parse_maybe_bool(val: *const c_char) -> c_int;\n> > +\n> >      // From version.c\n> >      pub fn libgit_user_agent() -> *const c_char;\n> >      pub fn libgit_user_agent_sanitized() -> *const c_char;\n> \n> I think it would be helpful if we didn't expose the raw C API in Rust.\n> Nobody is going to want to write code that uses that in Rust.\n> \n> If we _do_ expose that, it should be in a separate cgit-sys crate, which\n> is the customary naming, that exposes only that and then we can offer\n> nicer wrappers around it.\n\nYeah, that's a known NEEDSWORK that I forgot to mention in the cover\nletter. I'm also going to get in touch soon with the gitoxide &\nlibgit2-rs folks to see if there's any joint work that we can do in\nterms of defining nicer Rust APIs.\n\nSemi-relatedly, I was wondering if you might be able to answer a cargo /\ncrates.io question: for a crate such as cgit-rs, which is not located in\nthe root of its VCS repo, will cargo balk at downloading the full\ngit.git worktree? Our build.rs expects the full Git source to be\navailable at \"../..\" relative to the crate root. We've currently only\ntested consuming cgit-rs in JJ via a local path rather than through\ncrates.io.\n\nlibgit2-rs avoids this issue by including the C source of libgit2 as a\nsubmodule, but I'd prefer for cgit-rs to live in the git.git repo if at\nall possible.\n\n> > diff --git a/contrib/cgit-rs/src/main.rs b/contrib/cgit-rs/src/main.rs\n> > index 1794e3f43e..c5f8644fca 100644\n> > --- a/contrib/cgit-rs/src/main.rs\n> > +++ b/contrib/cgit-rs/src/main.rs\n> > @@ -1,4 +1,4 @@\n> > -use std::ffi::CStr;\n> > +use std::ffi::{CStr, CString};\n> >  \n> >  fn main() {\n> >      println!(\"Let's print some strings provided by Git\");\n> > @@ -7,4 +7,25 @@ fn main() {\n> >      println!(\"git_user_agent() = {:?}\", c_str);\n> >      println!(\"git_user_agent_sanitized() = {:?}\",\n> >          unsafe { CStr::from_ptr(cgit::libgit_user_agent_sanitized()) });\n> > +\n> > +    println!(\"\\nNow try passing args\");\n> > +    let test_arg = CString::new(\"test_arg\").unwrap();\n> > +    println!(\"git_parse_maybe_bool(...) = {:?}\",\n> > +        unsafe { cgit::libgit_parse_maybe_bool(test_arg.as_ptr()) });\n> > +\n> > +    println!(\"\\nCan we get an int out of our config??\");\n> > +    unsafe {\n> > +        cgit::libgit_initialize_the_repository();\n> > +        cgit::libgit_setup_git_directory();\n> > +        let mut val: libc::c_int = 0;\n> > +        let key = CString::new(\"trace2.eventNesting\").unwrap();\n> > +        cgit::libgit_config_get_int(\n> > +            key.as_ptr(),\n> > +            &mut val as *mut i32\n> > +        );\n> > +        println!(\n> > +            \"git_config_get_int(\\\"trace2.eventNesting\\\") -> {:?}\",\n> > +            val\n> > +        );\n> > +    };\n> >  }\n> \n> It seems like this code wants to be a set of tests and maybe it would\n> be helpful to write it as a few instead.  For example, we can assume\n> that our user-agent starts with `git/` assuming it hasn't been\n> overridden, so maybe we could write that as a test, or at least that we\n> got a valid C string.\n\nAgreed.\n\n> -- \n> brian m. carlson (they/them or he/him)\n> Toronto, Ontario, CA\n\n\n"},{"id":"500368","messageId":"izpeuh3hjhsutfubyipqairso4pmqghhryndu64skib5p2yvvc@wetoctwy2jty","threadId":"61917","inReplyTo":"ZrPvSWoUOEaUIjWq@tapette.crustytoothpaste.net","subject":"Re: [RFC PATCH 0/6] [RFC] Introduce cgit-rs, a Rust wrapper around libgit.a","fromName":"Josh Steadmon","fromEmail":"steadmon@google.com","sentAt":"2024-08-07T23:19:17Z","receivedAt":"2024-08-07T23:19:23Z","isPatch":true,"sender":{"key":"steadmon@google.com","avatar":"https://avatars.githubusercontent.com/u/2654920?v=4"},"body":"On 2024.08.07 22:03, brian m. carlson wrote:\n> On 2024-08-07 at 18:21:25, Josh Steadmon wrote:\n> > We are putting error handling on hold for now since it is too complex\n> > and we intend other CLIs to be our first customers, in which case\n> > printing out errors is not the worst.\n> \n> While I think that's okay for now, that really needs to be one of the\n> first priorities of things we fix.  I have some ideas about an approach\n> which we can take, which I'll send out in the next few days in a\n> separate thread.\n\nI'll be very interested in seeing this, please CC me.\n\n\n> > While the wrapper itself lives in contrib/, there are a couple of\n> > patches that touch git.git code. These patches are necessary for the\n> > wrapper, but not for git.git itself, which may seem unnecessary to\n> > merge. However, I would argue that other languages (not just limited to\n> > Rust) have issues calling functions that require a pointer to\n> > non-generic objects and essentially require a redefinition in their own\n> > language.\n> \n> I don't see a problem with this.  It seems very self contained.\n> \n> > We're sending this series as RFC because there is remaining work\n> > we'd like to do, but we'd like to get early feedback on this approach,\n> > and particularly to ask for advice on a few topics:\n> > \n> > * alternative methods of exposing only a subset of symbols in our\n> >   library\n> \n> We should probably support symbol versioning in the libgit_ code on\n> supported platforms if we're going to expose it.  Otherwise, we're\n> setting ourselves and distributors up for a world of pain when we change\n> the ABI, such as when we add error handling.\n\nSymbol visibility and versioning are both areas I'm not very familiar\nwith. I'll do some homework but might need additional help later on, so\nplease keep an eye out for beginner mistakes I might make in later\nversions.\n\n\n> > * bikeshedding on the name (yes, really). There is an active, unrelated\n> >   CGit project [4] that we only recently became aware of. We originally\n> >   took the name \"cgit\" because at $DAYJOB we sometimes refer to git.git\n> >   as \"cgit\" to distinguish it from jgit [5].\n> > \n> > * gauging the level of interest in calling Git code from Rust\n> \n> I left some comments in the series.  I think this is a nice first step\n> as a proof of concept, and I'm very pleased to see it.\n> \n> If your goal is to simply expose C APIs, I suggest calling it something\n> like cgit-sys, since the -sys suffix is customary for wrappers of C\n> libraries.  If your goal is to provide actual wrappers that people will\n> use in real Rust code, then I think that we should _not_ expose the C\n> API, since most Rust users will not want to use that.\n> -- \n> brian m. carlson (they/them or he/him)\n> Toronto, Ontario, CA\n\n\n"},{"id":"500369","messageId":"5miqrfkepbzi4qzu65invte3tk7weshq2fcnkyufnoih3ou6rp@siodpag6csmy","threadId":"61917","inReplyTo":"20240807225246.7jhqioyqh4e2ibza@glandium.org","subject":"Re: [RFC PATCH 2/6] repository: add initialize_repo wrapper without pointer","fromName":"Josh Steadmon","fromEmail":"steadmon@google.com","sentAt":"2024-08-07T23:23:05Z","receivedAt":"2024-08-07T23:23:11Z","isPatch":true,"sender":{"key":"steadmon@google.com","avatar":"https://avatars.githubusercontent.com/u/2654920?v=4"},"body":"On 2024.08.08 07:52, Mike Hommey wrote:\n> On Wed, Aug 07, 2024 at 11:21:27AM -0700, Josh Steadmon wrote:\n> > Non-C external consumers of libgit.a have to redefine the `repository`\n> > object in their own language if they want to call\n> > initialize_repository() to ensure memory for the object is allocated\n> > correctly. This is not ideal for external consumers that have no need\n> > for the entire `the_repository` object but need to call other functions\n> > from an initialized repository. Therefore, add a friendly\n> > initialize_repository() wrapper without a `the_repository` pointer.\n> \n> Technically speaking, you don't really need this.\n> \n> You can define `repository` as an opaque type in Rust:\n> ```\n> #[allow(non_camel_case_types)]\n> #[repr(C)]\n> pub struct repository([u8; 0]);\n> ```\n> \n> And define `the_repository` as an extern symbol:\n> ```\n> extern \"C\" {\n>     pub static mut the_repository: *mut repository;\n> }\n> ```\n> \n> Mike\n\nI've actually already done a refactor for V2 that will avoid using this\npatch entirely, but thank you for the pointer. We do something similar\nto opaquely wrap configset pointers in a later patch (we use an empty\nenum there, I'm not sure whether that approach or a zero-size array is\npreferred).\n"},{"id":"500370","messageId":"20240807232916.as2jaivlbvk6pa3r@glandium.org","threadId":"61917","inReplyTo":"5miqrfkepbzi4qzu65invte3tk7weshq2fcnkyufnoih3ou6rp@siodpag6csmy","subject":"Re: [RFC PATCH 2/6] repository: add initialize_repo wrapper without pointer","fromName":"Mike Hommey","fromEmail":"mh@glandium.org","sentAt":"2024-08-07T23:29:16Z","receivedAt":"2024-08-07T23:29:23Z","isPatch":true,"sender":{"key":"mh@glandium.org","avatar":"https://avatars.githubusercontent.com/u/1038527?v=4"},"body":"On Wed, Aug 07, 2024 at 04:23:05PM -0700, Josh Steadmon wrote:\n> On 2024.08.08 07:52, Mike Hommey wrote:\n> > On Wed, Aug 07, 2024 at 11:21:27AM -0700, Josh Steadmon wrote:\n> > > Non-C external consumers of libgit.a have to redefine the `repository`\n> > > object in their own language if they want to call\n> > > initialize_repository() to ensure memory for the object is allocated\n> > > correctly. This is not ideal for external consumers that have no need\n> > > for the entire `the_repository` object but need to call other functions\n> > > from an initialized repository. Therefore, add a friendly\n> > > initialize_repository() wrapper without a `the_repository` pointer.\n> > \n> > Technically speaking, you don't really need this.\n> > \n> > You can define `repository` as an opaque type in Rust:\n> > ```\n> > #[allow(non_camel_case_types)]\n> > #[repr(C)]\n> > pub struct repository([u8; 0]);\n> > ```\n> > \n> > And define `the_repository` as an extern symbol:\n> > ```\n> > extern \"C\" {\n> >     pub static mut the_repository: *mut repository;\n> > }\n> > ```\n> > \n> > Mike\n> \n> I've actually already done a refactor for V2 that will avoid using this\n> patch entirely, but thank you for the pointer. We do something similar\n> to opaquely wrap configset pointers in a later patch (we use an empty\n> enum there, I'm not sure whether that approach or a zero-size array is\n> preferred).\n\nAn empty enum is a never type, I wouldn't recommend using it as an opaque\nwrapper. It will likely lead to the compiler doing bad things.\nhttps://rust-lang.github.io/never-type-initiative/RFC.html\n\n`#[repr(C)]` and `[u8; 0]` are recommended by the nomicon.\nhttps://doc.rust-lang.org/nomicon/ffi.html#representing-opaque-structs\n\n(the PhantomPinned wasn't there last time I saw that page)\n\nMike\n"},{"id":"500371","messageId":"njekhcypd7x7vdxmkzbdbeblwm4bwiiz74saxdmzqeod6zz547@qkkwkmpjvwkd","threadId":"61917","inReplyTo":"20240807224756.2zq5hkfq5j43b7jk@glandium.org","subject":"Re: [RFC PATCH 3/6] contrib/cgit-rs: introduce Rust wrapper for libgit.a","fromName":"Josh Steadmon","fromEmail":"steadmon@google.com","sentAt":"2024-08-07T23:29:54Z","receivedAt":"2024-08-07T23:30:00Z","isPatch":true,"sender":{"key":"steadmon@google.com","avatar":"https://avatars.githubusercontent.com/u/2654920?v=4"},"body":"On 2024.08.08 07:47, Mike Hommey wrote:\n> On Wed, Aug 07, 2024 at 11:21:28AM -0700, Josh Steadmon wrote:\n> > +contrib/cgit-rs/hidden_symbol_export.o: contrib/cgit-rs/partial_symbol_export.o\n> > +\t$(OBJCOPY) --localize-hidden $^ $@\n> \n> This is ELF-specific and won't work on Mac or Windows.\n> \n> > +    let make_output = std::process::Command::new(\"make\")\n> > +        .env_remove(\"PROFILE\")\n> > +        .current_dir(git_root.clone())\n> > +        .args(&[\n> > +            \"CC=clang\",\n> \n> You should probably not set CC at all here.\n\nAck, fixed in V2.\n\n> > +            \"CFLAGS=-fvisibility=hidden\",\n> \n> This won't work for Windows targets.\n\nUnderstood. I'll have to do some studying on the symbol visibility\noptions.\n\n\n> > +            \"contrib/cgit-rs/libcgit.a\"\n> > +        ])\n> > +        .output()\n> > +        .expect(\"Make failed to run\");\n> > +    if !make_output.status.success() {\n> > +        panic!(\n> > +                \"Make failed:\\n  stdout = {}\\n  stderr = {}\\n\",\n> > +                String::from_utf8(make_output.stdout).unwrap(),\n> > +                String::from_utf8(make_output.stderr).unwrap()\n> > +        );\n> > +    }\n> > +    std::fs::copy(crate_root.join(\"libcgit.a\"), dst.join(\"libcgit.a\"))?;\n> > +    println!(\"cargo::rustc-link-search=native={}\", dst.into_os_string().into_string().unwrap());\n> \n> You might as well use `dst.display()`.\n\nWouldn't that fail silently in the event that the path is non-UTF-8? I\nthink I'd prefer to explicitly fail in that case, even if it seems\nunlikely.\n\n> > +    println!(\"cargo::rustc-link-lib=cgit\");\n> > +    println!(\"cargo::rustc-link-lib=z\");\n> > +    println!(\"cargo::rerun-if-changed={}\", git_root.into_os_string().into_string().unwrap());\n> \n> Likewise.\n> \n> Mike\n"},{"id":"500372","messageId":"0a8401dae924$c9e8e9b0$5dbabd10$@nexbridge.com","threadId":"61917","inReplyTo":"zrbylipleb7fd7jrlnsampufeiuyiw2suwvd3tuxqoukhz6h52@dnjr7urrwzn3","subject":"RE: [RFC PATCH 3/6] contrib/cgit-rs: introduce Rust wrapper for libgit.a","fromName":"","fromEmail":"rsbecker@nexbridge.com","sentAt":"2024-08-07T23:51:51Z","receivedAt":"2024-08-07T23:52:05Z","isPatch":true,"sender":{"key":"randall.becker@nexbridge.ca","avatar":"https://avatars.githubusercontent.com/u/28956764?v=4"},"body":"On Wednesday, August 7, 2024 7:08 PM, Josh Steadmon wrote:\n>On 2024.08.07 17:40, rsbecker@nexbridge.com wrote:\n>> On Wednesday, August 7, 2024 5:21 PM, brian m. carlson wrote:\n>> >On 2024-08-07 at 18:21:28, Josh Steadmon wrote:\n>> >> Introduce cgit-rs, a Rust wrapper crate that allows Rust code to\n>> >> call functions in libgit.a. This initial patch defines build rules\n>> >> and an interface that exposes user agent string getter functions as\n>> >> a proof of concept. A proof-of-concept library consumer is provided\n>> >> in contrib/cgit-rs/src/main.rs. This executable can be run with\n>> >> `cargo run`\n>> >>\n>> >> Symbols in cgit can collide with symbols from other libraries such\n>> >> as libgit2. We avoid this by first exposing library symbols in\n>> >> public_symbol_export.[ch]. These symbols are prepended with \"libgit_\"\n>> >> to avoid collisions and set to visible using a visibility pragma.\n>> >> In build.rs, Rust builds contrib/cgit-rs/libcgit.a, which also\n>> >> contains libgit.a and other dependent libraries, with\n>> >> -fvisibility=hidden to hide all symbols within those libraries that\n>> >> haven't been exposed with a visibility pragma.\n>> >\n>> >I think this is a good idea.  It's optional and it allows us to add\n>> >functionality as we go along.  Platforms that don't have Rust can just\nomit\n>building it.\n>> >\n>> >> +[dependencies]\n>> >> +libc = \"0.2.155\"\n>> >\n>> >I don't love that we're using libc here.  It would be better to use\n>> >rustix because that provides safe APIs that are compatible with\n>> >POSIX, but I think for now we need this because rustix doesn't offer\n>> >memory management like free(3).  I'd really prefer that we didn't\n>> >have to do memory management in Rust, but maybe that can come in with a\n>future series.\n>>\n>> This is a good point. Libc is not portable, but because I can't build\n>> with RUST anyway, I hope that libc is restricted to this facility if\n>> used. It should not be included in the git C build. It is probably\n>> moot for me anyway for this series, but I have to mention it in case\nanyone else\n>gets the idea to include it as a dependency for git C.\n>\n>I know you don't have access to Rust, but would you be able to test the\nsymbol\n>visibility steps with `make contrib/cgit-rs/libcgit.a`?\n\nOf course. Branch? URI?\n\n"},{"id":"500373","messageId":"ZrQJe32sYNOTSJGf@tapette.crustytoothpaste.net","threadId":"61917","inReplyTo":"b5epjlsptw3punygmx2abmfnrkki6n6ta4fk3yse7iodlabr63@zss4z3575r7v","subject":"Re: [RFC PATCH 3/6] contrib/cgit-rs: introduce Rust wrapper for libgit.a","fromName":"brian m. carlson","fromEmail":"sandals@crustytoothpaste.net","sentAt":"2024-08-07T23:55:39Z","receivedAt":"2024-08-07T23:55:41Z","isPatch":true,"sender":{"key":"sandals@crustytoothpaste.net","avatar":"https://avatars.githubusercontent.com/u/497054?v=4"},"body":"On 2024-08-07 at 23:05:00, Josh Steadmon wrote:\n> Yeah, needing to free() is the only thing we striclty need from libc\n> right now. Please correct me if I'm wrong, but IIUC then any memory that\n> is allocated on the C side and then passed to Rust needs one of:\n> 1) freed by libc::free() on the Rust side,\n> 2) passed back to the C side to be freed there, or\n> 3) leaked\n> \n> Am I correct in assuming that your opinion is that writing additional\n> *_free() functions on the C side is worth it to avoid libc? If so, then\n> I'm fine with including that in V2.\n\nI think if we're going to be writing a general purpose API for\nlibification, we probably should provide free functions.  Normally, that\nwill be a call to free(3), but in some cases we may need more complex\nlogic, and by providing those, we're making the API more consistent and\neasy to use.\n-- \nbrian m. carlson (they/them or he/him)\nToronto, Ontario, CA\n"},{"id":"500374","messageId":"ZrQLeQ9P8OcDWZX-@tapette.crustytoothpaste.net","threadId":"61917","inReplyTo":"2p3fkcw2iie53ojnqybhyxhzwsyhkm6qhreip7emeftkjprqrw@r6drczjhy7hu","subject":"Re: [RFC PATCH 4/6] contrib/cgit-rs: add repo initialization and config access","fromName":"brian m. carlson","fromEmail":"sandals@crustytoothpaste.net","sentAt":"2024-08-08T00:04:09Z","receivedAt":"2024-08-08T00:04:11Z","isPatch":true,"sender":{"key":"sandals@crustytoothpaste.net","avatar":"https://avatars.githubusercontent.com/u/497054?v=4"},"body":"On 2024-08-07 at 23:14:43, Josh Steadmon wrote:\n> Semi-relatedly, I was wondering if you might be able to answer a cargo /\n> crates.io question: for a crate such as cgit-rs, which is not located in\n> the root of its VCS repo, will cargo balk at downloading the full\n> git.git worktree? Our build.rs expects the full Git source to be\n> available at \"../..\" relative to the crate root. We've currently only\n> tested consuming cgit-rs in JJ via a local path rather than through\n> crates.io.\n\nThe documentation[0] says this:\n\n  Cargo fetches the git repository at that location and traverses the\n  file tree to find Cargo.toml file for the requested crate anywhere\n  inside the git repository. For example, regex-lite and regex-syntax\n  are members of rust-lang/regex repo and can be referred to by the\n  repo’s root URL (https://github.com/rust-lang/regex.git) regardless of\n  where in the file tree they reside.\n\nIf you want to test, you should be able to push it to a repository\nsomewhere, even if that's a local path, and specify like so to test:\n\n----\n[dependencies]\ncgit-rs = { version = \"0.1.0\", git = \"file:///path/to/git.git\", branch = \"cgit-rs\" }\n----\n\nand see if it finds it.\n\n[0] https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html\n-- \nbrian m. carlson (they/them or he/him)\nToronto, Ontario, CA\n"},{"id":"500375","messageId":"20240808001743.4sx32gbjtypq3rje@glandium.org","threadId":"61917","inReplyTo":"njekhcypd7x7vdxmkzbdbeblwm4bwiiz74saxdmzqeod6zz547@qkkwkmpjvwkd","subject":"Re: [RFC PATCH 3/6] contrib/cgit-rs: introduce Rust wrapper for libgit.a","fromName":"Mike Hommey","fromEmail":"mh@glandium.org","sentAt":"2024-08-08T00:17:43Z","receivedAt":"2024-08-08T00:17:55Z","isPatch":true,"sender":{"key":"mh@glandium.org","avatar":"https://avatars.githubusercontent.com/u/1038527?v=4"},"body":"On Wed, Aug 07, 2024 at 04:29:54PM -0700, Josh Steadmon wrote:\n> > You might as well use `dst.display()`.\n> \n> Wouldn't that fail silently in the event that the path is non-UTF-8? I\n> think I'd prefer to explicitly fail in that case, even if it seems\n> unlikely.\n\nThat's the theory, unfortunately, reality is that even the most central\nRust crates don't care:\nhttps://github.com/rust-lang/cc-rs/blob/main/src/lib.rs#L1357-L1360\n\nEven better, last time I tried, cargo or rustc (I don't remember which\none it was) would blatantly fail to work if the path is not UTF-8 in the\nfirst place.\n\nMike\n"},{"id":"500380","messageId":"ZrQgY1pehCH5O78w@tapette.crustytoothpaste.net","threadId":"61917","inReplyTo":"ZrPvSWoUOEaUIjWq@tapette.crustytoothpaste.net","subject":"Re: [RFC PATCH 0/6] [RFC] Introduce cgit-rs, a Rust wrapper around libgit.a","fromName":"brian m. carlson","fromEmail":"sandals@crustytoothpaste.net","sentAt":"2024-08-08T01:33:23Z","receivedAt":"2024-08-08T01:33:25Z","isPatch":true,"sender":{"key":"sandals@crustytoothpaste.net","avatar":"https://avatars.githubusercontent.com/u/497054?v=4"},"body":"On 2024-08-07 at 22:03:53, brian m. carlson wrote:\n> I left some comments in the series.  I think this is a nice first step\n> as a proof of concept, and I'm very pleased to see it.\n\nI noticed a couple of other things.  First, the code has not been run\nthrough rustfmt.  I think it would be helpful for us to do that since it\nmakes it easier to not argue about style and it can be easily enforced\nin CI.  It will also reduce diff noise, which I expect Junio will\nappreciate.\n\nSecond, cargo clippy complains about some of the code.  It's again\nhelpful if we can fix those warnings or, if they're not appropriate, to\ndisable them with an appropriate `allow` pragma.  (In this case, I think\nthey're both spot on, but I have seen some cases where I've disabled a\nwarning.)  This is something we may also want to test in CI in the\nfuture, and downstream users of our crate will appreciate not getting\nwarnings when using clippy themselves, so we should be kind to them.\n\nI noticed these because my editor complains about the latter and I have\nnow intuited enough of rustfmt's output that I can tell sometimes when\nthings aren't formatted with it.\n\nFor those members of the list who are less familiar with Rust, rustfmt\nis the standard code formatter (and formatting verifier) and clippy is a\nlint tool recommending best practices.  Both are shipped with Rust and\nusing both is customary for Rust projects.\n-- \nbrian m. carlson (they/them or he/him)\nToronto, Ontario, CA\n"},{"id":"500382","messageId":"xmqqo763lppr.fsf@gitster.g","threadId":"61917","inReplyTo":"ZrQJe32sYNOTSJGf@tapette.crustytoothpaste.net","subject":"Re: [RFC PATCH 3/6] contrib/cgit-rs: introduce Rust wrapper for libgit.a","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2024-08-08T02:21:04Z","receivedAt":"2024-08-08T02:21:07Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"brian m. carlson\" <sandals@crustytoothpaste.net> writes:\n\n> I think if we're going to be writing a general purpose API for\n> libification, we probably should provide free functions.  Normally, that\n> will be a call to free(3), but in some cases we may need more complex\n> logic, and by providing those, we're making the API more consistent and\n> easy to use.\n\nDo you mean that we should have variants of free() that are specific\nto each data structure?  E.g., Patrick taught fetch_task_release()\nto release the task structure itself, in addition to the resources\nit holds, while renaming fetch_task_release() to fetch_task_free(),\nwith ff25992c (submodule: fix leaking fetch tasks, 2024-08-07), so\nif cgit-sys wants to expose fetch_task object to the outside world,\nthe consumers would call fetch_task_free() on it?\n\n"},{"id":"500433","messageId":"ZrSxLU-7rmlvdTHC@zx2c4.com","threadId":"61917","inReplyTo":"cover.1723054623.git.steadmon@google.com","subject":"Re: [RFC PATCH 0/6] [RFC] Introduce cgit-rs, a Rust wrapper around libgit.a","fromName":"Jason A. Donenfeld","fromEmail":"jason@zx2c4.com","sentAt":"2024-08-08T11:51:09Z","receivedAt":"2024-08-08T11:51:18Z","isPatch":true,"sender":{"key":"jason@zx2c4.com","avatar":"https://gravatar.com/avatar/8a3d6c2049b8353b1e0fb78142042d142c9dc72975698d944933997974d9fac5?d=mp&s=160"},"body":"Hi,\n\nCgit maintainer here...\n\nOn Wed, Aug 07, 2024 at 11:21:25AM -0700, Josh Steadmon wrote:\n> * bikeshedding on the name (yes, really). There is an active, unrelated\n>   CGit project [4] that we only recently became aware of. We originally\n>   took the name \"cgit\" because at $DAYJOB we sometimes refer to git.git\n>   as \"cgit\" to distinguish it from jgit [5].\n\nIndeed, please find something else. Cgit is a real project, used by\nmany, such as git.kernel.org, and it'll turn into a real hassle for both\nof us if there's ambiguity and confusion.\n\nWhat about libgit-rs? Or even libgit3, where the rustness of it is\nsimply an implementation detail, so the name won't feel dated 15 years\nfrom now when everything is written in Rust anyway and -rs is so 2020s?\n\nJason\n"},{"id":"500466","messageId":"9fbe5ca8bf133db8f614c0e90ac5fccb@manjaro.org","threadId":"61917","inReplyTo":"ZrSxLU-7rmlvdTHC@zx2c4.com","subject":"Re: [RFC PATCH 0/6] [RFC] Introduce cgit-rs, a Rust wrapper around libgit.a","fromName":"Dragan Simic","fromEmail":"dsimic@manjaro.org","sentAt":"2024-08-08T13:59:12Z","receivedAt":"2024-08-08T13:59:15Z","isPatch":true,"sender":{"key":"dsimic@manjaro.org","avatar":null},"body":"On 2024-08-08 13:51, Jason A. Donenfeld wrote:\n> Cgit maintainer here...\n> \n> On Wed, Aug 07, 2024 at 11:21:25AM -0700, Josh Steadmon wrote:\n>> * bikeshedding on the name (yes, really). There is an active, \n>> unrelated\n>>   CGit project [4] that we only recently became aware of. We \n>> originally\n>>   took the name \"cgit\" because at $DAYJOB we sometimes refer to \n>> git.git\n>>   as \"cgit\" to distinguish it from jgit [5].\n> \n> Indeed, please find something else. Cgit is a real project, used by\n> many, such as git.kernel.org, and it'll turn into a real hassle for \n> both\n> of us if there's ambiguity and confusion.\n\nTotally agreed, naming it cgit-rs makes pretty much no sense.\n\n> What about libgit-rs? Or even libgit3, where the rustness of it is\n> simply an implementation detail, so the name won't feel dated 15 years\n> from now when everything is written in Rust anyway and -rs is so 2020s?\n\nWell, there are still very active commercial projects written in COBOL\nor Clipper, for example, so I wouldn't go as far as _everything_ being\nwritten in Rust at some point.\n"},{"id":"500479","messageId":"009101dae9a8$fc66bd20$f5343760$@nexbridge.com","threadId":"61917","inReplyTo":"9fbe5ca8bf133db8f614c0e90ac5fccb@manjaro.org","subject":"RE: [RFC PATCH 0/6] [RFC] Introduce cgit-rs, a Rust wrapper around libgit.a","fromName":"","fromEmail":"rsbecker@nexbridge.com","sentAt":"2024-08-08T15:38:09Z","receivedAt":"2024-08-08T15:38:20Z","isPatch":true,"sender":{"key":"randall.becker@nexbridge.ca","avatar":"https://avatars.githubusercontent.com/u/28956764?v=4"},"body":"On Thursday, August 8, 2024 9:59 AM, Dragan Simic wrote:\n>On 2024-08-08 13:51, Jason A. Donenfeld wrote:\n>> Cgit maintainer here...\n>>\n>> On Wed, Aug 07, 2024 at 11:21:25AM -0700, Josh Steadmon wrote:\n>>> * bikeshedding on the name (yes, really). There is an active,\n>>> unrelated\n>>>   CGit project [4] that we only recently became aware of. We\n>>> originally\n>>>   took the name \"cgit\" because at $DAYJOB we sometimes refer to\n>>> git.git\n>>>   as \"cgit\" to distinguish it from jgit [5].\n>>\n>> Indeed, please find something else. Cgit is a real project, used by\n>> many, such as git.kernel.org, and it'll turn into a real hassle for\n>> both of us if there's ambiguity and confusion.\n>\n>Totally agreed, naming it cgit-rs makes pretty much no sense.\n>\n>> What about libgit-rs? Or even libgit3, where the rustness of it is\n>> simply an implementation detail, so the name won't feel dated 15 years\n>> from now when everything is written in Rust anyway and -rs is so 2020s?\n>\n>Well, there are still very active commercial projects written in COBOL or Clipper, for\n>example, so I wouldn't go as far as _everything_ being written in Rust at some point.\n\nThere are hundreds of millions of lines of code in the NonStop (TAL, COBOL, C, Java,\nin that order) community that is actively maintained to this day, with no Rust\ninvolvement (not for lack of trying to get Rust ported). Without these projects, most\nof your credit and debit cards would probably not work. 😉 Many of these repositories\nare more than 500Mb, with a couple I have seen exceeding 2Gb (just for source code).\n\n"},{"id":"500481","messageId":"6668417d9663e805643c504369bf2506@manjaro.org","threadId":"61917","inReplyTo":"009101dae9a8$fc66bd20$f5343760$@nexbridge.com","subject":"Re: [RFC PATCH 0/6] [RFC] Introduce cgit-rs, a Rust wrapper around libgit.a","fromName":"Dragan Simic","fromEmail":"dsimic@manjaro.org","sentAt":"2024-08-08T15:47:37Z","receivedAt":"2024-08-08T15:47:39Z","isPatch":true,"sender":{"key":"dsimic@manjaro.org","avatar":null},"body":"On 2024-08-08 17:38, rsbecker@nexbridge.com wrote:\n> On Thursday, August 8, 2024 9:59 AM, Dragan Simic wrote:\n>> On 2024-08-08 13:51, Jason A. Donenfeld wrote:\n>>> Cgit maintainer here...\n>>> \n>>> On Wed, Aug 07, 2024 at 11:21:25AM -0700, Josh Steadmon wrote:\n>>>> * bikeshedding on the name (yes, really). There is an active,\n>>>> unrelated\n>>>>   CGit project [4] that we only recently became aware of. We\n>>>> originally\n>>>>   took the name \"cgit\" because at $DAYJOB we sometimes refer to\n>>>> git.git\n>>>>   as \"cgit\" to distinguish it from jgit [5].\n>>> \n>>> Indeed, please find something else. Cgit is a real project, used by\n>>> many, such as git.kernel.org, and it'll turn into a real hassle for\n>>> both of us if there's ambiguity and confusion.\n>> \n>> Totally agreed, naming it cgit-rs makes pretty much no sense.\n>> \n>>> What about libgit-rs? Or even libgit3, where the rustness of it is\n>>> simply an implementation detail, so the name won't feel dated 15 \n>>> years\n>>> from now when everything is written in Rust anyway and -rs is so \n>>> 2020s?\n>> \n>> Well, there are still very active commercial projects written in COBOL \n>> or Clipper, for\n>> example, so I wouldn't go as far as _everything_ being written in Rust \n>> at some point.\n> \n> There are hundreds of millions of lines of code in the NonStop (TAL,\n> COBOL, C, Java,\n> in that order) community that is actively maintained to this day, with \n> no Rust\n> involvement (not for lack of trying to get Rust ported). Without these\n> projects, most\n> of your credit and debit cards would probably not work. 😉 Many of\n> these repositories\n> are more than 500Mb, with a couple I have seen exceeding 2Gb (just for\n> source code).\n\nThose are good examples.  Also, I believe that the entire financial \nsystem of\na country from South America runs on software written in Clipper, and \nthey keep\nproducing modern Clipper compilers that run on Windows.  Let me remind \nyou that\nCliper was used back in the days of DOS and 5.25-inch floppies. :)\n"},{"id":"500505","messageId":"bs36iyp3yzb5l52t6yw5364odvugrt22jfeypqohtzdwp22ecj@kurf4p62qibu","threadId":"61917","inReplyTo":"0a8401dae924$c9e8e9b0$5dbabd10$@nexbridge.com","subject":"Re: [RFC PATCH 3/6] contrib/cgit-rs: introduce Rust wrapper for libgit.a","fromName":"Josh Steadmon","fromEmail":"steadmon@google.com","sentAt":"2024-08-08T17:13:12Z","receivedAt":"2024-08-08T17:13:18Z","isPatch":true,"sender":{"key":"steadmon@google.com","avatar":"https://avatars.githubusercontent.com/u/2654920?v=4"},"body":"On 2024.08.07 19:51, rsbecker@nexbridge.com wrote:\n> On Wednesday, August 7, 2024 7:08 PM, Josh Steadmon wrote:\n> >On 2024.08.07 17:40, rsbecker@nexbridge.com wrote:\n> >> On Wednesday, August 7, 2024 5:21 PM, brian m. carlson wrote:\n> >> >On 2024-08-07 at 18:21:28, Josh Steadmon wrote:\n> >> >> Introduce cgit-rs, a Rust wrapper crate that allows Rust code to\n> >> >> call functions in libgit.a. This initial patch defines build rules\n> >> >> and an interface that exposes user agent string getter functions as\n> >> >> a proof of concept. A proof-of-concept library consumer is provided\n> >> >> in contrib/cgit-rs/src/main.rs. This executable can be run with\n> >> >> `cargo run`\n> >> >>\n> >> >> Symbols in cgit can collide with symbols from other libraries such\n> >> >> as libgit2. We avoid this by first exposing library symbols in\n> >> >> public_symbol_export.[ch]. These symbols are prepended with \"libgit_\"\n> >> >> to avoid collisions and set to visible using a visibility pragma.\n> >> >> In build.rs, Rust builds contrib/cgit-rs/libcgit.a, which also\n> >> >> contains libgit.a and other dependent libraries, with\n> >> >> -fvisibility=hidden to hide all symbols within those libraries that\n> >> >> haven't been exposed with a visibility pragma.\n> >> >\n> >> >I think this is a good idea.  It's optional and it allows us to add\n> >> >functionality as we go along.  Platforms that don't have Rust can just\n> omit\n> >building it.\n> >> >\n> >> >> +[dependencies]\n> >> >> +libc = \"0.2.155\"\n> >> >\n> >> >I don't love that we're using libc here.  It would be better to use\n> >> >rustix because that provides safe APIs that are compatible with\n> >> >POSIX, but I think for now we need this because rustix doesn't offer\n> >> >memory management like free(3).  I'd really prefer that we didn't\n> >> >have to do memory management in Rust, but maybe that can come in with a\n> >future series.\n> >>\n> >> This is a good point. Libc is not portable, but because I can't build\n> >> with RUST anyway, I hope that libc is restricted to this facility if\n> >> used. It should not be included in the git C build. It is probably\n> >> moot for me anyway for this series, but I have to mention it in case\n> anyone else\n> >gets the idea to include it as a dependency for git C.\n> >\n> >I know you don't have access to Rust, but would you be able to test the\n> symbol\n> >visibility steps with `make contrib/cgit-rs/libcgit.a`?\n> \n> Of course. Branch? URI?\n\nhttps://github.com/steadmon/git/tree/cgit-dev\n"},{"id":"500506","messageId":"em2spwpn4c3uzi2v2bnwn5y34vnmjkkajlmhkqmyjf547q7xgy@m2d4y2z4yypl","threadId":"61917","inReplyTo":"xmqqo763lppr.fsf@gitster.g","subject":"Re: [RFC PATCH 3/6] contrib/cgit-rs: introduce Rust wrapper for libgit.a","fromName":"Josh Steadmon","fromEmail":"steadmon@google.com","sentAt":"2024-08-08T17:15:57Z","receivedAt":"2024-08-08T17:16:03Z","isPatch":true,"sender":{"key":"steadmon@google.com","avatar":"https://avatars.githubusercontent.com/u/2654920?v=4"},"body":"On 2024.08.07 19:21, Junio C Hamano wrote:\n> \"brian m. carlson\" <sandals@crustytoothpaste.net> writes:\n> \n> > I think if we're going to be writing a general purpose API for\n> > libification, we probably should provide free functions.  Normally, that\n> > will be a call to free(3), but in some cases we may need more complex\n> > logic, and by providing those, we're making the API more consistent and\n> > easy to use.\n> \n> Do you mean that we should have variants of free() that are specific\n> to each data structure?  E.g., Patrick taught fetch_task_release()\n> to release the task structure itself, in addition to the resources\n> it holds, while renaming fetch_task_release() to fetch_task_free(),\n> with ff25992c (submodule: fix leaking fetch tasks, 2024-08-07), so\n> if cgit-sys wants to expose fetch_task object to the outside world,\n> the consumers would call fetch_task_free() on it?\n\nYes, although hopefully the higher-level API we build on top of cgit-sys\nwill be able to hide this from the Rust consumers.\n"},{"id":"500507","messageId":"xmqqv80bgcde.fsf@gitster.g","threadId":"61917","inReplyTo":"ZrSxLU-7rmlvdTHC@zx2c4.com","subject":"Re: [RFC PATCH 0/6] [RFC] Introduce cgit-rs, a Rust wrapper around libgit.a","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2024-08-08T17:20:29Z","receivedAt":"2024-08-08T17:20:35Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"Jason A. Donenfeld\" <Jason@zx2c4.com> writes:\n\n> Cgit maintainer here...\n>\n> On Wed, Aug 07, 2024 at 11:21:25AM -0700, Josh Steadmon wrote:\n>> * bikeshedding on the name (yes, really). There is an active, unrelated\n>>   CGit project [4] that we only recently became aware of. We originally\n>>   took the name \"cgit\" because at $DAYJOB we sometimes refer to git.git\n>>   as \"cgit\" to distinguish it from jgit [5].\n>\n> Indeed, please find something else. Cgit is a real project, used by\n> many, such as git.kernel.org, and it'll turn into a real hassle for both\n> of us if there's ambiguity and confusion.\n>\n> What about libgit-rs? Or even libgit3, where the rustness of it is\n> simply an implementation detail, so the name won't feel dated 15 years\n> from now when everything is written in Rust anyway and -rs is so 2020s?\n\nlibgit-rs sounds like a descriptive and good name.\n\nI am not sure about the wisdom of _not_ saying anything about Rust.\nIn 2035 maybe nobody is writing in Rust but in not-yet-invented\nalternative that is even better.\n"},{"id":"500509","messageId":"33jdrobowiv25qk3cfjctozeemtyox4ywlj33dqnhcjnua6n36@alzbo7hjbbll","threadId":"61917","inReplyTo":"20240808001743.4sx32gbjtypq3rje@glandium.org","subject":"Re: [RFC PATCH 3/6] contrib/cgit-rs: introduce Rust wrapper for libgit.a","fromName":"Josh Steadmon","fromEmail":"steadmon@google.com","sentAt":"2024-08-08T17:23:27Z","receivedAt":"2024-08-08T17:23:33Z","isPatch":true,"sender":{"key":"steadmon@google.com","avatar":"https://avatars.githubusercontent.com/u/2654920?v=4"},"body":"On 2024.08.08 09:17, Mike Hommey wrote:\n> On Wed, Aug 07, 2024 at 04:29:54PM -0700, Josh Steadmon wrote:\n> > > You might as well use `dst.display()`.\n> > \n> > Wouldn't that fail silently in the event that the path is non-UTF-8? I\n> > think I'd prefer to explicitly fail in that case, even if it seems\n> > unlikely.\n> \n> That's the theory, unfortunately, reality is that even the most central\n> Rust crates don't care:\n> https://github.com/rust-lang/cc-rs/blob/main/src/lib.rs#L1357-L1360\n> \n> Even better, last time I tried, cargo or rustc (I don't remember which\n> one it was) would blatantly fail to work if the path is not UTF-8 in the\n> first place.\n> \n> Mike\n\nThanks for the pointers. Fixed in V2.\n"},{"id":"500512","messageId":"om763zxrkc63svsxp2dzyzwgcglbq743xcsgijf7trzb2i5e6z@3vtm6dzws7i5","threadId":"61917","inReplyTo":"20240807232916.as2jaivlbvk6pa3r@glandium.org","subject":"Re: [RFC PATCH 2/6] repository: add initialize_repo wrapper without pointer","fromName":"Josh Steadmon","fromEmail":"steadmon@google.com","sentAt":"2024-08-08T17:50:32Z","receivedAt":"2024-08-08T17:50:38Z","isPatch":true,"sender":{"key":"steadmon@google.com","avatar":"https://avatars.githubusercontent.com/u/2654920?v=4"},"body":"On 2024.08.08 08:29, Mike Hommey wrote:\n> On Wed, Aug 07, 2024 at 04:23:05PM -0700, Josh Steadmon wrote:\n> > On 2024.08.08 07:52, Mike Hommey wrote:\n> > > On Wed, Aug 07, 2024 at 11:21:27AM -0700, Josh Steadmon wrote:\n> > > > Non-C external consumers of libgit.a have to redefine the `repository`\n> > > > object in their own language if they want to call\n> > > > initialize_repository() to ensure memory for the object is allocated\n> > > > correctly. This is not ideal for external consumers that have no need\n> > > > for the entire `the_repository` object but need to call other functions\n> > > > from an initialized repository. Therefore, add a friendly\n> > > > initialize_repository() wrapper without a `the_repository` pointer.\n> > > \n> > > Technically speaking, you don't really need this.\n> > > \n> > > You can define `repository` as an opaque type in Rust:\n> > > ```\n> > > #[allow(non_camel_case_types)]\n> > > #[repr(C)]\n> > > pub struct repository([u8; 0]);\n> > > ```\n> > > \n> > > And define `the_repository` as an extern symbol:\n> > > ```\n> > > extern \"C\" {\n> > >     pub static mut the_repository: *mut repository;\n> > > }\n> > > ```\n> > > \n> > > Mike\n> > \n> > I've actually already done a refactor for V2 that will avoid using this\n> > patch entirely, but thank you for the pointer. We do something similar\n> > to opaquely wrap configset pointers in a later patch (we use an empty\n> > enum there, I'm not sure whether that approach or a zero-size array is\n> > preferred).\n> \n> An empty enum is a never type, I wouldn't recommend using it as an opaque\n> wrapper. It will likely lead to the compiler doing bad things.\n> https://rust-lang.github.io/never-type-initiative/RFC.html\n> \n> `#[repr(C)]` and `[u8; 0]` are recommended by the nomicon.\n> https://doc.rust-lang.org/nomicon/ffi.html#representing-opaque-structs\n> \n> (the PhantomPinned wasn't there last time I saw that page)\n> \n> Mike\n\nFixed in V2 (for ConfigSet). Thanks!\n"},{"id":"500514","messageId":"htakxe76kl7ll3q7trjj6cjnsrg4tnue2k46zo25bnf3zre7t7@r74vgvebdhsx","threadId":"61917","inReplyTo":"ZrQJe32sYNOTSJGf@tapette.crustytoothpaste.net","subject":"Re: [RFC PATCH 3/6] contrib/cgit-rs: introduce Rust wrapper for libgit.a","fromName":"Josh Steadmon","fromEmail":"steadmon@google.com","sentAt":"2024-08-08T18:22:15Z","receivedAt":"2024-08-08T18:22:21Z","isPatch":true,"sender":{"key":"steadmon@google.com","avatar":"https://avatars.githubusercontent.com/u/2654920?v=4"},"body":"On 2024.08.07 23:55, brian m. carlson wrote:\n> On 2024-08-07 at 23:05:00, Josh Steadmon wrote:\n> > Yeah, needing to free() is the only thing we striclty need from libc\n> > right now. Please correct me if I'm wrong, but IIUC then any memory that\n> > is allocated on the C side and then passed to Rust needs one of:\n> > 1) freed by libc::free() on the Rust side,\n> > 2) passed back to the C side to be freed there, or\n> > 3) leaked\n> > \n> > Am I correct in assuming that your opinion is that writing additional\n> > *_free() functions on the C side is worth it to avoid libc? If so, then\n> > I'm fine with including that in V2.\n> \n> I think if we're going to be writing a general purpose API for\n> libification, we probably should provide free functions.  Normally, that\n> will be a call to free(3)\n\n[snip]\n\nSo in this case, does that mean we'd replace our call to `libc::free()`\nwith just `free()`, and then add a declaration for `free` in our\n`extern \"C\"` section of cgit-sys? It seems to work on my machine, but is\nthat actually the more portable option compared to using libc::free? Or\nhave I misunderstood something?\n"},{"id":"500515","messageId":"00d901dae9c2$dc9c29c0$95d47d40$@nexbridge.com","threadId":"61917","inReplyTo":"bs36iyp3yzb5l52t6yw5364odvugrt22jfeypqohtzdwp22ecj@kurf4p62qibu","subject":"RE: [RFC PATCH 3/6] contrib/cgit-rs: introduce Rust wrapper for libgit.a","fromName":"","fromEmail":"rsbecker@nexbridge.com","sentAt":"2024-08-08T18:43:23Z","receivedAt":"2024-08-08T18:43:38Z","isPatch":true,"sender":{"key":"randall.becker@nexbridge.ca","avatar":"https://avatars.githubusercontent.com/u/28956764?v=4"},"body":"On Thursday, August 8, 2024 1:13 PM, Josh Steadmon wrote:\n>On 2024.08.07 19:51, rsbecker@nexbridge.com wrote:\n>> On Wednesday, August 7, 2024 7:08 PM, Josh Steadmon wrote:\n>> >On 2024.08.07 17:40, rsbecker@nexbridge.com wrote:\n>> >> On Wednesday, August 7, 2024 5:21 PM, brian m. carlson wrote:\n>> >> >On 2024-08-07 at 18:21:28, Josh Steadmon wrote:\n>> >> >> Introduce cgit-rs, a Rust wrapper crate that allows Rust code to\n>> >> >> call functions in libgit.a. This initial patch defines build\n>> >> >> rules and an interface that exposes user agent string getter\n>> >> >> functions as a proof of concept. A proof-of-concept library\n>> >> >> consumer is provided in contrib/cgit-rs/src/main.rs. This\n>> >> >> executable can be run with `cargo run`\n>> >> >>\n>> >> >> Symbols in cgit can collide with symbols from other libraries\n>> >> >> such as libgit2. We avoid this by first exposing library symbols\n>> >> >> in public_symbol_export.[ch]. These symbols are prepended with\n\"libgit_\"\n>> >> >> to avoid collisions and set to visible using a visibility pragma.\n>> >> >> In build.rs, Rust builds contrib/cgit-rs/libcgit.a, which also\n>> >> >> contains libgit.a and other dependent libraries, with\n>> >> >> -fvisibility=hidden to hide all symbols within those libraries\n>> >> >> that haven't been exposed with a visibility pragma.\n>> >> >\n>> >> >I think this is a good idea.  It's optional and it allows us to\n>> >> >add functionality as we go along.  Platforms that don't have Rust\n>> >> >can just\n>> omit\n>> >building it.\n>> >> >\n>> >> >> +[dependencies]\n>> >> >> +libc = \"0.2.155\"\n>> >> >\n>> >> >I don't love that we're using libc here.  It would be better to\n>> >> >use rustix because that provides safe APIs that are compatible\n>> >> >with POSIX, but I think for now we need this because rustix\n>> >> >doesn't offer memory management like free(3).  I'd really prefer\n>> >> >that we didn't have to do memory management in Rust, but maybe\n>> >> >that can come in with a\n>> >future series.\n>> >>\n>> >> This is a good point. Libc is not portable, but because I can't\n>> >> build with RUST anyway, I hope that libc is restricted to this\n>> >> facility if used. It should not be included in the git C build. It\n>> >> is probably moot for me anyway for this series, but I have to\n>> >> mention it in case\n>> anyone else\n>> >gets the idea to include it as a dependency for git C.\n>> >\n>> >I know you don't have access to Rust, but would you be able to test\n>> >the\n>> symbol\n>> >visibility steps with `make contrib/cgit-rs/libcgit.a`?\n>>\n>> Of course. Branch? URI?\n>\n>https://github.com/steadmon/git/tree/cgit-dev\n\nI got to:\n\nld -r contrib/cgit-rs/public_symbol_export.o libgit.a reftable/libreftable.a\nxdiff/lib.a -o contrib/cgit-rs/partial_symbol_export.o\n/usr/bin/c89: illegal option -- r\n\nThe -r option is not supported on NonStop. I think we had discussed this.\n\n"},{"id":"500516","messageId":"xmqqcymihjny.fsf@gitster.g","threadId":"61917","inReplyTo":"00d901dae9c2$dc9c29c0$95d47d40$@nexbridge.com","subject":"Re: [RFC PATCH 3/6] contrib/cgit-rs: introduce Rust wrapper for libgit.a","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2024-08-08T19:57:37Z","receivedAt":"2024-08-08T19:57:40Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"<rsbecker@nexbridge.com> writes:\n\n> I got to:\n>\n> ld -r contrib/cgit-rs/public_symbol_export.o libgit.a reftable/libreftable.a\n> xdiff/lib.a -o contrib/cgit-rs/partial_symbol_export.o\n> /usr/bin/c89: illegal option -- r\n>\n> The -r option is not supported on NonStop. I think we had discussed this.\n\nDoes it happen to call the feature under different name?  'r' stands\nfor relocatable output, but some linkers may call the feature as\nincremental linking and use '-i'.\n"},{"id":"500517","messageId":"00e901dae9cf$9065e570$b131b050$@nexbridge.com","threadId":"61917","inReplyTo":"xmqqcymihjny.fsf@gitster.g","subject":"RE: [RFC PATCH 3/6] contrib/cgit-rs: introduce Rust wrapper for libgit.a","fromName":"","fromEmail":"rsbecker@nexbridge.com","sentAt":"2024-08-08T20:14:19Z","receivedAt":"2024-08-08T20:14:35Z","isPatch":true,"sender":{"key":"randall.becker@nexbridge.ca","avatar":"https://avatars.githubusercontent.com/u/28956764?v=4"},"body":"On Thursday, August 8, 2024 3:58 PM, Junio C Hamano wrote:\n><rsbecker@nexbridge.com> writes:\n>\n>> I got to:\n>>\n>> ld -r contrib/cgit-rs/public_symbol_export.o libgit.a\n>> reftable/libreftable.a xdiff/lib.a -o\n>> contrib/cgit-rs/partial_symbol_export.o\n>> /usr/bin/c89: illegal option -- r\n>>\n>> The -r option is not supported on NonStop. I think we had discussed this.\n>\n>Does it happen to call the feature under different name?  'r' stands for\nrelocatable\n>output, but some linkers may call the feature as incremental linking and\nuse '-i'.\n\nWell... depends on some factors. ld should not be run directly. Other links\nare done\nvia the CC setting in config.mak.uname. That would invoke c99 for us for the\nlinker\nas with the rest of git C, and that would make thinks somewhat easier as the\nlinker\nis either xld or eld depending on the hardware.\n\nThe -Wr option to c99 creates a load file in this situation, which might or\nmight not\nwork. I need to look at it after built. I don't think it is what we want.\nWhat I think\nwe need is -Wxld=unres_symbols ignore which builds an object that can be\nlinked to other objects even with undefined link symbols - although maybe\nnot.\nI need to be able to select a different option on ia64, but that may not\nmatter\nmuch. I still worry that we are depending on non-portable linker semantics\nthat\nmay not give the result we ultimately want.\n\nWould not the build option be best selected in config.mak.uname?\n\nMaybe GITRS_LINKER= or GITRS_CFLAGS=, etc.\n\n"},{"id":"500518","messageId":"CAO_smVjEU4QH6JsLxt2v3ZYPEKwyd9gyJ4OjNgf1+mPVS6xoGg@mail.gmail.com","threadId":"61917","inReplyTo":"htakxe76kl7ll3q7trjj6cjnsrg4tnue2k46zo25bnf3zre7t7@r74vgvebdhsx","subject":"Re: [RFC PATCH 3/6] contrib/cgit-rs: introduce Rust wrapper for libgit.a","fromName":"Kyle Lippincott","fromEmail":"spectral@google.com","sentAt":"2024-08-08T20:18:26Z","receivedAt":"2024-08-08T20:18:40Z","isPatch":true,"sender":{"key":"spectral@google.com","avatar":"https://avatars.githubusercontent.com/u/6371650?v=4"},"body":"On Thu, Aug 8, 2024 at 11:22 AM Josh Steadmon <steadmon@google.com> wrote:\n>\n> On 2024.08.07 23:55, brian m. carlson wrote:\n> > On 2024-08-07 at 23:05:00, Josh Steadmon wrote:\n> > > Yeah, needing to free() is the only thing we striclty need from libc\n> > > right now. Please correct me if I'm wrong, but IIUC then any memory that\n> > > is allocated on the C side and then passed to Rust needs one of:\n> > > 1) freed by libc::free() on the Rust side,\n> > > 2) passed back to the C side to be freed there, or\n> > > 3) leaked\n> > >\n> > > Am I correct in assuming that your opinion is that writing additional\n> > > *_free() functions on the C side is worth it to avoid libc? If so, then\n> > > I'm fine with including that in V2.\n> >\n> > I think if we're going to be writing a general purpose API for\n> > libification, we probably should provide free functions.  Normally, that\n> > will be a call to free(3)\n>\n> [snip]\n>\n> So in this case, does that mean we'd replace our call to `libc::free()`\n> with just `free()`, and then add a declaration for `free` in our\n> `extern \"C\"` section of cgit-sys? It seems to work on my machine, but is\n> that actually the more portable option compared to using libc::free? Or\n> have I misunderstood something?\n\nI think both having a generic 'free' function, or requiring your API\nconsumer to have a compatible 'free' function is undesirable. If the\nAPI hands you something that you must return/free, there should be a\nfunction for that specifically. So I would expect if the API has a\n`libgit_foo_get(foo** f)` function, there'd be a paired\n`libgit_foo_release(foo* f)` (ignoring whatever squabbles we want to\nhave about the names). Requiring `libgit_foo_get(foo** f)` to be\npaired with `libc::free(f)` limits us to always using libc malloc;\npairing it with `libgit_free((void*)f)` means we can't refcount it /\nignore it if it's a part of a parent object, etc.\n"},{"id":"500520","messageId":"bglok2kx6kxebh3oicdhhij6ibynse26rkhdorkrbd4wyfek6v@dab4xzsyrujv","threadId":"61917","inReplyTo":"CAO_smVjEU4QH6JsLxt2v3ZYPEKwyd9gyJ4OjNgf1+mPVS6xoGg@mail.gmail.com","subject":"Re: [RFC PATCH 3/6] contrib/cgit-rs: introduce Rust wrapper for libgit.a","fromName":"Josh Steadmon","fromEmail":"steadmon@google.com","sentAt":"2024-08-08T20:43:07Z","receivedAt":"2024-08-08T20:43:14Z","isPatch":true,"sender":{"key":"steadmon@google.com","avatar":"https://avatars.githubusercontent.com/u/2654920?v=4"},"body":"On 2024.08.08 13:18, Kyle Lippincott wrote:\n> On Thu, Aug 8, 2024 at 11:22 AM Josh Steadmon <steadmon@google.com> wrote:\n> >\n> > On 2024.08.07 23:55, brian m. carlson wrote:\n> > > On 2024-08-07 at 23:05:00, Josh Steadmon wrote:\n> > > > Yeah, needing to free() is the only thing we striclty need from libc\n> > > > right now. Please correct me if I'm wrong, but IIUC then any memory that\n> > > > is allocated on the C side and then passed to Rust needs one of:\n> > > > 1) freed by libc::free() on the Rust side,\n> > > > 2) passed back to the C side to be freed there, or\n> > > > 3) leaked\n> > > >\n> > > > Am I correct in assuming that your opinion is that writing additional\n> > > > *_free() functions on the C side is worth it to avoid libc? If so, then\n> > > > I'm fine with including that in V2.\n> > >\n> > > I think if we're going to be writing a general purpose API for\n> > > libification, we probably should provide free functions.  Normally, that\n> > > will be a call to free(3)\n> >\n> > [snip]\n> >\n> > So in this case, does that mean we'd replace our call to `libc::free()`\n> > with just `free()`, and then add a declaration for `free` in our\n> > `extern \"C\"` section of cgit-sys? It seems to work on my machine, but is\n> > that actually the more portable option compared to using libc::free? Or\n> > have I misunderstood something?\n> \n> I think both having a generic 'free' function, or requiring your API\n> consumer to have a compatible 'free' function is undesirable. If the\n> API hands you something that you must return/free, there should be a\n> function for that specifically. So I would expect if the API has a\n> `libgit_foo_get(foo** f)` function, there'd be a paired\n> `libgit_foo_release(foo* f)` (ignoring whatever squabbles we want to\n> have about the names). Requiring `libgit_foo_get(foo** f)` to be\n> paired with `libc::free(f)` limits us to always using libc malloc;\n> pairing it with `libgit_free((void*)f)` means we can't refcount it /\n> ignore it if it's a part of a parent object, etc.\n\nSorry, the diff context got removed, the the particular case I was\ntalking about here is where we get back a strdup()ed string from some of\nthe config API calls. I guess we could add a c_str_free() somewhere on\nthe Git side and call that, but it seems like a bit of overkill.\n\nI do agree with you about providing _free() [or in our case,\n_clear_and_free()] functions for more complicated data types though.\n"},{"id":"500526","messageId":"lclyd6lzrtoc3mndaz3ghfpbuaa4yu2q65mar24ge6qwpt3rit@trwicfugsx23","threadId":"61917","inReplyTo":"ZrPpxE7OZtqsbD81@tapette.crustytoothpaste.net","subject":"Re: [RFC PATCH 6/6] contrib/cgit-rs: add a subset of configset wrappers","fromName":"Josh Steadmon","fromEmail":"steadmon@google.com","sentAt":"2024-08-08T21:44:23Z","receivedAt":"2024-08-08T21:44:29Z","isPatch":true,"sender":{"key":"steadmon@google.com","avatar":"https://avatars.githubusercontent.com/u/2654920?v=4"},"body":"On 2024.08.07 21:40, brian m. carlson wrote:\n> On 2024-08-07 at 18:21:31, Josh Steadmon wrote:\n> > diff --git a/contrib/cgit-rs/Cargo.toml b/contrib/cgit-rs/Cargo.toml\n> > index 7b55e6f3e1..5768fce9e5 100644\n> > --- a/contrib/cgit-rs/Cargo.toml\n> > +++ b/contrib/cgit-rs/Cargo.toml\n> > @@ -14,3 +14,4 @@ path = \"src/lib.rs\"\n> >  \n> >  [dependencies]\n> >  libc = \"0.2.155\"\n> > +home = \"0.5.9\"\n> \n> Okay, here's where we get to my previous mention of supported platforms.\n> This depends on Rust 1.70, and Debian stable has only 1.63.  Trying\n> `cargo build --release` on that version returns this:\n> \n>   Downloaded home v0.5.9\n>   Downloaded libc v0.2.155\n>   Downloaded 2 crates (752.3 KB) in 0.17s\n> error: package `home v0.5.9` cannot be built because it requires rustc 1.70.0 or newer, while the currently active rustc version is 1.63.0\n> \n> My recommended approach here is to support the version in Debian stable,\n> plus the version in Debian oldstable for a year after the new stable\n> comes out, which is what I do.  That gives people a year to upgrade if\n> they want to use our code.  We _don't_ want to follow the\n> latest-stable-Rust approach because it isn't appropriate that software\n> has a six-week lifespan of support and that isn't going to work for\n> software like Git that people often compile locally on older versions.\n> \n> We also need to be conscious that while Rust upstream provides some\n> binaries for some platforms, many platforms rely on the distro packages\n> because Rust upstream doesn't ship binaries for their target.  Thus,\n> simply using rustup is not viable for many targets, which is another\n> reason that latest-stable-Rust won't fly.\n> \n> Debian stable is the version that most projects who have defined\n> lifespans track, so it's also what we should track.  According to my\n> recommended approach, that would be 1.63.\n> \n> If the Rust project agrees to provide LTS versions, then we can switch\n> to those.\n> \n> In any event, whatever we decide is necessarily going to involve us very\n> carefully planning our dependencies since some crates depend on the\n> latest version whenever it comes out and we're not going to want to do\n> that.\n> \n> I'd also note that we don't actually want the home crate.  The\n> README says this:\n> \n>   The definition of home_dir provided by the standard library is\n>   incorrect because it considers the HOME environment variable on\n>   Windows. This causes surprising situations where a Rust program will\n>   behave differently depending on whether it is run under a Unix\n>   emulation environment like Cygwin or MinGW. Neither Cargo nor rustup\n>   use the standard library's definition - they use the definition here.\n> \n> Except that in Git, we _do_ want to honour `HOME` at all times.  Thus,\n> the use of the `home` crate instead of the standard library provides\n> exactly the wrong behaviour, and we should remove this dependency.\n> -- \n> brian m. carlson (they/them or he/him)\n> Toronto, Ontario, CA\n\nI've replaced home::home_dir with std::env::home_dir, however the latter\nis deprecated, so we may want to reimplement this entirely.\n"},{"id":"500561","messageId":"xmqqbk21cxhn.fsf@gitster.g","threadId":"61917","inReplyTo":"cover.1723054623.git.steadmon@google.com","subject":"Re: [RFC PATCH 0/6] [RFC] Introduce cgit-rs, a Rust wrapper around libgit.a","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2024-08-09T19:22:28Z","receivedAt":"2024-08-09T19:22:31Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Josh Steadmon <steadmon@google.com> writes:\n\n> We're sending this series as RFC because there is remaining work\n> we'd like to do, but we'd like to get early feedback on this approach,\n> and particularly to ask for advice on a few topics:\n\nI am not sure how much this is reusable, after seeing comments that\n\"cgit-rs\" may not be the best name for this thing and pathnames may\nhave to change, but I needed the following merge-fix to get this\ninto \"seen\" and have the result pass \"make\", due to interactions\nwith the ps/config-wo-the-repository topic.\n\n contrib/cgit-rs/public_symbol_export.c | 4 +++-\n 1 file changed, 3 insertions(+), 1 deletion(-)\n\ndiff --git a/contrib/cgit-rs/public_symbol_export.c b/contrib/cgit-rs/public_symbol_export.c\nindex 9641afca89..2732f5898e 100644\n--- a/contrib/cgit-rs/public_symbol_export.c\n+++ b/contrib/cgit-rs/public_symbol_export.c\n@@ -9,6 +9,8 @@\n #include \"setup.h\"\n #include \"version.h\"\n \n+extern struct repository *the_repository;\n+\n #pragma GCC visibility push(default)\n \n const char *libgit_setup_git_directory(void)\n@@ -18,7 +20,7 @@ const char *libgit_setup_git_directory(void)\n \n int libgit_config_get_int(const char *key, int *dest)\n {\n-\treturn git_config_get_int(key, dest);\n+\treturn repo_config_get_int(the_repository, key, dest);\n }\n \n void libgit_initialize_the_repository(void)\n-- \n2.46.0-326-g1e046905fc\n\n"},{"id":"500564","messageId":"xmqq34ndcx5h.fsf@gitster.g","threadId":"61917","inReplyTo":"xmqqbk21cxhn.fsf@gitster.g","subject":"Re: [RFC PATCH 0/6] [RFC] Introduce cgit-rs, a Rust wrapper around libgit.a","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2024-08-09T19:29:46Z","receivedAt":"2024-08-09T19:29:58Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n> Josh Steadmon <steadmon@google.com> writes:\n>\n>> We're sending this series as RFC because there is remaining work\n>> we'd like to do, but we'd like to get early feedback on this approach,\n>> and particularly to ask for advice on a few topics:\n>\n> I am not sure how much this is reusable, after seeing comments that\n> \"cgit-rs\" may not be the best name for this thing and pathnames may\n> have to change, but I needed the following merge-fix to get this\n> into \"seen\" and have the result pass \"make\", due to interactions\n> with the ps/config-wo-the-repository topic.\n\nIn case you are wondering \"so... is there anything actionable for\n*US*???\", there isn't, exactly.  But you'd need to holler if the\n\"merge-fix\" you saw in the message not correctly addressing the\nsemantic clash between these two topics.\n\nThanks.\n\n>\n>  contrib/cgit-rs/public_symbol_export.c | 4 +++-\n>  1 file changed, 3 insertions(+), 1 deletion(-)\n>\n> diff --git a/contrib/cgit-rs/public_symbol_export.c b/contrib/cgit-rs/public_symbol_export.c\n> index 9641afca89..2732f5898e 100644\n> --- a/contrib/cgit-rs/public_symbol_export.c\n> +++ b/contrib/cgit-rs/public_symbol_export.c\n> @@ -9,6 +9,8 @@\n>  #include \"setup.h\"\n>  #include \"version.h\"\n>  \n> +extern struct repository *the_repository;\n> +\n>  #pragma GCC visibility push(default)\n>  \n>  const char *libgit_setup_git_directory(void)\n> @@ -18,7 +20,7 @@ const char *libgit_setup_git_directory(void)\n>  \n>  int libgit_config_get_int(const char *key, int *dest)\n>  {\n> -\treturn git_config_get_int(key, dest);\n> +\treturn repo_config_get_int(the_repository, key, dest);\n>  }\n>  \n>  void libgit_initialize_the_repository(void)\n"},{"id":"500571","messageId":"xmqqv809beoe.fsf@gitster.g","threadId":"61917","inReplyTo":"xmqqbk21cxhn.fsf@gitster.g","subject":"Re: [RFC PATCH 0/6] [RFC] Introduce cgit-rs, a Rust wrapper around libgit.a","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2024-08-09T20:54:09Z","receivedAt":"2024-08-09T20:54:14Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n> Josh Steadmon <steadmon@google.com> writes:\n>\n>> We're sending this series as RFC because there is remaining work\n>> we'd like to do, but we'd like to get early feedback on this approach,\n>> and particularly to ask for advice on a few topics:\n>\n> I am not sure how much this is reusable, after seeing comments that\n> \"cgit-rs\" may not be the best name for this thing and pathnames may\n> have to change, but I needed the following merge-fix to get this\n> into \"seen\" and have the result pass \"make\", due to interactions\n> with the ps/config-wo-the-repository topic.\n>\n>  contrib/cgit-rs/public_symbol_export.c | 4 +++-\n>  1 file changed, 3 insertions(+), 1 deletion(-)\n\nThere is another thing.\n\nListing this file in $(OBJECTS) means that you should be able to\npass \"make sparse\" to build contrib/cgit-rs/public_symbol_export.sp\nbut it seems to fail rather miserably.  I am tempted to suggest in\nthe meantime to futz with $(SP_OBJ) to filter it out in the top\nlevel Makefile.\n\n"},{"id":"500577","messageId":"mhwbjer3ti7psyuves7defo4grpdb4gaiz3axq6523pjglbkzh@cw2vfdyccgxf","threadId":"61917","inReplyTo":"ZrQgY1pehCH5O78w@tapette.crustytoothpaste.net","subject":"Re: [RFC PATCH 0/6] [RFC] Introduce cgit-rs, a Rust wrapper around libgit.a","fromName":"Josh Steadmon","fromEmail":"steadmon@google.com","sentAt":"2024-08-09T22:16:05Z","receivedAt":"2024-08-09T22:16:12Z","isPatch":true,"sender":{"key":"steadmon@google.com","avatar":"https://avatars.githubusercontent.com/u/2654920?v=4"},"body":"On 2024.08.08 01:33, brian m. carlson wrote:\n> On 2024-08-07 at 22:03:53, brian m. carlson wrote:\n> > I left some comments in the series.  I think this is a nice first step\n> > as a proof of concept, and I'm very pleased to see it.\n> \n> I noticed a couple of other things.  First, the code has not been run\n> through rustfmt.  I think it would be helpful for us to do that since it\n> makes it easier to not argue about style and it can be easily enforced\n> in CI.  It will also reduce diff noise, which I expect Junio will\n> appreciate.\n> \n> Second, cargo clippy complains about some of the code.  It's again\n> helpful if we can fix those warnings or, if they're not appropriate, to\n> disable them with an appropriate `allow` pragma.  (In this case, I think\n> they're both spot on, but I have seen some cases where I've disabled a\n> warning.)  This is something we may also want to test in CI in the\n> future, and downstream users of our crate will appreciate not getting\n> warnings when using clippy themselves, so we should be kind to them.\n> \n> I noticed these because my editor complains about the latter and I have\n> now intuited enough of rustfmt's output that I can tell sometimes when\n> things aren't formatted with it.\n> \n> For those members of the list who are less familiar with Rust, rustfmt\n> is the standard code formatter (and formatting verifier) and clippy is a\n> lint tool recommending best practices.  Both are shipped with Rust and\n> using both is customary for Rust projects.\n> -- \n> brian m. carlson (they/them or he/him)\n> Toronto, Ontario, CA\n\nApplied Clippy and rustfmt fixes throughout V2, thanks for the catch.\n"},{"id":"500578","messageId":"xmqqa5hlbae0.fsf@gitster.g","threadId":"61917","inReplyTo":"xmqqv809beoe.fsf@gitster.g","subject":"Re: [RFC PATCH 0/6] [RFC] Introduce cgit-rs, a Rust wrapper around libgit.a","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2024-08-09T22:26:47Z","receivedAt":"2024-08-09T22:26:56Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n>>  contrib/cgit-rs/public_symbol_export.c | 4 +++-\n>>  1 file changed, 3 insertions(+), 1 deletion(-)\n>\n> There is another thing.\n>\n> Listing this file in $(OBJECTS) means that you should be able to\n> pass \"make sparse\" to build contrib/cgit-rs/public_symbol_export.sp\n> but it seems to fail rather miserably.  I am tempted to suggest in\n> the meantime to futz with $(SP_OBJ) to filter it out in the top\n> level Makefile.\n\nFWIW, this one _is_ actionable on your end ;-)  Make sure \"make sparse\"\ndoes not barf due to the addition of libgit-rust.\n"},{"id":"500579","messageId":"zjiyu63z55y3eubkov5avdylahz6v37euc4d22obnzrxtzwycq@ah6ejsp4jpsu","threadId":"61917","inReplyTo":"xmqq34ndcx5h.fsf@gitster.g","subject":"Re: [RFC PATCH 0/6] [RFC] Introduce cgit-rs, a Rust wrapper around libgit.a","fromName":"Josh Steadmon","fromEmail":"steadmon@google.com","sentAt":"2024-08-09T22:27:21Z","receivedAt":"2024-08-09T22:27:27Z","isPatch":true,"sender":{"key":"steadmon@google.com","avatar":"https://avatars.githubusercontent.com/u/2654920?v=4"},"body":"On 2024.08.09 12:29, Junio C Hamano wrote:\n> Junio C Hamano <gitster@pobox.com> writes:\n> \n> > Josh Steadmon <steadmon@google.com> writes:\n> >\n> >> We're sending this series as RFC because there is remaining work\n> >> we'd like to do, but we'd like to get early feedback on this approach,\n> >> and particularly to ask for advice on a few topics:\n> >\n> > I am not sure how much this is reusable, after seeing comments that\n> > \"cgit-rs\" may not be the best name for this thing and pathnames may\n> > have to change, but I needed the following merge-fix to get this\n> > into \"seen\" and have the result pass \"make\", due to interactions\n> > with the ps/config-wo-the-repository topic.\n> \n> In case you are wondering \"so... is there anything actionable for\n> *US*???\", there isn't, exactly.  But you'd need to holler if the\n> \"merge-fix\" you saw in the message not correctly addressing the\n> semantic clash between these two topics.\n> \n> Thanks.\n\nLooks fine to me, applied in V2.\n"},{"id":"500580","messageId":"jennrdjgi76ev4npmklozfpwfijmxttulwsp2h2pwqfuysyjvb@2gw3qyyt3su4","threadId":"61917","inReplyTo":"xmqqv809beoe.fsf@gitster.g","subject":"Re: [RFC PATCH 0/6] [RFC] Introduce cgit-rs, a Rust wrapper around libgit.a","fromName":"Josh Steadmon","fromEmail":"steadmon@google.com","sentAt":"2024-08-09T22:28:40Z","receivedAt":"2024-08-09T22:28:46Z","isPatch":true,"sender":{"key":"steadmon@google.com","avatar":"https://avatars.githubusercontent.com/u/2654920?v=4"},"body":"On 2024.08.09 13:54, Junio C Hamano wrote:\n> Junio C Hamano <gitster@pobox.com> writes:\n> \n> > Josh Steadmon <steadmon@google.com> writes:\n> >\n> >> We're sending this series as RFC because there is remaining work\n> >> we'd like to do, but we'd like to get early feedback on this approach,\n> >> and particularly to ask for advice on a few topics:\n> >\n> > I am not sure how much this is reusable, after seeing comments that\n> > \"cgit-rs\" may not be the best name for this thing and pathnames may\n> > have to change, but I needed the following merge-fix to get this\n> > into \"seen\" and have the result pass \"make\", due to interactions\n> > with the ps/config-wo-the-repository topic.\n> >\n> >  contrib/cgit-rs/public_symbol_export.c | 4 +++-\n> >  1 file changed, 3 insertions(+), 1 deletion(-)\n> \n> There is another thing.\n> \n> Listing this file in $(OBJECTS) means that you should be able to\n> pass \"make sparse\" to build contrib/cgit-rs/public_symbol_export.sp\n> but it seems to fail rather miserably.  I am tempted to suggest in\n> the meantime to futz with $(SP_OBJ) to filter it out in the top\n> level Makefile.\n\nI believe that I fixed `make sparse` (at least in GitHub CI, it fails\nfor seemingly unrelated reasons on my desktop) by removing some\nunnecessarily exposed symbols in public_symbol_export.c. If it still\nfails for you in V2, please let me know.\n"},{"id":"500583","messageId":"xmqqmsll9vki.fsf@gitster.g","threadId":"61917","inReplyTo":"jennrdjgi76ev4npmklozfpwfijmxttulwsp2h2pwqfuysyjvb@2gw3qyyt3su4","subject":"Re: [RFC PATCH 0/6] [RFC] Introduce cgit-rs, a Rust wrapper around libgit.a","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2024-08-09T22:32:13Z","receivedAt":"2024-08-09T22:32:18Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Josh Steadmon <steadmon@google.com> writes:\n\n> On 2024.08.09 13:54, Junio C Hamano wrote:\n>> Junio C Hamano <gitster@pobox.com> writes:\n>> \n>> > Josh Steadmon <steadmon@google.com> writes:\n>> >\n>> >> We're sending this series as RFC because there is remaining work\n>> >> we'd like to do, but we'd like to get early feedback on this approach,\n>> >> and particularly to ask for advice on a few topics:\n>> >\n>> > I am not sure how much this is reusable, after seeing comments that\n>> > \"cgit-rs\" may not be the best name for this thing and pathnames may\n>> > have to change, but I needed the following merge-fix to get this\n>> > into \"seen\" and have the result pass \"make\", due to interactions\n>> > with the ps/config-wo-the-repository topic.\n>> >\n>> >  contrib/cgit-rs/public_symbol_export.c | 4 +++-\n>> >  1 file changed, 3 insertions(+), 1 deletion(-)\n>> \n>> There is another thing.\n>> \n>> Listing this file in $(OBJECTS) means that you should be able to\n>> pass \"make sparse\" to build contrib/cgit-rs/public_symbol_export.sp\n>> but it seems to fail rather miserably.  I am tempted to suggest in\n>> the meantime to futz with $(SP_OBJ) to filter it out in the top\n>> level Makefile.\n>\n> I believe that I fixed `make sparse` (at least in GitHub CI, it fails\n> for seemingly unrelated reasons on my desktop) by removing some\n> unnecessarily exposed symbols in public_symbol_export.c. If it still\n> fails for you in V2, please let me know.\n\nThanks.  Please let me know when you send v2 out ;-)\n"},{"id":"500585","messageId":"cover.1723242556.git.steadmon@google.com","threadId":"61917","inReplyTo":"cover.1723054623.git.steadmon@google.com","subject":"[PATCH v2 0/5] Introduce cgit-rs, a Rust wrapper around libgit.a","fromName":"Josh Steadmon","fromEmail":"steadmon@google.com","sentAt":"2024-08-09T22:41:12Z","receivedAt":"2024-08-09T22:41:20Z","isPatch":true,"sender":{"key":"steadmon@google.com","avatar":"https://avatars.githubusercontent.com/u/2654920?v=4"},"body":"This series provides two small Rust wrapper libraries around parts of\nGit: \"cgit-sys\", which exposes a few functions from libgit.a, and\n\"cgit\", which provides a more Rust-friendly interface to some of those\nfunctions. In addition to included unit tests, at $DAYJOB we have tested\nbuilding JJ[1] with our library and used it to replace some of the\nlibgit2-rs uses.\n\n[1] https://github.com/martinvonz/jj\n\nPlease find V1 cover letter here:\nhttps://lore.kernel.org/git/cover.1723054623.git.steadmon@google.com/\n\nChanges in V2:\n* Split out the exposed C APIs into a cgit-sys module, and build nicer\n  Rust interfaces on top of that in cgit-rs\n\n* Replace the proof-of-concept cgit-info binary with unit tests\n\n* In addition to splitting common_exit() into a new file, also move the\n  initialization code in main() to a new init_git() function in its own\n  file, and include this in libgit.a. This allows us to drop V1 patch #2\n  [which added a wrapper around initialize_repo()]\n\n* Remove libc dependency (by declaring an extern \"C\" free function, not\n  sure if this is the best approach)\n\n* Add git_configset_clear_and_free to also support the above\n\n* Use \"std::env::home_dir\" instead of the \"home\" crate due to desired\n  behavior on Windows. \"std::env::home_dir\" is deprecated, but is only\n  used in unit tests which will need to be rewritten anyway to provide a\n  testdata config.\n\n* Use recommended empty array + PhantomData approach instead of empty\n  enums for wrapping opaque C structs/pointers\n\n* Don't force CC=clang in build.rs\n\n* Simplify conversion of PathBufs to Strings in build.rs\n\n* Don't unnecessarily expose git_attr__true and git_attr__false in\n  public_symbol_export.c. That fixes `make sparse`.\n\n* Clippy and rustfmt cleanups throughout\n\n* Whitespace fix\n\nKnown NEEDSWORK:\n* Support older Rust versions\n\n* Investigate alternative methods of managing symbol visibility.\n\n* Figure out symbol versioning\n\n* Bikeshed on the name\n\n* Makefile cleanup, particularly adding config.mak options that\n  developers can set to run Rust builds and tests by default\n\n* Automate the process of exporting additional functions in cgit-sys\n  (possibly with a wrapper script around bindgen)\n\n* Provide testdata configs for unit tests\n\n* Rethink the Rust-y ConfigSet API, particularly for Path vs. &str\n\n\nCalvin Wan (2):\n  contrib/cgit-rs: add repo initialization and config access\n  contrib/cgit-rs: add a subset of configset wrappers\n\nJosh Steadmon (3):\n  common-main: split init and exit code into new files\n  contrib/cgit-rs: introduce Rust wrapper for libgit.a\n  config: add git_configset_alloc() and git_configset_clear_and_free()\n\n .gitignore                                    |   2 +\n Makefile                                      |  15 +++\n common-exit.c                                 |  26 ++++\n common-init.c                                 |  63 ++++++++++\n common-init.h                                 |   6 +\n common-main.c                                 |  83 +------------\n config.c                                      |  11 ++\n config.h                                      |  10 ++\n contrib/cgit-rs/Cargo.lock                    |  14 +++\n contrib/cgit-rs/Cargo.toml                    |  10 ++\n contrib/cgit-rs/cgit-sys/Cargo.lock           |   7 ++\n contrib/cgit-rs/cgit-sys/Cargo.toml           |   9 ++\n contrib/cgit-rs/cgit-sys/README.md            |  15 +++\n contrib/cgit-rs/cgit-sys/build.rs             |  32 +++++\n .../cgit-rs/cgit-sys/public_symbol_export.c   |  76 ++++++++++++\n .../cgit-rs/cgit-sys/public_symbol_export.h   |  28 +++++\n contrib/cgit-rs/cgit-sys/src/lib.rs           | 112 ++++++++++++++++++\n contrib/cgit-rs/src/lib.rs                    |  82 +++++++++++++\n 18 files changed, 520 insertions(+), 81 deletions(-)\n create mode 100644 common-exit.c\n create mode 100644 common-init.c\n create mode 100644 common-init.h\n create mode 100644 contrib/cgit-rs/Cargo.lock\n create mode 100644 contrib/cgit-rs/Cargo.toml\n create mode 100644 contrib/cgit-rs/cgit-sys/Cargo.lock\n create mode 100644 contrib/cgit-rs/cgit-sys/Cargo.toml\n create mode 100644 contrib/cgit-rs/cgit-sys/README.md\n create mode 100644 contrib/cgit-rs/cgit-sys/build.rs\n create mode 100644 contrib/cgit-rs/cgit-sys/public_symbol_export.c\n create mode 100644 contrib/cgit-rs/cgit-sys/public_symbol_export.h\n create mode 100644 contrib/cgit-rs/cgit-sys/src/lib.rs\n create mode 100644 contrib/cgit-rs/src/lib.rs\n\nRange-diff against v1:\n1:  78c2aa2ef9 ! 1:  800b37d16b common-main: split common_exit() into a new file\n    @@ Metadata\n     Author: Josh Steadmon <steadmon@google.com>\n     \n      ## Commit message ##\n    -    common-main: split common_exit() into a new file\n    +    common-main: split init and exit code into new files\n     \n         Currently, object files in libgit.a reference common_exit(), which is\n         contained in common-main.o. However, common-main.o also includes main(),\n    @@ Commit message\n         conflicts. Now we are trying to make libgit.a more self-contained, so\n         hopefully we can revisit this approach.\n     \n    +    Additionally, move the initialization code out of main() into a new\n    +    init_git() function in its own file. Include this in libgit.a as well,\n    +    so that external users can share our setup code without calling our\n    +    main().\n    +\n         [1] https://lore.kernel.org/git/Yp+wjCPhqieTku3X@google.com/\n         [2] https://lore.kernel.org/git/20230517-unit-tests-v2-v2-1-21b5b60f4b32@google.com/\n     \n    -    Signed-off-by: Josh Steadmon <steadmon@google.com>\n    -\n     \n      ## Makefile ##\n    @@ Makefile: LIB_OBJS += combine-diff.o\n      LIB_OBJS += commit-reach.o\n      LIB_OBJS += commit.o\n     +LIB_OBJS += common-exit.o\n    ++LIB_OBJS += common-init.o\n      LIB_OBJS += compat/nonblock.o\n      LIB_OBJS += compat/obstack.o\n      LIB_OBJS += compat/terminal.o\n    @@ common-exit.c (new)\n     +\treturn code;\n     +}\n     \n    + ## common-init.c (new) ##\n    +@@\n    ++#define USE_THE_REPOSITORY_VARIABLE\n    ++\n    ++#include \"git-compat-util.h\"\n    ++#include \"common-init.h\"\n    ++#include \"exec-cmd.h\"\n    ++#include \"gettext.h\"\n    ++#include \"attr.h\"\n    ++#include \"repository.h\"\n    ++#include \"setup.h\"\n    ++#include \"strbuf.h\"\n    ++#include \"trace2.h\"\n    ++\n    ++/*\n    ++ * Many parts of Git have subprograms communicate via pipe, expect the\n    ++ * upstream of a pipe to die with SIGPIPE when the downstream of a\n    ++ * pipe does not need to read all that is written.  Some third-party\n    ++ * programs that ignore or block SIGPIPE for their own reason forget\n    ++ * to restore SIGPIPE handling to the default before spawning Git and\n    ++ * break this carefully orchestrated machinery.\n    ++ *\n    ++ * Restore the way SIGPIPE is handled to default, which is what we\n    ++ * expect.\n    ++ */\n    ++static void restore_sigpipe_to_default(void)\n    ++{\n    ++\tsigset_t unblock;\n    ++\n    ++\tsigemptyset(&unblock);\n    ++\tsigaddset(&unblock, SIGPIPE);\n    ++\tsigprocmask(SIG_UNBLOCK, &unblock, NULL);\n    ++\tsignal(SIGPIPE, SIG_DFL);\n    ++}\n    ++\n    ++void init_git(const char **argv)\n    ++{\n    ++\tstruct strbuf tmp = STRBUF_INIT;\n    ++\n    ++\ttrace2_initialize_clock();\n    ++\n    ++\t/*\n    ++\t * Always open file descriptors 0/1/2 to avoid clobbering files\n    ++\t * in die().  It also avoids messing up when the pipes are dup'ed\n    ++\t * onto stdin/stdout/stderr in the child processes we spawn.\n    ++\t */\n    ++\tsanitize_stdfds();\n    ++\trestore_sigpipe_to_default();\n    ++\n    ++\tgit_resolve_executable_dir(argv[0]);\n    ++\n    ++\tsetlocale(LC_CTYPE, \"\");\n    ++\tgit_setup_gettext();\n    ++\n    ++\tinitialize_repository(the_repository);\n    ++\n    ++\tattr_start();\n    ++\n    ++\ttrace2_initialize();\n    ++\ttrace2_cmd_start(argv);\n    ++\ttrace2_collect_process_info(TRACE2_PROCESS_INFO_STARTUP);\n    ++\n    ++\tif (!strbuf_getcwd(&tmp))\n    ++\t\ttmp_original_cwd = strbuf_detach(&tmp, NULL);\n    ++}\n    +\n    + ## common-init.h (new) ##\n    +@@\n    ++#ifndef COMMON_INIT_H\n    ++#define COMMON_INIT_H\n    ++\n    ++void init_git(const char **argv);\n    ++\n    ++#endif /* COMMON_INIT_H */\n    +\n      ## common-main.c ##\n    -@@ common-main.c: int main(int argc, const char **argv)\n    +@@\n    +-#define USE_THE_REPOSITORY_VARIABLE\n    +-\n    + #include \"git-compat-util.h\"\n    +-#include \"exec-cmd.h\"\n    +-#include \"gettext.h\"\n    +-#include \"attr.h\"\n    +-#include \"repository.h\"\n    +-#include \"setup.h\"\n    +-#include \"strbuf.h\"\n    +-#include \"trace2.h\"\n    +-\n    +-/*\n    +- * Many parts of Git have subprograms communicate via pipe, expect the\n    +- * upstream of a pipe to die with SIGPIPE when the downstream of a\n    +- * pipe does not need to read all that is written.  Some third-party\n    +- * programs that ignore or block SIGPIPE for their own reason forget\n    +- * to restore SIGPIPE handling to the default before spawning Git and\n    +- * break this carefully orchestrated machinery.\n    +- *\n    +- * Restore the way SIGPIPE is handled to default, which is what we\n    +- * expect.\n    +- */\n    +-static void restore_sigpipe_to_default(void)\n    +-{\n    +-\tsigset_t unblock;\n    +-\n    +-\tsigemptyset(&unblock);\n    +-\tsigaddset(&unblock, SIGPIPE);\n    +-\tsigprocmask(SIG_UNBLOCK, &unblock, NULL);\n    +-\tsignal(SIGPIPE, SIG_DFL);\n    +-}\n    ++#include \"common-init.h\"\n    + \n    + int main(int argc, const char **argv)\n    + {\n    + \tint result;\n    +-\tstruct strbuf tmp = STRBUF_INIT;\n    +-\n    +-\ttrace2_initialize_clock();\n    +-\n    +-\t/*\n    +-\t * Always open file descriptors 0/1/2 to avoid clobbering files\n    +-\t * in die().  It also avoids messing up when the pipes are dup'ed\n    +-\t * onto stdin/stdout/stderr in the child processes we spawn.\n    +-\t */\n    +-\tsanitize_stdfds();\n    +-\trestore_sigpipe_to_default();\n    +-\n    +-\tgit_resolve_executable_dir(argv[0]);\n    +-\n    +-\tsetlocale(LC_CTYPE, \"\");\n    +-\tgit_setup_gettext();\n    +-\n    +-\tinitialize_repository(the_repository);\n    +-\n    +-\tattr_start();\n    +-\n    +-\ttrace2_initialize();\n    +-\ttrace2_cmd_start(argv);\n    +-\ttrace2_collect_process_info(TRACE2_PROCESS_INFO_STARTUP);\n    +-\n    +-\tif (!strbuf_getcwd(&tmp))\n    +-\t\ttmp_original_cwd = strbuf_detach(&tmp, NULL);\n    + \n    ++\tinit_git(argv);\n    + \tresult = cmd_main(argc, argv);\n    + \n      \t/* Not exit(3), but a wrapper calling our common_exit() */\n      \texit(result);\n      }\n2:  5f2e816cf6 < -:  ---------- repository: add initialize_repo wrapper without pointer\n3:  9a846c17c8 ! 2:  3589d2d6a2 contrib/cgit-rs: introduce Rust wrapper for libgit.a\n    @@ Commit message\n         Co-authored-by: Josh Steadmon <steadmon@google.com>\n         Signed-off-by: Calvin Wan <calvinwan@google.com>\n         Signed-off-by: Kyle Lippincott <spectral@google.com>\n    -    Signed-off-by: Josh Steadmon <steadmon@google.com>\n    -\n     \n      ## .gitignore ##\n    @@ .gitignore: Release/\n      /git.VC.db\n      *.dSYM\n      /contrib/buildsystems/out\n    -+/contrib/cgit-rs/target\n    ++/contrib/cgit-rs/cgit-sys/target\n     \n      ## Makefile ##\n     @@ Makefile: CURL_CONFIG = curl-config\n    @@ Makefile: OBJECTS += $(XDIFF_OBJS)\n      OBJECTS += $(FUZZ_OBJS)\n      OBJECTS += $(REFTABLE_OBJS) $(REFTABLE_TEST_OBJS)\n      OBJECTS += $(UNIT_TEST_OBJS)\n    -+OBJECTS += contrib/cgit-rs/public_symbol_export.o\n    ++OBJECTS += contrib/cgit-rs/cgit-sys/public_symbol_export.o\n      \n      ifndef NO_CURL\n      \tOBJECTS += http.o http-walker.o remote-curl.o\n    @@ Makefile: clean: profile-clean coverage-clean cocciclean\n      \t$(RM) $(htmldocs).tar.gz $(manpages).tar.gz\n      \t$(MAKE) -C Documentation/ clean\n      \t$(RM) Documentation/GIT-EXCLUDED-PROGRAMS\n    -+\t$(RM) -r contrib/cgit-rs/target\n    ++\t$(RM) -r contrib/cgit-rs/cgit-sys/target\n      ifndef NO_PERL\n      \t$(RM) -r perl/build/\n      endif\n    @@ Makefile: $(UNIT_TEST_PROGS): $(UNIT_TEST_BIN)/%$X: $(UNIT_TEST_DIR)/%.o \\\n      unit-tests: $(UNIT_TEST_PROGS) t/helper/test-tool$X\n      \t$(MAKE) -C t/ unit-tests\n     +\n    -+contrib/cgit-rs/partial_symbol_export.o: contrib/cgit-rs/public_symbol_export.o libgit.a reftable/libreftable.a xdiff/lib.a\n    ++contrib/cgit-rs/cgit-sys/partial_symbol_export.o: contrib/cgit-rs/cgit-sys/public_symbol_export.o libgit.a reftable/libreftable.a xdiff/lib.a\n     +\t$(LD) -r $^ -o $@\n     +\n    -+contrib/cgit-rs/hidden_symbol_export.o: contrib/cgit-rs/partial_symbol_export.o\n    ++contrib/cgit-rs/cgit-sys/hidden_symbol_export.o: contrib/cgit-rs/cgit-sys/partial_symbol_export.o\n     +\t$(OBJCOPY) --localize-hidden $^ $@\n     +\n    -+contrib/cgit-rs/libcgit.a: contrib/cgit-rs/hidden_symbol_export.o\n    ++contrib/cgit-rs/cgit-sys/libcgit.a: contrib/cgit-rs/cgit-sys/hidden_symbol_export.o\n     +\t$(AR) $(ARFLAGS) $@ $^\n     \n    - ## contrib/cgit-rs/Cargo.lock (new) ##\n    + ## contrib/cgit-rs/cgit-sys/Cargo.lock (new) ##\n     @@\n     +# This file is automatically @generated by Cargo.\n     +# It is not intended for manual editing.\n     +version = 3\n     +\n     +[[package]]\n    -+name = \"cgit\"\n    ++name = \"cgit-sys\"\n     +version = \"0.1.0\"\n    -+dependencies = [\n    -+ \"libc\",\n    -+]\n    -+\n    -+[[package]]\n    -+name = \"libc\"\n    -+version = \"0.2.155\"\n    -+source = \"registry+https://github.com/rust-lang/crates.io-index\"\n    -+checksum = \"97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c\"\n     \n    - ## contrib/cgit-rs/Cargo.toml (new) ##\n    + ## contrib/cgit-rs/cgit-sys/Cargo.toml (new) ##\n     @@\n     +[package]\n    -+name = \"cgit\"\n    ++name = \"cgit-sys\"\n     +version = \"0.1.0\"\n     +edition = \"2021\"\n     +build = \"build.rs\"\n     +links = \"git\"\n     +\n    -+[[bin]]\n    -+name = \"cgit-test\"\n    -+path = \"src/main.rs\"\n    -+\n     +[lib]\n     +path = \"src/lib.rs\"\n    -+\n    -+[dependencies]\n    -+libc = \"0.2.155\"\n     \n    - ## contrib/cgit-rs/README.md (new) ##\n    + ## contrib/cgit-rs/cgit-sys/README.md (new) ##\n     @@\n     +# cgit-info\n     +\n    @@ contrib/cgit-rs/README.md (new)\n     +Assuming you don't make any changes to the Git source, you can just work from\n     +`contrib/cgit-rs` and use `cargo build` or `cargo run` as usual.\n     \n    - ## contrib/cgit-rs/build.rs (new) ##\n    + ## contrib/cgit-rs/cgit-sys/build.rs (new) ##\n     @@\n     +use std::env;\n     +use std::path::PathBuf;\n     +\n     +pub fn main() -> std::io::Result<()> {\n     +    let crate_root = PathBuf::from(env::var_os(\"CARGO_MANIFEST_DIR\").unwrap());\n    -+    let git_root = crate_root.join(\"../..\");\n    ++    let git_root = crate_root.join(\"../../..\");\n     +    let dst = PathBuf::from(env::var_os(\"OUT_DIR\").unwrap());\n     +\n     +    let make_output = std::process::Command::new(\"make\")\n     +        .env_remove(\"PROFILE\")\n     +        .current_dir(git_root.clone())\n    -+        .args(&[\n    -+            \"CC=clang\",\n    ++        .args([\n     +            \"CFLAGS=-fvisibility=hidden\",\n    -+            \"contrib/cgit-rs/libcgit.a\"\n    ++            \"contrib/cgit-rs/cgit-sys/libcgit.a\",\n     +        ])\n     +        .output()\n     +        .expect(\"Make failed to run\");\n     +    if !make_output.status.success() {\n     +        panic!(\n    -+                \"Make failed:\\n  stdout = {}\\n  stderr = {}\\n\",\n    -+                String::from_utf8(make_output.stdout).unwrap(),\n    -+                String::from_utf8(make_output.stderr).unwrap()\n    ++            \"Make failed:\\n  stdout = {}\\n  stderr = {}\\n\",\n    ++            String::from_utf8(make_output.stdout).unwrap(),\n    ++            String::from_utf8(make_output.stderr).unwrap()\n     +        );\n     +    }\n     +    std::fs::copy(crate_root.join(\"libcgit.a\"), dst.join(\"libcgit.a\"))?;\n    -+    println!(\"cargo::rustc-link-search=native={}\", dst.into_os_string().into_string().unwrap());\n    ++    println!(\"cargo::rustc-link-search=native={}\", dst.display());\n     +    println!(\"cargo::rustc-link-lib=cgit\");\n     +    println!(\"cargo::rustc-link-lib=z\");\n    -+    println!(\"cargo::rerun-if-changed={}\", git_root.into_os_string().into_string().unwrap());\n    ++    println!(\"cargo::rerun-if-changed={}\", git_root.display());\n     +\n     +    Ok(())\n     +}\n     \n    - ## contrib/cgit-rs/public_symbol_export.c (new) ##\n    + ## contrib/cgit-rs/cgit-sys/public_symbol_export.c (new) ##\n     @@\n     +// Shim to publicly export Git symbols. These must be renamed so that the\n     +// original symbols can be hidden. Renaming these with a \"libgit_\" prefix also\n     +// avoid conflicts with other libraries such as libgit2.\n     +\n    -+#include \"contrib/cgit-rs/public_symbol_export.h\"\n    ++#include \"contrib/cgit-rs/cgit-sys/public_symbol_export.h\"\n     +#include \"version.h\"\n     +\n     +#pragma GCC visibility push(default)\n    @@ contrib/cgit-rs/public_symbol_export.c (new)\n     +\n     +#pragma GCC visibility pop\n     \n    - ## contrib/cgit-rs/public_symbol_export.h (new) ##\n    + ## contrib/cgit-rs/cgit-sys/public_symbol_export.h (new) ##\n     @@\n     +#ifndef PUBLIC_SYMBOL_EXPORT_H\n     +#define PUBLIC_SYMBOL_EXPORT_H\n    @@ contrib/cgit-rs/public_symbol_export.h (new)\n     +\n     +#endif /* PUBLIC_SYMBOL_EXPORT_H */\n     \n    - ## contrib/cgit-rs/src/lib.rs (new) ##\n    + ## contrib/cgit-rs/cgit-sys/src/lib.rs (new) ##\n     @@\n    -+use libc::c_char;\n    ++use std::ffi::c_char;\n     +\n     +extern \"C\" {\n     +    // From version.c\n     +    pub fn libgit_user_agent() -> *const c_char;\n     +    pub fn libgit_user_agent_sanitized() -> *const c_char;\n     +}\n    -\n    - ## contrib/cgit-rs/src/main.rs (new) ##\n    -@@\n    -+use std::ffi::CStr;\n    -+\n    -+fn main() {\n    -+    println!(\"Let's print some strings provided by Git\");\n    -+    let c_buf = unsafe { cgit::libgit_user_agent() };\n    -+    let c_str = unsafe { CStr::from_ptr(c_buf) };\n    -+    println!(\"git_user_agent() = {:?}\", c_str);\n    -+    println!(\"git_user_agent_sanitized() = {:?}\",\n    -+        unsafe { CStr::from_ptr(cgit::libgit_user_agent_sanitized()) });\n    ++\n    ++#[cfg(test)]\n    ++mod tests {\n    ++    use std::ffi::CStr;\n    ++\n    ++    use super::*;\n    ++\n    ++    #[test]\n    ++    fn user_agent_starts_with_git() {\n    ++        let c_str = unsafe { CStr::from_ptr(libgit_user_agent()) };\n    ++        let agent = c_str\n    ++            .to_str()\n    ++            .expect(\"User agent contains invalid UTF-8 data\");\n    ++        assert!(\n    ++            agent.starts_with(\"git/\"),\n    ++            r#\"Expected user agent to start with \"git/\", got: {}\"#,\n    ++            agent\n    ++        );\n    ++    }\n    ++\n    ++    #[test]\n    ++    fn sanitized_user_agent_starts_with_git() {\n    ++        let c_str = unsafe { CStr::from_ptr(libgit_user_agent_sanitized()) };\n    ++        let agent = c_str\n    ++            .to_str()\n    ++            .expect(\"Sanitized user agent contains invalid UTF-8 data\");\n    ++        assert!(\n    ++            agent.starts_with(\"git/\"),\n    ++            r#\"Expected user agent to start with \"git/\", got: {}\"#,\n    ++            agent\n    ++        );\n    ++    }\n     +}\n4:  b84a8210a0 ! 3:  527780f816 contrib/cgit-rs: add repo initialization and config access\n    @@ Commit message\n         contrib/cgit-rs: add repo initialization and config access\n     \n         Co-authored-by: Calvin Wan <calvinwan@google.com>\n         Signed-off-by: Calvin Wan <calvinwan@google.com>\n    -    Signed-off-by: Josh Steadmon <steadmon@google.com>\n     \n    - ## contrib/cgit-rs/public_symbol_export.c ##\n    + ## contrib/cgit-rs/cgit-sys/public_symbol_export.c ##\n     @@\n      // original symbols can be hidden. Renaming these with a \"libgit_\" prefix also\n      // avoid conflicts with other libraries such as libgit2.\n      \n     +#include \"git-compat-util.h\"\n    - #include \"contrib/cgit-rs/public_symbol_export.h\"\n    -+#include \"attr.h\"\n    + #include \"contrib/cgit-rs/cgit-sys/public_symbol_export.h\"\n    ++#include \"common-init.h\"\n     +#include \"config.h\"\n     +#include \"setup.h\"\n      #include \"version.h\"\n      \n    ++extern struct repository *the_repository;\n    ++\n      #pragma GCC visibility push(default)\n      \n     +const char *libgit_setup_git_directory(void)\n    @@ contrib/cgit-rs/public_symbol_export.c\n     +\n     +int libgit_config_get_int(const char *key, int *dest)\n     +{\n    -+\treturn git_config_get_int(key, dest);\n    ++\treturn repo_config_get_int(the_repository, key, dest);\n     +}\n     +\n    -+void libgit_initialize_the_repository(void)\n    ++void libgit_init_git(const char **argv)\n     +{\n    -+\tinitialize_the_repository();\n    ++\tinit_git(argv);\n     +}\n     +\n     +int libgit_parse_maybe_bool(const char *val)\n    @@ contrib/cgit-rs/public_symbol_export.c\n      const char *libgit_user_agent(void)\n      {\n      \treturn git_user_agent();\n    -@@ contrib/cgit-rs/public_symbol_export.c: const char *libgit_user_agent_sanitized(void)\n    - \treturn git_user_agent_sanitized();\n    - }\n    - \n    -+const char *libgit_attr__true = git_attr__true;\n    -+const char *libgit_attr__false = git_attr__false;\n    -+\n    - #pragma GCC visibility pop\n     \n    - ## contrib/cgit-rs/public_symbol_export.h ##\n    + ## contrib/cgit-rs/cgit-sys/public_symbol_export.h ##\n     @@\n      #ifndef PUBLIC_SYMBOL_EXPORT_H\n      #define PUBLIC_SYMBOL_EXPORT_H\n    @@ contrib/cgit-rs/public_symbol_export.h\n     +\n     +int libgit_config_get_int(const char *key, int *dest);\n     +\n    -+void libgit_initialize_the_repository(void);\n    ++void libgit_init_git(const char **argv);\n     +\n     +int libgit_parse_maybe_bool(const char *val);\n     +\n    @@ contrib/cgit-rs/public_symbol_export.h\n      \n      const char *libgit_user_agent_sanitized(void);\n     \n    - ## contrib/cgit-rs/src/lib.rs ##\n    + ## contrib/cgit-rs/cgit-sys/src/lib.rs ##\n     @@\n    --use libc::c_char;\n    -+use libc::{c_char, c_int};\n    +-use std::ffi::c_char;\n    ++use std::ffi::{c_char, c_int};\n      \n      extern \"C\" {\n     +    pub fn libgit_setup_git_directory() -> *const c_char;\n     +\n     +    // From config.c\n    -+    pub fn libgit_config_get_int(key: *const c_char, dest: *mut c_int) ->c_int;\n    ++    pub fn libgit_config_get_int(key: *const c_char, dest: *mut c_int) -> c_int;\n     +\n    -+    // From repository.c\n    -+    pub fn libgit_initialize_the_repository();\n    ++    // From common-init.c\n    ++    pub fn libgit_init_git(argv: *const *const c_char);\n     +\n     +    // From parse.c\n     +    pub fn libgit_parse_maybe_bool(val: *const c_char) -> c_int;\n    @@ contrib/cgit-rs/src/lib.rs\n          // From version.c\n          pub fn libgit_user_agent() -> *const c_char;\n          pub fn libgit_user_agent_sanitized() -> *const c_char;\n    -\n    - ## contrib/cgit-rs/src/main.rs ##\n    -@@\n    --use std::ffi::CStr;\n    -+use std::ffi::{CStr, CString};\n    +@@ contrib/cgit-rs/cgit-sys/src/lib.rs: extern \"C\" {\n      \n    - fn main() {\n    -     println!(\"Let's print some strings provided by Git\");\n    -@@ contrib/cgit-rs/src/main.rs: fn main() {\n    -     println!(\"git_user_agent() = {:?}\", c_str);\n    -     println!(\"git_user_agent_sanitized() = {:?}\",\n    -         unsafe { CStr::from_ptr(cgit::libgit_user_agent_sanitized()) });\n    -+\n    -+    println!(\"\\nNow try passing args\");\n    -+    let test_arg = CString::new(\"test_arg\").unwrap();\n    -+    println!(\"git_parse_maybe_bool(...) = {:?}\",\n    -+        unsafe { cgit::libgit_parse_maybe_bool(test_arg.as_ptr()) });\n    -+\n    -+    println!(\"\\nCan we get an int out of our config??\");\n    -+    unsafe {\n    -+        cgit::libgit_initialize_the_repository();\n    -+        cgit::libgit_setup_git_directory();\n    -+        let mut val: libc::c_int = 0;\n    + #[cfg(test)]\n    + mod tests {\n    +-    use std::ffi::CStr;\n    ++    use std::ffi::{CStr, CString};\n    + \n    +     use super::*;\n    + \n    +@@ contrib/cgit-rs/cgit-sys/src/lib.rs: mod tests {\n    +             agent\n    +         );\n    +     }\n    ++\n    ++    #[test]\n    ++    fn parse_bools_from_strings() {\n    ++        let arg = CString::new(\"true\").unwrap();\n    ++        assert_eq!(unsafe { libgit_parse_maybe_bool(arg.as_ptr()) }, 1);\n    ++\n    ++        let arg = CString::new(\"yes\").unwrap();\n    ++        assert_eq!(unsafe { libgit_parse_maybe_bool(arg.as_ptr()) }, 1);\n    ++\n    ++        let arg = CString::new(\"false\").unwrap();\n    ++        assert_eq!(unsafe { libgit_parse_maybe_bool(arg.as_ptr()) }, 0);\n    ++\n    ++        let arg = CString::new(\"no\").unwrap();\n    ++        assert_eq!(unsafe { libgit_parse_maybe_bool(arg.as_ptr()) }, 0);\n    ++\n    ++        let arg = CString::new(\"maybe\").unwrap();\n    ++        assert_eq!(unsafe { libgit_parse_maybe_bool(arg.as_ptr()) }, -1);\n    ++    }\n    ++\n    ++    #[test]\n    ++    fn access_configs() {\n    ++        // NEEDSWORK: we need to supply a testdata config\n    ++        let fake_argv = [std::ptr::null::<c_char>()];\n    ++        unsafe {\n    ++            libgit_init_git(fake_argv.as_ptr());\n    ++            libgit_setup_git_directory();\n    ++        }\n    ++        let mut val: c_int = 0;\n     +        let key = CString::new(\"trace2.eventNesting\").unwrap();\n    -+        cgit::libgit_config_get_int(\n    -+            key.as_ptr(),\n    -+            &mut val as *mut i32\n    -+        );\n    -+        println!(\n    -+            \"git_config_get_int(\\\"trace2.eventNesting\\\") -> {:?}\",\n    -+            val\n    -+        );\n    -+    };\n    ++        unsafe { libgit_config_get_int(key.as_ptr(), &mut val as *mut i32) };\n    ++        assert_eq!(val, 5);\n    ++    }\n      }\n5:  c8befb680e ! 4:  908ad0b82f config: add git_configset_alloc\n    @@ Metadata\n     Author: Josh Steadmon <steadmon@google.com>\n     \n      ## Commit message ##\n    -    config: add git_configset_alloc\n    +    config: add git_configset_alloc() and git_configset_clear_and_free()\n     \n    -    Add git_configset_alloc so that non-C external consumers can use\n    -    configset functions without redefining config_set.\n    +    Add git_configset_alloc() so that and git_configset_clear_and_free()\n    +    functions so that callers can manage config_set structs on the heap.\n    +    This also allows non-C external consumers to treat config_sets as opaque\n    +    structs.\n     \n         Co-authored-by: Calvin Wan <calvinwan@google.com>\n         Signed-off-by: Calvin Wan <calvinwan@google.com>\n    -    Signed-off-by: Josh Steadmon <steadmon@google.com>\n    -\n     \n    +    Also add _clear_and_free\n    +\n    +\n      ## config.c ##\n     @@ config.c: static int config_set_element_cmp(const void *cmp_data UNUSED,\n      \treturn strcmp(e1->key, e2->key);\n    @@ config.c: static int config_set_element_cmp(const void *cmp_data UNUSED,\n      void git_configset_init(struct config_set *set)\n      {\n      \thashmap_init(&set->config_hash, config_set_element_cmp, NULL, 0);\n    +@@ config.c: void git_configset_clear(struct config_set *set)\n    + \tset->list.items = NULL;\n    + }\n    + \n    ++void git_configset_clear_and_free(struct config_set *set)\n    ++{\n    ++\tgit_configset_clear(set);\n    ++\tfree(set);\n    ++}\n    ++\n    + static int config_set_callback(const char *key, const char *value,\n    + \t\t\t       const struct config_context *ctx,\n    + \t\t\t       void *cb)\n     \n      ## config.h ##\n     @@ config.h: struct config_set {\n    @@ config.h: struct config_set {\n      /**\n       * Initializes the config_set `cs`.\n       */\n    +@@ config.h: int git_configset_get_string_multi(struct config_set *cs, const char *key,\n    +  */\n    + void git_configset_clear(struct config_set *cs);\n    + \n    ++/**\n    ++ * Clears and frees a heap-allocated `config_set` structure.\n    ++ */\n    ++void git_configset_clear_and_free(struct config_set *cs);\n    ++\n    + /*\n    +  * These functions return 1 if not found, and 0 if found, leaving the found\n    +  * value in the 'dest' pointer.\n6:  1e981a6880 ! 5:  514c744ba4 contrib/cgit-rs: add a subset of configset wrappers\n    @@ Metadata\n      ## Commit message ##\n         contrib/cgit-rs: add a subset of configset wrappers\n     \n         Signed-off-by: Calvin Wan <calvinwan@google.com>\n    -    Signed-off-by: Josh Steadmon <steadmon@google.com>\n     \n    - ## contrib/cgit-rs/Cargo.lock ##\n    -@@ contrib/cgit-rs/Cargo.lock: version = 3\n    - name = \"cgit\"\n    - version = \"0.1.0\"\n    - dependencies = [\n    -+ \"home\",\n    -  \"libc\",\n    - ]\n    - \n    -+[[package]]\n    -+name = \"home\"\n    -+version = \"0.5.9\"\n    -+source = \"registry+https://github.com/rust-lang/crates.io-index\"\n    -+checksum = \"e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5\"\n    -+dependencies = [\n    -+ \"windows-sys\",\n    -+]\n    -+\n    - [[package]]\n    - name = \"libc\"\n    - version = \"0.2.155\"\n    - source = \"registry+https://github.com/rust-lang/crates.io-index\"\n    - checksum = \"97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c\"\n    -+\n    -+[[package]]\n    -+name = \"windows-sys\"\n    -+version = \"0.52.0\"\n    -+source = \"registry+https://github.com/rust-lang/crates.io-index\"\n    -+checksum = \"282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d\"\n    -+dependencies = [\n    -+ \"windows-targets\",\n    -+]\n    + ## .gitignore ##\n    +@@ .gitignore: Release/\n    + /git.VC.db\n    + *.dSYM\n    + /contrib/buildsystems/out\n    ++/contrib/cgit-rs/target\n    + /contrib/cgit-rs/cgit-sys/target\n    +\n    + ## Makefile ##\n    +@@ Makefile: clean: profile-clean coverage-clean cocciclean\n    + \t$(RM) $(htmldocs).tar.gz $(manpages).tar.gz\n    + \t$(MAKE) -C Documentation/ clean\n    + \t$(RM) Documentation/GIT-EXCLUDED-PROGRAMS\n    +-\t$(RM) -r contrib/cgit-rs/cgit-sys/target\n    ++\t$(RM) -r contrib/cgit-rs/target contrib/cgit-rs/cgit-sys/target\n    + ifndef NO_PERL\n    + \t$(RM) -r perl/build/\n    + endif\n    +\n    + ## contrib/cgit-rs/Cargo.lock (new) ##\n    +@@\n    ++# This file is automatically @generated by Cargo.\n    ++# It is not intended for manual editing.\n    ++version = 3\n     +\n     +[[package]]\n    -+name = \"windows-targets\"\n    -+version = \"0.52.6\"\n    -+source = \"registry+https://github.com/rust-lang/crates.io-index\"\n    -+checksum = \"9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973\"\n    ++name = \"cgit\"\n    ++version = \"0.1.0\"\n     +dependencies = [\n    -+ \"windows_aarch64_gnullvm\",\n    -+ \"windows_aarch64_msvc\",\n    -+ \"windows_i686_gnu\",\n    -+ \"windows_i686_gnullvm\",\n    -+ \"windows_i686_msvc\",\n    -+ \"windows_x86_64_gnu\",\n    -+ \"windows_x86_64_gnullvm\",\n    -+ \"windows_x86_64_msvc\",\n    ++ \"cgit-sys\",\n     +]\n     +\n     +[[package]]\n    -+name = \"windows_aarch64_gnullvm\"\n    -+version = \"0.52.6\"\n    -+source = \"registry+https://github.com/rust-lang/crates.io-index\"\n    -+checksum = \"32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3\"\n    -+\n    -+[[package]]\n    -+name = \"windows_aarch64_msvc\"\n    -+version = \"0.52.6\"\n    -+source = \"registry+https://github.com/rust-lang/crates.io-index\"\n    -+checksum = \"09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469\"\n    -+\n    -+[[package]]\n    -+name = \"windows_i686_gnu\"\n    -+version = \"0.52.6\"\n    -+source = \"registry+https://github.com/rust-lang/crates.io-index\"\n    -+checksum = \"8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b\"\n    -+\n    -+[[package]]\n    -+name = \"windows_i686_gnullvm\"\n    -+version = \"0.52.6\"\n    -+source = \"registry+https://github.com/rust-lang/crates.io-index\"\n    -+checksum = \"0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66\"\n    -+\n    -+[[package]]\n    -+name = \"windows_i686_msvc\"\n    -+version = \"0.52.6\"\n    -+source = \"registry+https://github.com/rust-lang/crates.io-index\"\n    -+checksum = \"240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66\"\n    -+\n    -+[[package]]\n    -+name = \"windows_x86_64_gnu\"\n    -+version = \"0.52.6\"\n    -+source = \"registry+https://github.com/rust-lang/crates.io-index\"\n    -+checksum = \"147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78\"\n    ++name = \"cgit-sys\"\n    ++version = \"0.1.0\"\n    +\n    + ## contrib/cgit-rs/Cargo.toml (new) ##\n    +@@\n    ++[package]\n    ++name = \"cgit\"\n    ++version = \"0.1.0\"\n    ++edition = \"2021\"\n     +\n    -+[[package]]\n    -+name = \"windows_x86_64_gnullvm\"\n    -+version = \"0.52.6\"\n    -+source = \"registry+https://github.com/rust-lang/crates.io-index\"\n    -+checksum = \"24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d\"\n    ++[lib]\n    ++path = \"src/lib.rs\"\n     +\n    -+[[package]]\n    -+name = \"windows_x86_64_msvc\"\n    -+version = \"0.52.6\"\n    -+source = \"registry+https://github.com/rust-lang/crates.io-index\"\n    -+checksum = \"589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec\"\n    ++[dependencies]\n    ++cgit-sys = { version = \"0.1.0\", path = \"cgit-sys\" }\n     \n    - ## contrib/cgit-rs/Cargo.toml ##\n    -@@ contrib/cgit-rs/Cargo.toml: path = \"src/lib.rs\"\n    - \n    - [dependencies]\n    - libc = \"0.2.155\"\n    -+home = \"0.5.9\"\n    -\n    - ## contrib/cgit-rs/public_symbol_export.c ##\n    -@@ contrib/cgit-rs/public_symbol_export.c: int libgit_parse_maybe_bool(const char *val)\n    + ## contrib/cgit-rs/cgit-sys/public_symbol_export.c ##\n    +@@ contrib/cgit-rs/cgit-sys/public_symbol_export.c: int libgit_parse_maybe_bool(const char *val)\n      \treturn git_parse_maybe_bool(val);\n      }\n      \n    @@ contrib/cgit-rs/public_symbol_export.c: int libgit_parse_maybe_bool(const char *\n     +\treturn git_configset_alloc();\n     +}\n     +\n    ++void libgit_configset_clear_and_free(struct config_set *cs)\n    ++{\n    ++\tgit_configset_clear_and_free(cs);\n    ++}\n    ++\n     +void libgit_configset_init(struct config_set *cs)\n     +{\n     +\tgit_configset_init(cs);\n    @@ contrib/cgit-rs/public_symbol_export.c: int libgit_parse_maybe_bool(const char *\n      {\n      \treturn git_user_agent();\n     \n    - ## contrib/cgit-rs/public_symbol_export.h ##\n    -@@ contrib/cgit-rs/public_symbol_export.h: void libgit_initialize_the_repository(void);\n    + ## contrib/cgit-rs/cgit-sys/public_symbol_export.h ##\n    +@@ contrib/cgit-rs/cgit-sys/public_symbol_export.h: void libgit_init_git(const char **argv);\n      \n      int libgit_parse_maybe_bool(const char *val);\n      \n     +struct config_set *libgit_configset_alloc(void);\n     +\n    ++void libgit_configset_clear_and_free(struct config_set *cs);\n    ++\n     +void libgit_configset_init(struct config_set *cs);\n     +\n     +int libgit_configset_add_file(struct config_set *cs, const char *filename);\n    @@ contrib/cgit-rs/public_symbol_export.h: void libgit_initialize_the_repository(vo\n      \n      const char *libgit_user_agent_sanitized(void);\n     \n    - ## contrib/cgit-rs/src/lib.rs ##\n    + ## contrib/cgit-rs/cgit-sys/src/lib.rs ##\n     @@\n    --use libc::{c_char, c_int};\n    -+use std::ffi::{CStr, CString};\n    +-use std::ffi::{c_char, c_int};\n    ++use std::ffi::{c_char, c_int, c_void};\n    ++\n    ++#[allow(non_camel_case_types)]\n    ++#[repr(C)]\n    ++pub struct config_set {\n    ++    _data: [u8; 0],\n    ++    _marker: core::marker::PhantomData<(*mut u8, core::marker::PhantomPinned)>,\n    ++}\n    + \n    + extern \"C\" {\n    ++    pub fn free(ptr: *mut c_void);\n     +\n    -+use libc::{c_char, c_int, c_void};\n    +     pub fn libgit_setup_git_directory() -> *const c_char;\n    + \n    +     // From config.c\n    +@@ contrib/cgit-rs/cgit-sys/src/lib.rs: extern \"C\" {\n    +     // From version.c\n    +     pub fn libgit_user_agent() -> *const c_char;\n    +     pub fn libgit_user_agent_sanitized() -> *const c_char;\n     +\n    -+pub enum GitConfigSet {}\n    ++    pub fn libgit_configset_alloc() -> *mut config_set;\n    ++    pub fn libgit_configset_clear_and_free(cs: *mut config_set);\n     +\n    -+pub struct ConfigSet(*mut GitConfigSet);\n    -+impl ConfigSet {\n    ++    pub fn libgit_configset_init(cs: *mut config_set);\n    ++\n    ++    pub fn libgit_configset_add_file(cs: *mut config_set, filename: *const c_char) -> c_int;\n    ++\n    ++    pub fn libgit_configset_get_int(\n    ++        cs: *mut config_set,\n    ++        key: *const c_char,\n    ++        int: *mut c_int,\n    ++    ) -> c_int;\n     +\n    ++    pub fn libgit_configset_get_string(\n    ++        cs: *mut config_set,\n    ++        key: *const c_char,\n    ++        dest: *mut *mut c_char,\n    ++    ) -> c_int;\n    ++\n    + }\n    + \n    + #[cfg(test)]\n    +\n    + ## contrib/cgit-rs/src/lib.rs (new) ##\n    +@@\n    ++use std::ffi::{c_char, c_int, c_void, CStr, CString};\n    ++\n    ++use cgit_sys::*;\n    ++\n    ++pub struct ConfigSet(*mut config_set);\n    ++impl ConfigSet {\n     +    pub fn new() -> Self {\n     +        unsafe {\n    -+            // TODO: we need to handle freeing this when the ConfigSet is dropped\n    -+            // git_configset_clear(ptr) and then libc::free(ptr)\n     +            let ptr = libgit_configset_alloc();\n     +            libgit_configset_init(ptr);\n     +            ConfigSet(ptr)\n     +        }\n     +    }\n     +\n    ++    // NEEDSWORK: maybe replace &str with &Path\n     +    pub fn add_files(&mut self, files: &[&str]) {\n     +        for file in files {\n     +            let rs = CString::new(*file).expect(\"Couldn't convert to CString\");\n    @@ contrib/cgit-rs/src/lib.rs\n     +        let key = CString::new(key).expect(\"Couldn't convert to CString\");\n     +        let mut val: *mut c_char = std::ptr::null_mut();\n     +        unsafe {\n    -+            if libgit_configset_get_string(self.0, key.as_ptr(), &mut val as *mut *mut c_char) != 0 {\n    ++            if libgit_configset_get_string(self.0, key.as_ptr(), &mut val as *mut *mut c_char) != 0\n    ++            {\n     +                return None;\n     +            }\n     +            let borrowed_str = CStr::from_ptr(val);\n     +            let owned_str = CString::from_vec_with_nul(borrowed_str.to_bytes_with_nul().to_vec());\n    -+            libc::free(val as *mut c_void); // Free the xstrdup()ed pointer from the C side\n    ++            free(val as *mut c_void); // Free the xstrdup()ed pointer from the C side\n     +            Some(owned_str.unwrap())\n     +        }\n     +    }\n     +}\n    - \n    - extern \"C\" {\n    -     pub fn libgit_setup_git_directory() -> *const c_char;\n    -@@ contrib/cgit-rs/src/lib.rs: extern \"C\" {\n    -     // From version.c\n    -     pub fn libgit_user_agent() -> *const c_char;\n    -     pub fn libgit_user_agent_sanitized() -> *const c_char;\n    -+\n    -+    fn libgit_configset_alloc() -> *mut GitConfigSet;\n    -+\n    -+    fn libgit_configset_init(cs: *mut GitConfigSet);\n     +\n    -+    fn libgit_configset_add_file(cs: *mut GitConfigSet, filename: *const c_char) -> c_int;\n    ++impl Default for ConfigSet {\n    ++    fn default() -> Self {\n    ++        Self::new()\n    ++    }\n    ++}\n     +\n    -+    pub fn libgit_configset_get_int(cs: *mut GitConfigSet, key: *const c_char, int: *mut c_int) -> c_int;\n    -+    pub fn libgit_configset_get_string(cs: *mut GitConfigSet, key: *const c_char, dest: *mut *mut c_char) -> c_int;\n    ++impl Drop for ConfigSet {\n    ++    fn drop(&mut self) {\n    ++        unsafe {\n    ++            libgit_configset_clear_and_free(self.0);\n    ++        }\n    ++    }\n    ++}\n     +\n    - }\n    -\n    - ## contrib/cgit-rs/src/main.rs ##\n    -@@\n    - use std::ffi::{CStr, CString};\n    -+use home::home_dir;\n    - \n    - fn main() {\n    -     println!(\"Let's print some strings provided by Git\");\n    -@@ contrib/cgit-rs/src/main.rs: fn main() {\n    -             val\n    -         );\n    -     };\n    -+\n    -+    println!(\"\\nTry out our configset wrappers\");\n    -+    let mut cs = cgit::ConfigSet::new();\n    -+    let mut path = home_dir().expect(\"cannot get home directory path\");\n    -+    path.push(\".gitconfig\");\n    -+    let path:String = path.into_os_string().into_string().unwrap();\n    -+    cs.add_files(&[\"/etc/gitconfig\", \".gitconfig\", &path]);\n    -+    /*\n    -+     * Returns Some(x) if defined in local config, otherwise None\n    -+     */\n    -+    println!(\"get_configset_get_int = {:?}\", cs.get_int(\"trace2.eventNesting\")); \n    -+    println!(\"cs.get_str(\\\"garbage\\\") = {:?}\", cs.get_str(\"this_string_does_not_exist\"));\n    - }\n    ++#[cfg(test)]\n    ++mod tests {\n    ++    use super::*;\n    ++\n    ++    #[test]\n    ++    fn load_configs_via_configset() {\n    ++        // NEEDSWORK: we need to supply a testdata config\n    ++        let mut cs = ConfigSet::new();\n    ++        let mut path = std::env::home_dir().expect(\"cannot get home directory path\");\n    ++        path.push(\".gitconfig\");\n    ++        let path: String = path.into_os_string().into_string().unwrap();\n    ++        cs.add_files(&[\"/etc/gitconfig\", \".gitconfig\", &path]);\n    ++        assert_eq!(cs.get_int(\"trace2.eventNesting\"), Some(5));\n    ++        assert_eq!(cs.get_str(\"no_such_config_item\"), None);\n    ++    }\n    ++}\n\nbase-commit: 557ae147e6cdc9db121269b058c757ac5092f9c9\n-- \n2.46.0.76.ge559c4bf1a-goog\n\n"},{"id":"500586","messageId":"800b37d16b8324faed8b7f84d30f797fc5664bf7.1723242556.git.steadmon@google.com","threadId":"61917","inReplyTo":"cover.1723242556.git.steadmon@google.com","subject":"[PATCH v2 1/5] common-main: split init and exit code into new files","fromName":"Josh Steadmon","fromEmail":"steadmon@google.com","sentAt":"2024-08-09T22:41:13Z","receivedAt":"2024-08-09T22:41:21Z","isPatch":true,"sender":{"key":"steadmon@google.com","avatar":"https://avatars.githubusercontent.com/u/2654920?v=4"},"body":"Currently, object files in libgit.a reference common_exit(), which is\ncontained in common-main.o. However, common-main.o also includes main(),\nwhich references cmd_main() in git.o, which in turn depends on all the\nbuiltin/*.o objects.\n\nWe would like to allow external users to link libgit.a without needing\nto include so many extra objects. Enable this by splitting common_exit()\nand check_bug_if_BUG() into a new file common-exit.c, and add\ncommon-exit.o to LIB_OBJS so that these are included in libgit.a.\n\nThis split has previously been proposed ([1], [2]) to support fuzz tests\nand unit tests by avoiding conflicting definitions for main(). However,\nboth of those issues were resolved by other methods of avoiding symbol\nconflicts. Now we are trying to make libgit.a more self-contained, so\nhopefully we can revisit this approach.\n\nAdditionally, move the initialization code out of main() into a new\ninit_git() function in its own file. Include this in libgit.a as well,\nso that external users can share our setup code without calling our\nmain().\n\n[1] https://lore.kernel.org/git/Yp+wjCPhqieTku3X@google.com/\n[2] https://lore.kernel.org/git/20230517-unit-tests-v2-v2-1-21b5b60f4b32@google.com/\n\nSigned-off-by: Josh Steadmon <steadmon@google.com>\n---\n Makefile      |  2 ++\n common-exit.c | 26 ++++++++++++++++\n common-init.c | 63 ++++++++++++++++++++++++++++++++++++++\n common-init.h |  6 ++++\n common-main.c | 83 ++-------------------------------------------------\n 5 files changed, 99 insertions(+), 81 deletions(-)\n create mode 100644 common-exit.c\n create mode 100644 common-init.c\n create mode 100644 common-init.h\n\ndiff --git a/Makefile b/Makefile\nindex 3eab701b10..7caeb3c872 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -979,6 +979,8 @@ LIB_OBJS += combine-diff.o\n LIB_OBJS += commit-graph.o\n LIB_OBJS += commit-reach.o\n LIB_OBJS += commit.o\n+LIB_OBJS += common-exit.o\n+LIB_OBJS += common-init.o\n LIB_OBJS += compat/nonblock.o\n LIB_OBJS += compat/obstack.o\n LIB_OBJS += compat/terminal.o\ndiff --git a/common-exit.c b/common-exit.c\nnew file mode 100644\nindex 0000000000..1aaa538be3\n--- /dev/null\n+++ b/common-exit.c\n@@ -0,0 +1,26 @@\n+#include \"git-compat-util.h\"\n+#include \"trace2.h\"\n+\n+static void check_bug_if_BUG(void)\n+{\n+\tif (!bug_called_must_BUG)\n+\t\treturn;\n+\tBUG(\"on exit(): had bug() call(s) in this process without explicit BUG_if_bug()\");\n+}\n+\n+/* We wrap exit() to call common_exit() in git-compat-util.h */\n+int common_exit(const char *file, int line, int code)\n+{\n+\t/*\n+\t * For non-POSIX systems: Take the lowest 8 bits of the \"code\"\n+\t * to e.g. turn -1 into 255. On a POSIX system this is\n+\t * redundant, see exit(3) and wait(2), but as it doesn't harm\n+\t * anything there we don't need to guard this with an \"ifdef\".\n+\t */\n+\tcode &= 0xff;\n+\n+\tcheck_bug_if_BUG();\n+\ttrace2_cmd_exit_fl(file, line, code);\n+\n+\treturn code;\n+}\ndiff --git a/common-init.c b/common-init.c\nnew file mode 100644\nindex 0000000000..5cc73f058c\n--- /dev/null\n+++ b/common-init.c\n@@ -0,0 +1,63 @@\n+#define USE_THE_REPOSITORY_VARIABLE\n+\n+#include \"git-compat-util.h\"\n+#include \"common-init.h\"\n+#include \"exec-cmd.h\"\n+#include \"gettext.h\"\n+#include \"attr.h\"\n+#include \"repository.h\"\n+#include \"setup.h\"\n+#include \"strbuf.h\"\n+#include \"trace2.h\"\n+\n+/*\n+ * Many parts of Git have subprograms communicate via pipe, expect the\n+ * upstream of a pipe to die with SIGPIPE when the downstream of a\n+ * pipe does not need to read all that is written.  Some third-party\n+ * programs that ignore or block SIGPIPE for their own reason forget\n+ * to restore SIGPIPE handling to the default before spawning Git and\n+ * break this carefully orchestrated machinery.\n+ *\n+ * Restore the way SIGPIPE is handled to default, which is what we\n+ * expect.\n+ */\n+static void restore_sigpipe_to_default(void)\n+{\n+\tsigset_t unblock;\n+\n+\tsigemptyset(&unblock);\n+\tsigaddset(&unblock, SIGPIPE);\n+\tsigprocmask(SIG_UNBLOCK, &unblock, NULL);\n+\tsignal(SIGPIPE, SIG_DFL);\n+}\n+\n+void init_git(const char **argv)\n+{\n+\tstruct strbuf tmp = STRBUF_INIT;\n+\n+\ttrace2_initialize_clock();\n+\n+\t/*\n+\t * Always open file descriptors 0/1/2 to avoid clobbering files\n+\t * in die().  It also avoids messing up when the pipes are dup'ed\n+\t * onto stdin/stdout/stderr in the child processes we spawn.\n+\t */\n+\tsanitize_stdfds();\n+\trestore_sigpipe_to_default();\n+\n+\tgit_resolve_executable_dir(argv[0]);\n+\n+\tsetlocale(LC_CTYPE, \"\");\n+\tgit_setup_gettext();\n+\n+\tinitialize_repository(the_repository);\n+\n+\tattr_start();\n+\n+\ttrace2_initialize();\n+\ttrace2_cmd_start(argv);\n+\ttrace2_collect_process_info(TRACE2_PROCESS_INFO_STARTUP);\n+\n+\tif (!strbuf_getcwd(&tmp))\n+\t\ttmp_original_cwd = strbuf_detach(&tmp, NULL);\n+}\ndiff --git a/common-init.h b/common-init.h\nnew file mode 100644\nindex 0000000000..3e6db20cae\n--- /dev/null\n+++ b/common-init.h\n@@ -0,0 +1,6 @@\n+#ifndef COMMON_INIT_H\n+#define COMMON_INIT_H\n+\n+void init_git(const char **argv);\n+\n+#endif /* COMMON_INIT_H */\ndiff --git a/common-main.c b/common-main.c\nindex 8e68ac9e42..6b7ab077b0 100644\n--- a/common-main.c\n+++ b/common-main.c\n@@ -1,92 +1,13 @@\n-#define USE_THE_REPOSITORY_VARIABLE\n-\n #include \"git-compat-util.h\"\n-#include \"exec-cmd.h\"\n-#include \"gettext.h\"\n-#include \"attr.h\"\n-#include \"repository.h\"\n-#include \"setup.h\"\n-#include \"strbuf.h\"\n-#include \"trace2.h\"\n-\n-/*\n- * Many parts of Git have subprograms communicate via pipe, expect the\n- * upstream of a pipe to die with SIGPIPE when the downstream of a\n- * pipe does not need to read all that is written.  Some third-party\n- * programs that ignore or block SIGPIPE for their own reason forget\n- * to restore SIGPIPE handling to the default before spawning Git and\n- * break this carefully orchestrated machinery.\n- *\n- * Restore the way SIGPIPE is handled to default, which is what we\n- * expect.\n- */\n-static void restore_sigpipe_to_default(void)\n-{\n-\tsigset_t unblock;\n-\n-\tsigemptyset(&unblock);\n-\tsigaddset(&unblock, SIGPIPE);\n-\tsigprocmask(SIG_UNBLOCK, &unblock, NULL);\n-\tsignal(SIGPIPE, SIG_DFL);\n-}\n+#include \"common-init.h\"\n \n int main(int argc, const char **argv)\n {\n \tint result;\n-\tstruct strbuf tmp = STRBUF_INIT;\n-\n-\ttrace2_initialize_clock();\n-\n-\t/*\n-\t * Always open file descriptors 0/1/2 to avoid clobbering files\n-\t * in die().  It also avoids messing up when the pipes are dup'ed\n-\t * onto stdin/stdout/stderr in the child processes we spawn.\n-\t */\n-\tsanitize_stdfds();\n-\trestore_sigpipe_to_default();\n-\n-\tgit_resolve_executable_dir(argv[0]);\n-\n-\tsetlocale(LC_CTYPE, \"\");\n-\tgit_setup_gettext();\n-\n-\tinitialize_repository(the_repository);\n-\n-\tattr_start();\n-\n-\ttrace2_initialize();\n-\ttrace2_cmd_start(argv);\n-\ttrace2_collect_process_info(TRACE2_PROCESS_INFO_STARTUP);\n-\n-\tif (!strbuf_getcwd(&tmp))\n-\t\ttmp_original_cwd = strbuf_detach(&tmp, NULL);\n \n+\tinit_git(argv);\n \tresult = cmd_main(argc, argv);\n \n \t/* Not exit(3), but a wrapper calling our common_exit() */\n \texit(result);\n }\n-\n-static void check_bug_if_BUG(void)\n-{\n-\tif (!bug_called_must_BUG)\n-\t\treturn;\n-\tBUG(\"on exit(): had bug() call(s) in this process without explicit BUG_if_bug()\");\n-}\n-\n-/* We wrap exit() to call common_exit() in git-compat-util.h */\n-int common_exit(const char *file, int line, int code)\n-{\n-\t/*\n-\t * For non-POSIX systems: Take the lowest 8 bits of the \"code\"\n-\t * to e.g. turn -1 into 255. On a POSIX system this is\n-\t * redundant, see exit(3) and wait(2), but as it doesn't harm\n-\t * anything there we don't need to guard this with an \"ifdef\".\n-\t */\n-\tcode &= 0xff;\n-\n-\tcheck_bug_if_BUG();\n-\ttrace2_cmd_exit_fl(file, line, code);\n-\n-\treturn code;\n-}\n-- \n2.46.0.76.ge559c4bf1a-goog\n\n"},{"id":"500587","messageId":"3589d2d6a2a24c2cc558d8e8fd05b56e28627eae.1723242556.git.steadmon@google.com","threadId":"61917","inReplyTo":"cover.1723242556.git.steadmon@google.com","subject":"[PATCH v2 2/5] cgit-sys: introduce Rust wrapper for libgit.a","fromName":"Josh Steadmon","fromEmail":"steadmon@google.com","sentAt":"2024-08-09T22:41:14Z","receivedAt":"2024-08-09T22:41:24Z","isPatch":true,"sender":{"key":"steadmon@google.com","avatar":"https://avatars.githubusercontent.com/u/2654920?v=4"},"body":"Introduce cgit-sys, a Rust wrapper crate that allows Rust code to call\nfunctions in libgit.a. This initial patch defines build rules and an\ninterface that exposes user agent string getter functions as a proof of\nconcept. This library can be tested with `cargo test`. In later commits,\na higher-level library providing a more Rust-friendly interface will be\nadded at `contrib/cgit-rs`.\n\nSymbols in cgit-sys can collide with symbols from other libraries such\nas libgit2. We avoid this by first exposing library symbols in\npublic_symbol_export.[ch]. These symbols are prepended with \"libgit_\" to\navoid collisions and set to visible using a visibility pragma. In\nbuild.rs, Rust builds contrib/cgit-rs/cgit-sys/libcgit.a, which also\ncontains libgit.a and other dependent libraries, with\n-fvisibility=hidden to hide all symbols within those libraries that\nhaven't been exposed with a visibility pragma.\n\nCo-authored-by: Kyle Lippincott <spectral@google.com>\nCo-authored-by: Calvin Wan <calvinwan@google.com>\nCo-authored-by: Josh Steadmon <steadmon@google.com>\nSigned-off-by: Calvin Wan <calvinwan@google.com>\nSigned-off-by: Kyle Lippincott <spectral@google.com>\nSigned-off-by: Josh Steadmon <steadmon@google.com>\n---\n .gitignore                                    |  1 +\n Makefile                                      | 13 ++++++\n contrib/cgit-rs/cgit-sys/Cargo.lock           |  7 ++++\n contrib/cgit-rs/cgit-sys/Cargo.toml           |  9 +++++\n contrib/cgit-rs/cgit-sys/README.md            | 15 +++++++\n contrib/cgit-rs/cgit-sys/build.rs             | 32 +++++++++++++++\n .../cgit-rs/cgit-sys/public_symbol_export.c   | 20 ++++++++++\n .../cgit-rs/cgit-sys/public_symbol_export.h   |  8 ++++\n contrib/cgit-rs/cgit-sys/src/lib.rs           | 40 +++++++++++++++++++\n 9 files changed, 145 insertions(+)\n create mode 100644 contrib/cgit-rs/cgit-sys/Cargo.lock\n create mode 100644 contrib/cgit-rs/cgit-sys/Cargo.toml\n create mode 100644 contrib/cgit-rs/cgit-sys/README.md\n create mode 100644 contrib/cgit-rs/cgit-sys/build.rs\n create mode 100644 contrib/cgit-rs/cgit-sys/public_symbol_export.c\n create mode 100644 contrib/cgit-rs/cgit-sys/public_symbol_export.h\n create mode 100644 contrib/cgit-rs/cgit-sys/src/lib.rs\n\ndiff --git a/.gitignore b/.gitignore\nindex 8caf3700c2..567cc9888f 100644\n--- a/.gitignore\n+++ b/.gitignore\n@@ -248,3 +248,4 @@ Release/\n /git.VC.db\n *.dSYM\n /contrib/buildsystems/out\n+/contrib/cgit-rs/cgit-sys/target\ndiff --git a/Makefile b/Makefile\nindex 7caeb3c872..db8af99f20 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -653,6 +653,8 @@ CURL_CONFIG = curl-config\n GCOV = gcov\n STRIP = strip\n SPATCH = spatch\n+LD = ld\n+OBJCOPY = objcopy\n \n export TCL_PATH TCLTK_PATH\n \n@@ -2713,6 +2715,7 @@ OBJECTS += $(XDIFF_OBJS)\n OBJECTS += $(FUZZ_OBJS)\n OBJECTS += $(REFTABLE_OBJS) $(REFTABLE_TEST_OBJS)\n OBJECTS += $(UNIT_TEST_OBJS)\n+OBJECTS += contrib/cgit-rs/cgit-sys/public_symbol_export.o\n \n ifndef NO_CURL\n \tOBJECTS += http.o http-walker.o remote-curl.o\n@@ -3720,6 +3723,7 @@ clean: profile-clean coverage-clean cocciclean\n \t$(RM) $(htmldocs).tar.gz $(manpages).tar.gz\n \t$(MAKE) -C Documentation/ clean\n \t$(RM) Documentation/GIT-EXCLUDED-PROGRAMS\n+\t$(RM) -r contrib/cgit-rs/cgit-sys/target\n ifndef NO_PERL\n \t$(RM) -r perl/build/\n endif\n@@ -3865,3 +3869,12 @@ $(UNIT_TEST_PROGS): $(UNIT_TEST_BIN)/%$X: $(UNIT_TEST_DIR)/%.o \\\n build-unit-tests: $(UNIT_TEST_PROGS)\n unit-tests: $(UNIT_TEST_PROGS) t/helper/test-tool$X\n \t$(MAKE) -C t/ unit-tests\n+\n+contrib/cgit-rs/cgit-sys/partial_symbol_export.o: contrib/cgit-rs/cgit-sys/public_symbol_export.o libgit.a reftable/libreftable.a xdiff/lib.a\n+\t$(LD) -r $^ -o $@\n+\n+contrib/cgit-rs/cgit-sys/hidden_symbol_export.o: contrib/cgit-rs/cgit-sys/partial_symbol_export.o\n+\t$(OBJCOPY) --localize-hidden $^ $@\n+\n+contrib/cgit-rs/cgit-sys/libcgit.a: contrib/cgit-rs/cgit-sys/hidden_symbol_export.o\n+\t$(AR) $(ARFLAGS) $@ $^\ndiff --git a/contrib/cgit-rs/cgit-sys/Cargo.lock b/contrib/cgit-rs/cgit-sys/Cargo.lock\nnew file mode 100644\nindex 0000000000..419a6d42f2\n--- /dev/null\n+++ b/contrib/cgit-rs/cgit-sys/Cargo.lock\n@@ -0,0 +1,7 @@\n+# This file is automatically @generated by Cargo.\n+# It is not intended for manual editing.\n+version = 3\n+\n+[[package]]\n+name = \"cgit-sys\"\n+version = \"0.1.0\"\ndiff --git a/contrib/cgit-rs/cgit-sys/Cargo.toml b/contrib/cgit-rs/cgit-sys/Cargo.toml\nnew file mode 100644\nindex 0000000000..e840f04024\n--- /dev/null\n+++ b/contrib/cgit-rs/cgit-sys/Cargo.toml\n@@ -0,0 +1,9 @@\n+[package]\n+name = \"cgit-sys\"\n+version = \"0.1.0\"\n+edition = \"2021\"\n+build = \"build.rs\"\n+links = \"git\"\n+\n+[lib]\n+path = \"src/lib.rs\"\ndiff --git a/contrib/cgit-rs/cgit-sys/README.md b/contrib/cgit-rs/cgit-sys/README.md\nnew file mode 100644\nindex 0000000000..7a59602c30\n--- /dev/null\n+++ b/contrib/cgit-rs/cgit-sys/README.md\n@@ -0,0 +1,15 @@\n+# cgit-info\n+\n+A small hacky proof-of-concept showing how to provide a Rust FFI for the Git\n+library.\n+\n+## Building\n+\n+`cargo build` automatically builds and picks up on changes made to both\n+the Rust wrapper and git.git code so there is no need to run `make`\n+beforehand.\n+\n+## Running\n+\n+Assuming you don't make any changes to the Git source, you can just work from\n+`contrib/cgit-rs` and use `cargo build` or `cargo run` as usual.\ndiff --git a/contrib/cgit-rs/cgit-sys/build.rs b/contrib/cgit-rs/cgit-sys/build.rs\nnew file mode 100644\nindex 0000000000..e29d703870\n--- /dev/null\n+++ b/contrib/cgit-rs/cgit-sys/build.rs\n@@ -0,0 +1,32 @@\n+use std::env;\n+use std::path::PathBuf;\n+\n+pub fn main() -> std::io::Result<()> {\n+    let crate_root = PathBuf::from(env::var_os(\"CARGO_MANIFEST_DIR\").unwrap());\n+    let git_root = crate_root.join(\"../../..\");\n+    let dst = PathBuf::from(env::var_os(\"OUT_DIR\").unwrap());\n+\n+    let make_output = std::process::Command::new(\"make\")\n+        .env_remove(\"PROFILE\")\n+        .current_dir(git_root.clone())\n+        .args([\n+            \"CFLAGS=-fvisibility=hidden\",\n+            \"contrib/cgit-rs/cgit-sys/libcgit.a\",\n+        ])\n+        .output()\n+        .expect(\"Make failed to run\");\n+    if !make_output.status.success() {\n+        panic!(\n+            \"Make failed:\\n  stdout = {}\\n  stderr = {}\\n\",\n+            String::from_utf8(make_output.stdout).unwrap(),\n+            String::from_utf8(make_output.stderr).unwrap()\n+        );\n+    }\n+    std::fs::copy(crate_root.join(\"libcgit.a\"), dst.join(\"libcgit.a\"))?;\n+    println!(\"cargo::rustc-link-search=native={}\", dst.display());\n+    println!(\"cargo::rustc-link-lib=cgit\");\n+    println!(\"cargo::rustc-link-lib=z\");\n+    println!(\"cargo::rerun-if-changed={}\", git_root.display());\n+\n+    Ok(())\n+}\ndiff --git a/contrib/cgit-rs/cgit-sys/public_symbol_export.c b/contrib/cgit-rs/cgit-sys/public_symbol_export.c\nnew file mode 100644\nindex 0000000000..deef4488c0\n--- /dev/null\n+++ b/contrib/cgit-rs/cgit-sys/public_symbol_export.c\n@@ -0,0 +1,20 @@\n+// Shim to publicly export Git symbols. These must be renamed so that the\n+// original symbols can be hidden. Renaming these with a \"libgit_\" prefix also\n+// avoid conflicts with other libraries such as libgit2.\n+\n+#include \"contrib/cgit-rs/cgit-sys/public_symbol_export.h\"\n+#include \"version.h\"\n+\n+#pragma GCC visibility push(default)\n+\n+const char *libgit_user_agent(void)\n+{\n+\treturn git_user_agent();\n+}\n+\n+const char *libgit_user_agent_sanitized(void)\n+{\n+\treturn git_user_agent_sanitized();\n+}\n+\n+#pragma GCC visibility pop\ndiff --git a/contrib/cgit-rs/cgit-sys/public_symbol_export.h b/contrib/cgit-rs/cgit-sys/public_symbol_export.h\nnew file mode 100644\nindex 0000000000..a3372f93fa\n--- /dev/null\n+++ b/contrib/cgit-rs/cgit-sys/public_symbol_export.h\n@@ -0,0 +1,8 @@\n+#ifndef PUBLIC_SYMBOL_EXPORT_H\n+#define PUBLIC_SYMBOL_EXPORT_H\n+\n+const char *libgit_user_agent(void);\n+\n+const char *libgit_user_agent_sanitized(void);\n+\n+#endif /* PUBLIC_SYMBOL_EXPORT_H */\ndiff --git a/contrib/cgit-rs/cgit-sys/src/lib.rs b/contrib/cgit-rs/cgit-sys/src/lib.rs\nnew file mode 100644\nindex 0000000000..bc33c6c498\n--- /dev/null\n+++ b/contrib/cgit-rs/cgit-sys/src/lib.rs\n@@ -0,0 +1,40 @@\n+use std::ffi::c_char;\n+\n+extern \"C\" {\n+    // From version.c\n+    pub fn libgit_user_agent() -> *const c_char;\n+    pub fn libgit_user_agent_sanitized() -> *const c_char;\n+}\n+\n+#[cfg(test)]\n+mod tests {\n+    use std::ffi::CStr;\n+\n+    use super::*;\n+\n+    #[test]\n+    fn user_agent_starts_with_git() {\n+        let c_str = unsafe { CStr::from_ptr(libgit_user_agent()) };\n+        let agent = c_str\n+            .to_str()\n+            .expect(\"User agent contains invalid UTF-8 data\");\n+        assert!(\n+            agent.starts_with(\"git/\"),\n+            r#\"Expected user agent to start with \"git/\", got: {}\"#,\n+            agent\n+        );\n+    }\n+\n+    #[test]\n+    fn sanitized_user_agent_starts_with_git() {\n+        let c_str = unsafe { CStr::from_ptr(libgit_user_agent_sanitized()) };\n+        let agent = c_str\n+            .to_str()\n+            .expect(\"Sanitized user agent contains invalid UTF-8 data\");\n+        assert!(\n+            agent.starts_with(\"git/\"),\n+            r#\"Expected user agent to start with \"git/\", got: {}\"#,\n+            agent\n+        );\n+    }\n+}\n-- \n2.46.0.76.ge559c4bf1a-goog\n\n"},{"id":"500588","messageId":"527780f816028f4c60db8f51ce06de090cb51286.1723242556.git.steadmon@google.com","threadId":"61917","inReplyTo":"cover.1723242556.git.steadmon@google.com","subject":"[PATCH v2 3/5] cgit-sys: add repo initialization and config access","fromName":"Josh Steadmon","fromEmail":"steadmon@google.com","sentAt":"2024-08-09T22:41:15Z","receivedAt":"2024-08-09T22:41:25Z","isPatch":true,"sender":{"key":"steadmon@google.com","avatar":"https://avatars.githubusercontent.com/u/2654920?v=4"},"body":"From: Calvin Wan <calvinwan@google.com>\n\nWrap a few repo setup and config access functions in cgit-sys. These\nwere selected as proof-of-concept items to show that we can access local\nconfig from Rust.\n\nCo-authored-by: Josh Steadmon <steadmon@google.com>\nSigned-off-by: Calvin Wan <calvinwan@google.com>\nSigned-off-by: Josh Steadmon <steadmon@google.com>\n---\n .../cgit-rs/cgit-sys/public_symbol_export.c   | 26 ++++++++++\n .../cgit-rs/cgit-sys/public_symbol_export.h   |  8 ++++\n contrib/cgit-rs/cgit-sys/src/lib.rs           | 47 ++++++++++++++++++-\n 3 files changed, 79 insertions(+), 2 deletions(-)\n\ndiff --git a/contrib/cgit-rs/cgit-sys/public_symbol_export.c b/contrib/cgit-rs/cgit-sys/public_symbol_export.c\nindex deef4488c0..62a91f76d0 100644\n--- a/contrib/cgit-rs/cgit-sys/public_symbol_export.c\n+++ b/contrib/cgit-rs/cgit-sys/public_symbol_export.c\n@@ -2,11 +2,37 @@\n // original symbols can be hidden. Renaming these with a \"libgit_\" prefix also\n // avoid conflicts with other libraries such as libgit2.\n \n+#include \"git-compat-util.h\"\n #include \"contrib/cgit-rs/cgit-sys/public_symbol_export.h\"\n+#include \"common-init.h\"\n+#include \"config.h\"\n+#include \"setup.h\"\n #include \"version.h\"\n \n+extern struct repository *the_repository;\n+\n #pragma GCC visibility push(default)\n \n+const char *libgit_setup_git_directory(void)\n+{\n+\treturn setup_git_directory();\n+}\n+\n+int libgit_config_get_int(const char *key, int *dest)\n+{\n+\treturn repo_config_get_int(the_repository, key, dest);\n+}\n+\n+void libgit_init_git(const char **argv)\n+{\n+\tinit_git(argv);\n+}\n+\n+int libgit_parse_maybe_bool(const char *val)\n+{\n+\treturn git_parse_maybe_bool(val);\n+}\n+\n const char *libgit_user_agent(void)\n {\n \treturn git_user_agent();\ndiff --git a/contrib/cgit-rs/cgit-sys/public_symbol_export.h b/contrib/cgit-rs/cgit-sys/public_symbol_export.h\nindex a3372f93fa..64332f30de 100644\n--- a/contrib/cgit-rs/cgit-sys/public_symbol_export.h\n+++ b/contrib/cgit-rs/cgit-sys/public_symbol_export.h\n@@ -1,6 +1,14 @@\n #ifndef PUBLIC_SYMBOL_EXPORT_H\n #define PUBLIC_SYMBOL_EXPORT_H\n \n+const char *libgit_setup_git_directory(void);\n+\n+int libgit_config_get_int(const char *key, int *dest);\n+\n+void libgit_init_git(const char **argv);\n+\n+int libgit_parse_maybe_bool(const char *val);\n+\n const char *libgit_user_agent(void);\n \n const char *libgit_user_agent_sanitized(void);\ndiff --git a/contrib/cgit-rs/cgit-sys/src/lib.rs b/contrib/cgit-rs/cgit-sys/src/lib.rs\nindex bc33c6c498..8c3ccc2859 100644\n--- a/contrib/cgit-rs/cgit-sys/src/lib.rs\n+++ b/contrib/cgit-rs/cgit-sys/src/lib.rs\n@@ -1,6 +1,17 @@\n-use std::ffi::c_char;\n+use std::ffi::{c_char, c_int};\n \n extern \"C\" {\n+    pub fn libgit_setup_git_directory() -> *const c_char;\n+\n+    // From config.c\n+    pub fn libgit_config_get_int(key: *const c_char, dest: *mut c_int) -> c_int;\n+\n+    // From common-init.c\n+    pub fn libgit_init_git(argv: *const *const c_char);\n+\n+    // From parse.c\n+    pub fn libgit_parse_maybe_bool(val: *const c_char) -> c_int;\n+\n     // From version.c\n     pub fn libgit_user_agent() -> *const c_char;\n     pub fn libgit_user_agent_sanitized() -> *const c_char;\n@@ -8,7 +19,7 @@ extern \"C\" {\n \n #[cfg(test)]\n mod tests {\n-    use std::ffi::CStr;\n+    use std::ffi::{CStr, CString};\n \n     use super::*;\n \n@@ -37,4 +48,36 @@ mod tests {\n             agent\n         );\n     }\n+\n+    #[test]\n+    fn parse_bools_from_strings() {\n+        let arg = CString::new(\"true\").unwrap();\n+        assert_eq!(unsafe { libgit_parse_maybe_bool(arg.as_ptr()) }, 1);\n+\n+        let arg = CString::new(\"yes\").unwrap();\n+        assert_eq!(unsafe { libgit_parse_maybe_bool(arg.as_ptr()) }, 1);\n+\n+        let arg = CString::new(\"false\").unwrap();\n+        assert_eq!(unsafe { libgit_parse_maybe_bool(arg.as_ptr()) }, 0);\n+\n+        let arg = CString::new(\"no\").unwrap();\n+        assert_eq!(unsafe { libgit_parse_maybe_bool(arg.as_ptr()) }, 0);\n+\n+        let arg = CString::new(\"maybe\").unwrap();\n+        assert_eq!(unsafe { libgit_parse_maybe_bool(arg.as_ptr()) }, -1);\n+    }\n+\n+    #[test]\n+    fn access_configs() {\n+        // NEEDSWORK: we need to supply a testdata config\n+        let fake_argv = [std::ptr::null::<c_char>()];\n+        unsafe {\n+            libgit_init_git(fake_argv.as_ptr());\n+            libgit_setup_git_directory();\n+        }\n+        let mut val: c_int = 0;\n+        let key = CString::new(\"trace2.eventNesting\").unwrap();\n+        unsafe { libgit_config_get_int(key.as_ptr(), &mut val as *mut i32) };\n+        assert_eq!(val, 5);\n+    }\n }\n-- \n2.46.0.76.ge559c4bf1a-goog\n\n"},{"id":"500589","messageId":"908ad0b82fa084fc4e56d7f6dff49e4f255af6ec.1723242556.git.steadmon@google.com","threadId":"61917","inReplyTo":"cover.1723242556.git.steadmon@google.com","subject":"[PATCH v2 4/5] config: add git_configset_alloc() and git_configset_clear_and_free()","fromName":"Josh Steadmon","fromEmail":"steadmon@google.com","sentAt":"2024-08-09T22:41:16Z","receivedAt":"2024-08-09T22:41:27Z","isPatch":true,"sender":{"key":"steadmon@google.com","avatar":"https://avatars.githubusercontent.com/u/2654920?v=4"},"body":"Add git_configset_alloc() and git_configset_clear_and_free() functions\nso that callers can manage config_set structs on the heap. This also\nallows non-C external consumers to treat config_sets as opaque structs.\n\nCo-authored-by: Calvin Wan <calvinwan@google.com>\nSigned-off-by: Calvin Wan <calvinwan@google.com>\nSigned-off-by: Josh Steadmon <steadmon@google.com>\n---\n config.c | 11 +++++++++++\n config.h | 10 ++++++++++\n 2 files changed, 21 insertions(+)\n\ndiff --git a/config.c b/config.c\nindex 6421894614..444db8de79 100644\n--- a/config.c\n+++ b/config.c\n@@ -2324,6 +2324,11 @@ static int config_set_element_cmp(const void *cmp_data UNUSED,\n \treturn strcmp(e1->key, e2->key);\n }\n \n+struct config_set *git_configset_alloc(void)\n+{\n+\treturn xmalloc(sizeof(struct config_set));\n+}\n+\n void git_configset_init(struct config_set *set)\n {\n \thashmap_init(&set->config_hash, config_set_element_cmp, NULL, 0);\n@@ -2353,6 +2358,12 @@ void git_configset_clear(struct config_set *set)\n \tset->list.items = NULL;\n }\n \n+void git_configset_clear_and_free(struct config_set *set)\n+{\n+\tgit_configset_clear(set);\n+\tfree(set);\n+}\n+\n static int config_set_callback(const char *key, const char *value,\n \t\t\t       const struct config_context *ctx,\n \t\t\t       void *cb)\ndiff --git a/config.h b/config.h\nindex 54b47dec9e..074c85a788 100644\n--- a/config.h\n+++ b/config.h\n@@ -472,6 +472,11 @@ struct config_set {\n \tstruct configset_list list;\n };\n \n+/**\n+ * Alloc a config_set\n+ */\n+struct config_set *git_configset_alloc(void);\n+\n /**\n  * Initializes the config_set `cs`.\n  */\n@@ -520,6 +525,11 @@ int git_configset_get_string_multi(struct config_set *cs, const char *key,\n  */\n void git_configset_clear(struct config_set *cs);\n \n+/**\n+ * Clears and frees a heap-allocated `config_set` structure.\n+ */\n+void git_configset_clear_and_free(struct config_set *cs);\n+\n /*\n  * These functions return 1 if not found, and 0 if found, leaving the found\n  * value in the 'dest' pointer.\n-- \n2.46.0.76.ge559c4bf1a-goog\n\n"},{"id":"500590","messageId":"514c744ba41cf48c4ffd85640b7ef0c5783cc77d.1723242556.git.steadmon@google.com","threadId":"61917","inReplyTo":"cover.1723242556.git.steadmon@google.com","subject":"[PATCH v2 5/5] cgit: add higher-level cgit crate","fromName":"Josh Steadmon","fromEmail":"steadmon@google.com","sentAt":"2024-08-09T22:41:17Z","receivedAt":"2024-08-09T22:41:29Z","isPatch":true,"sender":{"key":"steadmon@google.com","avatar":"https://avatars.githubusercontent.com/u/2654920?v=4"},"body":"From: Calvin Wan <calvinwan@google.com>\n\nWrap `struct config_set` and a few of its associated functions in\ncgit-sys. Also introduce a higher-level \"cgit\" crate which provides a\nmore Rust-friendly interface to config_set structs.\n\nCo-authored-by: Josh Steadmon <steadmon@google.com>\nSigned-off-by: Calvin Wan <calvinwan@google.com>\nSigned-off-by: Josh Steadmon <steadmon@google.com>\n---\n .gitignore                                    |  1 +\n Makefile                                      |  2 +-\n contrib/cgit-rs/Cargo.lock                    | 14 ++++\n contrib/cgit-rs/Cargo.toml                    | 10 +++\n .../cgit-rs/cgit-sys/public_symbol_export.c   | 30 +++++++\n .../cgit-rs/cgit-sys/public_symbol_export.h   | 12 +++\n contrib/cgit-rs/cgit-sys/src/lib.rs           | 31 ++++++-\n contrib/cgit-rs/src/lib.rs                    | 82 +++++++++++++++++++\n 8 files changed, 180 insertions(+), 2 deletions(-)\n create mode 100644 contrib/cgit-rs/Cargo.lock\n create mode 100644 contrib/cgit-rs/Cargo.toml\n create mode 100644 contrib/cgit-rs/src/lib.rs\n\ndiff --git a/.gitignore b/.gitignore\nindex 567cc9888f..7f2ee89b8a 100644\n--- a/.gitignore\n+++ b/.gitignore\n@@ -248,4 +248,5 @@ Release/\n /git.VC.db\n *.dSYM\n /contrib/buildsystems/out\n+/contrib/cgit-rs/target\n /contrib/cgit-rs/cgit-sys/target\ndiff --git a/Makefile b/Makefile\nindex db8af99f20..3a71d10fbe 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -3723,7 +3723,7 @@ clean: profile-clean coverage-clean cocciclean\n \t$(RM) $(htmldocs).tar.gz $(manpages).tar.gz\n \t$(MAKE) -C Documentation/ clean\n \t$(RM) Documentation/GIT-EXCLUDED-PROGRAMS\n-\t$(RM) -r contrib/cgit-rs/cgit-sys/target\n+\t$(RM) -r contrib/cgit-rs/target contrib/cgit-rs/cgit-sys/target\n ifndef NO_PERL\n \t$(RM) -r perl/build/\n endif\ndiff --git a/contrib/cgit-rs/Cargo.lock b/contrib/cgit-rs/Cargo.lock\nnew file mode 100644\nindex 0000000000..991e775c34\n--- /dev/null\n+++ b/contrib/cgit-rs/Cargo.lock\n@@ -0,0 +1,14 @@\n+# This file is automatically @generated by Cargo.\n+# It is not intended for manual editing.\n+version = 3\n+\n+[[package]]\n+name = \"cgit\"\n+version = \"0.1.0\"\n+dependencies = [\n+ \"cgit-sys\",\n+]\n+\n+[[package]]\n+name = \"cgit-sys\"\n+version = \"0.1.0\"\ndiff --git a/contrib/cgit-rs/Cargo.toml b/contrib/cgit-rs/Cargo.toml\nnew file mode 100644\nindex 0000000000..31f4d84522\n--- /dev/null\n+++ b/contrib/cgit-rs/Cargo.toml\n@@ -0,0 +1,10 @@\n+[package]\n+name = \"cgit\"\n+version = \"0.1.0\"\n+edition = \"2021\"\n+\n+[lib]\n+path = \"src/lib.rs\"\n+\n+[dependencies]\n+cgit-sys = { version = \"0.1.0\", path = \"cgit-sys\" }\ndiff --git a/contrib/cgit-rs/cgit-sys/public_symbol_export.c b/contrib/cgit-rs/cgit-sys/public_symbol_export.c\nindex 62a91f76d0..adf8b21f11 100644\n--- a/contrib/cgit-rs/cgit-sys/public_symbol_export.c\n+++ b/contrib/cgit-rs/cgit-sys/public_symbol_export.c\n@@ -33,6 +33,36 @@ int libgit_parse_maybe_bool(const char *val)\n \treturn git_parse_maybe_bool(val);\n }\n \n+struct config_set *libgit_configset_alloc(void)\n+{\n+\treturn git_configset_alloc();\n+}\n+\n+void libgit_configset_clear_and_free(struct config_set *cs)\n+{\n+\tgit_configset_clear_and_free(cs);\n+}\n+\n+void libgit_configset_init(struct config_set *cs)\n+{\n+\tgit_configset_init(cs);\n+}\n+\n+int libgit_configset_add_file(struct config_set *cs, const char *filename)\n+{\n+\treturn git_configset_add_file(cs, filename);\n+}\n+\n+int libgit_configset_get_int(struct config_set *cs, const char *key, int *dest)\n+{\n+\treturn git_configset_get_int(cs, key, dest);\n+}\n+\n+int libgit_configset_get_string(struct config_set *cs, const char *key, char **dest)\n+{\n+\treturn git_configset_get_string(cs, key, dest);\n+}\n+\n const char *libgit_user_agent(void)\n {\n \treturn git_user_agent();\ndiff --git a/contrib/cgit-rs/cgit-sys/public_symbol_export.h b/contrib/cgit-rs/cgit-sys/public_symbol_export.h\nindex 64332f30de..882c7932e8 100644\n--- a/contrib/cgit-rs/cgit-sys/public_symbol_export.h\n+++ b/contrib/cgit-rs/cgit-sys/public_symbol_export.h\n@@ -9,6 +9,18 @@ void libgit_init_git(const char **argv);\n \n int libgit_parse_maybe_bool(const char *val);\n \n+struct config_set *libgit_configset_alloc(void);\n+\n+void libgit_configset_clear_and_free(struct config_set *cs);\n+\n+void libgit_configset_init(struct config_set *cs);\n+\n+int libgit_configset_add_file(struct config_set *cs, const char *filename);\n+\n+int libgit_configset_get_int(struct config_set *cs, const char *key, int *dest);\n+\n+int libgit_configset_get_string(struct config_set *cs, const char *key, char **dest);\n+\n const char *libgit_user_agent(void);\n \n const char *libgit_user_agent_sanitized(void);\ndiff --git a/contrib/cgit-rs/cgit-sys/src/lib.rs b/contrib/cgit-rs/cgit-sys/src/lib.rs\nindex 8c3ccc2859..d5072ea587 100644\n--- a/contrib/cgit-rs/cgit-sys/src/lib.rs\n+++ b/contrib/cgit-rs/cgit-sys/src/lib.rs\n@@ -1,6 +1,15 @@\n-use std::ffi::{c_char, c_int};\n+use std::ffi::{c_char, c_int, c_void};\n+\n+#[allow(non_camel_case_types)]\n+#[repr(C)]\n+pub struct config_set {\n+    _data: [u8; 0],\n+    _marker: core::marker::PhantomData<(*mut u8, core::marker::PhantomPinned)>,\n+}\n \n extern \"C\" {\n+    pub fn free(ptr: *mut c_void);\n+\n     pub fn libgit_setup_git_directory() -> *const c_char;\n \n     // From config.c\n@@ -15,6 +24,26 @@ extern \"C\" {\n     // From version.c\n     pub fn libgit_user_agent() -> *const c_char;\n     pub fn libgit_user_agent_sanitized() -> *const c_char;\n+\n+    pub fn libgit_configset_alloc() -> *mut config_set;\n+    pub fn libgit_configset_clear_and_free(cs: *mut config_set);\n+\n+    pub fn libgit_configset_init(cs: *mut config_set);\n+\n+    pub fn libgit_configset_add_file(cs: *mut config_set, filename: *const c_char) -> c_int;\n+\n+    pub fn libgit_configset_get_int(\n+        cs: *mut config_set,\n+        key: *const c_char,\n+        int: *mut c_int,\n+    ) -> c_int;\n+\n+    pub fn libgit_configset_get_string(\n+        cs: *mut config_set,\n+        key: *const c_char,\n+        dest: *mut *mut c_char,\n+    ) -> c_int;\n+\n }\n \n #[cfg(test)]\ndiff --git a/contrib/cgit-rs/src/lib.rs b/contrib/cgit-rs/src/lib.rs\nnew file mode 100644\nindex 0000000000..74c83d161a\n--- /dev/null\n+++ b/contrib/cgit-rs/src/lib.rs\n@@ -0,0 +1,82 @@\n+use std::ffi::{c_char, c_int, c_void, CStr, CString};\n+\n+use cgit_sys::*;\n+\n+pub struct ConfigSet(*mut config_set);\n+impl ConfigSet {\n+    pub fn new() -> Self {\n+        unsafe {\n+            let ptr = libgit_configset_alloc();\n+            libgit_configset_init(ptr);\n+            ConfigSet(ptr)\n+        }\n+    }\n+\n+    // NEEDSWORK: maybe replace &str with &Path\n+    pub fn add_files(&mut self, files: &[&str]) {\n+        for file in files {\n+            let rs = CString::new(*file).expect(\"Couldn't convert to CString\");\n+            unsafe {\n+                libgit_configset_add_file(self.0, rs.as_ptr());\n+            }\n+        }\n+    }\n+\n+    pub fn get_int(&mut self, key: &str) -> Option<c_int> {\n+        let key = CString::new(key).expect(\"Couldn't convert to CString\");\n+        let mut val: c_int = 0;\n+        unsafe {\n+            if libgit_configset_get_int(self.0, key.as_ptr(), &mut val as *mut c_int) != 0 {\n+                return None;\n+            }\n+        }\n+\n+        Some(val)\n+    }\n+\n+    pub fn get_str(&mut self, key: &str) -> Option<CString> {\n+        let key = CString::new(key).expect(\"Couldn't convert to CString\");\n+        let mut val: *mut c_char = std::ptr::null_mut();\n+        unsafe {\n+            if libgit_configset_get_string(self.0, key.as_ptr(), &mut val as *mut *mut c_char) != 0\n+            {\n+                return None;\n+            }\n+            let borrowed_str = CStr::from_ptr(val);\n+            let owned_str = CString::from_vec_with_nul(borrowed_str.to_bytes_with_nul().to_vec());\n+            free(val as *mut c_void); // Free the xstrdup()ed pointer from the C side\n+            Some(owned_str.unwrap())\n+        }\n+    }\n+}\n+\n+impl Default for ConfigSet {\n+    fn default() -> Self {\n+        Self::new()\n+    }\n+}\n+\n+impl Drop for ConfigSet {\n+    fn drop(&mut self) {\n+        unsafe {\n+            libgit_configset_clear_and_free(self.0);\n+        }\n+    }\n+}\n+\n+#[cfg(test)]\n+mod tests {\n+    use super::*;\n+\n+    #[test]\n+    fn load_configs_via_configset() {\n+        // NEEDSWORK: we need to supply a testdata config\n+        let mut cs = ConfigSet::new();\n+        let mut path = std::env::home_dir().expect(\"cannot get home directory path\");\n+        path.push(\".gitconfig\");\n+        let path: String = path.into_os_string().into_string().unwrap();\n+        cs.add_files(&[\"/etc/gitconfig\", \".gitconfig\", &path]);\n+        assert_eq!(cs.get_int(\"trace2.eventNesting\"), Some(5));\n+        assert_eq!(cs.get_str(\"no_such_config_item\"), None);\n+    }\n+}\n-- \n2.46.0.76.ge559c4bf1a-goog\n\n"},{"id":"500591","messageId":"xmqqfrrd9slb.fsf@gitster.g","threadId":"61917","inReplyTo":"cover.1723242556.git.steadmon@google.com","subject":"Re: [PATCH v2 0/5] Introduce cgit-rs, a Rust wrapper around libgit.a","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2024-08-09T23:36:32Z","receivedAt":"2024-08-09T23:36:37Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Josh Steadmon <steadmon@google.com> writes:\n\n> This series provides two small Rust wrapper libraries around parts of\n> Git: \"cgit-sys\", which exposes a few functions from libgit.a, and\n> \"cgit\", which provides a more Rust-friendly interface to some of those\n> functions. In addition to included unit tests, at $DAYJOB we have tested\n> building JJ[1] with our library and used it to replace some of the\n> libgit2-rs uses.\n\nThanks, queued.\n"},{"id":"500596","messageId":"Zrdn6QcnfmZhyEqJ@zx2c4.com","threadId":"61917","inReplyTo":"cover.1723242556.git.steadmon@google.com","subject":"Re: [PATCH v2 0/5] Introduce cgit-rs, a Rust wrapper around libgit.a","fromName":"Jason A. Donenfeld","fromEmail":"jason@zx2c4.com","sentAt":"2024-08-10T13:15:21Z","receivedAt":"2024-08-10T13:15:30Z","isPatch":true,"sender":{"key":"jason@zx2c4.com","avatar":"https://gravatar.com/avatar/8a3d6c2049b8353b1e0fb78142042d142c9dc72975698d944933997974d9fac5?d=mp&s=160"},"body":"Still the same name for v2? Cmon.\n"},{"id":"500602","messageId":"6398d60387a6607398e4b8731363572e@manjaro.org","threadId":"61917","inReplyTo":"Zrdn6QcnfmZhyEqJ@zx2c4.com","subject":"Re: [PATCH v2 0/5] Introduce cgit-rs, a Rust wrapper around libgit.a","fromName":"Dragan Simic","fromEmail":"dsimic@manjaro.org","sentAt":"2024-08-11T17:26:52Z","receivedAt":"2024-08-11T17:27:01Z","isPatch":true,"sender":{"key":"dsimic@manjaro.org","avatar":null},"body":"On 2024-08-10 15:15, Jason A. Donenfeld wrote:\n> Still the same name for v2? Cmon.\n\nYeah, I was also surprised to see that.  This _isn't_ cgit.\n"},{"id":"500607","messageId":"CAPig+cSotr8CNZLy4xnm4qyJsuQsxjzsYMVU5sf3eeoEiE8aXg@mail.gmail.com","threadId":"61917","inReplyTo":"6398d60387a6607398e4b8731363572e@manjaro.org","subject":"Re: [PATCH v2 0/5] Introduce cgit-rs, a Rust wrapper around libgit.a","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2024-08-11T23:03:08Z","receivedAt":"2024-08-11T23:03:22Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Sun, Aug 11, 2024 at 1:27 PM Dragan Simic <dsimic@manjaro.org> wrote:\n> On 2024-08-10 15:15, Jason A. Donenfeld wrote:\n> > Still the same name for v2? Cmon.\n>\n> Yeah, I was also surprised to see that.  This _isn't_ cgit.\n\nJosh addressed this point in the v2 cover letter by saying:\n\n    Known NEEDSWORK:\n    ...\n    * Bikeshed on the name\n"},{"id":"500608","messageId":"ee460e193b567b73e789ee77579f41c4@manjaro.org","threadId":"61917","inReplyTo":"CAPig+cSotr8CNZLy4xnm4qyJsuQsxjzsYMVU5sf3eeoEiE8aXg@mail.gmail.com","subject":"Re: [PATCH v2 0/5] Introduce cgit-rs, a Rust wrapper around libgit.a","fromName":"Dragan Simic","fromEmail":"dsimic@manjaro.org","sentAt":"2024-08-11T23:23:47Z","receivedAt":"2024-08-11T23:23:50Z","isPatch":true,"sender":{"key":"dsimic@manjaro.org","avatar":null},"body":"On 2024-08-12 01:03, Eric Sunshine wrote:\n> On Sun, Aug 11, 2024 at 1:27 PM Dragan Simic <dsimic@manjaro.org> \n> wrote:\n>> On 2024-08-10 15:15, Jason A. Donenfeld wrote:\n>> > Still the same name for v2? Cmon.\n>> \n>> Yeah, I was also surprised to see that.  This _isn't_ cgit.\n> \n> Josh addressed this point in the v2 cover letter by saying:\n> \n>     Known NEEDSWORK:\n>     ...\n>     * Bikeshed on the name\n\nBut didn't Junio already say that the v2 of this series will be merged\nas-is? [1]  That's what actually made me surprised and confused.\n\n[1] https://lore.kernel.org/git/xmqqfrrd9slb.fsf@gitster.g/\n"},{"id":"500609","messageId":"CAPig+cQnLkXdfokGBFdkkrZ3_nWrYGj3XyMRJ+0h5zrC5Kw0bw@mail.gmail.com","threadId":"61917","inReplyTo":"ee460e193b567b73e789ee77579f41c4@manjaro.org","subject":"Re: [PATCH v2 0/5] Introduce cgit-rs, a Rust wrapper around libgit.a","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2024-08-11T23:33:30Z","receivedAt":"2024-08-11T23:33:42Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Sun, Aug 11, 2024 at 7:23 PM Dragan Simic <dsimic@manjaro.org> wrote:\n> On 2024-08-12 01:03, Eric Sunshine wrote:\n> > Josh addressed this point in the v2 cover letter by saying:\n> >\n> >     Known NEEDSWORK:\n> >     ...\n> >     * Bikeshed on the name\n>\n> But didn't Junio already say that the v2 of this series will be merged\n> as-is? [1]  That's what actually made me surprised and confused.\n>\n> [1] https://lore.kernel.org/git/xmqqfrrd9slb.fsf@gitster.g/\n\nThat only means that Junio placed the topic in his \"seen\" branch,\nhowever, no promises about ultimate acceptance are attached to topics\nin that branch. Quoting from Junio's \"What's Cooking\" emails (such as\n[*]):\n\n    Commits prefixed with '-' are only in 'seen', and aren't\n    considered \"accepted\" at all and may be annotated with an URL to a\n    message that raises issues but they are no means exhaustive.  A\n    topic without enough support may be discarded after a long period\n    of no activity (of course they can be resubmit when new interests\n    arise).\n\n[*]: https://lore.kernel.org/git/xmqqo762frkz.fsf@gitster.g/\n"},{"id":"500610","messageId":"6b5b57147ce38099a57ecc4f256ef2b5@manjaro.org","threadId":"61917","inReplyTo":"CAPig+cQnLkXdfokGBFdkkrZ3_nWrYGj3XyMRJ+0h5zrC5Kw0bw@mail.gmail.com","subject":"Re: [PATCH v2 0/5] Introduce cgit-rs, a Rust wrapper around libgit.a","fromName":"Dragan Simic","fromEmail":"dsimic@manjaro.org","sentAt":"2024-08-11T23:37:30Z","receivedAt":"2024-08-11T23:37:33Z","isPatch":true,"sender":{"key":"dsimic@manjaro.org","avatar":null},"body":"On 2024-08-12 01:33, Eric Sunshine wrote:\n> On Sun, Aug 11, 2024 at 7:23 PM Dragan Simic <dsimic@manjaro.org> \n> wrote:\n>> On 2024-08-12 01:03, Eric Sunshine wrote:\n>> > Josh addressed this point in the v2 cover letter by saying:\n>> >\n>> >     Known NEEDSWORK:\n>> >     ...\n>> >     * Bikeshed on the name\n>> \n>> But didn't Junio already say that the v2 of this series will be merged\n>> as-is? [1]  That's what actually made me surprised and confused.\n>> \n>> [1] https://lore.kernel.org/git/xmqqfrrd9slb.fsf@gitster.g/\n> \n> That only means that Junio placed the topic in his \"seen\" branch,\n> however, no promises about ultimate acceptance are attached to topics\n> in that branch. Quoting from Junio's \"What's Cooking\" emails (such as\n> [*]):\n> \n>     Commits prefixed with '-' are only in 'seen', and aren't\n>     considered \"accepted\" at all and may be annotated with an URL to a\n>     message that raises issues but they are no means exhaustive.  A\n>     topic without enough support may be discarded after a long period\n>     of no activity (of course they can be resubmit when new interests\n>     arise).\n> \n> [*]: https://lore.kernel.org/git/xmqqo762frkz.fsf@gitster.g/\n\nAh, good, thanks for the clarification.  It's all fine then, as far as\nI'm concerned.\n"},{"id":"500612","messageId":"02a101daec5b$5c76ce80$15646b80$@nexbridge.com","threadId":"61917","inReplyTo":"zrbylipleb7fd7jrlnsampufeiuyiw2suwvd3tuxqoukhz6h52@dnjr7urrwzn3","subject":"RE: [RFC PATCH 3/6] contrib/cgit-rs: introduce Rust wrapper for libgit.a","fromName":"","fromEmail":"rsbecker@nexbridge.com","sentAt":"2024-08-12T02:00:03Z","receivedAt":"2024-08-12T02:00:23Z","isPatch":true,"sender":{"key":"randall.becker@nexbridge.ca","avatar":"https://avatars.githubusercontent.com/u/28956764?v=4"},"body":"On Wednesday, August 7, 2024 7:08 PM, Josh Steadmon wrote:\n>On 2024.08.07 17:40, rsbecker@nexbridge.com wrote:\n>> On Wednesday, August 7, 2024 5:21 PM, brian m. carlson wrote:\n>> >On 2024-08-07 at 18:21:28, Josh Steadmon wrote:\n>> >> Introduce cgit-rs, a Rust wrapper crate that allows Rust code to\n>> >> call functions in libgit.a. This initial patch defines build rules\n>> >> and an interface that exposes user agent string getter functions as\n>> >> a proof of concept. A proof-of-concept library consumer is provided\n>> >> in contrib/cgit-rs/src/main.rs. This executable can be run with\n>> >> `cargo run`\n>> >>\n>> >> Symbols in cgit can collide with symbols from other libraries such\n>> >> as libgit2. We avoid this by first exposing library symbols in\n>> >> public_symbol_export.[ch]. These symbols are prepended with \"libgit_\"\n>> >> to avoid collisions and set to visible using a visibility pragma.\n>> >> In build.rs, Rust builds contrib/cgit-rs/libcgit.a, which also\n>> >> contains libgit.a and other dependent libraries, with\n>> >> -fvisibility=hidden to hide all symbols within those libraries that\n>> >> haven't been exposed with a visibility pragma.\n>> >\n>> >I think this is a good idea.  It's optional and it allows us to add\n>> >functionality as we go along.  Platforms that don't have Rust can just\nomit\n>building it.\n>> >\n>> >> +[dependencies]\n>> >> +libc = \"0.2.155\"\n>> >\n>> >I don't love that we're using libc here.  It would be better to use\n>> >rustix because that provides safe APIs that are compatible with\n>> >POSIX, but I think for now we need this because rustix doesn't offer\n>> >memory management like free(3).  I'd really prefer that we didn't\n>> >have to do memory management in Rust, but maybe that can come in with a\n>future series.\n>>\n>> This is a good point. Libc is not portable, but because I can't build\n>> with RUST anyway, I hope that libc is restricted to this facility if\n>> used. It should not be included in the git C build. It is probably\n>> moot for me anyway for this series, but I have to mention it in case\nanyone else\n>gets the idea to include it as a dependency for git C.\n>\n>I know you don't have access to Rust, but would you be able to test the\nsymbol\n>visibility steps with `make contrib/cgit-rs/libcgit.a`?\n\nThis target is no longer valid. Is there another target I can try?\n\n"},{"id":"500618","messageId":"xmqq5xs688cz.fsf@gitster.g","threadId":"61917","inReplyTo":"CAPig+cSotr8CNZLy4xnm4qyJsuQsxjzsYMVU5sf3eeoEiE8aXg@mail.gmail.com","subject":"Re: [PATCH v2 0/5] Introduce cgit-rs, a Rust wrapper around libgit.a","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2024-08-12T08:15:40Z","receivedAt":"2024-08-12T08:15:51Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Eric Sunshine <sunshine@sunshineco.com> writes:\n\n> On Sun, Aug 11, 2024 at 1:27 PM Dragan Simic <dsimic@manjaro.org> wrote:\n>> On 2024-08-10 15:15, Jason A. Donenfeld wrote:\n>> > Still the same name for v2? Cmon.\n>>\n>> Yeah, I was also surprised to see that.  This _isn't_ cgit.\n>\n> Josh addressed this point in the v2 cover letter by saying:\n>\n>     Known NEEDSWORK:\n>     ...\n>     * Bikeshed on the name\n\nI do not quite consider it as as \"addressed this point\" to just slap\na NEEDSWORK label and doing nothing else, though.\n\nThe original iteration had this:\n\n    * bikeshedding on the name (yes, really). There is an active, unrelated\n      CGit project [4] that we only recently became aware of. We originally\n      took the name \"cgit\" because at $DAYJOB we sometimes refer to git.git\n      as \"cgit\" to distinguish it from jgit [5].\n\nand then now they as well as reviewers all have seen the tentative\ncgit name, saw the reaction it caused, and now know that not just\npotentially confusing other project _exists_, but it does matter.\n\nReviewers already have spent some time on suggesting that \"git\" part\nshould not be \"c\"git, as well as \"rs\" part may better be \"sys\",\netc.?.  There should be _some_ response, even if it does not yet\npropose a new name.\n\nIf it acknowledged that the time and knowledge reviewers gave the\ntopic were appreciated, e.g., \"The proposers of this topic saw THIS\npoint and THAT point as a input that we WILL need to consider when\nwe decide on the name.  We acknowledge that the name \"cgit-rs\" is\nnot ideal and needs to be changed.  But we haven't reached any\nconcrete alternative name yet, so this round still uses the same\nname\", I'd call that \"addressed this point\", though.\n\nBut just a dismissing \"Bikeshed on the name\", as if they do not care\nto be mistaken as saying \"those who complain about the name are only\nbikeshedding and not worth listening to\"?\n\nWe should do better than that.\n\n"},{"id":"500621","messageId":"CAPig+cSVNqBPjV3_41f6Ag2X4+-q4HidEo0D=1UaMG-Kv7pa5Q@mail.gmail.com","threadId":"61917","inReplyTo":"xmqq5xs688cz.fsf@gitster.g","subject":"Re: [PATCH v2 0/5] Introduce cgit-rs, a Rust wrapper around libgit.a","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2024-08-12T09:03:57Z","receivedAt":"2024-08-12T09:04:11Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Mon, Aug 12, 2024 at 4:15 AM Junio C Hamano <gitster@pobox.com> wrote:\n> The original iteration had this:\n>\n>     * bikeshedding on the name (yes, really). There is an active, unrelated\n>       CGit project [4] that we only recently became aware of. We originally\n>       took the name \"cgit\" because at $DAYJOB we sometimes refer to git.git\n>       as \"cgit\" to distinguish it from jgit [5].\n\nA tangent: Speaking of external/other projects, I don't think we've\nseen an explanation yet as to why this Rust wrapper is proposed as a\n`contrib/` item of Git itself, as opposed to being a separate project.\n\nI can only think of two possible reasons why they might want it in the\nGit project itself...\n\n(1) Easier access to the library portions of Git (\"libgit\") since that\nportion of the code is not otherwise published as a standalone\nlibrary. However, a workable alternative would be for the Rust wrapper\nto carry its own \"vendored\"[1] copy of Git. This would also ensure\nmore reliable builds since they wouldn't have to worry about the\n\"libgit\" API changing from under them, and can adjust for \"libgit\" API\nchanges when they manually pull in a new vendored copy. Hence, I'm not\nconvinced that this is a valid reason to carry the Rust wrapper in\nGit.\n\n(2) Perhaps the intention is that this Rust wrapper work will allow\nRust to be used within Git itself[3]? If that's the case, then\n`contrib/` seems the wrong resting place for this code.\n\nOn the other hand, as a standalone project, a big benefit is that the\nRust wrapper could have its own release cadence distinct from Git,\nwhich would likely be very beneficial since it is such a young\n(indeed, nascent) library; it is likely that the maintainers will want\nto release early and often at this stage.\n\n[1]: Other Rust projects carry vendored copies of projects upon which\nthey rely. For instance, the \"native_tls\" crate has a vendored copy of\nOpenSSL[2].\n\n[2]: https://docs.rs/native-tls/latest/native_tls/#cargo-features\n\n[3]: https://lore.kernel.org/git/CABPp-BFWsWCGogqQ=haMsS4OhOdSwc3frcAxa6soQR5ORTceOA@mail.gmail.com/\n"},{"id":"500623","messageId":"47b18fa4-f01b-4f42-8d04-9e145515ccc1@gmail.com","threadId":"61917","inReplyTo":"908ad0b82fa084fc4e56d7f6dff49e4f255af6ec.1723242556.git.steadmon@google.com","subject":"Re: [PATCH v2 4/5] config: add git_configset_alloc() and git_configset_clear_and_free()","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2024-08-12T09:10:48Z","receivedAt":"2024-08-12T09:10:51Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Josh\n\nOn 09/08/2024 23:41, Josh Steadmon wrote:\n> Add git_configset_alloc() and git_configset_clear_and_free() functions\n> so that callers can manage config_set structs on the heap. This also\n> allows non-C external consumers to treat config_sets as opaque structs.\n\nDo we really need to add this code to config.c rather than handling it \nin the wrapper layer in the next patch?\n\nLooking ahead I wonder how useful it is to users of the library to \nseparate out allocation from initialization. A function that allocates \nand initializes a configset would be more convenient and harder to \nmisuse. Calling release functions *_free() rather than \n*_clear_and_free() would be more convenient as well. I also noticed that \nthe data types are not namespaced when they are exported. So perhaps we \ncould drop this patch and add the following to the next patch.\n\n/*\n  * Namespace data types as well as functions and ensure consistent\n  * naming of data types and the functions that operate on them.\n  */\nstruct libgit_configset {\n\tstruct config_set set;\n};\n\nstruct libgit_configset *libgit_configset_new() {\n\tstruct libgit_configset *set = xmalloc(sizeof(*set));\n\n\tgit_configset_init(&set->set);\n\treturn set;\n}\n\nvoid libgit_configset_free(struct libgit_configset *set) {\n\tgit_configset_clear(&set->set);\n\tfree(set);\n}\n\nBest Wishes\n\nPhillip\n\n> Co-authored-by: Calvin Wan <calvinwan@google.com>\n> Signed-off-by: Calvin Wan <calvinwan@google.com>\n> Signed-off-by: Josh Steadmon <steadmon@google.com>\n> ---\n>   config.c | 11 +++++++++++\n>   config.h | 10 ++++++++++\n>   2 files changed, 21 insertions(+)\n> \n> diff --git a/config.c b/config.c\n> index 6421894614..444db8de79 100644\n> --- a/config.c\n> +++ b/config.c\n> @@ -2324,6 +2324,11 @@ static int config_set_element_cmp(const void *cmp_data UNUSED,\n>   \treturn strcmp(e1->key, e2->key);\n>   }\n>   \n> +struct config_set *git_configset_alloc(void)\n> +{\n> +\treturn xmalloc(sizeof(struct config_set));\n> +}\n> +\n>   void git_configset_init(struct config_set *set)\n>   {\n>   \thashmap_init(&set->config_hash, config_set_element_cmp, NULL, 0);\n> @@ -2353,6 +2358,12 @@ void git_configset_clear(struct config_set *set)\n>   \tset->list.items = NULL;\n>   }\n>   \n> +void git_configset_clear_and_free(struct config_set *set)\n> +{\n> +\tgit_configset_clear(set);\n> +\tfree(set);\n> +}\n> +\n>   static int config_set_callback(const char *key, const char *value,\n>   \t\t\t       const struct config_context *ctx,\n>   \t\t\t       void *cb)\n> diff --git a/config.h b/config.h\n> index 54b47dec9e..074c85a788 100644\n> --- a/config.h\n> +++ b/config.h\n> @@ -472,6 +472,11 @@ struct config_set {\n>   \tstruct configset_list list;\n>   };\n>   \n> +/**\n> + * Alloc a config_set\n> + */\n> +struct config_set *git_configset_alloc(void);\n> +\n>   /**\n>    * Initializes the config_set `cs`.\n>    */\n> @@ -520,6 +525,11 @@ int git_configset_get_string_multi(struct config_set *cs, const char *key,\n>    */\n>   void git_configset_clear(struct config_set *cs);\n>   \n> +/**\n> + * Clears and frees a heap-allocated `config_set` structure.\n> + */\n> +void git_configset_clear_and_free(struct config_set *cs);\n> +\n>   /*\n>    * These functions return 1 if not found, and 0 if found, leaving the found\n>    * value in the 'dest' pointer.\n"},{"id":"500626","messageId":"e3f7d292-ee51-4784-8c85-14b5223c380f@gmail.com","threadId":"61917","inReplyTo":"514c744ba41cf48c4ffd85640b7ef0c5783cc77d.1723242556.git.steadmon@google.com","subject":"Re: [PATCH v2 5/5] cgit: add higher-level cgit crate","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2024-08-12T09:26:29Z","receivedAt":"2024-08-12T09:26:32Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Josh\n\nOn 09/08/2024 23:41, Josh Steadmon wrote:\n> From: Calvin Wan <calvinwan@google.com>\n> \n> Wrap `struct config_set` and a few of its associated functions in\n> cgit-sys. Also introduce a higher-level \"cgit\" crate which provides a\n> more Rust-friendly interface to config_set structs.\n\nHaving an ergonamic interface is a really good idea. As far as the \nnaming goes I think the suggestion of \"libgit-rs\" is a good one.\n\n> diff --git a/contrib/cgit-rs/cgit-sys/public_symbol_export.h b/contrib/cgit-rs/cgit-sys/public_symbol_export.h\n> index 64332f30de..882c7932e8 100644\n> --- a/contrib/cgit-rs/cgit-sys/public_symbol_export.h\n> +++ b/contrib/cgit-rs/cgit-sys/public_symbol_export.h\n> @@ -9,6 +9,18 @@ void libgit_init_git(const char **argv);\n>   \n>   int libgit_parse_maybe_bool(const char *val);\n\nI'm suprised the compiler does not complain that 'struct config_set' is \nnot declared in this header - I was expecting to see\n\n\tstruct config_set;\n\nbefore the function declarations. As I said in my comments on the last \npatch I think we'd be better to namespace our types as well as our \nfunctions in this library layer so this can be resued by other language \nbindings.\n\n > [...]\n> +    pub fn get_str(&mut self, key: &str) -> Option<CString> {\n\nIf we're adding an ergonomic api then having return CString isn't ideal. \nI think the equivalent function in libgit2-rs has variants that return a \nString which is convinent if the caller is expecting utf8 values or \nVec<u8> for non-utf8 values.\n\nBest Wishes\n\nPhillip\n"},{"id":"500652","messageId":"3df4270de4e37121084aafcbce1c7450@manjaro.org","threadId":"61917","inReplyTo":"xmqq5xs688cz.fsf@gitster.g","subject":"Re: [PATCH v2 0/5] Introduce cgit-rs, a Rust wrapper around libgit.a","fromName":"Dragan Simic","fromEmail":"dsimic@manjaro.org","sentAt":"2024-08-12T18:08:28Z","receivedAt":"2024-08-12T18:08:38Z","isPatch":true,"sender":{"key":"dsimic@manjaro.org","avatar":null},"body":"On 2024-08-12 10:15, Junio C Hamano wrote:\n> Eric Sunshine <sunshine@sunshineco.com> writes:\n> \n>> On Sun, Aug 11, 2024 at 1:27 PM Dragan Simic <dsimic@manjaro.org> \n>> wrote:\n>>> On 2024-08-10 15:15, Jason A. Donenfeld wrote:\n>>> > Still the same name for v2? Cmon.\n>>> \n>>> Yeah, I was also surprised to see that.  This _isn't_ cgit.\n>> \n>> Josh addressed this point in the v2 cover letter by saying:\n>> \n>>     Known NEEDSWORK:\n>>     ...\n>>     * Bikeshed on the name\n> \n> I do not quite consider it as as \"addressed this point\" to just slap\n> a NEEDSWORK label and doing nothing else, though.\n> \n> The original iteration had this:\n> \n>     * bikeshedding on the name (yes, really). There is an active, \n> unrelated\n>       CGit project [4] that we only recently became aware of. We \n> originally\n>       took the name \"cgit\" because at $DAYJOB we sometimes refer to \n> git.git\n>       as \"cgit\" to distinguish it from jgit [5].\n> \n> and then now they as well as reviewers all have seen the tentative\n> cgit name, saw the reaction it caused, and now know that not just\n> potentially confusing other project _exists_, but it does matter.\n> \n> Reviewers already have spent some time on suggesting that \"git\" part\n> should not be \"c\"git, as well as \"rs\" part may better be \"sys\",\n> etc.?.  There should be _some_ response, even if it does not yet\n> propose a new name.\n> \n> If it acknowledged that the time and knowledge reviewers gave the\n> topic were appreciated, e.g., \"The proposers of this topic saw THIS\n> point and THAT point as a input that we WILL need to consider when\n> we decide on the name.  We acknowledge that the name \"cgit-rs\" is\n> not ideal and needs to be changed.  But we haven't reached any\n> concrete alternative name yet, so this round still uses the same\n> name\", I'd call that \"addressed this point\", though.\n> \n> But just a dismissing \"Bikeshed on the name\", as if they do not care\n> to be mistaken as saying \"those who complain about the name are only\n> bikeshedding and not worth listening to\"?\n> \n> We should do better than that.\n\nI really appreciate and support your response, Junio.  Brushing it off\noriginally as \"bikeshedding\" did leave a sour taste, because it \npresented\nthe conflicting naming as a non-issue.\n"},{"id":"500654","messageId":"xmqqed6t4nno.fsf@gitster.g","threadId":"61917","inReplyTo":"CAPig+cSVNqBPjV3_41f6Ag2X4+-q4HidEo0D=1UaMG-Kv7pa5Q@mail.gmail.com","subject":"Re: [PATCH v2 0/5] Introduce cgit-rs, a Rust wrapper around libgit.a","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2024-08-12T18:11:07Z","receivedAt":"2024-08-12T18:11:12Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Eric Sunshine <sunshine@sunshineco.com> writes:\n\n> A tangent: Speaking of external/other projects, I don't think we've\n> seen an explanation yet as to why this Rust wrapper is proposed as a\n> `contrib/` item of Git itself, as opposed to being a separate project.\n>\n> I can only think of two possible reasons why they might want it in the\n> Git project itself...\n>\n> (1) Easier access to the library portions of Git (\"libgit\") since that\n> portion of the code is not otherwise published as a standalone\n> library.\n\nThis is not a good reason at all, if we look at what (the real) cgit\nproject does ;-), which is to bind us as their submodule.\n\n> (2) Perhaps the intention is that this Rust wrapper work will allow\n> Rust to be used within Git itself[3]? If that's the case, then\n> `contrib/` seems the wrong resting place for this code.\n\nThe contrib/ hierarchy is a mixed bag, and we may want to clean it\nup by Git 3.0 happens.\n\n - We used to put things that one-way depend on Git that are (1)\n   useless by themselves, if Git did not exist, and (2) Git can do\n   fine without them in the contrib/ hierarchy.\n\n   The primary reason for doing so was because the Git was young and\n   relatively unknown, and our community was small.  Even for an\n   add-on that may be at most \"nice to have\" from Git's point of\n   view, it made some sense to give these add-ons wider exposure by\n   simply being bundled with Git.  We stopped doing that after Git\n   ecosystem have matured enough and encouraged them to move either\n   up (i.e. prove that Git cannot really do fine without it because\n   the add-on is so useful, and become the first class part of Git)\n   or out (i.e. it may be a fine add-on, but there are other similar\n   add-ons that aim to achieve the same, similar, or overlapping\n   goal---unlike the nascent era of Git, it should be able to become\n   an independent project to compete fairly with others on its merit\n   alone).\n\n - We have some stuff like completion and prompt scripts that have\n   proven that the end-user experience cannot be \"fine without\", but\n   haven't moved out of \"contrib/\", mostly by inercia.\n\n - We also have things that are meant to help Git developers in the\n   hierarchy (Cocci rules come to mind).  This one could fall into\n   that category, but I am not sure.\n\n> On the other hand, as a standalone project, a big benefit is that the\n> Rust wrapper could have its own release cadence distinct from Git,\n> which would likely be very beneficial since it is such a young\n> (indeed, nascent) library; it is likely that the maintainers will want\n> to release early and often at this stage.\n\nThat's cgit approach.  If something is useless if Git did not exist,\nbut Git does not need it at all to function, that may be a sensible\nthing to do.\n\nIf we plan to eventually be able to rewrite the logic for say\nhistory traveral or specifying \"set of commits\" in such a way that\nis more involved than just \"those that can be reached from these tip\ncommits excluding the ones that can be reached from those bottom\ncommits\" (in other words, \"A..B C..D\" that is not \"^A B ^C D\"), and\nfind it unpleasant to do in C and want to employ Rust, for example,\nwe'd certainly need it somewhere in _our_ tree.  Perhaps by the time\nit happens we might have an extension/dll mechanism in such a way\nthat those who do not want (or cannot build) certain extensions can\nopt out and these \"optional\" things would by convention live\nsomewhere other than the main source code live, but even then,\ncontrib/ sounds like a wrong place and we'd do so perhaps in extra/\nor something.\n\n> [1]: Other Rust projects carry vendored copies of projects upon which\n> they rely. For instance, the \"native_tls\" crate has a vendored copy of\n> OpenSSL[2].\n>\n> [2]: https://docs.rs/native-tls/latest/native_tls/#cargo-features\n>\n> [3]: https://lore.kernel.org/git/CABPp-BFWsWCGogqQ=haMsS4OhOdSwc3frcAxa6soQR5ORTceOA@mail.gmail.com/\n"},{"id":"500664","messageId":"rt4ruismrzhtkbry4kuube5fwf2zprajjen7wagl5nyyjivhvz@xuxq6paidcmr","threadId":"61917","inReplyTo":"xmqq5xs688cz.fsf@gitster.g","subject":"Re: [PATCH v2 0/5] Introduce cgit-rs, a Rust wrapper around libgit.a","fromName":"Josh Steadmon","fromEmail":"steadmon@google.com","sentAt":"2024-08-12T21:24:03Z","receivedAt":"2024-08-12T21:24:11Z","isPatch":true,"sender":{"key":"steadmon@google.com","avatar":"https://avatars.githubusercontent.com/u/2654920?v=4"},"body":"On 2024.08.12 01:15, Junio C Hamano wrote:\n> Eric Sunshine <sunshine@sunshineco.com> writes:\n> \n> > On Sun, Aug 11, 2024 at 1:27 PM Dragan Simic <dsimic@manjaro.org> wrote:\n> >> On 2024-08-10 15:15, Jason A. Donenfeld wrote:\n> >> > Still the same name for v2? Cmon.\n> >>\n> >> Yeah, I was also surprised to see that.  This _isn't_ cgit.\n> >\n> > Josh addressed this point in the v2 cover letter by saying:\n> >\n> >     Known NEEDSWORK:\n> >     ...\n> >     * Bikeshed on the name\n> \n> I do not quite consider it as as \"addressed this point\" to just slap\n> a NEEDSWORK label and doing nothing else, though.\n> \n> The original iteration had this:\n> \n>     * bikeshedding on the name (yes, really). There is an active, unrelated\n>       CGit project [4] that we only recently became aware of. We originally\n>       took the name \"cgit\" because at $DAYJOB we sometimes refer to git.git\n>       as \"cgit\" to distinguish it from jgit [5].\n> \n> and then now they as well as reviewers all have seen the tentative\n> cgit name, saw the reaction it caused, and now know that not just\n> potentially confusing other project _exists_, but it does matter.\n> \n> Reviewers already have spent some time on suggesting that \"git\" part\n> should not be \"c\"git, as well as \"rs\" part may better be \"sys\",\n> etc.?.  There should be _some_ response, even if it does not yet\n> propose a new name.\n> \n> If it acknowledged that the time and knowledge reviewers gave the\n> topic were appreciated, e.g., \"The proposers of this topic saw THIS\n> point and THAT point as a input that we WILL need to consider when\n> we decide on the name.  We acknowledge that the name \"cgit-rs\" is\n> not ideal and needs to be changed.  But we haven't reached any\n> concrete alternative name yet, so this round still uses the same\n> name\", I'd call that \"addressed this point\", though.\n> \n> But just a dismissing \"Bikeshed on the name\", as if they do not care\n> to be mistaken as saying \"those who complain about the name are only\n> bikeshedding and not worth listening to\"?\n> \n> We should do better than that.\n\nI am quite surprised that people felt this was dismissive. So to be\nclear: yes, we need a new name before this lands in next. I thought that\nleaving that as a known needs-work item was sufficient to call that out,\nbut I guess I was wrong.\n\nAs far as proposed names go, I am not super happy with either \"libgit\"\nor \"libgit3\", because that seems confusing when we consider there are\nalso a separate libgit2 and libgit2-rs projects. I know that we're using\n\"libgit_\" as a prefix for visible symbols at the moment, but I plan on\nrenaming those once we have settled on the actual library name.\n\nI was avoiding addressing this topic in the hopes that folks might\nsuggest additional name options, but as it stands right now, I'm leaning\ntowards just \"git-sys\" for the low-level wrapper crate, \"git\" for the\nRust API, and \"git-rs\" for the directory in contrib/ which will contain\nthe crates.\n"},{"id":"500665","messageId":"k4cokm5xtwazlv44ys2uzmrfufubbq7tcmcwzasuccog3zcojf@4ftvda4nfqxj","threadId":"61917","inReplyTo":"CAPig+cSVNqBPjV3_41f6Ag2X4+-q4HidEo0D=1UaMG-Kv7pa5Q@mail.gmail.com","subject":"Re: [PATCH v2 0/5] Introduce cgit-rs, a Rust wrapper around libgit.a","fromName":"Josh Steadmon","fromEmail":"steadmon@google.com","sentAt":"2024-08-12T21:32:12Z","receivedAt":"2024-08-12T21:32:18Z","isPatch":true,"sender":{"key":"steadmon@google.com","avatar":"https://avatars.githubusercontent.com/u/2654920?v=4"},"body":"On 2024.08.12 05:03, Eric Sunshine wrote:\n> On Mon, Aug 12, 2024 at 4:15 AM Junio C Hamano <gitster@pobox.com> wrote:\n> > The original iteration had this:\n> >\n> >     * bikeshedding on the name (yes, really). There is an active, unrelated\n> >       CGit project [4] that we only recently became aware of. We originally\n> >       took the name \"cgit\" because at $DAYJOB we sometimes refer to git.git\n> >       as \"cgit\" to distinguish it from jgit [5].\n> \n> A tangent: Speaking of external/other projects, I don't think we've\n> seen an explanation yet as to why this Rust wrapper is proposed as a\n> `contrib/` item of Git itself, as opposed to being a separate project.\n> \n> I can only think of two possible reasons why they might want it in the\n> Git project itself...\n> \n> (1) Easier access to the library portions of Git (\"libgit\") since that\n> portion of the code is not otherwise published as a standalone\n> library. However, a workable alternative would be for the Rust wrapper\n> to carry its own \"vendored\"[1] copy of Git. This would also ensure\n> more reliable builds since they wouldn't have to worry about the\n> \"libgit\" API changing from under them, and can adjust for \"libgit\" API\n> changes when they manually pull in a new vendored copy. Hence, I'm not\n> convinced that this is a valid reason to carry the Rust wrapper in\n> Git.\n> \n> (2) Perhaps the intention is that this Rust wrapper work will allow\n> Rust to be used within Git itself[3]? If that's the case, then\n> `contrib/` seems the wrong resting place for this code.\n\nNeither, actually. We hope that by keeping the crate definition in the\nmain Git repo and by allowing developers to optionally run them by\ndefault as part of CI and `make test`, we can catch breaking bugs as\nearly as possible. We're also hoping that we'll get more attention from\ninterested developers by staying in the main repo rather than in a\nseparate project.\n\n> On the other hand, as a standalone project, a big benefit is that the\n> Rust wrapper could have its own release cadence distinct from Git,\n> which would likely be very beneficial since it is such a young\n> (indeed, nascent) library; it is likely that the maintainers will want\n> to release early and often at this stage.\n\nAFAIK, the crate releases don't have to be strictly tied to Git\nreleases. In theory we could choose to publish updates to the crate\nwhenever \"master\" or any other branch is updated; whether or not that is\na good idea is a separate discussion.\n\n\n> [1]: Other Rust projects carry vendored copies of projects upon which\n> they rely. For instance, the \"native_tls\" crate has a vendored copy of\n> OpenSSL[2].\n> \n> [2]: https://docs.rs/native-tls/latest/native_tls/#cargo-features\n> \n> [3]: https://lore.kernel.org/git/CABPp-BFWsWCGogqQ=haMsS4OhOdSwc3frcAxa6soQR5ORTceOA@mail.gmail.com/\n"},{"id":"500666","messageId":"xmqqmslhzakp.fsf@gitster.g","threadId":"61917","inReplyTo":"rt4ruismrzhtkbry4kuube5fwf2zprajjen7wagl5nyyjivhvz@xuxq6paidcmr","subject":"Re: [PATCH v2 0/5] Introduce cgit-rs, a Rust wrapper around libgit.a","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2024-08-12T21:37:58Z","receivedAt":"2024-08-12T21:38:04Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Josh Steadmon <steadmon@google.com> writes:\n\n> I was avoiding addressing this topic in the hopes that folks might\n> suggest additional name options,...\n\nI've seen libgit-rs (or was it libgit-rust?) suggested already and\nfound them reasonable.  If libgit2 folks do not use libgit_ prefix,\nI do not see any reason for us to avoid it, either.\n\nThanks.\n\n\n"},{"id":"500667","messageId":"bifcs4ijz4gsnrfjs3naqclo7nj7ajhek6eppgaj6a27ixp372@6oariraac4rz","threadId":"61917","inReplyTo":"47b18fa4-f01b-4f42-8d04-9e145515ccc1@gmail.com","subject":"Re: [PATCH v2 4/5] config: add git_configset_alloc() and git_configset_clear_and_free()","fromName":"Josh Steadmon","fromEmail":"steadmon@google.com","sentAt":"2024-08-12T21:39:27Z","receivedAt":"2024-08-12T21:39:33Z","isPatch":true,"sender":{"key":"steadmon@google.com","avatar":"https://avatars.githubusercontent.com/u/2654920?v=4"},"body":"On 2024.08.12 10:10, Phillip Wood wrote:\n> Hi Josh\n> \n> On 09/08/2024 23:41, Josh Steadmon wrote:\n> > Add git_configset_alloc() and git_configset_clear_and_free() functions\n> > so that callers can manage config_set structs on the heap. This also\n> > allows non-C external consumers to treat config_sets as opaque structs.\n> \n> Do we really need to add this code to config.c rather than handling it in\n> the wrapper layer in the next patch?\n> \n> Looking ahead I wonder how useful it is to users of the library to separate\n> out allocation from initialization. A function that allocates and\n> initializes a configset would be more convenient and harder to misuse.\n> Calling release functions *_free() rather than *_clear_and_free() would be\n> more convenient as well. I also noticed that the data types are not\n> namespaced when they are exported. So perhaps we could drop this patch and\n> add the following to the next patch.\n> \n> /*\n>  * Namespace data types as well as functions and ensure consistent\n>  * naming of data types and the functions that operate on them.\n>  */\n> struct libgit_configset {\n> \tstruct config_set set;\n> };\n> \n> struct libgit_configset *libgit_configset_new() {\n> \tstruct libgit_configset *set = xmalloc(sizeof(*set));\n> \n> \tgit_configset_init(&set->set);\n> \treturn set;\n> }\n> \n> void libgit_configset_free(struct libgit_configset *set) {\n> \tgit_configset_clear(&set->set);\n> \tfree(set);\n> }\n> \n> Best Wishes\n> \n> Phillip\n\nHmmm I see your point, but I am also hoping to keep the symbol export\nshim as small as possible, so that we can try to autogenerate it rather\nthan add entries by hand. However, if people feel strongly that we don't\nwant to add helper functions like *_alloc() or *_free() for types that don't\nalready have them upstream, perhaps we can just put them in a separate\nrust-helpers.c file or something.\n"},{"id":"500668","messageId":"CAO_smViBnmc4r3MizV5kE406_TvXiwCWtC0jyDb-FT-zp9H9bg@mail.gmail.com","threadId":"61917","inReplyTo":"bifcs4ijz4gsnrfjs3naqclo7nj7ajhek6eppgaj6a27ixp372@6oariraac4rz","subject":"Re: [PATCH v2 4/5] config: add git_configset_alloc() and git_configset_clear_and_free()","fromName":"Kyle Lippincott","fromEmail":"spectral@google.com","sentAt":"2024-08-12T21:55:33Z","receivedAt":"2024-08-12T21:55:49Z","isPatch":true,"sender":{"key":"spectral@google.com","avatar":"https://avatars.githubusercontent.com/u/6371650?v=4"},"body":"On Mon, Aug 12, 2024 at 2:39 PM Josh Steadmon <steadmon@google.com> wrote:\n>\n> On 2024.08.12 10:10, Phillip Wood wrote:\n> > Hi Josh\n> >\n> > On 09/08/2024 23:41, Josh Steadmon wrote:\n> > > Add git_configset_alloc() and git_configset_clear_and_free() functions\n> > > so that callers can manage config_set structs on the heap. This also\n> > > allows non-C external consumers to treat config_sets as opaque structs.\n> >\n> > Do we really need to add this code to config.c rather than handling it in\n> > the wrapper layer in the next patch?\n> >\n> > Looking ahead I wonder how useful it is to users of the library to separate\n> > out allocation from initialization. A function that allocates and\n> > initializes a configset would be more convenient and harder to misuse.\n> > Calling release functions *_free() rather than *_clear_and_free() would be\n> > more convenient as well. I also noticed that the data types are not\n> > namespaced when they are exported. So perhaps we could drop this patch and\n> > add the following to the next patch.\n> >\n> > /*\n> >  * Namespace data types as well as functions and ensure consistent\n> >  * naming of data types and the functions that operate on them.\n> >  */\n> > struct libgit_configset {\n> >       struct config_set set;\n> > };\n> >\n> > struct libgit_configset *libgit_configset_new() {\n> >       struct libgit_configset *set = xmalloc(sizeof(*set));\n> >\n> >       git_configset_init(&set->set);\n> >       return set;\n> > }\n> >\n> > void libgit_configset_free(struct libgit_configset *set) {\n> >       git_configset_clear(&set->set);\n> >       free(set);\n> > }\n> >\n> > Best Wishes\n> >\n> > Phillip\n>\n> Hmmm I see your point, but I am also hoping to keep the symbol export\n> shim as small as possible, so that we can try to autogenerate it rather\n> than add entries by hand. However, if people feel strongly that we don't\n> want to add helper functions like *_alloc() or *_free() for types that don't\n> already have them upstream, perhaps we can just put them in a separate\n> rust-helpers.c file or something.\n\nI'm thinking of this patch series as two closely related but\ntechnically separable things: the creation of a .a/.so that can be\nused outside of git, and the rust wrapper around that library. I think\nthese functions would be needed by all users of the library,\nregardless of what language they're implemented in. i.e. they\nshouldn't be thought of as 'rust helpers' and instead just the way\nthat the library is designed. _All_ functions that allocate memory\nshould have a paired \"free\" method, and that should be used\nexclusively, regardless of host language.\n\nSo nit: I wouldn't call it rust-helpers.c ;)\n"},{"id":"500669","messageId":"xmqqy1519z8i.fsf@gitster.g","threadId":"61917","inReplyTo":"rt4ruismrzhtkbry4kuube5fwf2zprajjen7wagl5nyyjivhvz@xuxq6paidcmr","subject":"Re: [PATCH v2 0/5] Introduce cgit-rs, a Rust wrapper around libgit.a","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2024-08-12T22:02:05Z","receivedAt":"2024-08-12T22:02:11Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Josh Steadmon <steadmon@google.com> writes:\n\n>> But just a dismissing \"Bikeshed on the name\", as if they do not care\n>> to be mistaken as saying \"those who complain about the name are only\n>> bikeshedding and not worth listening to\"?\n>> \n>> We should do better than that.\n>\n> I am quite surprised that people felt this was dismissive. So to be\n> clear: yes, we need a new name before this lands in next. I thought that\n> leaving that as a known needs-work item was sufficient to call that out,\n> but I guess I was wrong.\n\nYes, I had a similar initial reaction, but I guess that comes\nprimarily from the fact that we both misjudged how \"cgit\" is already\ndeeply established name that refers to something other than this\nproject.\n"},{"id":"500672","messageId":"ZrqJM-vmPaJbdHP2@tapette.crustytoothpaste.net","threadId":"61917","inReplyTo":"CAPig+cSVNqBPjV3_41f6Ag2X4+-q4HidEo0D=1UaMG-Kv7pa5Q@mail.gmail.com","subject":"Re: [PATCH v2 0/5] Introduce cgit-rs, a Rust wrapper around libgit.a","fromName":"brian m. carlson","fromEmail":"sandals@crustytoothpaste.net","sentAt":"2024-08-12T22:14:11Z","receivedAt":"2024-08-12T22:14:13Z","isPatch":true,"sender":{"key":"sandals@crustytoothpaste.net","avatar":"https://avatars.githubusercontent.com/u/497054?v=4"},"body":"On 2024-08-12 at 09:03:57, Eric Sunshine wrote:\n> On Mon, Aug 12, 2024 at 4:15 AM Junio C Hamano <gitster@pobox.com> wrote:\n> > The original iteration had this:\n> >\n> >     * bikeshedding on the name (yes, really). There is an active, unrelated\n> >       CGit project [4] that we only recently became aware of. We originally\n> >       took the name \"cgit\" because at $DAYJOB we sometimes refer to git.git\n> >       as \"cgit\" to distinguish it from jgit [5].\n> \n> A tangent: Speaking of external/other projects, I don't think we've\n> seen an explanation yet as to why this Rust wrapper is proposed as a\n> `contrib/` item of Git itself, as opposed to being a separate project.\n> \n> I can only think of two possible reasons why they might want it in the\n> Git project itself...\n> \n> (1) Easier access to the library portions of Git (\"libgit\") since that\n> portion of the code is not otherwise published as a standalone\n> library. However, a workable alternative would be for the Rust wrapper\n> to carry its own \"vendored\"[1] copy of Git. This would also ensure\n> more reliable builds since they wouldn't have to worry about the\n> \"libgit\" API changing from under them, and can adjust for \"libgit\" API\n> changes when they manually pull in a new vendored copy. Hence, I'm not\n> convinced that this is a valid reason to carry the Rust wrapper in\n> Git.\n\nPlease don't vendor packages like this.  This means that every time\nthere's a security vulnerability in Git, even if we ship a public libgit\nand even if the security impact doesn't affect libgit, every security\nscanning tool will demand that the crate be updated immediately.  This\nwastes colossal amounts of work and it's hostile to distros as well, who\nwill have to repackage the source to remove the vendoring.\n\nAt work, I maintain a codebase using Nokogiri, which is a Ruby gem that\nvendors libxml2 (which has an absolutely abysmal security record), and I\ncannot tell you how much useless work I perform updating this gem\nbecause of security scanners screaming about the vendored libxml2, even\nthough it's completely possible to use the system library.\n\n> (2) Perhaps the intention is that this Rust wrapper work will allow\n> Rust to be used within Git itself[3]? If that's the case, then\n> `contrib/` seems the wrong resting place for this code.\n\nI think contrib is the right place for now.  We may in the future want\nto include some Rust code in the codebase in some context, and this is a\ngood way for us to experiment with it on an interim basis and discover\nwhether that's a viable approach for us.  Perhaps it is, and perhaps it\nis not, but we'll have built the experience to know for certain.\n\nI could imagine (and this is hypothetical and speculative) that we might\nallow some sorts of performance optimizations and optional features in\nRust, but require core functionality be in C until Rust is better\nsupported on some platforms.  That might be a nice way to improve things\nlike multithreading, which Rust excels at, for example.\n\nAs an example, I'd like to write a random-access tree parser[0] in Rust\n(such as for \"dev:README.md\"), since our current approach performs\nterribly on large trees (it's linear instead of logarithmic).  However,\nI'm not willing to write that code in C because it's a giant landmine of\nmemory unsafety and I think it's too likely to be buggy in C.\n\n[0] I am aware that due to the structure of trees, this is not _always_\npossible to do, but it is possible in _many_ cases, and optimistically\ntrying and falling back to the old approach may still be faster.\n-- \nbrian m. carlson (they/them or he/him)\nToronto, Ontario, CA\n"},{"id":"500760","messageId":"5720d5b9-a850-4024-a1fd-54acc6b15a74@gmail.com","threadId":"61917","inReplyTo":"bifcs4ijz4gsnrfjs3naqclo7nj7ajhek6eppgaj6a27ixp372@6oariraac4rz","subject":"Re: [PATCH v2 4/5] config: add git_configset_alloc() and git_configset_clear_and_free()","fromName":"","fromEmail":"phillip.wood123@gmail.com","sentAt":"2024-08-13T09:48:41Z","receivedAt":"2024-08-13T09:48:46Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Josh\n\nOn 12/08/2024 22:39, Josh Steadmon wrote:\n> On 2024.08.12 10:10, Phillip Wood wrote:\n>> Hi Josh\n>>\n>> Do we really need to add this code to config.c rather than handling it in\n>> the wrapper layer in the next patch?\n>>\n>> Looking ahead I wonder how useful it is to users of the library to separate\n>> out allocation from initialization. A function that allocates and\n>> initializes a configset would be more convenient and harder to misuse.\n>> Calling release functions *_free() rather than *_clear_and_free() would be\n>> more convenient as well. I also noticed that the data types are not\n>> namespaced when they are exported. So perhaps we could drop this patch and\n>> add the following to the next patch.\n>>\n>> /*\n>>   * Namespace data types as well as functions and ensure consistent\n>>   * naming of data types and the functions that operate on them.\n>>   */\n>> struct libgit_configset {\n>> \tstruct config_set set;\n>> };\n>>\n>> struct libgit_configset *libgit_configset_new() {\n>> \tstruct libgit_configset *set = xmalloc(sizeof(*set));\n>>\n>> \tgit_configset_init(&set->set);\n>> \treturn set;\n>> }\n>>\n>> void libgit_configset_free(struct libgit_configset *set) {\n>> \tgit_configset_clear(&set->set);\n>> \tfree(set);\n>> }\n> \n> Hmmm I see your point, but I am also hoping to keep the symbol export\n> shim as small as possible, so that we can try to autogenerate it rather\n> than add entries by hand.\n\nThat's a nice aim - how do you plan to address namespacing data types \nwith that approach? The autogenerator would need to know \"struct \nconfig_set\" wants to be wrapped as \"struct libgit_configset\" to ensure \nwe end up with consistent naming for the data type and its functions. \nWe'd also still want to make sure we end up with an ergonomic api which \nmeans one function to allocate and initialize each data type, not \nseparate functions for allocation and initialization.\n\n> However, if people feel strongly that we don't\n> want to add helper functions like *_alloc() or *_free() for types that don't\n> already have them upstream, perhaps we can just put them in a separate\n> rust-helpers.c file or something.\n\nIf we're not using them upstream they're just clutter as far as git is \nconcerned. Having said that if you get the autogeneration working well \nso the output is usable without a lot of manual tweaking I can see an \nargument for having these functions upstream.\n\nBest Wishes\n\nPhillip\n"},{"id":"500761","messageId":"92278af3-0ade-4e7a-a024-f66b008b9cd2@gmail.com","threadId":"61917","inReplyTo":"CAO_smViBnmc4r3MizV5kE406_TvXiwCWtC0jyDb-FT-zp9H9bg@mail.gmail.com","subject":"Re: [PATCH v2 4/5] config: add git_configset_alloc() and git_configset_clear_and_free()","fromName":"","fromEmail":"phillip.wood123@gmail.com","sentAt":"2024-08-13T09:51:44Z","receivedAt":"2024-08-13T09:51:49Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Kyle\n\nOn 12/08/2024 22:55, Kyle Lippincott wrote:\n> On Mon, Aug 12, 2024 at 2:39 PM Josh Steadmon <steadmon@google.com> wrote:\n>> Hmmm I see your point, but I am also hoping to keep the symbol export\n>> shim as small as possible, so that we can try to autogenerate it rather\n>> than add entries by hand. However, if people feel strongly that we don't\n>> want to add helper functions like *_alloc() or *_free() for types that don't\n>> already have them upstream, perhaps we can just put them in a separate\n>> rust-helpers.c file or something.\n> \n> I'm thinking of this patch series as two closely related but\n> technically separable things: the creation of a .a/.so that can be\n> used outside of git, and the rust wrapper around that library. I think\n> these functions would be needed by all users of the library,\n> regardless of what language they're implemented in. i.e. they\n> shouldn't be thought of as 'rust helpers' and instead just the way\n> that the library is designed. _All_ functions that allocate memory\n> should have a paired \"free\" method, and that should be used\n> exclusively, regardless of host language.\n\nThanks for writing this, I agree we should be designing the library \nwrapper as a general purpose library, not as an implementation detail of \nthe rust code.\n\nBest Wishes\n\nPhillip\n\n> So nit: I wouldn't call it rust-helpers.c ;)\n"},{"id":"500794","messageId":"xmqqzfpg8nc0.fsf@gitster.g","threadId":"61917","inReplyTo":"92278af3-0ade-4e7a-a024-f66b008b9cd2@gmail.com","subject":"Re: [PATCH v2 4/5] config: add git_configset_alloc() and git_configset_clear_and_free()","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2024-08-13T15:16:47Z","receivedAt":"2024-08-13T15:16:58Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"phillip.wood123@gmail.com writes:\n\n>> I'm thinking of this patch series as two closely related but\n>> technically separable things: the creation of a .a/.so that can be\n>> used outside of git, and the rust wrapper around that library. I think\n>> these functions would be needed by all users of the library,\n>> regardless of what language they're implemented in. i.e. they\n>> shouldn't be thought of as 'rust helpers' and instead just the way\n>> that the library is designed.\n> ...\n> Thanks for writing this, I agree we should be designing the library\n> wrapper as a general purpose library, not as an implementation detail\n> of the rust code.\n\nExcellent.  Yes, make it wrapped for Rust may be a good motivator,\nbut cleaning up the interface so that it is more pleasant to wrap\nfor or directly call from any language should be an important goal\nof this effort.\n\nThanks, all.\n"},{"id":"501132","messageId":"Zr827Bd5jrQwhIEp@tanuki","threadId":"61917","inReplyTo":"47b18fa4-f01b-4f42-8d04-9e145515ccc1@gmail.com","subject":"Re: [PATCH v2 4/5] config: add git_configset_alloc() and git_configset_clear_and_free()","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2024-08-16T11:24:36Z","receivedAt":"2024-08-16T11:24:43Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"On Mon, Aug 12, 2024 at 10:10:48AM +0100, Phillip Wood wrote:\n> Hi Josh\n> \n> On 09/08/2024 23:41, Josh Steadmon wrote:\n> > Add git_configset_alloc() and git_configset_clear_and_free() functions\n> > so that callers can manage config_set structs on the heap. This also\n> > allows non-C external consumers to treat config_sets as opaque structs.\n> \n> Do we really need to add this code to config.c rather than handling it in\n> the wrapper layer in the next patch?\n> \n> Looking ahead I wonder how useful it is to users of the library to separate\n> out allocation from initialization. A function that allocates and\n> initializes a configset would be more convenient and harder to misuse.\n> Calling release functions *_free() rather than *_clear_and_free() would be\n> more convenient as well. I also noticed that the data types are not\n> namespaced when they are exported. So perhaps we could drop this patch and\n> add the following to the next patch.\n\nAgreed. This is also part of our coding guidelines as of 10f0723c8d\n(Documentation: document idiomatic function names, 2024-07-30).\n\nPatrick\n"},{"id":"501133","messageId":"Zr86bF3y_YMZx0CQ@tanuki","threadId":"61917","inReplyTo":"k4cokm5xtwazlv44ys2uzmrfufubbq7tcmcwzasuccog3zcojf@4ftvda4nfqxj","subject":"Re: [PATCH v2 0/5] Introduce cgit-rs, a Rust wrapper around libgit.a","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2024-08-16T11:39:24Z","receivedAt":"2024-08-16T11:39:31Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"On Mon, Aug 12, 2024 at 02:32:12PM -0700, Josh Steadmon wrote:\n> On 2024.08.12 05:03, Eric Sunshine wrote:\n> > On Mon, Aug 12, 2024 at 4:15 AM Junio C Hamano <gitster@pobox.com> wrote:\n> > > The original iteration had this:\n> > >\n> > >     * bikeshedding on the name (yes, really). There is an active, unrelated\n> > >       CGit project [4] that we only recently became aware of. We originally\n> > >       took the name \"cgit\" because at $DAYJOB we sometimes refer to git.git\n> > >       as \"cgit\" to distinguish it from jgit [5].\n> > \n> > A tangent: Speaking of external/other projects, I don't think we've\n> > seen an explanation yet as to why this Rust wrapper is proposed as a\n> > `contrib/` item of Git itself, as opposed to being a separate project.\n> > \n> > I can only think of two possible reasons why they might want it in the\n> > Git project itself...\n> > \n> > (1) Easier access to the library portions of Git (\"libgit\") since that\n> > portion of the code is not otherwise published as a standalone\n> > library. However, a workable alternative would be for the Rust wrapper\n> > to carry its own \"vendored\"[1] copy of Git. This would also ensure\n> > more reliable builds since they wouldn't have to worry about the\n> > \"libgit\" API changing from under them, and can adjust for \"libgit\" API\n> > changes when they manually pull in a new vendored copy. Hence, I'm not\n> > convinced that this is a valid reason to carry the Rust wrapper in\n> > Git.\n> > \n> > (2) Perhaps the intention is that this Rust wrapper work will allow\n> > Rust to be used within Git itself[3]? If that's the case, then\n> > `contrib/` seems the wrong resting place for this code.\n> \n> Neither, actually. We hope that by keeping the crate definition in the\n> main Git repo and by allowing developers to optionally run them by\n> default as part of CI and `make test`, we can catch breaking bugs as\n> early as possible. We're also hoping that we'll get more attention from\n> interested developers by staying in the main repo rather than in a\n> separate project.\n\nThat to me raises an important question: who is the one fixing breakage?\nHypothetically speaking, if I continue with my refactoring spree to drop\n`the_repository`, do I also have to fix the Rust bindings that I break\nas a consequence?\n\nPatrick\n"},{"id":"501161","messageId":"Zr_G59cz9HwtYUk3@tapette.crustytoothpaste.net","threadId":"61917","inReplyTo":"Zr86bF3y_YMZx0CQ@tanuki","subject":"Re: [PATCH v2 0/5] Introduce cgit-rs, a Rust wrapper around libgit.a","fromName":"brian m. carlson","fromEmail":"sandals@crustytoothpaste.net","sentAt":"2024-08-16T21:38:47Z","receivedAt":"2024-08-16T21:38:55Z","isPatch":true,"sender":{"key":"sandals@crustytoothpaste.net","avatar":"https://avatars.githubusercontent.com/u/497054?v=4"},"body":"On 2024-08-16 at 11:39:24, Patrick Steinhardt wrote:\n> That to me raises an important question: who is the one fixing breakage?\n> Hypothetically speaking, if I continue with my refactoring spree to drop\n> `the_repository`, do I also have to fix the Rust bindings that I break\n> as a consequence?\n\nIf we're testing it in CI, then you are responsible for not breaking it,\neven if you don't use it, just as I am responsible for not breaking\nWindows, even though I don't use that platform.  That's typically been\nthe policy here and elsewhere.\n\nThat being said, there are two things I'd like to point out.  First,\nyour work to drop the implicit `the_repository` is very useful, because\nRust code implicitly requires thread safety as part of the language, and\nyou will be making the dependency on the object explicit so Rust can\nguard against concurrent access.  I expect that you will end up breaking\nvery few endpoints with those series because we can't really expose\nendpoints with implicit global thread-unsafe state (such as\n`the_repository`) in Rust anyway except as `unsafe`.\n\nSecond, I and others will probably be happy to help if you have\nquestions or need assistance fixing bindings.  I've done a decent amount\nof work in Rust, including mostly porting a service at $DAYJOB from C to\nRust (the one that serves Git requests, no less), so I'm familiar with a\nlot of those kinds of problems, as I'm sure others are on the list as\nwell.\n-- \nbrian m. carlson (they/them or he/him)\nToronto, Ontario, CA\n"},{"id":"501173","messageId":"CAPig+cTYd_AbzKeXUnbY1cW75xzHC7n4u-5pD0Q6ss8TdX7NEw@mail.gmail.com","threadId":"61917","inReplyTo":"Zr_G59cz9HwtYUk3@tapette.crustytoothpaste.net","subject":"Re: [PATCH v2 0/5] Introduce cgit-rs, a Rust wrapper around libgit.a","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2024-08-17T00:15:07Z","receivedAt":"2024-08-17T00:15:19Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Fri, Aug 16, 2024 at 5:38 PM brian m. carlson\n<sandals@crustytoothpaste.net> wrote:\n> On 2024-08-16 at 11:39:24, Patrick Steinhardt wrote:\n> > That to me raises an important question: who is the one fixing breakage?\n> > Hypothetically speaking, if I continue with my refactoring spree to drop\n> > `the_repository`, do I also have to fix the Rust bindings that I break\n> > as a consequence?\n\nI share Patrick's concern, and it's one of the reasons I posed the\nquestion in the first place about why these Rust bindings are being\nproposed for Git's \"contrib/\" rather than as a standalone project.\n\n> If we're testing it in CI, then you are responsible for not breaking it,\n> even if you don't use it, just as I am responsible for not breaking\n> Windows, even though I don't use that platform.  That's typically been\n> the policy here and elsewhere.\n\nThis is an apples-and-oranges comparison, isn't it? Although the\nmajority of Git developers may be Unix folk, Git itself has a\nreasonably large Windows user population, so breaking it on Windows\ncarries some potentially real consequences (i.e. hurting the immediate\nuser community). On the other hand, the Rust bindings under discussion\nare (1) as yet effectively hypothetical, (2) will almost certainly be\nrelied upon by a tiny number of other projects, and (3) impact only\ndevelopers, not users, of Git-related tooling.\n\nAs such, it seems far too early to be placing the onus on *all* Git\ndevelopers to have to worry about the Rust bindings. If, at some point\nin the future, the Rust bindings gain significance or become\nindispensable to the Git project itself, then the onus might well be\nwarranted, but at this early stage, it seems unreasonable. A more\nfriendly approach to the overall Git developer community would be for\nthose interested in the Rust bindings to maintain them. (The case with\nthe CMake project files is similar. In the end, the people interested\nin utilizing them took on the responsibility of maintaining them.)\n"},{"id":"501232","messageId":"xmqqv7zyhaqz.fsf@gitster.g","threadId":"61917","inReplyTo":"CAPig+cTYd_AbzKeXUnbY1cW75xzHC7n4u-5pD0Q6ss8TdX7NEw@mail.gmail.com","subject":"Re: [PATCH v2 0/5] Introduce cgit-rs, a Rust wrapper around libgit.a","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2024-08-18T01:37:24Z","receivedAt":"2024-08-18T01:37:27Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Eric Sunshine <sunshine@sunshineco.com> writes:\n\n> (The case with the CMake project files is similar. In the end, the\n> people interested in utilizing them took on the responsibility of\n> maintaining them.)\n\nThat is a good analogy.  We would all be slowed down if you have to\nadjust CMake stuff (or autoconf stuff for that matter) when the\nbuild procedure changes you made only to Makefile (because you only\nhave a ready access and expertise on the \"make\" proper), but in\npractice, niche things that nobody cares about (and remember, if no\nstakeholder is there to volunteer, you cannot force others to care\nabout your niche) will bitrot enough to cause somebody to care and\nget fixed.  Another possibility is that it proves that nobody cares,\nand we just drop that part.  Either outcome is fine in the end.\n\nI think the Rust thing will proceed the same way.  At least, I am\nsensing that this time there are already motivated to lend help from\noutside the group of folks that are proposing this change.\n\n\n\n"},{"id":"501455","messageId":"20240821184605.341205-1-calvinwan@google.com","threadId":"61917","inReplyTo":"e3f7d292-ee51-4784-8c85-14b5223c380f@gmail.com","subject":"Re: [PATCH v2 5/5] cgit: add higher-level cgit crate","fromName":"Calvin Wan","fromEmail":"calvinwan@google.com","sentAt":"2024-08-21T18:46:05Z","receivedAt":"2024-08-21T18:46:15Z","isPatch":true,"sender":{"key":"calvinwan@google.com","avatar":"https://avatars.githubusercontent.com/u/92547554?v=4"},"body":"Phillip Wood <phillip.wood123@gmail.com> writes:\n> Hi Josh\n> \n> On 09/08/2024 23:41, Josh Steadmon wrote:\n> > From: Calvin Wan <calvinwan@google.com>\n> > \n> > Wrap `struct config_set` and a few of its associated functions in\n> > cgit-sys. Also introduce a higher-level \"cgit\" crate which provides a\n> > more Rust-friendly interface to config_set structs.\n> \n> Having an ergonamic interface is a really good idea. As far as the \n> naming goes I think the suggestion of \"libgit-rs\" is a good one.\n\nAgreed -- we plan on renaming it to \"libgit-rs\" in the next reroll.\n\n> \n> > diff --git a/contrib/cgit-rs/cgit-sys/public_symbol_export.h b/contrib/cgit-rs/cgit-sys/public_symbol_export.h\n> > index 64332f30de..882c7932e8 100644\n> > --- a/contrib/cgit-rs/cgit-sys/public_symbol_export.h\n> > +++ b/contrib/cgit-rs/cgit-sys/public_symbol_export.h\n> > @@ -9,6 +9,18 @@ void libgit_init_git(const char **argv);\n> >   \n> >   int libgit_parse_maybe_bool(const char *val);\n> \n> I'm suprised the compiler does not complain that 'struct config_set' is \n> not declared in this header - I was expecting to see\n> \n> \tstruct config_set;\n\nI'm surprised as well actually. Removing the forward declaration of\n\"struct config_set\" in repository.h doesn't result in any complaints\nfrom the compiler either. Will add it in the reroll, but am curious if\nanyone has any ideas why the compiler isn't complaining.\n\n> before the function declarations. As I said in my comments on the last \n> patch I think we'd be better to namespace our types as well as our \n> functions in this library layer so this can be resued by other language \n> bindings.\n\nAre you suggesting something like \"#define libgit_config_set\nconfig_set\"? I wouldn't be comfortable renaming config_set in git.git\njust yet until config/config_set can be a standalone library by itself.\n\n> \n>  > [...]\n> > +    pub fn get_str(&mut self, key: &str) -> Option<CString> {\n> \n> If we're adding an ergonomic api then having return CString isn't ideal. \n> I think the equivalent function in libgit2-rs has variants that return a \n> String which is convinent if the caller is expecting utf8 values or \n> Vec<u8> for non-utf8 values.\n\nHaving both get_cstr() and get_str() makes sense to me.\n"},{"id":"501460","messageId":"CAO_smViznLAxxGH+rfouUquq8tNJn16-CSoLbbGosy3fuYzTgQ@mail.gmail.com","threadId":"61917","inReplyTo":"20240821184605.341205-1-calvinwan@google.com","subject":"Re: [PATCH v2 5/5] cgit: add higher-level cgit crate","fromName":"Kyle Lippincott","fromEmail":"spectral@google.com","sentAt":"2024-08-21T19:23:14Z","receivedAt":"2024-08-21T19:23:32Z","isPatch":true,"sender":{"key":"spectral@google.com","avatar":"https://avatars.githubusercontent.com/u/6371650?v=4"},"body":"On Wed, Aug 21, 2024 at 11:46 AM Calvin Wan <calvinwan@google.com> wrote:\n>\n> Phillip Wood <phillip.wood123@gmail.com> writes:\n> > Hi Josh\n> >\n> > On 09/08/2024 23:41, Josh Steadmon wrote:\n> > > From: Calvin Wan <calvinwan@google.com>\n> > >\n> > > Wrap `struct config_set` and a few of its associated functions in\n> > > cgit-sys. Also introduce a higher-level \"cgit\" crate which provides a\n> > > more Rust-friendly interface to config_set structs.\n> >\n> > Having an ergonamic interface is a really good idea. As far as the\n> > naming goes I think the suggestion of \"libgit-rs\" is a good one.\n>\n> Agreed -- we plan on renaming it to \"libgit-rs\" in the next reroll.\n>\n> >\n> > > diff --git a/contrib/cgit-rs/cgit-sys/public_symbol_export.h b/contrib/cgit-rs/cgit-sys/public_symbol_export.h\n> > > index 64332f30de..882c7932e8 100644\n> > > --- a/contrib/cgit-rs/cgit-sys/public_symbol_export.h\n> > > +++ b/contrib/cgit-rs/cgit-sys/public_symbol_export.h\n> > > @@ -9,6 +9,18 @@ void libgit_init_git(const char **argv);\n> > >\n> > >   int libgit_parse_maybe_bool(const char *val);\n> >\n> > I'm suprised the compiler does not complain that 'struct config_set' is\n> > not declared in this header - I was expecting to see\n> >\n> >       struct config_set;\n>\n> I'm surprised as well actually. Removing the forward declaration of\n> \"struct config_set\" in repository.h doesn't result in any complaints\n> from the compiler either. Will add it in the reroll, but am curious if\n> anyone has any ideas why the compiler isn't complaining.\n\nC doesn't require structs be forward declared separately. You can\nchange the name to be anything you want, and as long as the forward\ndeclaration of the function and the function definition agree, you're\nfine. If they don't agree, well, let's hope you don't encounter that\n(it's the same problem as if you have a forward declaration that's\n*not* visible from the definition that disagrees with the definition:\n`void some_func();` vs. `int some_func(int arg) { ... }` -- if the\nforward declaration wasn't made in the same translation unit that\ndefines `some_func`, nothing detects this misuse in C).\n\nFor this reason, you should only ever use forward declarations that\nare provided by \"the code\" that's being forward declared. i.e. if\nyou're trying to use a function from foo.c, please forward declare it\nin foo.h, and only there. This way, assuming foo.c includes foo.h,\nyou'll detect mismatches.\n\n[apologies if people got multiple copies of this, I sent with HTML\nmode enabled the first time]\n\n>\n> > before the function declarations. As I said in my comments on the last\n> > patch I think we'd be better to namespace our types as well as our\n> > functions in this library layer so this can be resued by other language\n> > bindings.\n>\n> Are you suggesting something like \"#define libgit_config_set\n> config_set\"? I wouldn't be comfortable renaming config_set in git.git\n> just yet until config/config_set can be a standalone library by itself.\n>\n> >\n> >  > [...]\n> > > +    pub fn get_str(&mut self, key: &str) -> Option<CString> {\n> >\n> > If we're adding an ergonomic api then having return CString isn't ideal.\n> > I think the equivalent function in libgit2-rs has variants that return a\n> > String which is convinent if the caller is expecting utf8 values or\n> > Vec<u8> for non-utf8 values.\n>\n> Having both get_cstr() and get_str() makes sense to me.\n"},{"id":"501504","messageId":"9c6d8fea-997e-49d5-9195-e1721a750a36@gmail.com","threadId":"61917","inReplyTo":"20240821184605.341205-1-calvinwan@google.com","subject":"Re: [PATCH v2 5/5] cgit: add higher-level cgit crate","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2024-08-22T09:12:20Z","receivedAt":"2024-08-22T09:12:24Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Calvin\n\nOn 21/08/2024 19:46, Calvin Wan wrote:\n> Phillip Wood <phillip.wood123@gmail.com> writes:\n >\n>> before the function declarations. As I said in my comments on the last\n>> patch I think we'd be better to namespace our types as well as our\n>> functions in this library layer so this can be resued by other language\n>> bindings.\n> \n> Are you suggesting something like \"#define libgit_config_set\n> config_set\"? I wouldn't be comfortable renaming config_set in git.git\n> just yet until config/config_set can be a standalone library by itself.\n\nI was suggesting[1] adding\n\n     struct libgit_configset {\n\t    struct config_set set;\n     };\n\nto public_symbol_export.c and rewriting the wrappers in that file to use \nthis struct e.g.\n\n     int libgit_configset_get_int(struct libgit_configset *cs,\n\t\t\t\t const char *key, int *dest)\n     {\n\t    return git_configset_get_int(&cs.set, key, dest);\n     }\n\nIn public_symbol_export.h we'd then have\n\n     struct libgit_configset;\n\n     int libgit_configset_get_int(struct libgit_configset *,\n\t\t\t\t const char *, int *);\n\nIf we want the symbol exports to be useful outside of the rust bindings \nI think we need to namespace our types as well as our functions.\n\n[1] \nhttps://lore.kernel.org/git/5720d5b9-a850-4024-a1fd-54acc6b15a74@gmail.com\n\n>>> +    pub fn get_str(&mut self, key: &str) -> Option<CString> {\n>>\n>> If we're adding an ergonomic api then having return CString isn't ideal.\n>> I think the equivalent function in libgit2-rs has variants that return a\n>> String which is convinent if the caller is expecting utf8 values or\n>> Vec<u8> for non-utf8 values.\n> \n> Having both get_cstr() and get_str() makes sense to me.\n\nJust to be clear get_cstr() would return Vec<u8>? I'm far from a rust \nexpert but my understanding was that crates that wrap C libraries \ngenerally avoid using CString in their API.\n\nBest Wishes\n\nPhillip\n"},{"id":"501535","messageId":"27a37848-cdf8-46bb-9764-d8f952504c12@gmail.com","threadId":"61917","inReplyTo":"CAO_smViznLAxxGH+rfouUquq8tNJn16-CSoLbbGosy3fuYzTgQ@mail.gmail.com","subject":"Re: [PATCH v2 5/5] cgit: add higher-level cgit crate","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2024-08-22T13:24:08Z","receivedAt":"2024-08-22T13:24:12Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"On 21/08/2024 20:23, Kyle Lippincott wrote:\n> On Wed, Aug 21, 2024 at 11:46 AM Calvin Wan <calvinwan@google.com> wrote:\n>>\n>> Phillip Wood <phillip.wood123@gmail.com> writes:\n>>> \n>>> I'm suprised the compiler does not complain that 'struct config_set' is\n>>> not declared in this header - I was expecting to see\n>>>\n>>>        struct config_set;\n>>\n>> I'm surprised as well actually. Removing the forward declaration of\n>> \"struct config_set\" in repository.h doesn't result in any complaints\n>> from the compiler either. Will add it in the reroll, but am curious if\n>> anyone has any ideas why the compiler isn't complaining.\n> \n> C doesn't require structs be forward declared separately. You can\n> change the name to be anything you want, and as long as the forward\n> declaration of the function and the function definition agree, you're\n> fine.\n\nOh, TIL. The declaration\n\n     struct foo *f(struct bar*);\n\ndeclares \"struct foo\" and \"struct bar\" as well as function \"f\". The \ndeclaration of \"struct bar\" is scoped to the parameter list and so is \nnot visible to the rest of the file. This is why we forward declare \nstructs that are used in parameter lists and why the compiler complains \nif we don't. The declaration of \"struct foo\" does have file scope though \nwhich explains why the compiler does not complain about \npublic_symbol_export.h because \"struct config_set\" is declared as a \nreturn value before it is used in any parameter lists.\n\nThanks\n\nPhillip\n\n> If they don't agree, well, let's hope you don't encounter that\n> (it's the same problem as if you have a forward declaration that's\n> *not* visible from the definition that disagrees with the definition:\n> `void some_func();` vs. `int some_func(int arg) { ... }` -- if the\n> forward declaration wasn't made in the same translation unit that\n> defines `some_func`, nothing detects this misuse in C).\n> \n> For this reason, you should only ever use forward declarations that\n> are provided by \"the code\" that's being forward declared. i.e. if\n> you're trying to use a function from foo.c, please forward declare it\n> in foo.h, and only there. This way, assuming foo.c includes foo.h,\n> you'll detect mismatches.\n> \n> [apologies if people got multiple copies of this, I sent with HTML\n> mode enabled the first time]\n> \n>>\n>>> before the function declarations. As I said in my comments on the last\n>>> patch I think we'd be better to namespace our types as well as our\n>>> functions in this library layer so this can be resued by other language\n>>> bindings.\n>>\n>> Are you suggesting something like \"#define libgit_config_set\n>> config_set\"? I wouldn't be comfortable renaming config_set in git.git\n>> just yet until config/config_set can be a standalone library by itself.\n>>\n>>>\n>>>   > [...]\n>>>> +    pub fn get_str(&mut self, key: &str) -> Option<CString> {\n>>>\n>>> If we're adding an ergonomic api then having return CString isn't ideal.\n>>> I think the equivalent function in libgit2-rs has variants that return a\n>>> String which is convinent if the caller is expecting utf8 values or\n>>> Vec<u8> for non-utf8 values.\n>>\n>> Having both get_cstr() and get_str() makes sense to me.\n> \n\n"},{"id":"502156","messageId":"20240904173053.1220621-1-calvinwan@google.com","threadId":"61917","inReplyTo":"ZrPpxE7OZtqsbD81@tapette.crustytoothpaste.net","subject":"Re: [RFC PATCH 6/6] contrib/cgit-rs: add a subset of configset wrappers","fromName":"Calvin Wan","fromEmail":"calvinwan@google.com","sentAt":"2024-09-04T17:30:53Z","receivedAt":"2024-09-04T17:30:57Z","isPatch":true,"sender":{"key":"calvinwan@google.com","avatar":"https://avatars.githubusercontent.com/u/92547554?v=4"},"body":"\"brian m. carlson\" <sandals@crustytoothpaste.net> writes:\n> On 2024-08-07 at 18:21:31, Josh Steadmon wrote:\n> > diff --git a/contrib/cgit-rs/Cargo.toml b/contrib/cgit-rs/Cargo.toml\n> > index 7b55e6f3e1..5768fce9e5 100644\n> > --- a/contrib/cgit-rs/Cargo.toml\n> > +++ b/contrib/cgit-rs/Cargo.toml\n> > @@ -14,3 +14,4 @@ path = \"src/lib.rs\"\n> >  \n> >  [dependencies]\n> >  libc = \"0.2.155\"\n> > +home = \"0.5.9\"\n> \n> Okay, here's where we get to my previous mention of supported platforms.\n> This depends on Rust 1.70, and Debian stable has only 1.63.  Trying\n> `cargo build --release` on that version returns this:\n> \n>   Downloaded home v0.5.9\n>   Downloaded libc v0.2.155\n>   Downloaded 2 crates (752.3 KB) in 0.17s\n> error: package `home v0.5.9` cannot be built because it requires rustc 1.70.0 or newer, while the currently active rustc version is 1.63.0\n> \n> My recommended approach here is to support the version in Debian stable,\n> plus the version in Debian oldstable for a year after the new stable\n> comes out, which is what I do.  That gives people a year to upgrade if\n> they want to use our code.  We _don't_ want to follow the\n> latest-stable-Rust approach because it isn't appropriate that software\n> has a six-week lifespan of support and that isn't going to work for\n> software like Git that people often compile locally on older versions.\n> \n> We also need to be conscious that while Rust upstream provides some\n> binaries for some platforms, many platforms rely on the distro packages\n> because Rust upstream doesn't ship binaries for their target.  Thus,\n> simply using rustup is not viable for many targets, which is another\n> reason that latest-stable-Rust won't fly.\n> \n> Debian stable is the version that most projects who have defined\n> lifespans track, so it's also what we should track.  According to my\n> recommended approach, that would be 1.63.\n\nAfter getting rid of the `home` crate, the only other issue we ran into\ndowngrading the version to 1.63 was with `std::ffi{c_char, c_int}`.\nThose were only made available in 1.64 and they are obviously quite\nnecessary for us to be able to call C functions. Do you know of any\nalternatives we can use? I also don't think reinventing the wheel with\nour own implementation makes sense in this case, and even if Debian were\nto upgrade stable to a higher version today, we would still need to\nsupport oldstable for another year.\n"},{"id":"502157","messageId":"ZtidjMThNwiI_QG8@tapette.crustytoothpaste.net","threadId":"61917","inReplyTo":"20240904173053.1220621-1-calvinwan@google.com","subject":"Re: [RFC PATCH 6/6] contrib/cgit-rs: add a subset of configset wrappers","fromName":"brian m. carlson","fromEmail":"sandals@crustytoothpaste.net","sentAt":"2024-09-04T17:49:00Z","receivedAt":"2024-09-04T17:49:03Z","isPatch":true,"sender":{"key":"sandals@crustytoothpaste.net","avatar":"https://avatars.githubusercontent.com/u/497054?v=4"},"body":"On 2024-09-04 at 17:30:53, Calvin Wan wrote:\n> After getting rid of the `home` crate, the only other issue we ran into\n> downgrading the version to 1.63 was with `std::ffi{c_char, c_int}`.\n> Those were only made available in 1.64 and they are obviously quite\n> necessary for us to be able to call C functions. Do you know of any\n> alternatives we can use? I also don't think reinventing the wheel with\n> our own implementation makes sense in this case, and even if Debian were\n> to upgrade stable to a higher version today, we would still need to\n> support oldstable for another year.\n\nI think we can do this with libc, which you're importing at the moment.\nYou can do something like this:\n\nsrc/types.rs:\n----\npub use libc::{c_char, c_int};\n----\n\nand then do `use crate::types::{c_char, c_int}` wherever you want them.\n\nThen, when we upgrade to 1.64 or newer, we can simply replace the\ncontents of `src/types.rs` with this:\n\n----\npub use std::ffi::{c_char, c_int};\n----\n\nand that's the only thing we'll need to change.\n\nIf we switch to using rustix instead, then it will look like this:\n\n----\npub use rustix::ffi::c_char;\n\npub type c_int = i32;\n----\n\nWhile the C standard requires that `int` only has 16 bits, Git doesn't\ntarget any platforms where `int` is anything but 32 bits and it won't\nwork with any other configuration without serious changes.\n-- \nbrian m. carlson (they/them or he/him)\nToronto, Ontario, CA\n"},{"id":"502163","messageId":"xmqq8qw79slu.fsf@gitster.g","threadId":"61917","inReplyTo":"20240904173053.1220621-1-calvinwan@google.com","subject":"Re: [RFC PATCH 6/6] contrib/cgit-rs: add a subset of configset wrappers","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2024-09-04T18:33:17Z","receivedAt":"2024-09-04T18:33:20Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Calvin Wan <calvinwan@google.com> writes:\n\n> \"brian m. carlson\" <sandals@crustytoothpaste.net> writes:\n>> ...\n>> Debian stable is the version that most projects who have defined\n>> lifespans track, so it's also what we should track.  According to my\n>> recommended approach, that would be 1.63.\n>\n> ... I also don't think reinventing the wheel with\n> our own implementation makes sense in this case,\n\nI do agree that we would want to avoid that.\n\n> and even if Debian were\n> to upgrade stable to a higher version today, we would still need to\n> support oldstable for another year.\n\nI doubt that part, though.  As long as the rust binding stays an\noptional code, as long as we are supported by the \"current\" system,\nwe would still have enough audience to matter.\n\nWhat's the primary objective of this effort, by the way?  \n\nIs it \"we need to access the guts of Git implementation from Rust\"?\nOr does it merely serve as an example application to have Rust\nbindings, a good goal to have to give us an incentive to clean up\nthe subsystem interactions in our code?  \n\nIf it is the former, we cannot reasonably achieve that goal until\nsome form of standardized foreign function interface becomes\navailable to wide audience.  If it is the latter, on the other hand,\nit does not have to be Rust---if the version of Rust that is\ndistirbuted to the mainstream users is not yet ready to be used in\nsuch a way, we could pick another goal (like, \"Can we clean-up the\ninterface cgit uses to call into us, so that the parts used by them\nlook more like a proper library?\").\n\nThanks.\n"},{"id":"502166","messageId":"ZtivGeDZ_MZDEDB_@tapette.crustytoothpaste.net","threadId":"61917","inReplyTo":"xmqq8qw79slu.fsf@gitster.g","subject":"Re: [RFC PATCH 6/6] contrib/cgit-rs: add a subset of configset wrappers","fromName":"brian m. carlson","fromEmail":"sandals@crustytoothpaste.net","sentAt":"2024-09-04T19:03:53Z","receivedAt":"2024-09-04T19:03:55Z","isPatch":true,"sender":{"key":"sandals@crustytoothpaste.net","avatar":"https://avatars.githubusercontent.com/u/497054?v=4"},"body":"On 2024-09-04 at 18:33:17, Junio C Hamano wrote:\n> Is it \"we need to access the guts of Git implementation from Rust\"?\n> Or does it merely serve as an example application to have Rust\n> bindings, a good goal to have to give us an incentive to clean up\n> the subsystem interactions in our code?\n> \n> If it is the former, we cannot reasonably achieve that goal until\n> some form of standardized foreign function interface becomes\n> available to wide audience.  If it is the latter, on the other hand,\n> it does not have to be Rust---if the version of Rust that is\n> distirbuted to the mainstream users is not yet ready to be used in\n> such a way, we could pick another goal (like, \"Can we clean-up the\n> interface cgit uses to call into us, so that the parts used by them\n> look more like a proper library?\").\n\nTraditionally, in Rust, you don't use the C-style types because that\nleads to portability problems.  Look at how using \"unsigned long\" as\n\"equivalent in size to void *\" has gotten our C code to have sharp edges\non Windows, where that isn't true. The approach one typically uses is to\nuse things like int32_t, which is i32 in Rust, and size_t, which is\nusize in Rust.  This leads to much more predictable behaviour on both\nsides of the FFI.\n\nThe C-style types have long been available in libc and other crates that\nare designed to work with C FFI, and as a practical matter you do need\nto use that crate somewhere in your stack (or reimplement it) to call\nfunctions in libc and the other core system libraries, so you're not\nreally lacking those types.\n\nThey're also available in the `std::os::raw` module as of Rust 1.1; it's\njust that as of Rust 1.64, they're in `std::ffi` and `core::ffi` as\nwell, mostly to help embedded systems (which don't have `std`, and thus,\ndon't have `std::os::raw`).  Using `std::os::raw` or `libc` should be\nfine for Git, since we're not targeting operating system kernels,\nbootloaders, or firmware such as UEFI (I hope).\n-- \nbrian m. carlson (they/them or he/him)\nToronto, Ontario, CA\n"},{"id":"502174","messageId":"xmqq7cbr86uk.fsf@gitster.g","threadId":"61917","inReplyTo":"ZtivGeDZ_MZDEDB_@tapette.crustytoothpaste.net","subject":"Re: [RFC PATCH 6/6] contrib/cgit-rs: add a subset of configset wrappers","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2024-09-04T21:08:35Z","receivedAt":"2024-09-04T21:08:38Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"brian m. carlson\" <sandals@crustytoothpaste.net> writes:\n\n> The C-style types have long been available in libc and other crates that\n> are designed to work with C FFI, and as a practical matter you do need\n> to use that crate somewhere in your stack (or reimplement it) to call\n> functions in libc and the other core system libraries, so you're not\n> really lacking those types.\n\nGlad to know that the feature we need was already there.  So, it is\njust the interface was in flux when they worked on the version\nDebian happens to ship right now, I guess.\n\n> They're also available in the `std::os::raw` module as of Rust 1.1; it's\n> just that as of Rust 1.64, they're in `std::ffi` and `core::ffi` as\n> well, mostly to help embedded systems (which don't have `std`, and thus,\n> don't have `std::os::raw`).  Using `std::os::raw` or `libc` should be\n> fine for Git, since we're not targeting operating system kernels,\n> bootloaders, or firmware such as UEFI (I hope).\n\nYup, as long as we have a clean migration path (like the one you\nshowed Calvin in your other message in this thread), and use of a\nslightly older way will not immediately be deprecated in a newer\nversion of Rust, then we should be fine going forward.\n\nThanks.\n"},{"id":"502175","messageId":"vtj54bv437e4libwv2a3yycdrva5wsoboo6rs2fbys23sxhvhr@64xsd2uqdbua","threadId":"61917","inReplyTo":"xmqq8qw79slu.fsf@gitster.g","subject":"Re: [RFC PATCH 6/6] contrib/cgit-rs: add a subset of configset wrappers","fromName":"Josh Steadmon","fromEmail":"steadmon@google.com","sentAt":"2024-09-04T21:29:50Z","receivedAt":"2024-09-04T21:29:57Z","isPatch":true,"sender":{"key":"steadmon@google.com","avatar":"https://avatars.githubusercontent.com/u/2654920?v=4"},"body":"On 2024.09.04 11:33, Junio C Hamano wrote:\n> Calvin Wan <calvinwan@google.com> writes:\n> \n> > \"brian m. carlson\" <sandals@crustytoothpaste.net> writes:\n> >> ...\n> >> Debian stable is the version that most projects who have defined\n> >> lifespans track, so it's also what we should track.  According to my\n> >> recommended approach, that would be 1.63.\n> >\n> > ... I also don't think reinventing the wheel with\n> > our own implementation makes sense in this case,\n> \n> I do agree that we would want to avoid that.\n> \n> > and even if Debian were\n> > to upgrade stable to a higher version today, we would still need to\n> > support oldstable for another year.\n> \n> I doubt that part, though.  As long as the rust binding stays an\n> optional code, as long as we are supported by the \"current\" system,\n> we would still have enough audience to matter.\n> \n> What's the primary objective of this effort, by the way?  \n> \n> Is it \"we need to access the guts of Git implementation from Rust\"?\n> Or does it merely serve as an example application to have Rust\n> bindings, a good goal to have to give us an incentive to clean up\n> the subsystem interactions in our code?  \n\nFor us at $DAYJOB, it's \"we need to access the guts of Git from Rust\".\n\n> If it is the former, we cannot reasonably achieve that goal until\n> some form of standardized foreign function interface becomes\n> available to wide audience.  If it is the latter, on the other hand,\n> it does not have to be Rust---if the version of Rust that is\n> distirbuted to the mainstream users is not yet ready to be used in\n> such a way, we could pick another goal (like, \"Can we clean-up the\n> interface cgit uses to call into us, so that the parts used by them\n> look more like a proper library?\").\n> \n> Thanks.\n\nI think brian already addressed your point about having a standard FFI,\nbut if there's anything that still needs clarification please let me\nknow.\n"},{"id":"502343","messageId":"CAFySSZB56V-h0zxcv+TtP0jmTYAV1gixanCf0toYqak7Mz-yGQ@mail.gmail.com","threadId":"61917","inReplyTo":"ZtidjMThNwiI_QG8@tapette.crustytoothpaste.net","subject":"Re: [RFC PATCH 6/6] contrib/cgit-rs: add a subset of configset wrappers","fromName":"Calvin Wan","fromEmail":"calvinwan@google.com","sentAt":"2024-09-06T19:37:18Z","receivedAt":"2024-09-06T19:37:32Z","isPatch":true,"sender":{"key":"calvinwan@google.com","avatar":"https://avatars.githubusercontent.com/u/92547554?v=4"},"body":"On Wed, Sep 4, 2024 at 10:49 AM brian m. carlson\n<sandals@crustytoothpaste.net> wrote:\n>\n> On 2024-09-04 at 17:30:53, Calvin Wan wrote:\n> > After getting rid of the `home` crate, the only other issue we ran into\n> > downgrading the version to 1.63 was with `std::ffi{c_char, c_int}`.\n> > Those were only made available in 1.64 and they are obviously quite\n> > necessary for us to be able to call C functions. Do you know of any\n> > alternatives we can use? I also don't think reinventing the wheel with\n> > our own implementation makes sense in this case, and even if Debian were\n> > to upgrade stable to a higher version today, we would still need to\n> > support oldstable for another year.\n>\n> I think we can do this with libc, which you're importing at the moment.\n> You can do something like this:\n>\n> src/types.rs:\n> ----\n> pub use libc::{c_char, c_int};\n> ----\n>\n> and then do `use crate::types::{c_char, c_int}` wherever you want them.\n\nWhile this gets rid of `std::ffi{c_char, c_int}` dependency,\n`std::ffi{CStr, CString}` are also 1.64 unfortunately, and I don't see\nthe equivalent of it in libc. I'll reroll v3 without this change for\nnow. Hopefully Debian stable just upgrades to 1.64 or higher and we\ndon't have to worry about these ffi conversions :)\n"},{"id":"502365","messageId":"20240906221853.257984-1-calvinwan@google.com","threadId":"61917","inReplyTo":"cover.1723242556.git.steadmon@google.com","subject":"[PATCH v3 0/6] Introduce libgit-rs, a Rust wrapper around libgit.a","fromName":"Calvin Wan","fromEmail":"calvinwan@google.com","sentAt":"2024-09-06T22:18:52Z","receivedAt":"2024-09-06T22:19:11Z","isPatch":true,"sender":{"key":"calvinwan@google.com","avatar":"https://avatars.githubusercontent.com/u/92547554?v=4"},"body":"This series provides two small Rust wrapper libraries around parts of\nGit: \"libgit-sys\", which exposes a few functions from libgit.a, and\n\"libgit\", which provides a more Rust-friendly interface to some of those\nfunctions. In addition to included unit tests, at $DAYJOB we have tested\nbuilding JJ[1] with our library and used it to replace some of the\nlibgit2-rs uses.\n\n[1] https://github.com/martinvonz/jj\n\nPlease find V1 cover letter here:\nhttps://lore.kernel.org/git/cover.1723054623.git.steadmon@google.com/\n\nChanges in V3:\n* Renamed cgit-rs to libgit-rs and cgit-sys to libgit-sys\n\n* Makefile cleanup, particularly adding config.mak options that\n  developers can set to run Rust builds and tests by default (Patch 6)\n\n* Provide testdata configs for unit tests\n\n* ConfigSet API now uses &Path instead of &str -- more ergonomic for\n  Rust users to pass in and errors out if the path string isn't UTF-8\n\n* Fixed unresolved dependency on libz in Cargo.toml\n\nKnown NEEDSWORK:\n\n* Supports older Rust versions by removing any dependencies introduced\n  after 1.63.0 (Debian stable)\n\n* Investigate alternative methods of managing symbol visibility.\n\n* Figure out symbol versioning\n\n* Automate the process of exporting additional functions in libgit-sys\n  (possibly with a wrapper script around bindgen)\n\nCalvin Wan (3):\n  libgit-sys: add repo initialization and config access\n  libgit: add higher-level libgit crate\n  Makefile: add option to build and test libgit-rs and libgit-rs-sys\n\nJosh Steadmon (3):\n  common-main: split init and exit code into new files\n  libgit-sys: introduce Rust wrapper for libgit.a\n  config: add git_configset_alloc() and git_configset_clear_and_free()\n\n .gitignore                                    |   2 +\n Makefile                                      |  31 +++++\n common-exit.c                                 |  26 ++++\n common-init.c                                 |  63 +++++++++\n common-init.h                                 |   6 +\n common-main.c                                 |  83 +-----------\n config.c                                      |  11 ++\n config.h                                      |  10 ++\n contrib/libgit-rs/Cargo.lock                  |  62 +++++++++\n contrib/libgit-rs/Cargo.toml                  |  10 ++\n contrib/libgit-rs/libgit-sys/Cargo.lock       |  55 ++++++++\n contrib/libgit-rs/libgit-sys/Cargo.toml       |  12 ++\n contrib/libgit-rs/libgit-sys/README.md        |  15 +++\n contrib/libgit-rs/libgit-sys/build.rs         |  31 +++++\n .../libgit-sys/public_symbol_export.c         |  76 +++++++++++\n .../libgit-sys/public_symbol_export.h         |  28 ++++\n contrib/libgit-rs/libgit-sys/src/lib.rs       | 124 ++++++++++++++++++\n contrib/libgit-rs/src/lib.rs                  |  85 ++++++++++++\n contrib/libgit-rs/testdata/config1            |   2 +\n contrib/libgit-rs/testdata/config2            |   2 +\n contrib/libgit-rs/testdata/config3            |   2 +\n t/Makefile                                    |  16 +++\n 22 files changed, 671 insertions(+), 81 deletions(-)\n create mode 100644 common-exit.c\n create mode 100644 common-init.c\n create mode 100644 common-init.h\n create mode 100644 contrib/libgit-rs/Cargo.lock\n create mode 100644 contrib/libgit-rs/Cargo.toml\n create mode 100644 contrib/libgit-rs/libgit-sys/Cargo.lock\n create mode 100644 contrib/libgit-rs/libgit-sys/Cargo.toml\n create mode 100644 contrib/libgit-rs/libgit-sys/README.md\n create mode 100644 contrib/libgit-rs/libgit-sys/build.rs\n create mode 100644 contrib/libgit-rs/libgit-sys/public_symbol_export.c\n create mode 100644 contrib/libgit-rs/libgit-sys/public_symbol_export.h\n create mode 100644 contrib/libgit-rs/libgit-sys/src/lib.rs\n create mode 100644 contrib/libgit-rs/src/lib.rs\n create mode 100644 contrib/libgit-rs/testdata/config1\n create mode 100644 contrib/libgit-rs/testdata/config2\n create mode 100644 contrib/libgit-rs/testdata/config3\n\n-- \n2.46.0.469.g59c65b2a67-goog\n\n"},{"id":"502366","messageId":"20240906222116.270196-1-calvinwan@google.com","threadId":"61917","inReplyTo":"20240906221853.257984-1-calvinwan@google.com","subject":"[PATCH v3 1/6] common-main: split init and exit code into new files","fromName":"Calvin Wan","fromEmail":"calvinwan@google.com","sentAt":"2024-09-06T22:21:11Z","receivedAt":"2024-09-06T22:21:23Z","isPatch":true,"sender":{"key":"calvinwan@google.com","avatar":"https://avatars.githubusercontent.com/u/92547554?v=4"},"body":"From: Josh Steadmon <steadmon@google.com>\n\nCurrently, object files in libgit.a reference common_exit(), which is\ncontained in common-main.o. However, common-main.o also includes main(),\nwhich references cmd_main() in git.o, which in turn depends on all the\nbuiltin/*.o objects.\n\nWe would like to allow external users to link libgit.a without needing\nto include so many extra objects. Enable this by splitting common_exit()\nand check_bug_if_BUG() into a new file common-exit.c, and add\ncommon-exit.o to LIB_OBJS so that these are included in libgit.a.\n\nThis split has previously been proposed ([1], [2]) to support fuzz tests\nand unit tests by avoiding conflicting definitions for main(). However,\nboth of those issues were resolved by other methods of avoiding symbol\nconflicts. Now we are trying to make libgit.a more self-contained, so\nhopefully we can revisit this approach.\n\nAdditionally, move the initialization code out of main() into a new\ninit_git() function in its own file. Include this in libgit.a as well,\nso that external users can share our setup code without calling our\nmain().\n\n[1] https://lore.kernel.org/git/Yp+wjCPhqieTku3X@google.com/\n[2] https://lore.kernel.org/git/20230517-unit-tests-v2-v2-1-21b5b60f4b32@google.com/\n\nChange-Id: I3d9b23ac28a50a4b79760987a3f37b0c6f4cbf9c\nSigned-off-by: Calvin Wan <calvinwan@google.com>\n---\n Makefile      |  2 ++\n common-exit.c | 26 ++++++++++++++++\n common-init.c | 63 ++++++++++++++++++++++++++++++++++++++\n common-init.h |  6 ++++\n common-main.c | 83 ++-------------------------------------------------\n 5 files changed, 99 insertions(+), 81 deletions(-)\n create mode 100644 common-exit.c\n create mode 100644 common-init.c\n create mode 100644 common-init.h\n\ndiff --git a/Makefile b/Makefile\nindex 3eab701b10..7caeb3c872 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -979,6 +979,8 @@ LIB_OBJS += combine-diff.o\n LIB_OBJS += commit-graph.o\n LIB_OBJS += commit-reach.o\n LIB_OBJS += commit.o\n+LIB_OBJS += common-exit.o\n+LIB_OBJS += common-init.o\n LIB_OBJS += compat/nonblock.o\n LIB_OBJS += compat/obstack.o\n LIB_OBJS += compat/terminal.o\ndiff --git a/common-exit.c b/common-exit.c\nnew file mode 100644\nindex 0000000000..1aaa538be3\n--- /dev/null\n+++ b/common-exit.c\n@@ -0,0 +1,26 @@\n+#include \"git-compat-util.h\"\n+#include \"trace2.h\"\n+\n+static void check_bug_if_BUG(void)\n+{\n+\tif (!bug_called_must_BUG)\n+\t\treturn;\n+\tBUG(\"on exit(): had bug() call(s) in this process without explicit BUG_if_bug()\");\n+}\n+\n+/* We wrap exit() to call common_exit() in git-compat-util.h */\n+int common_exit(const char *file, int line, int code)\n+{\n+\t/*\n+\t * For non-POSIX systems: Take the lowest 8 bits of the \"code\"\n+\t * to e.g. turn -1 into 255. On a POSIX system this is\n+\t * redundant, see exit(3) and wait(2), but as it doesn't harm\n+\t * anything there we don't need to guard this with an \"ifdef\".\n+\t */\n+\tcode &= 0xff;\n+\n+\tcheck_bug_if_BUG();\n+\ttrace2_cmd_exit_fl(file, line, code);\n+\n+\treturn code;\n+}\ndiff --git a/common-init.c b/common-init.c\nnew file mode 100644\nindex 0000000000..5cc73f058c\n--- /dev/null\n+++ b/common-init.c\n@@ -0,0 +1,63 @@\n+#define USE_THE_REPOSITORY_VARIABLE\n+\n+#include \"git-compat-util.h\"\n+#include \"common-init.h\"\n+#include \"exec-cmd.h\"\n+#include \"gettext.h\"\n+#include \"attr.h\"\n+#include \"repository.h\"\n+#include \"setup.h\"\n+#include \"strbuf.h\"\n+#include \"trace2.h\"\n+\n+/*\n+ * Many parts of Git have subprograms communicate via pipe, expect the\n+ * upstream of a pipe to die with SIGPIPE when the downstream of a\n+ * pipe does not need to read all that is written.  Some third-party\n+ * programs that ignore or block SIGPIPE for their own reason forget\n+ * to restore SIGPIPE handling to the default before spawning Git and\n+ * break this carefully orchestrated machinery.\n+ *\n+ * Restore the way SIGPIPE is handled to default, which is what we\n+ * expect.\n+ */\n+static void restore_sigpipe_to_default(void)\n+{\n+\tsigset_t unblock;\n+\n+\tsigemptyset(&unblock);\n+\tsigaddset(&unblock, SIGPIPE);\n+\tsigprocmask(SIG_UNBLOCK, &unblock, NULL);\n+\tsignal(SIGPIPE, SIG_DFL);\n+}\n+\n+void init_git(const char **argv)\n+{\n+\tstruct strbuf tmp = STRBUF_INIT;\n+\n+\ttrace2_initialize_clock();\n+\n+\t/*\n+\t * Always open file descriptors 0/1/2 to avoid clobbering files\n+\t * in die().  It also avoids messing up when the pipes are dup'ed\n+\t * onto stdin/stdout/stderr in the child processes we spawn.\n+\t */\n+\tsanitize_stdfds();\n+\trestore_sigpipe_to_default();\n+\n+\tgit_resolve_executable_dir(argv[0]);\n+\n+\tsetlocale(LC_CTYPE, \"\");\n+\tgit_setup_gettext();\n+\n+\tinitialize_repository(the_repository);\n+\n+\tattr_start();\n+\n+\ttrace2_initialize();\n+\ttrace2_cmd_start(argv);\n+\ttrace2_collect_process_info(TRACE2_PROCESS_INFO_STARTUP);\n+\n+\tif (!strbuf_getcwd(&tmp))\n+\t\ttmp_original_cwd = strbuf_detach(&tmp, NULL);\n+}\ndiff --git a/common-init.h b/common-init.h\nnew file mode 100644\nindex 0000000000..3e6db20cae\n--- /dev/null\n+++ b/common-init.h\n@@ -0,0 +1,6 @@\n+#ifndef COMMON_INIT_H\n+#define COMMON_INIT_H\n+\n+void init_git(const char **argv);\n+\n+#endif /* COMMON_INIT_H */\ndiff --git a/common-main.c b/common-main.c\nindex 8e68ac9e42..6b7ab077b0 100644\n--- a/common-main.c\n+++ b/common-main.c\n@@ -1,92 +1,13 @@\n-#define USE_THE_REPOSITORY_VARIABLE\n-\n #include \"git-compat-util.h\"\n-#include \"exec-cmd.h\"\n-#include \"gettext.h\"\n-#include \"attr.h\"\n-#include \"repository.h\"\n-#include \"setup.h\"\n-#include \"strbuf.h\"\n-#include \"trace2.h\"\n-\n-/*\n- * Many parts of Git have subprograms communicate via pipe, expect the\n- * upstream of a pipe to die with SIGPIPE when the downstream of a\n- * pipe does not need to read all that is written.  Some third-party\n- * programs that ignore or block SIGPIPE for their own reason forget\n- * to restore SIGPIPE handling to the default before spawning Git and\n- * break this carefully orchestrated machinery.\n- *\n- * Restore the way SIGPIPE is handled to default, which is what we\n- * expect.\n- */\n-static void restore_sigpipe_to_default(void)\n-{\n-\tsigset_t unblock;\n-\n-\tsigemptyset(&unblock);\n-\tsigaddset(&unblock, SIGPIPE);\n-\tsigprocmask(SIG_UNBLOCK, &unblock, NULL);\n-\tsignal(SIGPIPE, SIG_DFL);\n-}\n+#include \"common-init.h\"\n \n int main(int argc, const char **argv)\n {\n \tint result;\n-\tstruct strbuf tmp = STRBUF_INIT;\n-\n-\ttrace2_initialize_clock();\n-\n-\t/*\n-\t * Always open file descriptors 0/1/2 to avoid clobbering files\n-\t * in die().  It also avoids messing up when the pipes are dup'ed\n-\t * onto stdin/stdout/stderr in the child processes we spawn.\n-\t */\n-\tsanitize_stdfds();\n-\trestore_sigpipe_to_default();\n-\n-\tgit_resolve_executable_dir(argv[0]);\n-\n-\tsetlocale(LC_CTYPE, \"\");\n-\tgit_setup_gettext();\n-\n-\tinitialize_repository(the_repository);\n-\n-\tattr_start();\n-\n-\ttrace2_initialize();\n-\ttrace2_cmd_start(argv);\n-\ttrace2_collect_process_info(TRACE2_PROCESS_INFO_STARTUP);\n-\n-\tif (!strbuf_getcwd(&tmp))\n-\t\ttmp_original_cwd = strbuf_detach(&tmp, NULL);\n \n+\tinit_git(argv);\n \tresult = cmd_main(argc, argv);\n \n \t/* Not exit(3), but a wrapper calling our common_exit() */\n \texit(result);\n }\n-\n-static void check_bug_if_BUG(void)\n-{\n-\tif (!bug_called_must_BUG)\n-\t\treturn;\n-\tBUG(\"on exit(): had bug() call(s) in this process without explicit BUG_if_bug()\");\n-}\n-\n-/* We wrap exit() to call common_exit() in git-compat-util.h */\n-int common_exit(const char *file, int line, int code)\n-{\n-\t/*\n-\t * For non-POSIX systems: Take the lowest 8 bits of the \"code\"\n-\t * to e.g. turn -1 into 255. On a POSIX system this is\n-\t * redundant, see exit(3) and wait(2), but as it doesn't harm\n-\t * anything there we don't need to guard this with an \"ifdef\".\n-\t */\n-\tcode &= 0xff;\n-\n-\tcheck_bug_if_BUG();\n-\ttrace2_cmd_exit_fl(file, line, code);\n-\n-\treturn code;\n-}\n-- \n2.46.0.469.g59c65b2a67-goog\n\n"},{"id":"502367","messageId":"20240906222116.270196-2-calvinwan@google.com","threadId":"61917","inReplyTo":"20240906221853.257984-1-calvinwan@google.com","subject":"[PATCH v3 2/6] libgit-sys: introduce Rust wrapper for libgit.a","fromName":"Calvin Wan","fromEmail":"calvinwan@google.com","sentAt":"2024-09-06T22:21:12Z","receivedAt":"2024-09-06T22:21:25Z","isPatch":true,"sender":{"key":"calvinwan@google.com","avatar":"https://avatars.githubusercontent.com/u/92547554?v=4"},"body":"From: Josh Steadmon <steadmon@google.com>\n\nIntroduce libgit-sys, a Rust wrapper crate that allows Rust code to call\nfunctions in libgit.a. This initial patch defines build rules and an\ninterface that exposes user agent string getter functions as a proof of\nconcept. This library can be tested with `cargo test`. In later commits,\na higher-level library containing a more Rust-friendly interface will be\nadded at `contrib/libgit-rs`.\n\nSymbols in libgit can collide with symbols from other libraries such as\nlibgit2. We avoid this by first exposing library symbols in\npublic_symbol_export.[ch]. These symbols are prepended with \"libgit_\" to\navoid collisions and set to visible using a visibility pragma. In\nbuild.rs, Rust builds contrib/libgit-rs/libgit-sys/libgitpub.a, which also\ncontains libgit.a and other dependent libraries, with\n-fvisibility=hidden to hide all symbols within those libraries that\nhaven't been exposed with a visibility pragma.\n\nCo-authored-by: Kyle Lippincott <spectral@google.com>\nCo-authored-by: Calvin Wan <calvinwan@google.com>\nSigned-off-by: Calvin Wan <calvinwan@google.com>\nSigned-off-by: Kyle Lippincott <spectral@google.com>\nChange-Id: Ibb98a3907e69839231355c098b2488ef06d1ba45\n---\n .gitignore                                    |  1 +\n Makefile                                      | 13 +++++\n contrib/libgit-rs/libgit-sys/Cargo.lock       | 55 +++++++++++++++++++\n contrib/libgit-rs/libgit-sys/Cargo.toml       | 12 ++++\n contrib/libgit-rs/libgit-sys/README.md        | 15 +++++\n contrib/libgit-rs/libgit-sys/build.rs         | 31 +++++++++++\n .../libgit-sys/public_symbol_export.c         | 20 +++++++\n .../libgit-sys/public_symbol_export.h         |  8 +++\n contrib/libgit-rs/libgit-sys/src/lib.rs       | 42 ++++++++++++++\n 9 files changed, 197 insertions(+)\n create mode 100644 contrib/libgit-rs/libgit-sys/Cargo.lock\n create mode 100644 contrib/libgit-rs/libgit-sys/Cargo.toml\n create mode 100644 contrib/libgit-rs/libgit-sys/README.md\n create mode 100644 contrib/libgit-rs/libgit-sys/build.rs\n create mode 100644 contrib/libgit-rs/libgit-sys/public_symbol_export.c\n create mode 100644 contrib/libgit-rs/libgit-sys/public_symbol_export.h\n create mode 100644 contrib/libgit-rs/libgit-sys/src/lib.rs\n\ndiff --git a/.gitignore b/.gitignore\nindex 8caf3700c2..dfd72820fb 100644\n--- a/.gitignore\n+++ b/.gitignore\n@@ -248,3 +248,4 @@ Release/\n /git.VC.db\n *.dSYM\n /contrib/buildsystems/out\n+/contrib/libgit-rs/libgit-sys/target\ndiff --git a/Makefile b/Makefile\nindex 7caeb3c872..0090514e55 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -653,6 +653,8 @@ CURL_CONFIG = curl-config\n GCOV = gcov\n STRIP = strip\n SPATCH = spatch\n+LD = ld\n+OBJCOPY = objcopy\n \n export TCL_PATH TCLTK_PATH\n \n@@ -2713,6 +2715,7 @@ OBJECTS += $(XDIFF_OBJS)\n OBJECTS += $(FUZZ_OBJS)\n OBJECTS += $(REFTABLE_OBJS) $(REFTABLE_TEST_OBJS)\n OBJECTS += $(UNIT_TEST_OBJS)\n+OBJECTS += contrib/libgit-rs/libgit-sys/public_symbol_export.o\n \n ifndef NO_CURL\n \tOBJECTS += http.o http-walker.o remote-curl.o\n@@ -3720,6 +3723,7 @@ clean: profile-clean coverage-clean cocciclean\n \t$(RM) $(htmldocs).tar.gz $(manpages).tar.gz\n \t$(MAKE) -C Documentation/ clean\n \t$(RM) Documentation/GIT-EXCLUDED-PROGRAMS\n+\t$(RM) -r contrib/libgit-rs/libgit-sys/target\n ifndef NO_PERL\n \t$(RM) -r perl/build/\n endif\n@@ -3865,3 +3869,12 @@ $(UNIT_TEST_PROGS): $(UNIT_TEST_BIN)/%$X: $(UNIT_TEST_DIR)/%.o \\\n build-unit-tests: $(UNIT_TEST_PROGS)\n unit-tests: $(UNIT_TEST_PROGS) t/helper/test-tool$X\n \t$(MAKE) -C t/ unit-tests\n+\n+contrib/libgit-rs/libgit-sys/partial_symbol_export.o: contrib/libgit-rs/libgit-sys/public_symbol_export.o libgit.a reftable/libreftable.a xdiff/lib.a\n+\t$(LD) -r $^ -o $@\n+\n+contrib/libgit-rs/libgit-sys/hidden_symbol_export.o: contrib/libgit-rs/libgit-sys/partial_symbol_export.o\n+\t$(OBJCOPY) --localize-hidden $^ $@\n+\n+contrib/libgit-rs/libgit-sys/libgitpub.a: contrib/libgit-rs/libgit-sys/hidden_symbol_export.o\n+\t$(AR) $(ARFLAGS) $@ $^\ndiff --git a/contrib/libgit-rs/libgit-sys/Cargo.lock b/contrib/libgit-rs/libgit-sys/Cargo.lock\nnew file mode 100644\nindex 0000000000..af8676ab42\n--- /dev/null\n+++ b/contrib/libgit-rs/libgit-sys/Cargo.lock\n@@ -0,0 +1,55 @@\n+# This file is automatically @generated by Cargo.\n+# It is not intended for manual editing.\n+version = 3\n+\n+[[package]]\n+name = \"cc\"\n+version = \"1.1.15\"\n+source = \"registry+https://github.com/rust-lang/crates.io-index\"\n+checksum = \"57b6a275aa2903740dc87da01c62040406b8812552e97129a63ea8850a17c6e6\"\n+dependencies = [\n+ \"shlex\",\n+]\n+\n+[[package]]\n+name = \"libc\"\n+version = \"0.2.158\"\n+source = \"registry+https://github.com/rust-lang/crates.io-index\"\n+checksum = \"d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439\"\n+\n+[[package]]\n+name = \"libgit-sys\"\n+version = \"0.1.0\"\n+dependencies = [\n+ \"libz-sys\",\n+]\n+\n+[[package]]\n+name = \"libz-sys\"\n+version = \"1.1.20\"\n+source = \"registry+https://github.com/rust-lang/crates.io-index\"\n+checksum = \"d2d16453e800a8cf6dd2fc3eb4bc99b786a9b90c663b8559a5b1a041bf89e472\"\n+dependencies = [\n+ \"cc\",\n+ \"libc\",\n+ \"pkg-config\",\n+ \"vcpkg\",\n+]\n+\n+[[package]]\n+name = \"pkg-config\"\n+version = \"0.3.30\"\n+source = \"registry+https://github.com/rust-lang/crates.io-index\"\n+checksum = \"d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec\"\n+\n+[[package]]\n+name = \"shlex\"\n+version = \"1.3.0\"\n+source = \"registry+https://github.com/rust-lang/crates.io-index\"\n+checksum = \"0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64\"\n+\n+[[package]]\n+name = \"vcpkg\"\n+version = \"0.2.15\"\n+source = \"registry+https://github.com/rust-lang/crates.io-index\"\n+checksum = \"accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426\"\ndiff --git a/contrib/libgit-rs/libgit-sys/Cargo.toml b/contrib/libgit-rs/libgit-sys/Cargo.toml\nnew file mode 100644\nindex 0000000000..905fd6774f\n--- /dev/null\n+++ b/contrib/libgit-rs/libgit-sys/Cargo.toml\n@@ -0,0 +1,12 @@\n+[package]\n+name = \"libgit-sys\"\n+version = \"0.1.0\"\n+edition = \"2021\"\n+build = \"build.rs\"\n+links = \"gitpub\"\n+\n+[lib]\n+path = \"src/lib.rs\"\n+\n+[dependencies]\n+libz-sys = \"1.1.19\"\n\\ No newline at end of file\ndiff --git a/contrib/libgit-rs/libgit-sys/README.md b/contrib/libgit-rs/libgit-sys/README.md\nnew file mode 100644\nindex 0000000000..7a59602c30\n--- /dev/null\n+++ b/contrib/libgit-rs/libgit-sys/README.md\n@@ -0,0 +1,15 @@\n+# cgit-info\n+\n+A small hacky proof-of-concept showing how to provide a Rust FFI for the Git\n+library.\n+\n+## Building\n+\n+`cargo build` automatically builds and picks up on changes made to both\n+the Rust wrapper and git.git code so there is no need to run `make`\n+beforehand.\n+\n+## Running\n+\n+Assuming you don't make any changes to the Git source, you can just work from\n+`contrib/cgit-rs` and use `cargo build` or `cargo run` as usual.\ndiff --git a/contrib/libgit-rs/libgit-sys/build.rs b/contrib/libgit-rs/libgit-sys/build.rs\nnew file mode 100644\nindex 0000000000..c187ab6cd0\n--- /dev/null\n+++ b/contrib/libgit-rs/libgit-sys/build.rs\n@@ -0,0 +1,31 @@\n+use std::env;\n+use std::path::PathBuf;\n+\n+pub fn main() -> std::io::Result<()> {\n+    let crate_root = PathBuf::from(env::var_os(\"CARGO_MANIFEST_DIR\").unwrap());\n+    let git_root = crate_root.join(\"../../..\");\n+    let dst = PathBuf::from(env::var_os(\"OUT_DIR\").unwrap());\n+\n+    let make_output = std::process::Command::new(\"make\")\n+        .env_remove(\"PROFILE\")\n+        .current_dir(git_root.clone())\n+        .args([\n+            \"CFLAGS=-fvisibility=hidden\",\n+            \"contrib/libgit-rs/libgit-sys/libgitpub.a\",\n+        ])\n+        .output()\n+        .expect(\"Make failed to run\");\n+    if !make_output.status.success() {\n+        panic!(\n+            \"Make failed:\\n  stdout = {}\\n  stderr = {}\\n\",\n+            String::from_utf8(make_output.stdout).unwrap(),\n+            String::from_utf8(make_output.stderr).unwrap()\n+        );\n+    }\n+    std::fs::copy(crate_root.join(\"libgitpub.a\"), dst.join(\"libgitpub.a\"))?;\n+    println!(\"cargo::rustc-link-search=native={}\", dst.display());\n+    println!(\"cargo::rustc-link-lib=gitpub\");\n+    println!(\"cargo::rerun-if-changed={}\", git_root.display());\n+\n+    Ok(())\n+}\ndiff --git a/contrib/libgit-rs/libgit-sys/public_symbol_export.c b/contrib/libgit-rs/libgit-sys/public_symbol_export.c\nnew file mode 100644\nindex 0000000000..39c27d9c1a\n--- /dev/null\n+++ b/contrib/libgit-rs/libgit-sys/public_symbol_export.c\n@@ -0,0 +1,20 @@\n+// Shim to publicly export Git symbols. These must be renamed so that the\n+// original symbols can be hidden. Renaming these with a \"libgit_\" prefix also\n+// avoid conflicts with other libraries such as libgit2.\n+\n+#include \"contrib/libgit-rs/libgit-sys/public_symbol_export.h\"\n+#include \"version.h\"\n+\n+#pragma GCC visibility push(default)\n+\n+const char *libgit_user_agent(void)\n+{\n+\treturn git_user_agent();\n+}\n+\n+const char *libgit_user_agent_sanitized(void)\n+{\n+\treturn git_user_agent_sanitized();\n+}\n+\n+#pragma GCC visibility pop\ndiff --git a/contrib/libgit-rs/libgit-sys/public_symbol_export.h b/contrib/libgit-rs/libgit-sys/public_symbol_export.h\nnew file mode 100644\nindex 0000000000..a3372f93fa\n--- /dev/null\n+++ b/contrib/libgit-rs/libgit-sys/public_symbol_export.h\n@@ -0,0 +1,8 @@\n+#ifndef PUBLIC_SYMBOL_EXPORT_H\n+#define PUBLIC_SYMBOL_EXPORT_H\n+\n+const char *libgit_user_agent(void);\n+\n+const char *libgit_user_agent_sanitized(void);\n+\n+#endif /* PUBLIC_SYMBOL_EXPORT_H */\ndiff --git a/contrib/libgit-rs/libgit-sys/src/lib.rs b/contrib/libgit-rs/libgit-sys/src/lib.rs\nnew file mode 100644\nindex 0000000000..346b26083d\n--- /dev/null\n+++ b/contrib/libgit-rs/libgit-sys/src/lib.rs\n@@ -0,0 +1,42 @@\n+use std::ffi::c_char;\n+\n+extern crate libz_sys;\n+\n+extern \"C\" {\n+    // From version.c\n+    pub fn libgit_user_agent() -> *const c_char;\n+    pub fn libgit_user_agent_sanitized() -> *const c_char;\n+}\n+\n+#[cfg(test)]\n+mod tests {\n+    use std::ffi::CStr;\n+\n+    use super::*;\n+\n+    #[test]\n+    fn user_agent_starts_with_git() {\n+        let c_str = unsafe { CStr::from_ptr(libgit_user_agent()) };\n+        let agent = c_str\n+            .to_str()\n+            .expect(\"User agent contains invalid UTF-8 data\");\n+        assert!(\n+            agent.starts_with(\"git/\"),\n+            r#\"Expected user agent to start with \"git/\", got: {}\"#,\n+            agent\n+        );\n+    }\n+\n+    #[test]\n+    fn sanitized_user_agent_starts_with_git() {\n+        let c_str = unsafe { CStr::from_ptr(libgit_user_agent_sanitized()) };\n+        let agent = c_str\n+            .to_str()\n+            .expect(\"Sanitized user agent contains invalid UTF-8 data\");\n+        assert!(\n+            agent.starts_with(\"git/\"),\n+            r#\"Expected user agent to start with \"git/\", got: {}\"#,\n+            agent\n+        );\n+    }\n+}\n-- \n2.46.0.469.g59c65b2a67-goog\n\n"},{"id":"502368","messageId":"20240906222116.270196-3-calvinwan@google.com","threadId":"61917","inReplyTo":"20240906221853.257984-1-calvinwan@google.com","subject":"[PATCH v3 3/6] libgit-sys: add repo initialization and config access","fromName":"Calvin Wan","fromEmail":"calvinwan@google.com","sentAt":"2024-09-06T22:21:13Z","receivedAt":"2024-09-06T22:21:27Z","isPatch":true,"sender":{"key":"calvinwan@google.com","avatar":"https://avatars.githubusercontent.com/u/92547554?v=4"},"body":"Wrap a few repo setup and config access functions in libgit-sys. These\nwere selected as proof-of-concept items to show that we can access local\nconfig from Rust.\n\nCo-authored-by: Josh Steadmon <steadmon@google.com>\nSigned-off-by: Calvin Wan <calvinwan@google.com>\nChange-Id: I6dd886af8c63e1f0f3251064cd8903aecdf768bb\n---\n .../libgit-sys/public_symbol_export.c         | 26 +++++++++\n .../libgit-sys/public_symbol_export.h         |  8 +++\n contrib/libgit-rs/libgit-sys/src/lib.rs       | 57 ++++++++++++++++++-\n 3 files changed, 89 insertions(+), 2 deletions(-)\n\ndiff --git a/contrib/libgit-rs/libgit-sys/public_symbol_export.c b/contrib/libgit-rs/libgit-sys/public_symbol_export.c\nindex 39c27d9c1a..65d1620d28 100644\n--- a/contrib/libgit-rs/libgit-sys/public_symbol_export.c\n+++ b/contrib/libgit-rs/libgit-sys/public_symbol_export.c\n@@ -2,11 +2,37 @@\n // original symbols can be hidden. Renaming these with a \"libgit_\" prefix also\n // avoid conflicts with other libraries such as libgit2.\n \n+#include \"git-compat-util.h\"\n #include \"contrib/libgit-rs/libgit-sys/public_symbol_export.h\"\n+#include \"common-init.h\"\n+#include \"config.h\"\n+#include \"setup.h\"\n #include \"version.h\"\n \n+extern struct repository *the_repository;\n+\n #pragma GCC visibility push(default)\n \n+const char *libgit_setup_git_directory(void)\n+{\n+\treturn setup_git_directory();\n+}\n+\n+int libgit_config_get_int(const char *key, int *dest)\n+{\n+\treturn repo_config_get_int(the_repository, key, dest);\n+}\n+\n+void libgit_init_git(const char **argv)\n+{\n+\tinit_git(argv);\n+}\n+\n+int libgit_parse_maybe_bool(const char *val)\n+{\n+\treturn git_parse_maybe_bool(val);\n+}\n+\n const char *libgit_user_agent(void)\n {\n \treturn git_user_agent();\ndiff --git a/contrib/libgit-rs/libgit-sys/public_symbol_export.h b/contrib/libgit-rs/libgit-sys/public_symbol_export.h\nindex a3372f93fa..64332f30de 100644\n--- a/contrib/libgit-rs/libgit-sys/public_symbol_export.h\n+++ b/contrib/libgit-rs/libgit-sys/public_symbol_export.h\n@@ -1,6 +1,14 @@\n #ifndef PUBLIC_SYMBOL_EXPORT_H\n #define PUBLIC_SYMBOL_EXPORT_H\n \n+const char *libgit_setup_git_directory(void);\n+\n+int libgit_config_get_int(const char *key, int *dest);\n+\n+void libgit_init_git(const char **argv);\n+\n+int libgit_parse_maybe_bool(const char *val);\n+\n const char *libgit_user_agent(void);\n \n const char *libgit_user_agent_sanitized(void);\ndiff --git a/contrib/libgit-rs/libgit-sys/src/lib.rs b/contrib/libgit-rs/libgit-sys/src/lib.rs\nindex 346b26083d..9b2e85dff8 100644\n--- a/contrib/libgit-rs/libgit-sys/src/lib.rs\n+++ b/contrib/libgit-rs/libgit-sys/src/lib.rs\n@@ -1,8 +1,19 @@\n-use std::ffi::c_char;\n+use std::ffi::{c_char, c_int};\n \n extern crate libz_sys;\n \n extern \"C\" {\n+    pub fn libgit_setup_git_directory() -> *const c_char;\n+\n+    // From config.c\n+    pub fn libgit_config_get_int(key: *const c_char, dest: *mut c_int) -> c_int;\n+\n+    // From common-init.c\n+    pub fn libgit_init_git(argv: *const *const c_char);\n+\n+    // From parse.c\n+    pub fn libgit_parse_maybe_bool(val: *const c_char) -> c_int;\n+\n     // From version.c\n     pub fn libgit_user_agent() -> *const c_char;\n     pub fn libgit_user_agent_sanitized() -> *const c_char;\n@@ -10,7 +21,7 @@ extern \"C\" {\n \n #[cfg(test)]\n mod tests {\n-    use std::ffi::CStr;\n+    use std::ffi::{CStr, CString};\n \n     use super::*;\n \n@@ -39,4 +50,46 @@ mod tests {\n             agent\n         );\n     }\n+\n+    #[test]\n+    fn parse_bools_from_strings() {\n+        let arg = CString::new(\"true\").unwrap();\n+        assert_eq!(unsafe { libgit_parse_maybe_bool(arg.as_ptr()) }, 1);\n+\n+        let arg = CString::new(\"yes\").unwrap();\n+        assert_eq!(unsafe { libgit_parse_maybe_bool(arg.as_ptr()) }, 1);\n+\n+        let arg = CString::new(\"false\").unwrap();\n+        assert_eq!(unsafe { libgit_parse_maybe_bool(arg.as_ptr()) }, 0);\n+\n+        let arg = CString::new(\"no\").unwrap();\n+        assert_eq!(unsafe { libgit_parse_maybe_bool(arg.as_ptr()) }, 0);\n+\n+        let arg = CString::new(\"maybe\").unwrap();\n+        assert_eq!(unsafe { libgit_parse_maybe_bool(arg.as_ptr()) }, -1);\n+    }\n+\n+    #[test]\n+    fn access_configs() {\n+        /*\n+         * Since we can't easily set a config for the local repo, test\n+         * for the existence of a key rather than a specific value \n+         */\n+        let fake_argv = [std::ptr::null::<c_char>()];\n+        unsafe {\n+            libgit_init_git(fake_argv.as_ptr());\n+            libgit_setup_git_directory();\n+        }\n+        let mut dest: c_int = 0;\n+        let key = CString::new(\"core.repositoryformatversion\").unwrap();\n+        unsafe { \n+            let val = libgit_config_get_int(key.as_ptr(), &mut dest as *mut i32);\n+            assert_eq!(val, 0)\n+        };\n+        let missing_key = CString::new(\"foo.bar\").unwrap();\n+        unsafe { \n+            let val = libgit_config_get_int(missing_key.as_ptr(), &mut dest as *mut i32);\n+            assert_eq!(val, 1)\n+        };\n+    }\n }\n-- \n2.46.0.469.g59c65b2a67-goog\n\n"},{"id":"502369","messageId":"20240906222116.270196-4-calvinwan@google.com","threadId":"61917","inReplyTo":"20240906221853.257984-1-calvinwan@google.com","subject":"[PATCH v3 4/6] config: add git_configset_alloc() and git_configset_clear_and_free()","fromName":"Calvin Wan","fromEmail":"calvinwan@google.com","sentAt":"2024-09-06T22:21:14Z","receivedAt":"2024-09-06T22:21:29Z","isPatch":true,"sender":{"key":"calvinwan@google.com","avatar":"https://avatars.githubusercontent.com/u/92547554?v=4"},"body":"From: Josh Steadmon <steadmon@google.com>\n\nAdd git_configset_alloc() and git_configset_clear_and_free() functions\nso that callers can manage config_set structs on the heap. This also\nallows non-C external consumers to treat config_sets as opaque structs.\n\nCo-authored-by: Calvin Wan <calvinwan@google.com>\nSigned-off-by: Calvin Wan <calvinwan@google.com>\nChange-Id: I21684c87691d978e3303e04324428aa3567dbd70\n---\n config.c | 11 +++++++++++\n config.h | 10 ++++++++++\n 2 files changed, 21 insertions(+)\n\ndiff --git a/config.c b/config.c\nindex 6421894614..444db8de79 100644\n--- a/config.c\n+++ b/config.c\n@@ -2324,6 +2324,11 @@ static int config_set_element_cmp(const void *cmp_data UNUSED,\n \treturn strcmp(e1->key, e2->key);\n }\n \n+struct config_set *git_configset_alloc(void)\n+{\n+\treturn xmalloc(sizeof(struct config_set));\n+}\n+\n void git_configset_init(struct config_set *set)\n {\n \thashmap_init(&set->config_hash, config_set_element_cmp, NULL, 0);\n@@ -2353,6 +2358,12 @@ void git_configset_clear(struct config_set *set)\n \tset->list.items = NULL;\n }\n \n+void git_configset_clear_and_free(struct config_set *set)\n+{\n+\tgit_configset_clear(set);\n+\tfree(set);\n+}\n+\n static int config_set_callback(const char *key, const char *value,\n \t\t\t       const struct config_context *ctx,\n \t\t\t       void *cb)\ndiff --git a/config.h b/config.h\nindex 54b47dec9e..074c85a788 100644\n--- a/config.h\n+++ b/config.h\n@@ -472,6 +472,11 @@ struct config_set {\n \tstruct configset_list list;\n };\n \n+/**\n+ * Alloc a config_set\n+ */\n+struct config_set *git_configset_alloc(void);\n+\n /**\n  * Initializes the config_set `cs`.\n  */\n@@ -520,6 +525,11 @@ int git_configset_get_string_multi(struct config_set *cs, const char *key,\n  */\n void git_configset_clear(struct config_set *cs);\n \n+/**\n+ * Clears and frees a heap-allocated `config_set` structure.\n+ */\n+void git_configset_clear_and_free(struct config_set *cs);\n+\n /*\n  * These functions return 1 if not found, and 0 if found, leaving the found\n  * value in the 'dest' pointer.\n-- \n2.46.0.469.g59c65b2a67-goog\n\n"},{"id":"502370","messageId":"20240906222116.270196-5-calvinwan@google.com","threadId":"61917","inReplyTo":"20240906221853.257984-1-calvinwan@google.com","subject":"[PATCH v3 5/6] libgit: add higher-level libgit crate","fromName":"Calvin Wan","fromEmail":"calvinwan@google.com","sentAt":"2024-09-06T22:21:15Z","receivedAt":"2024-09-06T22:21:30Z","isPatch":true,"sender":{"key":"calvinwan@google.com","avatar":"https://avatars.githubusercontent.com/u/92547554?v=4"},"body":"Wrap `struct config_set` and a few of its associated functions in\nlibgit-sys. Also introduce a higher-level \"libgit\" crate which provides\na more Rust-friendly interface to config_set structs.\n\nCo-authored-by: Josh Steadmon <steadmon@google.com>\nSigned-off-by: Calvin Wan <calvinwan@google.com>\nChange-Id: If85e77809434f0ef05e2db35132ecf84621e10ef\n---\n .gitignore                                    |  1 +\n Makefile                                      |  2 +-\n contrib/libgit-rs/Cargo.lock                  | 62 ++++++++++++++\n contrib/libgit-rs/Cargo.toml                  | 10 +++\n .../libgit-sys/public_symbol_export.c         | 30 +++++++\n .../libgit-sys/public_symbol_export.h         | 12 +++\n contrib/libgit-rs/libgit-sys/src/lib.rs       | 31 ++++++-\n contrib/libgit-rs/src/lib.rs                  | 85 +++++++++++++++++++\n contrib/libgit-rs/testdata/config1            |  2 +\n contrib/libgit-rs/testdata/config2            |  2 +\n contrib/libgit-rs/testdata/config3            |  2 +\n 11 files changed, 237 insertions(+), 2 deletions(-)\n create mode 100644 contrib/libgit-rs/Cargo.lock\n create mode 100644 contrib/libgit-rs/Cargo.toml\n create mode 100644 contrib/libgit-rs/src/lib.rs\n create mode 100644 contrib/libgit-rs/testdata/config1\n create mode 100644 contrib/libgit-rs/testdata/config2\n create mode 100644 contrib/libgit-rs/testdata/config3\n\ndiff --git a/.gitignore b/.gitignore\nindex dfd72820fb..0a42f27117 100644\n--- a/.gitignore\n+++ b/.gitignore\n@@ -248,4 +248,5 @@ Release/\n /git.VC.db\n *.dSYM\n /contrib/buildsystems/out\n+/contrib/libgit-rs/target\n /contrib/libgit-rs/libgit-sys/target\ndiff --git a/Makefile b/Makefile\nindex 0090514e55..abeee01d9e 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -3723,7 +3723,7 @@ clean: profile-clean coverage-clean cocciclean\n \t$(RM) $(htmldocs).tar.gz $(manpages).tar.gz\n \t$(MAKE) -C Documentation/ clean\n \t$(RM) Documentation/GIT-EXCLUDED-PROGRAMS\n-\t$(RM) -r contrib/libgit-rs/libgit-sys/target\n+\t$(RM) -r contrib/libgit-rs/target contrib/libgit-rs/libgit-sys/target\n ifndef NO_PERL\n \t$(RM) -r perl/build/\n endif\ndiff --git a/contrib/libgit-rs/Cargo.lock b/contrib/libgit-rs/Cargo.lock\nnew file mode 100644\nindex 0000000000..187176f5df\n--- /dev/null\n+++ b/contrib/libgit-rs/Cargo.lock\n@@ -0,0 +1,62 @@\n+# This file is automatically @generated by Cargo.\n+# It is not intended for manual editing.\n+version = 3\n+\n+[[package]]\n+name = \"cc\"\n+version = \"1.1.15\"\n+source = \"registry+https://github.com/rust-lang/crates.io-index\"\n+checksum = \"57b6a275aa2903740dc87da01c62040406b8812552e97129a63ea8850a17c6e6\"\n+dependencies = [\n+ \"shlex\",\n+]\n+\n+[[package]]\n+name = \"libc\"\n+version = \"0.2.158\"\n+source = \"registry+https://github.com/rust-lang/crates.io-index\"\n+checksum = \"d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439\"\n+\n+[[package]]\n+name = \"libgit\"\n+version = \"0.1.0\"\n+dependencies = [\n+ \"libgit-sys\",\n+]\n+\n+[[package]]\n+name = \"libgit-sys\"\n+version = \"0.1.0\"\n+dependencies = [\n+ \"libz-sys\",\n+]\n+\n+[[package]]\n+name = \"libz-sys\"\n+version = \"1.1.20\"\n+source = \"registry+https://github.com/rust-lang/crates.io-index\"\n+checksum = \"d2d16453e800a8cf6dd2fc3eb4bc99b786a9b90c663b8559a5b1a041bf89e472\"\n+dependencies = [\n+ \"cc\",\n+ \"libc\",\n+ \"pkg-config\",\n+ \"vcpkg\",\n+]\n+\n+[[package]]\n+name = \"pkg-config\"\n+version = \"0.3.30\"\n+source = \"registry+https://github.com/rust-lang/crates.io-index\"\n+checksum = \"d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec\"\n+\n+[[package]]\n+name = \"shlex\"\n+version = \"1.3.0\"\n+source = \"registry+https://github.com/rust-lang/crates.io-index\"\n+checksum = \"0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64\"\n+\n+[[package]]\n+name = \"vcpkg\"\n+version = \"0.2.15\"\n+source = \"registry+https://github.com/rust-lang/crates.io-index\"\n+checksum = \"accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426\"\ndiff --git a/contrib/libgit-rs/Cargo.toml b/contrib/libgit-rs/Cargo.toml\nnew file mode 100644\nindex 0000000000..b8914517e0\n--- /dev/null\n+++ b/contrib/libgit-rs/Cargo.toml\n@@ -0,0 +1,10 @@\n+[package]\n+name = \"libgit\"\n+version = \"0.1.0\"\n+edition = \"2021\"\n+\n+[lib]\n+path = \"src/lib.rs\"\n+\n+[dependencies]\n+libgit-sys = { version = \"0.1.0\", path = \"libgit-sys\" }\ndiff --git a/contrib/libgit-rs/libgit-sys/public_symbol_export.c b/contrib/libgit-rs/libgit-sys/public_symbol_export.c\nindex 65d1620d28..07d6bfdd84 100644\n--- a/contrib/libgit-rs/libgit-sys/public_symbol_export.c\n+++ b/contrib/libgit-rs/libgit-sys/public_symbol_export.c\n@@ -33,6 +33,36 @@ int libgit_parse_maybe_bool(const char *val)\n \treturn git_parse_maybe_bool(val);\n }\n \n+struct libgit_config_set *libgit_configset_alloc(void)\n+{\n+\treturn git_configset_alloc();\n+}\n+\n+void libgit_configset_clear_and_free(struct libgit_config_set *cs)\n+{\n+\tgit_configset_clear_and_free(cs);\n+}\n+\n+void libgit_configset_init(struct libgit_config_set *cs)\n+{\n+\tgit_configset_init(cs);\n+}\n+\n+int libgit_configset_add_file(struct libgit_config_set *cs, const char *filename)\n+{\n+\treturn git_configset_add_file(cs, filename);\n+}\n+\n+int libgit_configset_get_int(struct libgit_config_set *cs, const char *key, int *dest)\n+{\n+\treturn git_configset_get_int(cs, key, dest);\n+}\n+\n+int libgit_configset_get_string(struct libgit_config_set *cs, const char *key, char **dest)\n+{\n+\treturn git_configset_get_string(cs, key, dest);\n+}\n+\n const char *libgit_user_agent(void)\n {\n \treturn git_user_agent();\ndiff --git a/contrib/libgit-rs/libgit-sys/public_symbol_export.h b/contrib/libgit-rs/libgit-sys/public_symbol_export.h\nindex 64332f30de..3933698976 100644\n--- a/contrib/libgit-rs/libgit-sys/public_symbol_export.h\n+++ b/contrib/libgit-rs/libgit-sys/public_symbol_export.h\n@@ -9,6 +9,18 @@ void libgit_init_git(const char **argv);\n \n int libgit_parse_maybe_bool(const char *val);\n \n+struct libgit_config_set *libgit_configset_alloc(void);\n+\n+void libgit_configset_clear_and_free(struct libgit_config_set *cs);\n+\n+void libgit_configset_init(struct libgit_config_set *cs);\n+\n+int libgit_configset_add_file(struct libgit_config_set *cs, const char *filename);\n+\n+int libgit_configset_get_int(struct libgit_config_set *cs, const char *key, int *dest);\n+\n+int libgit_configset_get_string(struct libgit_config_set *cs, const char *key, char **dest);\n+\n const char *libgit_user_agent(void);\n \n const char *libgit_user_agent_sanitized(void);\ndiff --git a/contrib/libgit-rs/libgit-sys/src/lib.rs b/contrib/libgit-rs/libgit-sys/src/lib.rs\nindex 9b2e85dff8..f3eb44a238 100644\n--- a/contrib/libgit-rs/libgit-sys/src/lib.rs\n+++ b/contrib/libgit-rs/libgit-sys/src/lib.rs\n@@ -1,8 +1,17 @@\n-use std::ffi::{c_char, c_int};\n+use std::ffi::{c_char, c_int, c_void};\n+\n+#[allow(non_camel_case_types)]\n+#[repr(C)]\n+pub struct libgit_config_set {\n+    _data: [u8; 0],\n+    _marker: core::marker::PhantomData<(*mut u8, core::marker::PhantomPinned)>,\n+}\n \n extern crate libz_sys;\n \n extern \"C\" {\n+    pub fn free(ptr: *mut c_void);\n+\n     pub fn libgit_setup_git_directory() -> *const c_char;\n \n     // From config.c\n@@ -17,6 +26,26 @@ extern \"C\" {\n     // From version.c\n     pub fn libgit_user_agent() -> *const c_char;\n     pub fn libgit_user_agent_sanitized() -> *const c_char;\n+\n+    pub fn libgit_configset_alloc() -> *mut libgit_config_set;\n+    pub fn libgit_configset_clear_and_free(cs: *mut libgit_config_set);\n+\n+    pub fn libgit_configset_init(cs: *mut libgit_config_set);\n+\n+    pub fn libgit_configset_add_file(cs: *mut libgit_config_set, filename: *const c_char) -> c_int;\n+\n+    pub fn libgit_configset_get_int(\n+        cs: *mut libgit_config_set,\n+        key: *const c_char,\n+        int: *mut c_int,\n+    ) -> c_int;\n+\n+    pub fn libgit_configset_get_string(\n+        cs: *mut libgit_config_set,\n+        key: *const c_char,\n+        dest: *mut *mut c_char,\n+    ) -> c_int;\n+\n }\n \n #[cfg(test)]\ndiff --git a/contrib/libgit-rs/src/lib.rs b/contrib/libgit-rs/src/lib.rs\nnew file mode 100644\nindex 0000000000..43aae09656\n--- /dev/null\n+++ b/contrib/libgit-rs/src/lib.rs\n@@ -0,0 +1,85 @@\n+use std::ffi::{c_char, c_int, c_void, CStr, CString};\n+use std::path::Path;\n+\n+use libgit_sys::*;\n+\n+pub struct ConfigSet(*mut libgit_config_set);\n+impl ConfigSet {\n+    pub fn new() -> Self {\n+        unsafe {\n+            let ptr = libgit_configset_alloc();\n+            libgit_configset_init(ptr);\n+            ConfigSet(ptr)\n+        }\n+    }\n+\n+    pub fn add_files(&mut self, files: &[&Path]) {\n+        for file in files {\n+            let pstr: &str = file.to_str().expect(\"Invalid UTF-8\");\n+            let rs = CString::new(&*pstr).expect(\"Couldn't convert to CString\");\n+            unsafe {\n+                libgit_configset_add_file(self.0, rs.as_ptr());\n+            }\n+        }\n+    }\n+\n+    pub fn get_int(&mut self, key: &str) -> Option<c_int> {\n+        let key = CString::new(key).expect(\"Couldn't convert to CString\");\n+        let mut val: c_int = 0;\n+        unsafe {\n+            if libgit_configset_get_int(self.0, key.as_ptr(), &mut val as *mut c_int) != 0 {\n+                return None;\n+            }\n+        }\n+\n+        Some(val)\n+    }\n+\n+    pub fn get_str(&mut self, key: &str) -> Option<CString> {\n+        let key = CString::new(key).expect(\"Couldn't convert to CString\");\n+        let mut val: *mut c_char = std::ptr::null_mut();\n+        unsafe {\n+            if libgit_configset_get_string(self.0, key.as_ptr(), &mut val as *mut *mut c_char) != 0\n+            {\n+                return None;\n+            }\n+            let borrowed_str = CStr::from_ptr(val);\n+            let owned_str = CString::from_vec_with_nul(borrowed_str.to_bytes_with_nul().to_vec());\n+            free(val as *mut c_void); // Free the xstrdup()ed pointer from the C side\n+            Some(owned_str.unwrap())\n+        }\n+    }\n+}\n+\n+impl Default for ConfigSet {\n+    fn default() -> Self {\n+        Self::new()\n+    }\n+}\n+\n+impl Drop for ConfigSet {\n+    fn drop(&mut self) {\n+        unsafe {\n+            libgit_configset_clear_and_free(self.0);\n+        }\n+    }\n+}\n+\n+#[cfg(test)]\n+mod tests {\n+    use super::*;\n+\n+    #[test]\n+    fn load_configs_via_configset() {\n+        let mut cs = ConfigSet::new();\n+        cs.add_files(&[Path::new(\"testdata/config1\"),\n+                       Path::new(\"testdata/config2\"),\n+                       Path::new(\"testdata/config3\")]);\n+        // ConfigSet retrieves correct value \n+        assert_eq!(cs.get_int(\"trace2.eventTarget\"), Some(1));\n+        // ConfigSet respects last config value set\n+        assert_eq!(cs.get_int(\"trace2.eventNesting\"), Some(3));\n+        // ConfigSet returns None for missing key\n+        assert_eq!(cs.get_str(\"foo.bar\"), None);\n+    }\n+}\ndiff --git a/contrib/libgit-rs/testdata/config1 b/contrib/libgit-rs/testdata/config1\nnew file mode 100644\nindex 0000000000..4e9a9d25d1\n--- /dev/null\n+++ b/contrib/libgit-rs/testdata/config1\n@@ -0,0 +1,2 @@\n+[trace2]\n+\teventNesting = 1\ndiff --git a/contrib/libgit-rs/testdata/config2 b/contrib/libgit-rs/testdata/config2\nnew file mode 100644\nindex 0000000000..b8d1eca423\n--- /dev/null\n+++ b/contrib/libgit-rs/testdata/config2\n@@ -0,0 +1,2 @@\n+[trace2]\n+\teventTarget = 1\ndiff --git a/contrib/libgit-rs/testdata/config3 b/contrib/libgit-rs/testdata/config3\nnew file mode 100644\nindex 0000000000..ca7b9a7c38\n--- /dev/null\n+++ b/contrib/libgit-rs/testdata/config3\n@@ -0,0 +1,2 @@\n+[trace2]\n+\teventNesting = 3\n-- \n2.46.0.469.g59c65b2a67-goog\n\n"},{"id":"502371","messageId":"20240906222116.270196-6-calvinwan@google.com","threadId":"61917","inReplyTo":"20240906221853.257984-1-calvinwan@google.com","subject":"[PATCH v3 6/6] Makefile: add option to build and test libgit-rs and libgit-rs-sys","fromName":"Calvin Wan","fromEmail":"calvinwan@google.com","sentAt":"2024-09-06T22:21:16Z","receivedAt":"2024-09-06T22:21:33Z","isPatch":true,"sender":{"key":"calvinwan@google.com","avatar":"https://avatars.githubusercontent.com/u/92547554?v=4"},"body":"Add libgitrs, libgitrs-sys, libgitrs-test, and libgitrs-sys-test targets\nto their respective Makefiles so they can be built and tested without\nhaving to run cargo build/test.\n\nAdd environment variable, INCLUDE_LIBGIT_RS, that when set,\nautomatically builds and tests libgit-rs and libgit-rs-sys when `make\nall` is ran.\n\nSigned-off-by: Calvin Wan <calvinwan@google.com>\n---\n Makefile   | 16 ++++++++++++++++\n t/Makefile | 16 ++++++++++++++++\n 2 files changed, 32 insertions(+)\n\ndiff --git a/Makefile b/Makefile\nindex abeee01d9e..41ad458aef 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -3870,6 +3870,22 @@ build-unit-tests: $(UNIT_TEST_PROGS)\n unit-tests: $(UNIT_TEST_PROGS) t/helper/test-tool$X\n \t$(MAKE) -C t/ unit-tests\n \n+.PHONY: libgitrs-sys\n+libgitrs-sys:\n+\t$(QUIET)(\\\n+\t\tcd contrib/libgit-rs/libgit-sys && \\\n+\t\tcargo build \\\n+\t)\n+.PHONY: libgitrs\n+libgitrs:\n+\t$(QUIET)(\\\n+\t\tcd contrib/libgit-rs && \\\n+\t\tcargo build \\\n+\t)\n+ifdef INCLUDE_LIBGIT_RS\n+all:: libgitrs\n+endif\n+\n contrib/libgit-rs/libgit-sys/partial_symbol_export.o: contrib/libgit-rs/libgit-sys/public_symbol_export.o libgit.a reftable/libreftable.a xdiff/lib.a\n \t$(LD) -r $^ -o $@\n \ndiff --git a/t/Makefile b/t/Makefile\nindex b2eb9f770b..066cb5c2b4 100644\n--- a/t/Makefile\n+++ b/t/Makefile\n@@ -169,3 +169,19 @@ perf:\n \n .PHONY: pre-clean $(T) aggregate-results clean valgrind perf \\\n \tcheck-chainlint clean-chainlint test-chainlint $(UNIT_TESTS)\n+\n+.PHONY: libgitrs-sys-test\n+libgitrs-sys-test:\n+\t$(QUIET)(\\\n+\t\tcd ../contrib/libgit-rs/libgit-sys && \\\n+\t\tcargo test \\\n+\t)\n+.PHONY: libgitrs-test\n+libgitrs-test:\n+\t$(QUIET)(\\\n+\t\tcd ../contrib/libgit-rs && \\\n+\t\tcargo test \\\n+\t)\n+ifdef INCLUDE_LIBGIT_RS\n+all:: libgitrs-sys-test libgitrs-test\n+endif\n-- \n2.46.0.469.g59c65b2a67-goog\n\n"},{"id":"502373","messageId":"CAPig+cQbPCWS4LVF7cdw_DD6K8dsrLTCmmhqkmGZpz1CO93vRQ@mail.gmail.com","threadId":"61917","inReplyTo":"20240906222116.270196-2-calvinwan@google.com","subject":"Re: [PATCH v3 2/6] libgit-sys: introduce Rust wrapper for libgit.a","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2024-09-06T22:39:17Z","receivedAt":"2024-09-06T22:39:29Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Fri, Sep 6, 2024 at 6:21 PM Calvin Wan <calvinwan@google.com> wrote:\n> From: Josh Steadmon <steadmon@google.com>\n\nThis is curious...\n\n> Co-authored-by: Kyle Lippincott <spectral@google.com>\n> Co-authored-by: Calvin Wan <calvinwan@google.com>\n> Signed-off-by: Calvin Wan <calvinwan@google.com>\n> Signed-off-by: Kyle Lippincott <spectral@google.com>\n\n... since there is no mention of Josh here.\n\n> diff --git a/contrib/libgit-rs/libgit-sys/Cargo.toml b/contrib/libgit-rs/libgit-sys/Cargo.toml\n> @@ -0,0 +1,12 @@\n> +[package]\n> +name = \"libgit-sys\"\n> [...]\n> +[dependencies]\n> +libz-sys = \"1.1.19\"\n> \\ No newline at end of file\n\nLet's give this file a proper line terminator.\n\n> diff --git a/contrib/libgit-rs/libgit-sys/build.rs b/contrib/libgit-rs/libgit-sys/build.rs\n> @@ -0,0 +1,31 @@\n> +pub fn main() -> std::io::Result<()> {\n> +    let crate_root = PathBuf::from(env::var_os(\"CARGO_MANIFEST_DIR\").unwrap());\n> +    let git_root = crate_root.join(\"../../..\");\n> +    let dst = PathBuf::from(env::var_os(\"OUT_DIR\").unwrap());\n> +\n> +    let make_output = std::process::Command::new(\"make\")\n\nProviding a mechanism for people to override this hardcoded spelling\nof \"make\" could be another item for your NEEDSWORK list; in\nparticular, I'm thinking about platforms on which GNU \"make\" is\ninstalled as \"gmake\".\n\n> diff --git a/contrib/libgit-rs/libgit-sys/public_symbol_export.c b/contrib/libgit-rs/libgit-sys/public_symbol_export.c\n> @@ -0,0 +1,20 @@\n> +// Shim to publicly export Git symbols. These must be renamed so that the\n> +// original symbols can be hidden. Renaming these with a \"libgit_\" prefix also\n> +// avoid conflicts with other libraries such as libgit2.\n\ns/avoid/avoids/\n"},{"id":"502374","messageId":"CAPig+cQDa7pw5p22CQJZsm8BF_0b6MMyoknAAkFO2zcrPciSiw@mail.gmail.com","threadId":"61917","inReplyTo":"20240906222116.270196-3-calvinwan@google.com","subject":"Re: [PATCH v3 3/6] libgit-sys: add repo initialization and config access","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2024-09-06T22:53:40Z","receivedAt":"2024-09-06T22:53:53Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Fri, Sep 6, 2024 at 6:21 PM Calvin Wan <calvinwan@google.com> wrote:\n> Wrap a few repo setup and config access functions in libgit-sys. These\n> were selected as proof-of-concept items to show that we can access local\n> config from Rust.\n>\n> Co-authored-by: Josh Steadmon <steadmon@google.com>\n> Signed-off-by: Calvin Wan <calvinwan@google.com>\n\nJosh's sign-off is missing?\n\n> ---\n> diff --git a/contrib/libgit-rs/libgit-sys/src/lib.rs b/contrib/libgit-rs/libgit-sys/src/lib.rs\n> @@ -1,8 +1,19 @@\n>  extern \"C\" {\n> +    pub fn libgit_setup_git_directory() -> *const c_char;\n> +\n> +    // From config.c\n> +    pub fn libgit_config_get_int(key: *const c_char, dest: *mut c_int) -> c_int;\n\nPerhaps add a comment above libgit_setup_git_directory() stating its\norigin, as you do for the other functions mentioned here?\n\n    // From setup.c\n    pub fn libgit_setup_git_directory() -> *const c_char;\n\n(Nit: I would probably drop the word \"From\" from these comments, as it\ndoesn't seem to add value and ends up being noise. Even better, drop\nthe comments altogether since they don't really add value and can\neasily become outdated if code is ever moved around.)\n\n> +\n> +    // From common-init.c\n> +    pub fn libgit_init_git(argv: *const *const c_char);\n> +\n> +    // From parse.c\n> +    pub fn libgit_parse_maybe_bool(val: *const c_char) -> c_int;\n"},{"id":"502379","messageId":"CAPig+cRKbvWT3Bx65-ULAYRSrrKTCL0C7r6xpNSdN5JjLBPjCQ@mail.gmail.com","threadId":"61917","inReplyTo":"20240906222116.270196-4-calvinwan@google.com","subject":"Re: [PATCH v3 4/6] config: add git_configset_alloc() and git_configset_clear_and_free()","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2024-09-06T23:24:22Z","receivedAt":"2024-09-06T23:24:34Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Fri, Sep 6, 2024 at 6:21 PM Calvin Wan <calvinwan@google.com> wrote:\n> From: Josh Steadmon <steadmon@google.com>\n>\n> Add git_configset_alloc() and git_configset_clear_and_free() functions\n> so that callers can manage config_set structs on the heap. This also\n> allows non-C external consumers to treat config_sets as opaque structs.\n>\n> Co-authored-by: Calvin Wan <calvinwan@google.com>\n> Signed-off-by: Calvin Wan <calvinwan@google.com>\n\nAlmost all these patches suffer from some sort of missing Josh\ntrailer, so I'll stop mentioning it now.\n\n> diff --git a/config.h b/config.h\n> @@ -472,6 +472,11 @@ struct config_set {\n> +/**\n> + * Alloc a config_set\n> + */\n> +struct config_set *git_configset_alloc(void);\n\nShould this documentation string mention that git_configset_alloc()\ndoes _not_ initialize the configset? Alternatively, should this\nfunction also initialize it as a convenience (and mention so in the\ndocumentation)?\n"},{"id":"502380","messageId":"20240906230419.r4hej7qsmx7yasl7@glandium.org","threadId":"61917","inReplyTo":"CAPig+cQbPCWS4LVF7cdw_DD6K8dsrLTCmmhqkmGZpz1CO93vRQ@mail.gmail.com","subject":"Re: [PATCH v3 2/6] libgit-sys: introduce Rust wrapper for libgit.a","fromName":"Mike Hommey","fromEmail":"mh@glandium.org","sentAt":"2024-09-06T23:04:19Z","receivedAt":"2024-09-06T23:42:15Z","isPatch":true,"sender":{"key":"mh@glandium.org","avatar":"https://avatars.githubusercontent.com/u/1038527?v=4"},"body":"On Fri, Sep 06, 2024 at 06:39:17PM -0400, Eric Sunshine wrote:\n> > diff --git a/contrib/libgit-rs/libgit-sys/build.rs b/contrib/libgit-rs/libgit-sys/build.rs\n> > @@ -0,0 +1,31 @@\n> > +pub fn main() -> std::io::Result<()> {\n> > +    let crate_root = PathBuf::from(env::var_os(\"CARGO_MANIFEST_DIR\").unwrap());\n> > +    let git_root = crate_root.join(\"../../..\");\n> > +    let dst = PathBuf::from(env::var_os(\"OUT_DIR\").unwrap());\n> > +\n> > +    let make_output = std::process::Command::new(\"make\")\n> \n> Providing a mechanism for people to override this hardcoded spelling\n> of \"make\" could be another item for your NEEDSWORK list; in\n> particular, I'm thinking about platforms on which GNU \"make\" is\n> installed as \"gmake\".\n\nAnd/or, use the `make_cmd` crate.\n\nMike\n"},{"id":"502381","messageId":"xmqq8qw4uz1q.fsf@gitster.g","threadId":"61917","inReplyTo":"20240906222116.270196-3-calvinwan@google.com","subject":"Re: [PATCH v3 3/6] libgit-sys: add repo initialization and config access","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2024-09-06T23:45:21Z","receivedAt":"2024-09-06T23:45:29Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Calvin Wan <calvinwan@google.com> writes:\n\n> Wrap a few repo setup and config access functions in libgit-sys. These\n> were selected as proof-of-concept items to show that we can access local\n> config from Rust.\n>\n> Co-authored-by: Josh Steadmon <steadmon@google.com>\n> Signed-off-by: Calvin Wan <calvinwan@google.com>\n> Change-Id: I6dd886af8c63e1f0f3251064cd8903aecdf768bb\n> ---\n\n\nCommon to all other steps, I suspect that you meant to but forgot to\nstrip out the Change-Id: thing here?\n\nAlso there are a few whitespace breakages in this step.\n\nApplying: libgit-sys: add repo initialization and config access\n.git/rebase-apply/patch:129: trailing whitespace.\n         * for the existence of a key rather than a specific value\n.git/rebase-apply/patch:138: trailing whitespace.\n        unsafe {\n.git/rebase-apply/patch:143: trailing whitespace.\n        unsafe {\nwarning: 3 lines add whitespace errors.\n\n\nThere is another one in a later step.\n\nApplying: libgit: add higher-level libgit crate\n.git/rebase-apply/patch:325: trailing whitespace.\n        // ConfigSet retrieves correct value\nwarning: 1 line adds whitespace errors.\n\n"},{"id":"502384","messageId":"xmqqv7z8tjd7.fsf@gitster.g","threadId":"61917","inReplyTo":"20240906222116.270196-5-calvinwan@google.com","subject":"Re: [PATCH v3 5/6] libgit: add higher-level libgit crate","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2024-09-07T00:09:24Z","receivedAt":"2024-09-07T00:09:26Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Calvin Wan <calvinwan@google.com> writes:\n\n> diff --git a/contrib/libgit-rs/libgit-sys/public_symbol_export.c b/contrib/libgit-rs/libgit-sys/public_symbol_export.c\n> index 65d1620d28..07d6bfdd84 100644\n> --- a/contrib/libgit-rs/libgit-sys/public_symbol_export.c\n> +++ b/contrib/libgit-rs/libgit-sys/public_symbol_export.c\n> @@ -33,6 +33,36 @@ int libgit_parse_maybe_bool(const char *val)\n>  \treturn git_parse_maybe_bool(val);\n>  }\n>  \n> +struct libgit_config_set *libgit_configset_alloc(void)\n> +{\n> +\treturn git_configset_alloc();\n> +}\n\ngit_configset_alloc() returns \"struct config_set *\" while this thing\nreturns an incompatible pointer.  \n\nSent out an outdated version or something?  This wouldn't have\npassed even a compile test, I suspect.\n\n\n\n$ make contrib/libgit-rs/libgit-sys/public_symbol_export.o\n    CC contrib/libgit-rs/libgit-sys/public_symbol_export.o\ncontrib/libgit-rs/libgit-sys/public_symbol_export.c: In function 'libgit_configset_alloc':\ncontrib/libgit-rs/libgit-sys/public_symbol_export.c:38:16: error: returning 'struct config_set *' from a function with incompatible return type 'struct libgit_config_set *' [-Werror=incompatible-pointer-types]\n   38 |         return git_configset_alloc();\n      |                ^~~~~~~~~~~~~~~~~~~~~\ncontrib/libgit-rs/libgit-sys/public_symbol_export.c: In function 'libgit_configset_clear_and_free':\ncontrib/libgit-rs/libgit-sys/public_symbol_export.c:43:38: error: passing argument 1 of 'git_configset_clear_and_free' from incompatible pointer type [-Werror=incompatible-pointer-types]\n   43 |         git_configset_clear_and_free(cs);\n      |                                      ^~\n      |                                      |\n      |                                      struct libgit_config_set *\nIn file included from contrib/libgit-rs/libgit-sys/public_symbol_export.c:8:\n./config.h:543:54: note: expected 'struct config_set *' but argument is of type 'struct libgit_config_set *'\n  543 | void git_configset_clear_and_free(struct config_set *cs);\n      |                                   ~~~~~~~~~~~~~~~~~~~^~\ncontrib/libgit-rs/libgit-sys/public_symbol_export.c: In function 'libgit_configset_init':\ncontrib/libgit-rs/libgit-sys/public_symbol_export.c:48:28: error: passing argument 1 of 'git_configset_init' from incompatible pointer type [-Werror=incompatible-pointer-types]\n   48 |         git_configset_init(cs);\n      |                            ^~\n      |                            |\n      |                            struct libgit_config_set *\n./config.h:495:44: note: expected 'struct config_set *' but argument is of type 'struct libgit_config_set *'\n  495 | void git_configset_init(struct config_set *cs);\n      |                         ~~~~~~~~~~~~~~~~~~~^~\ncontrib/libgit-rs/libgit-sys/public_symbol_export.c: In function 'libgit_configset_add_file':\ncontrib/libgit-rs/libgit-sys/public_symbol_export.c:53:39: error: passing argument 1 of 'git_configset_add_file' from incompatible pointer type [-Werror=incompatible-pointer-types]\n   53 |         return git_configset_add_file(cs, filename);\n      |                                       ^~\n      |                                       |\n      |                                       struct libgit_config_set *\n./config.h:504:47: note: expected 'struct config_set *' but argument is of type 'struct libgit_config_set *'\n  504 | int git_configset_add_file(struct config_set *cs, const char *filename);\n      |                            ~~~~~~~~~~~~~~~~~~~^~\ncontrib/libgit-rs/libgit-sys/public_symbol_export.c: In function 'libgit_configset_get_int':\ncontrib/libgit-rs/libgit-sys/public_symbol_export.c:58:38: error: passing argument 1 of 'git_configset_get_int' from incompatible pointer type [-Werror=incompatible-pointer-types]\n   58 |         return git_configset_get_int(cs, key, dest);\n      |                                      ^~\n      |                                      |\n      |                                      struct libgit_config_set *\n./config.h:568:46: note: expected 'struct config_set *' but argument is of type 'struct libgit_config_set *'\n  568 | int git_configset_get_int(struct config_set *cs, const char *key, int *dest);\n      |                           ~~~~~~~~~~~~~~~~~~~^~\ncontrib/libgit-rs/libgit-sys/public_symbol_export.c: In function 'libgit_configset_get_string':\ncontrib/libgit-rs/libgit-sys/public_symbol_export.c:63:41: error: passing argument 1 of 'git_configset_get_string' from incompatible pointer type [-Werror=incompatible-pointer-types]\n   63 |         return git_configset_get_string(cs, key, dest);\n      |                                         ^~\n      |                                         |\n      |                                         struct libgit_config_set *\n./config.h:567:49: note: expected 'struct config_set *' but argument is of type 'struct libgit_config_set *'\n  567 | int git_configset_get_string(struct config_set *cs, const char *key, char **dest);\n      |                              ~~~~~~~~~~~~~~~~~~~^~\ncc1: all warnings being treated as errors\ngmake: *** [Makefile:2802: contrib/libgit-rs/libgit-sys/public_symbol_export.o] Error 1\n"},{"id":"502389","messageId":"Ztxo_7ijHGmQKH5T@tapette.crustytoothpaste.net","threadId":"61917","inReplyTo":"CAFySSZB56V-h0zxcv+TtP0jmTYAV1gixanCf0toYqak7Mz-yGQ@mail.gmail.com","subject":"Re: [RFC PATCH 6/6] contrib/cgit-rs: add a subset of configset wrappers","fromName":"brian m. carlson","fromEmail":"sandals@crustytoothpaste.net","sentAt":"2024-09-07T14:53:51Z","receivedAt":"2024-09-07T14:53:59Z","isPatch":true,"sender":{"key":"sandals@crustytoothpaste.net","avatar":"https://avatars.githubusercontent.com/u/497054?v=4"},"body":"On 2024-09-06 at 19:37:18, Calvin Wan wrote:\n> While this gets rid of `std::ffi{c_char, c_int}` dependency,\n> `std::ffi{CStr, CString}` are also 1.64 unfortunately, and I don't see\n> the equivalent of it in libc. I'll reroll v3 without this change for\n> now. Hopefully Debian stable just upgrades to 1.64 or higher and we\n> don't have to worry about these ffi conversions :)\n\nI think that's actually a mistake, since some arguments like\n`from_bytes_with_nul` are labeled as 1.10.0.  In any event, the\nfollowing program compiles fine for me on 1.63, so I'm not worried about\ntheir usage:\n\n----\nuse std::ffi::CStr;\n\nfn main() {\n    let c = CStr::from_bytes_with_nul(b\"abc\\0\");\n    let c = c.to_owned();\n    println!(\"Hello, world!\\n\");\n}\n----\n-- \nbrian m. carlson (they/them or he/him)\nToronto, Ontario, CA\n"},{"id":"502390","messageId":"m0seubo5q7.fsf@epic96565.epic.com","threadId":"61917","inReplyTo":"20240906222116.270196-6-calvinwan@google.com","subject":"Re: [PATCH v3 6/6] Makefile: add option to build and test libgit-rs and libgit-rs-sys","fromName":"Sean Allred","fromEmail":"allred.sean@gmail.com","sentAt":"2024-09-07T15:15:12Z","receivedAt":"2024-09-07T15:15:16Z","isPatch":true,"sender":{"key":"allred.sean@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2082195?v=4"},"body":"Calvin Wan <calvinwan@google.com> writes:\n> Add libgitrs, libgitrs-sys, libgitrs-test, and libgitrs-sys-test targets\n> to their respective Makefiles so they can be built and tested without\n> having to run cargo build/test.\n\nI feel like clippy should be run as part of these somehow, but I'm not\nsure where.\n\n> +libgitrs-sys:\n> +\t$(QUIET)(\\\n> +\t\tcd contrib/libgit-rs/libgit-sys && \\\n> +\t\tcargo build \\\n> +\t)\n> +.PHONY: libgitrs\n> +libgitrs:\n> +\t$(QUIET)(\\\n> +\t\tcd contrib/libgit-rs && \\\n> +\t\tcargo build \\\n> +\t)\n\nWe should definitely be setting `RUSTFLAGS=-Dwarnings` as an analog to\n`-Wall` in the C world, no? These crates should build without warnings.\n\nVery excited to see this work; thank you for putting the time into it\n:-)\n\n-- \nSean Allred\n"},{"id":"502411","messageId":"xmqqa5ghx24h.fsf@gitster.g","threadId":"61917","inReplyTo":"20240906230419.r4hej7qsmx7yasl7@glandium.org","subject":"Re: [PATCH v3 2/6] libgit-sys: introduce Rust wrapper for libgit.a","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2024-09-08T21:32:46Z","receivedAt":"2024-09-08T21:32:48Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Mike Hommey <mh@glandium.org> writes:\n\n> On Fri, Sep 06, 2024 at 06:39:17PM -0400, Eric Sunshine wrote:\n>> > diff --git a/contrib/libgit-rs/libgit-sys/build.rs b/contrib/libgit-rs/libgit-sys/build.rs\n>> > @@ -0,0 +1,31 @@\n>> > +pub fn main() -> std::io::Result<()> {\n>> > +    let crate_root = PathBuf::from(env::var_os(\"CARGO_MANIFEST_DIR\").unwrap());\n>> > +    let git_root = crate_root.join(\"../../..\");\n>> > +    let dst = PathBuf::from(env::var_os(\"OUT_DIR\").unwrap());\n>> > +\n>> > +    let make_output = std::process::Command::new(\"make\")\n>> \n>> Providing a mechanism for people to override this hardcoded spelling\n>> of \"make\" could be another item for your NEEDSWORK list; in\n>> particular, I'm thinking about platforms on which GNU \"make\" is\n>> installed as \"gmake\".\n>\n> And/or, use the `make_cmd` crate.\n\nThanks for helping.\n"},{"id":"502412","messageId":"xmqq5xr5x23j.fsf@gitster.g","threadId":"61917","inReplyTo":"m0seubo5q7.fsf@epic96565.epic.com","subject":"Re: [PATCH v3 6/6] Makefile: add option to build and test libgit-rs and libgit-rs-sys","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2024-09-08T21:33:20Z","receivedAt":"2024-09-08T21:33:22Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Sean Allred <allred.sean@gmail.com> writes:\n\n> Calvin Wan <calvinwan@google.com> writes:\n>> Add libgitrs, libgitrs-sys, libgitrs-test, and libgitrs-sys-test targets\n>> to their respective Makefiles so they can be built and tested without\n>> having to run cargo build/test.\n>\n> I feel like clippy should be run as part of these somehow, but I'm not\n> sure where.\n>\n>> +libgitrs-sys:\n>> +\t$(QUIET)(\\\n>> +\t\tcd contrib/libgit-rs/libgit-sys && \\\n>> +\t\tcargo build \\\n>> +\t)\n>> +.PHONY: libgitrs\n>> +libgitrs:\n>> +\t$(QUIET)(\\\n>> +\t\tcd contrib/libgit-rs && \\\n>> +\t\tcargo build \\\n>> +\t)\n>\n> We should definitely be setting `RUSTFLAGS=-Dwarnings` as an analog to\n> `-Wall` in the C world, no? These crates should build without warnings.\n>\n> Very excited to see this work; thank you for putting the time into it\n> :-)\n\nThanks for helping.\n"},{"id":"502475","messageId":"xmqqcylcpnah.fsf@gitster.g","threadId":"61917","inReplyTo":"xmqqv7z8tjd7.fsf@gitster.g","subject":"Re: [PATCH v3 5/6] libgit: add higher-level libgit crate","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2024-09-09T20:47:18Z","receivedAt":"2024-09-09T20:47:21Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n>> +struct libgit_config_set *libgit_configset_alloc(void)\n>> +{\n>> +\treturn git_configset_alloc();\n>> +}\n>\n> git_configset_alloc() returns \"struct config_set *\" while this thing\n> returns an incompatible pointer.  \n>\n> Sent out an outdated version or something?  This wouldn't have\n> passed even a compile test, I suspect.\n\nThe \"shim\" layer should hide the details of interfacing to the git\nproper from callers, as well as it should hide the callers' from the\ngit proper.  So if you really want to hide \"struct config_set\" from\nyour library callers, you may need to do something like the\nattached, perhaps?  At least this does pass compilation test.\n\n .../libgit-rs/libgit-sys/public_symbol_export.h    | 11 +++++++++\n .../libgit-rs/libgit-sys/public_symbol_export.c    | 26 ++++++++++++----------\n 2 files changed, 25 insertions(+), 12 deletions(-)\n\ndiff --git c/contrib/libgit-rs/libgit-sys/public_symbol_export.h w/contrib/libgit-rs/libgit-sys/public_symbol_export.h\nindex 3933698976..70701ca63e 100644\n--- c/contrib/libgit-rs/libgit-sys/public_symbol_export.h\n+++ w/contrib/libgit-rs/libgit-sys/public_symbol_export.h\n@@ -1,6 +1,17 @@\n #ifndef PUBLIC_SYMBOL_EXPORT_H\n #define PUBLIC_SYMBOL_EXPORT_H\n \n+/* shim */\n+struct libgit_config_set {\n+\tstruct config_set cs;\n+\t/* \n+\t * the shim layer may want to add more members below, but then\n+\t * it may need to wrap underlying config_set differently,\n+\t * e.g., store a pointer of an allocated config_set in this\n+\t * shim structure.\n+\t */\n+};\n+\n const char *libgit_setup_git_directory(void);\n \n int libgit_config_get_int(const char *key, int *dest);\n\ndiff --git c/contrib/libgit-rs/libgit-sys/public_symbol_export.c w/contrib/libgit-rs/libgit-sys/public_symbol_export.c\nindex 07d6bfdd84..6f5eb3b249 100644\n--- c/contrib/libgit-rs/libgit-sys/public_symbol_export.c\n+++ w/contrib/libgit-rs/libgit-sys/public_symbol_export.c\n@@ -3,12 +3,13 @@\n // avoid conflicts with other libraries such as libgit2.\n \n #include \"git-compat-util.h\"\n-#include \"contrib/libgit-rs/libgit-sys/public_symbol_export.h\"\n #include \"common-init.h\"\n #include \"config.h\"\n #include \"setup.h\"\n #include \"version.h\"\n \n+#include \"contrib/libgit-rs/libgit-sys/public_symbol_export.h\"\n+\n extern struct repository *the_repository;\n \n #pragma GCC visibility push(default)\n@@ -35,32 +36,33 @@ int libgit_parse_maybe_bool(const char *val)\n \n struct libgit_config_set *libgit_configset_alloc(void)\n {\n-\treturn git_configset_alloc();\n+\tvoid *cs = git_configset_alloc();\n+\treturn (struct libgit_config_set *)cs;\n }\n \n-void libgit_configset_clear_and_free(struct libgit_config_set *cs)\n+void libgit_configset_clear_and_free(struct libgit_config_set *lgcs)\n {\n-\tgit_configset_clear_and_free(cs);\n+\tgit_configset_clear_and_free(&lgcs->cs);\n }\n \n-void libgit_configset_init(struct libgit_config_set *cs)\n+void libgit_configset_init(struct libgit_config_set *lgcs)\n {\n-\tgit_configset_init(cs);\n+\tgit_configset_init(&lgcs->cs);\n }\n \n-int libgit_configset_add_file(struct libgit_config_set *cs, const char *filename)\n+int libgit_configset_add_file(struct libgit_config_set *lgcs, const char *filename)\n {\n-\treturn git_configset_add_file(cs, filename);\n+\treturn git_configset_add_file(&lgcs->cs, filename);\n }\n \n-int libgit_configset_get_int(struct libgit_config_set *cs, const char *key, int *dest)\n+int libgit_configset_get_int(struct libgit_config_set *lgcs, const char *key, int *dest)\n {\n-\treturn git_configset_get_int(cs, key, dest);\n+\treturn git_configset_get_int(&lgcs->cs, key, dest);\n }\n \n-int libgit_configset_get_string(struct libgit_config_set *cs, const char *key, char **dest)\n+int libgit_configset_get_string(struct libgit_config_set *lgcs, const char *key, char **dest)\n {\n-\treturn git_configset_get_string(cs, key, dest);\n+\treturn git_configset_get_string(&lgcs->cs, key, dest);\n }\n \n const char *libgit_user_agent(void)\n"},{"id":"502542","messageId":"Zt_qM8ZGN4fnqUoY@pks.im","threadId":"61917","inReplyTo":"CAPig+cRKbvWT3Bx65-ULAYRSrrKTCL0C7r6xpNSdN5JjLBPjCQ@mail.gmail.com","subject":"Re: [PATCH v3 4/6] config: add git_configset_alloc() and git_configset_clear_and_free()","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2024-09-10T06:41:55Z","receivedAt":"2024-09-10T06:42:04Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"On Fri, Sep 06, 2024 at 07:24:22PM -0400, Eric Sunshine wrote:\n> On Fri, Sep 6, 2024 at 6:21 PM Calvin Wan <calvinwan@google.com> wrote:\n> > From: Josh Steadmon <steadmon@google.com>\n> >\n> > Add git_configset_alloc() and git_configset_clear_and_free() functions\n> > so that callers can manage config_set structs on the heap. This also\n> > allows non-C external consumers to treat config_sets as opaque structs.\n> >\n> > Co-authored-by: Calvin Wan <calvinwan@google.com>\n> > Signed-off-by: Calvin Wan <calvinwan@google.com>\n> \n> Almost all these patches suffer from some sort of missing Josh\n> trailer, so I'll stop mentioning it now.\n> \n> > diff --git a/config.h b/config.h\n> > @@ -472,6 +472,11 @@ struct config_set {\n> > +/**\n> > + * Alloc a config_set\n> > + */\n> > +struct config_set *git_configset_alloc(void);\n> \n> Should this documentation string mention that git_configset_alloc()\n> does _not_ initialize the configset? Alternatively, should this\n> function also initialize it as a convenience (and mention so in the\n> documentation)?\n\nI'd think so. I don't quite see the point in splitting this up into two\nseparate function calls. Is there ever a case where one wants to\nallocate the configset, but not use it?\n\nPatrick\n"},{"id":"502543","messageId":"Zt_qOrrWubdU_yvx@pks.im","threadId":"61917","inReplyTo":"20240906222116.270196-3-calvinwan@google.com","subject":"Re: [PATCH v3 3/6] libgit-sys: add repo initialization and config access","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2024-09-10T06:42:02Z","receivedAt":"2024-09-10T06:42:07Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"On Fri, Sep 06, 2024 at 10:21:13PM +0000, Calvin Wan wrote:\n> diff --git a/contrib/libgit-rs/libgit-sys/public_symbol_export.c b/contrib/libgit-rs/libgit-sys/public_symbol_export.c\n> index 39c27d9c1a..65d1620d28 100644\n> --- a/contrib/libgit-rs/libgit-sys/public_symbol_export.c\n> +++ b/contrib/libgit-rs/libgit-sys/public_symbol_export.c\n> @@ -2,11 +2,37 @@\n>  // original symbols can be hidden. Renaming these with a \"libgit_\" prefix also\n>  // avoid conflicts with other libraries such as libgit2.\n>  \n> +#include \"git-compat-util.h\"\n>  #include \"contrib/libgit-rs/libgit-sys/public_symbol_export.h\"\n> +#include \"common-init.h\"\n> +#include \"config.h\"\n> +#include \"setup.h\"\n>  #include \"version.h\"\n>  \n> +extern struct repository *the_repository;\n> +\n>  #pragma GCC visibility push(default)\n>  \n> +const char *libgit_setup_git_directory(void)\n> +{\n> +\treturn setup_git_directory();\n> +}\n> +\n> +int libgit_config_get_int(const char *key, int *dest)\n> +{\n> +\treturn repo_config_get_int(the_repository, key, dest);\n> +}\n> +\n> +void libgit_init_git(const char **argv)\n> +{\n> +\tinit_git(argv);\n> +}\n> +\n> +int libgit_parse_maybe_bool(const char *val)\n> +{\n> +\treturn git_parse_maybe_bool(val);\n> +}\n> +\n\nI don't quite get why we expose functionality that is inherently not\nlibified. Exposing the current state to library users certainly does not\nfeel right to me, doubly so because `the_repository` is deprecated and\nwill eventually go away. So we already know that we'll have to break the\nAPI here once that has happened. I'd rather want to see that introducing\nthe library makes us double down on providing properly encapsulated\ninterfaces from hereon.\n\nAlso, we already have ways to initialize a repository and read their\nconfig without relying on `the_repository`. So shouldn't we expose that\ninstead?\n\nPatrick\n"},{"id":"502555","messageId":"c8d65466-a9c3-4556-84c1-3daf0c7cea14@gmail.com","threadId":"61917","inReplyTo":"Zt_qM8ZGN4fnqUoY@pks.im","subject":"Re: [PATCH v3 4/6] config: add git_configset_alloc() and git_configset_clear_and_free()","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2024-09-10T08:50:25Z","receivedAt":"2024-09-10T08:50:31Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"On 10/09/2024 07:41, Patrick Steinhardt wrote:\n> Is there ever a case where one wants to\n> allocate the configset, but not use it?\n\nThat was my thought too - I suggested providing git_configset_new() that \nwould allocate and initialize a config set in my response to the last \nround [1]. It is good to see that the struct in now namespaced in the \nnext patch but separating out allocation and initialization makes the \napi harder to use than it needs to be. I'd also like to see \ngit_configset_clear_and_free() become git_configset_free().\n\nBest Wishes\n\nPhillip\n\n[1] \nhttps://lore.kernel.org/git/47b18fa4-f01b-4f42-8d04-9e145515ccc1@gmail.com\n"},{"id":"502560","messageId":"xmqqjzfjo9fp.fsf@gitster.g","threadId":"61917","inReplyTo":"c8d65466-a9c3-4556-84c1-3daf0c7cea14@gmail.com","subject":"Re: [PATCH v3 4/6] config: add git_configset_alloc() and git_configset_clear_and_free()","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2024-09-10T14:44:10Z","receivedAt":"2024-09-10T14:44:13Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Phillip Wood <phillip.wood123@gmail.com> writes:\n\n> On 10/09/2024 07:41, Patrick Steinhardt wrote:\n>> Is there ever a case where one wants to\n>> allocate the configset, but not use it?\n>\n> That was my thought too - I suggested providing git_configset_new()\n> that would allocate and initialize a config set in my response to the\n> last round [1]. It is good to see that the struct in now namespaced in\n> the next patch but separating out allocation and initialization makes\n> the api harder to use than it needs to be. I'd also like to see\n> git_configset_clear_and_free() become git_configset_free().\n\nMakes sense.\n"},{"id":"502581","messageId":"CAFySSZDFWinMHBjgLn-Vcc4U=_OHtTQc2UmWSyeo20C1TwLVoQ@mail.gmail.com","threadId":"61917","inReplyTo":"CAPig+cQbPCWS4LVF7cdw_DD6K8dsrLTCmmhqkmGZpz1CO93vRQ@mail.gmail.com","subject":"Re: [PATCH v3 2/6] libgit-sys: introduce Rust wrapper for libgit.a","fromName":"Calvin Wan","fromEmail":"calvinwan@google.com","sentAt":"2024-09-10T19:04:41Z","receivedAt":"2024-09-10T19:05:14Z","isPatch":true,"sender":{"key":"calvinwan@google.com","avatar":"https://avatars.githubusercontent.com/u/92547554?v=4"},"body":"Thanks for the catches -- will fix in the next reroll\n"},{"id":"502582","messageId":"CAFySSZBECCQafaLEv80WoK6SMovwC97-tf9gh_btPc+8OuP4NA@mail.gmail.com","threadId":"61917","inReplyTo":"xmqqcylcpnah.fsf@gitster.g","subject":"Re: [PATCH v3 5/6] libgit: add higher-level libgit crate","fromName":"Calvin Wan","fromEmail":"calvinwan@google.com","sentAt":"2024-09-10T19:04:37Z","receivedAt":"2024-09-10T19:05:14Z","isPatch":true,"sender":{"key":"calvinwan@google.com","avatar":"https://avatars.githubusercontent.com/u/92547554?v=4"},"body":"On Mon, Sep 9, 2024 at 1:47 PM Junio C Hamano <gitster@pobox.com> wrote:\n>\n> Junio C Hamano <gitster@pobox.com> writes:\n>\n> >> +struct libgit_config_set *libgit_configset_alloc(void)\n> >> +{\n> >> +    return git_configset_alloc();\n> >> +}\n> >\n> > git_configset_alloc() returns \"struct config_set *\" while this thing\n> > returns an incompatible pointer.\n> >\n> > Sent out an outdated version or something?  This wouldn't have\n> > passed even a compile test, I suspect.\n>\n> The \"shim\" layer should hide the details of interfacing to the git\n> proper from callers, as well as it should hide the callers' from the\n> git proper.  So if you really want to hide \"struct config_set\" from\n> your library callers, you may need to do something like the\n> attached, perhaps?  At least this does pass compilation test.\n\nYou're correct that this is supposed to be the shim layer, where\ncallers aren't supposed to be able to directly interface with the\nconfig_set struct. From Rust's perspective, all pointers that come\nback from C code are effectively void* so the naming doesn't make a\nfunctional difference for our use case (and consequently these\nwarnings did not show up from the build.rs script nor did the tests\nfail). However, I agree that the public interface should pass the\ncompilation test and your approach does that -- will reroll with those\nchanges and I believe that we should also fix the build.rs so that\nwarnings also show up during cargo build.\n"},{"id":"502583","messageId":"CAFySSZBr8wEP=Ge-p1rpfP+4NEdyguvAnw9aGo1YBxY2k-ZUpg@mail.gmail.com","threadId":"61917","inReplyTo":"m0seubo5q7.fsf@epic96565.epic.com","subject":"Re: [PATCH v3 6/6] Makefile: add option to build and test libgit-rs and libgit-rs-sys","fromName":"Calvin Wan","fromEmail":"calvinwan@google.com","sentAt":"2024-09-10T19:12:48Z","receivedAt":"2024-09-10T19:13:03Z","isPatch":true,"sender":{"key":"calvinwan@google.com","avatar":"https://avatars.githubusercontent.com/u/92547554?v=4"},"body":"On Sat, Sep 7, 2024 at 8:15 AM Sean Allred <allred.sean@gmail.com> wrote:\n>\n> Calvin Wan <calvinwan@google.com> writes:\n> > Add libgitrs, libgitrs-sys, libgitrs-test, and libgitrs-sys-test targets\n> > to their respective Makefiles so they can be built and tested without\n> > having to run cargo build/test.\n>\n> I feel like clippy should be run as part of these somehow, but I'm not\n> sure where.\n>\n> > +libgitrs-sys:\n> > +     $(QUIET)(\\\n> > +             cd contrib/libgit-rs/libgit-sys && \\\n> > +             cargo build \\\n> > +     )\n> > +.PHONY: libgitrs\n> > +libgitrs:\n> > +     $(QUIET)(\\\n> > +             cd contrib/libgit-rs && \\\n> > +             cargo build \\\n> > +     )\n>\n> We should definitely be setting `RUSTFLAGS=-Dwarnings` as an analog to\n> `-Wall` in the C world, no? These crates should build without warnings.\n>\n> Very excited to see this work; thank you for putting the time into it\n> :-)\n>\n> --\n> Sean Allred\n\nThanks for the suggestions! Will look into both for the next reroll.\n"},{"id":"502584","messageId":"xmqqttene2ya.fsf@gitster.g","threadId":"61917","inReplyTo":"CAFySSZBECCQafaLEv80WoK6SMovwC97-tf9gh_btPc+8OuP4NA@mail.gmail.com","subject":"Re: [PATCH v3 5/6] libgit: add higher-level libgit crate","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2024-09-10T19:14:21Z","receivedAt":"2024-09-10T19:14:24Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Calvin Wan <calvinwan@google.com> writes:\n\n> However, I agree that the public interface should pass the\n> compilation test and your approach does that -- will reroll with those\n> changes and I believe that we should also fix the build.rs so that\n> warnings also show up during cargo build.\n\nThanks.  I couldn't quite tell if *.c was supposed to be compilable\ninto *.o directly (if not, then Makefile needs fixing), and I am OK,\nif the shim layer is only internally used, if the public.h defined\nall pointers as \"void *\".\n\nI wasn't happy to cast \"struct config_set *\" between \"struct\nlibgit_config_set *\" merely because one embeds the other without\nadding any other member.  If they have to be bit-for-bit identical,\nshouldn't we just use the real name of the struct everywhere?\n\nThe situation might become different if you address others' comments\nlike \"why do we want allocation-only interface exposed?  shoudln't\nit also initialize?\" etc.\n\nThanks.\n\n\n\n"},{"id":"502585","messageId":"CAFySSZDj2wOmzGMvnCFgVLXQK97Q01R4QJRPbMbaTwZ4h3h8kg@mail.gmail.com","threadId":"61917","inReplyTo":"c8d65466-a9c3-4556-84c1-3daf0c7cea14@gmail.com","subject":"Re: [PATCH v3 4/6] config: add git_configset_alloc() and git_configset_clear_and_free()","fromName":"Calvin Wan","fromEmail":"calvinwan@google.com","sentAt":"2024-09-10T19:26:16Z","receivedAt":"2024-09-10T19:26:29Z","isPatch":true,"sender":{"key":"calvinwan@google.com","avatar":"https://avatars.githubusercontent.com/u/92547554?v=4"},"body":"On Tue, Sep 10, 2024 at 1:50 AM Phillip Wood <phillip.wood123@gmail.com> wrote:\n>\n> On 10/09/2024 07:41, Patrick Steinhardt wrote:\n> > Is there ever a case where one wants to\n> > allocate the configset, but not use it?\n>\n> That was my thought too - I suggested providing git_configset_new() that\n> would allocate and initialize a config set in my response to the last\n> round [1]. It is good to see that the struct in now namespaced in the\n> next patch but separating out allocation and initialization makes the\n> api harder to use than it needs to be. I'd also like to see\n> git_configset_clear_and_free() become git_configset_free().\n>\n> Best Wishes\n>\n> Phillip\n>\n> [1]\n> https://lore.kernel.org/git/47b18fa4-f01b-4f42-8d04-9e145515ccc1@gmail.com\n\nAgreed, it doesn't make sense to have both alloc() and init() when\nthey can be combined and same with renaming\ngit_configset_clear_and_free() to git_configset_free().\n"},{"id":"502774","messageId":"ZuSMFAyoD0CIfE3H@tapette.crustytoothpaste.net","threadId":"61917","inReplyTo":"m0seubo5q7.fsf@epic96565.epic.com","subject":"Re: [PATCH v3 6/6] Makefile: add option to build and test libgit-rs and libgit-rs-sys","fromName":"brian m. carlson","fromEmail":"sandals@crustytoothpaste.net","sentAt":"2024-09-13T19:01:40Z","receivedAt":"2024-09-13T19:01:44Z","isPatch":true,"sender":{"key":"sandals@crustytoothpaste.net","avatar":"https://avatars.githubusercontent.com/u/497054?v=4"},"body":"On 2024-09-07 at 15:15:12, Sean Allred wrote:\n> Calvin Wan <calvinwan@google.com> writes:\n> > Add libgitrs, libgitrs-sys, libgitrs-test, and libgitrs-sys-test targets\n> > to their respective Makefiles so they can be built and tested without\n> > having to run cargo build/test.\n> \n> I feel like clippy should be run as part of these somehow, but I'm not\n> sure where.\n\nYes, that seems like a good idea in CI.\n\n> > +libgitrs-sys:\n> > +\t$(QUIET)(\\\n> > +\t\tcd contrib/libgit-rs/libgit-sys && \\\n> > +\t\tcargo build \\\n> > +\t)\n> > +.PHONY: libgitrs\n> > +libgitrs:\n> > +\t$(QUIET)(\\\n> > +\t\tcd contrib/libgit-rs && \\\n> > +\t\tcargo build \\\n> > +\t)\n> \n> We should definitely be setting `RUSTFLAGS=-Dwarnings` as an analog to\n> `-Wall` in the C world, no? These crates should build without warnings.\n\nI believe -Dwarnings turns warnings into errors (at least it does in my\ntests), which is equivalent to -Werror.  We don't want that because it\nbreaks compiling older code with newer versions of the compiler, which\nmakes it harder to bisect changes or compiler on the system compiler (or\nsometimes, other architectures or OSes).\n\nThat would be fine for clippy, however, because that would only run in\nCI, where we _would_ want to catch newer changes, but we want to\ncompile nonetheless.\n-- \nbrian m. carlson (they/them or he/him)\nToronto, Ontario, CA\n"},{"id":"502971","messageId":"xlmgclzqydsyubjegobrjwhiqkb5b4fxnxciqz3ys4yyu3a3od@kkc77fobimkm","threadId":"61917","inReplyTo":"CAFySSZBECCQafaLEv80WoK6SMovwC97-tf9gh_btPc+8OuP4NA@mail.gmail.com","subject":"Re: [PATCH v3 5/6] libgit: add higher-level libgit crate","fromName":"Josh Steadmon","fromEmail":"steadmon@google.com","sentAt":"2024-09-17T21:37:32Z","receivedAt":"2024-09-17T21:37:39Z","isPatch":true,"sender":{"key":"steadmon@google.com","avatar":"https://avatars.githubusercontent.com/u/2654920?v=4"},"body":"On 2024.09.10 12:04, Calvin Wan wrote:\n> On Mon, Sep 9, 2024 at 1:47 PM Junio C Hamano <gitster@pobox.com> wrote:\n> >\n> > Junio C Hamano <gitster@pobox.com> writes:\n> >\n> > >> +struct libgit_config_set *libgit_configset_alloc(void)\n> > >> +{\n> > >> +    return git_configset_alloc();\n> > >> +}\n> > >\n> > > git_configset_alloc() returns \"struct config_set *\" while this thing\n> > > returns an incompatible pointer.\n> > >\n> > > Sent out an outdated version or something?  This wouldn't have\n> > > passed even a compile test, I suspect.\n> >\n> > The \"shim\" layer should hide the details of interfacing to the git\n> > proper from callers, as well as it should hide the callers' from the\n> > git proper.  So if you really want to hide \"struct config_set\" from\n> > your library callers, you may need to do something like the\n> > attached, perhaps?  At least this does pass compilation test.\n> \n> You're correct that this is supposed to be the shim layer, where\n> callers aren't supposed to be able to directly interface with the\n> config_set struct. From Rust's perspective, all pointers that come\n> back from C code are effectively void* so the naming doesn't make a\n> functional difference for our use case (and consequently these\n> warnings did not show up from the build.rs script nor did the tests\n> fail). However, I agree that the public interface should pass the\n> compilation test and your approach does that -- will reroll with those\n> changes and I believe that we should also fix the build.rs so that\n> warnings also show up during cargo build.\n\nYeah, cargo will complain if DEVELOPER=1 is set. We should probably\nenforce that in build.rs.\n"},{"id":"502973","messageId":"honvpowfa6zze7p56pcefrzokjjawcc43du7vuxbdbjbv2vzlv@eskr2npegzxd","threadId":"61917","inReplyTo":"xmqqttene2ya.fsf@gitster.g","subject":"Re: [PATCH v3 5/6] libgit: add higher-level libgit crate","fromName":"Josh Steadmon","fromEmail":"steadmon@google.com","sentAt":"2024-09-17T22:29:25Z","receivedAt":"2024-09-17T22:29:31Z","isPatch":true,"sender":{"key":"steadmon@google.com","avatar":"https://avatars.githubusercontent.com/u/2654920?v=4"},"body":"On 2024.09.10 12:14, Junio C Hamano wrote:\n> Calvin Wan <calvinwan@google.com> writes:\n> \n> > However, I agree that the public interface should pass the\n> > compilation test and your approach does that -- will reroll with those\n> > changes and I believe that we should also fix the build.rs so that\n> > warnings also show up during cargo build.\n> \n> Thanks.  I couldn't quite tell if *.c was supposed to be compilable\n> into *.o directly (if not, then Makefile needs fixing), and I am OK,\n> if the shim layer is only internally used, if the public.h defined\n> all pointers as \"void *\".\n> \n> I wasn't happy to cast \"struct config_set *\" between \"struct\n> libgit_config_set *\" merely because one embeds the other without\n> adding any other member.  If they have to be bit-for-bit identical,\n> shouldn't we just use the real name of the struct everywhere?\n\nWe want to namespace types as well as functions, as Phillip pointed out\nin 47b18fa4-f01b-4f42-8d04-9e145515ccc1@gmail.com.\n\nIs there a reason why we need the shim struct from your\nxmqqcylcpnah.fsf@gitster.g and can't just cast directly like so:\n\n contrib/libgit-rs/libgit-sys/public_symbol_export.c | 15 ++++++++-------\n 1 file changed, 8 insertions(+), 7 deletions(-)\n\ndiff --git a/contrib/libgit-rs/libgit-sys/public_symbol_export.c b/contrib/libgit-rs/libgit-sys/public_symbol_export.c\nindex 07d6bfdd84..c96fa15ab6 100644\n--- a/contrib/libgit-rs/libgit-sys/public_symbol_export.c\n+++ b/contrib/libgit-rs/libgit-sys/public_symbol_export.c\n@@ -3,12 +3,13 @@\n // avoid conflicts with other libraries such as libgit2.\n\n #include \"git-compat-util.h\"\n-#include \"contrib/libgit-rs/libgit-sys/public_symbol_export.h\"\n #include \"common-init.h\"\n #include \"config.h\"\n #include \"setup.h\"\n #include \"version.h\"\n\n+#include \"contrib/libgit-rs/libgit-sys/public_symbol_export.h\"\n+\n extern struct repository *the_repository;\n\n #pragma GCC visibility push(default)\n@@ -35,32 +36,32 @@ int libgit_parse_maybe_bool(const char *val)\n\n struct libgit_config_set *libgit_configset_alloc(void)\n {\n-       return git_configset_alloc();\n+       return (struct libgit_config_set *) git_configset_alloc();\n }\n\n void libgit_configset_clear_and_free(struct libgit_config_set *cs)\n {\n-       git_configset_clear_and_free(cs);\n+       git_configset_clear_and_free((struct config_set *) cs);\n }\n\n void libgit_configset_init(struct libgit_config_set *cs)\n {\n-       git_configset_init(cs);\n+       git_configset_init((struct config_set *) cs);\n }\n\n int libgit_configset_add_file(struct libgit_config_set *cs, const char *filename)\n {\n-       return git_configset_add_file(cs, filename);\n+       return git_configset_add_file((struct config_set *) cs, filename);\n }\n\n int libgit_configset_get_int(struct libgit_config_set *cs, const char *key, int *dest)\n {\n-       return git_configset_get_int(cs, key, dest);\n+       return git_configset_get_int((struct config_set *) cs, key, dest);\n }\n\n int libgit_configset_get_string(struct libgit_config_set *cs, const char *key, char **dest)\n {\n-       return git_configset_get_string(cs, key, dest);\n+       return git_configset_get_string((struct config_set *) cs, key, dest);\n }\n\n const char *libgit_user_agent(void)\n"},{"id":"503006","messageId":"xmqqo74kj4ys.fsf@gitster.g","threadId":"61917","inReplyTo":"honvpowfa6zze7p56pcefrzokjjawcc43du7vuxbdbjbv2vzlv@eskr2npegzxd","subject":"Re: [PATCH v3 5/6] libgit: add higher-level libgit crate","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2024-09-18T16:34:35Z","receivedAt":"2024-09-18T16:34:44Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Josh Steadmon <steadmon@google.com> writes:\n\n> We want to namespace types as well as functions, as Phillip pointed out\n> in 47b18fa4-f01b-4f42-8d04-9e145515ccc1@gmail.com.\n>\n> Is there a reason why we need the shim struct from your\n> xmqqcylcpnah.fsf@gitster.g and can't just cast directly like so:\n> ...\n>  int libgit_configset_get_string(struct libgit_config_set *cs, const char *key, char **dest)\n>  {\n> -       return git_configset_get_string(cs, key, dest);\n> +       return git_configset_get_string((struct config_set *) cs, key, dest);\n>  }\n\nNot at all.  I just didn't see your intentions in the patch if all\nyou wanted to do was merely to flip names, or wanted to leave the\npossibility to allow the wrapped ones to optionally have different\nshape (e.g. for bookkeeping purposes for either the host environment\nor the shim layer).  If it is merely \"we do not want to expose these\nnames but we want bit-for-bit identical data\", then you do not need\nextra logic at all---the casts would be suffficient[*].\n\nPS. I am not feeling well today, so please expect delayed and/or\nsparse responses.\n\n\n[Footnote]\n\n* Building objects that go to libgit.a, partially linking them to\n  resolve internal references, and then rewriting the symbol table\n  of the resulting relocatable object file to expose only the entry\n  points and data you want to show to the rust world to whatever\n  names you want, would be a less gross solution, I would imagine.\n  You only then need to write a (fake) public_symbol_export.h file\n  that will never has to be seen on the C side, but to be seen as\n  the header to describe that C library to the rust side (and\n  obviously you do not need public_symbol_export.c file only to keep\n  these casts).\n\n\n\n\n"},{"id":"503028","messageId":"e5bl6h4jpw76tgr2eobqhyoq7nozkbz3p7u5ljfrwlgir7ycdu@ppxdg2tdmgly","threadId":"61917","inReplyTo":"CAPig+cQbPCWS4LVF7cdw_DD6K8dsrLTCmmhqkmGZpz1CO93vRQ@mail.gmail.com","subject":"Re: [PATCH v3 2/6] libgit-sys: introduce Rust wrapper for libgit.a","fromName":"Josh Steadmon","fromEmail":"steadmon@google.com","sentAt":"2024-09-18T21:14:30Z","receivedAt":"2024-09-18T21:14:36Z","isPatch":true,"sender":{"key":"steadmon@google.com","avatar":"https://avatars.githubusercontent.com/u/2654920?v=4"},"body":"On 2024.09.06 18:39, Eric Sunshine wrote:\n> On Fri, Sep 6, 2024 at 6:21 PM Calvin Wan <calvinwan@google.com> wrote:\n> > From: Josh Steadmon <steadmon@google.com>\n> \n> This is curious...\n> \n> > Co-authored-by: Kyle Lippincott <spectral@google.com>\n> > Co-authored-by: Calvin Wan <calvinwan@google.com>\n> > Signed-off-by: Calvin Wan <calvinwan@google.com>\n> > Signed-off-by: Kyle Lippincott <spectral@google.com>\n> \n> ... since there is no mention of Josh here.\n> \n> > diff --git a/contrib/libgit-rs/libgit-sys/Cargo.toml b/contrib/libgit-rs/libgit-sys/Cargo.toml\n> > @@ -0,0 +1,12 @@\n> > +[package]\n> > +name = \"libgit-sys\"\n> > [...]\n> > +[dependencies]\n> > +libz-sys = \"1.1.19\"\n> > \\ No newline at end of file\n> \n> Let's give this file a proper line terminator.\n\nFixed in V4.\n\n> > diff --git a/contrib/libgit-rs/libgit-sys/build.rs b/contrib/libgit-rs/libgit-sys/build.rs\n> > @@ -0,0 +1,31 @@\n> > +pub fn main() -> std::io::Result<()> {\n> > +    let crate_root = PathBuf::from(env::var_os(\"CARGO_MANIFEST_DIR\").unwrap());\n> > +    let git_root = crate_root.join(\"../../..\");\n> > +    let dst = PathBuf::from(env::var_os(\"OUT_DIR\").unwrap());\n> > +\n> > +    let make_output = std::process::Command::new(\"make\")\n> \n> Providing a mechanism for people to override this hardcoded spelling\n> of \"make\" could be another item for your NEEDSWORK list; in\n> particular, I'm thinking about platforms on which GNU \"make\" is\n> installed as \"gmake\".\n\nAck, will use make_cmd as Mike suggests.\n\n> > diff --git a/contrib/libgit-rs/libgit-sys/public_symbol_export.c b/contrib/libgit-rs/libgit-sys/public_symbol_export.c\n> > @@ -0,0 +1,20 @@\n> > +// Shim to publicly export Git symbols. These must be renamed so that the\n> > +// original symbols can be hidden. Renaming these with a \"libgit_\" prefix also\n> > +// avoid conflicts with other libraries such as libgit2.\n> \n> s/avoid/avoids/\n\nFixed in V4.\n\nThanks for the review!\n"},{"id":"503029","messageId":"bl55ecnyupy4f2ypxn445apmkvpogkwcer2ckq7dlmjk3mgfrz@of5ucc3x2kv5","threadId":"61917","inReplyTo":"20240906230419.r4hej7qsmx7yasl7@glandium.org","subject":"Re: [PATCH v3 2/6] libgit-sys: introduce Rust wrapper for libgit.a","fromName":"Josh Steadmon","fromEmail":"steadmon@google.com","sentAt":"2024-09-18T21:14:51Z","receivedAt":"2024-09-18T21:14:57Z","isPatch":true,"sender":{"key":"steadmon@google.com","avatar":"https://avatars.githubusercontent.com/u/2654920?v=4"},"body":"On 2024.09.07 08:04, Mike Hommey wrote:\n> On Fri, Sep 06, 2024 at 06:39:17PM -0400, Eric Sunshine wrote:\n> > > diff --git a/contrib/libgit-rs/libgit-sys/build.rs b/contrib/libgit-rs/libgit-sys/build.rs\n> > > @@ -0,0 +1,31 @@\n> > > +pub fn main() -> std::io::Result<()> {\n> > > +    let crate_root = PathBuf::from(env::var_os(\"CARGO_MANIFEST_DIR\").unwrap());\n> > > +    let git_root = crate_root.join(\"../../..\");\n> > > +    let dst = PathBuf::from(env::var_os(\"OUT_DIR\").unwrap());\n> > > +\n> > > +    let make_output = std::process::Command::new(\"make\")\n> > \n> > Providing a mechanism for people to override this hardcoded spelling\n> > of \"make\" could be another item for your NEEDSWORK list; in\n> > particular, I'm thinking about platforms on which GNU \"make\" is\n> > installed as \"gmake\".\n> \n> And/or, use the `make_cmd` crate.\n\nWill do, thanks!\n"},{"id":"503030","messageId":"pci7ayjv2llvcj6yjtupxwslaa7gy4fx4tnxuenavlgn2xer5v@vcestj4xvbff","threadId":"61917","inReplyTo":"CAPig+cQDa7pw5p22CQJZsm8BF_0b6MMyoknAAkFO2zcrPciSiw@mail.gmail.com","subject":"Re: [PATCH v3 3/6] libgit-sys: add repo initialization and config access","fromName":"Josh Steadmon","fromEmail":"steadmon@google.com","sentAt":"2024-09-18T21:33:25Z","receivedAt":"2024-09-18T21:33:31Z","isPatch":true,"sender":{"key":"steadmon@google.com","avatar":"https://avatars.githubusercontent.com/u/2654920?v=4"},"body":"On 2024.09.06 18:53, Eric Sunshine wrote:\n> On Fri, Sep 6, 2024 at 6:21 PM Calvin Wan <calvinwan@google.com> wrote:\n> > Wrap a few repo setup and config access functions in libgit-sys. These\n> > were selected as proof-of-concept items to show that we can access local\n> > config from Rust.\n> >\n> > Co-authored-by: Josh Steadmon <steadmon@google.com>\n> > Signed-off-by: Calvin Wan <calvinwan@google.com>\n> \n> Josh's sign-off is missing?\n> \n> > ---\n> > diff --git a/contrib/libgit-rs/libgit-sys/src/lib.rs b/contrib/libgit-rs/libgit-sys/src/lib.rs\n> > @@ -1,8 +1,19 @@\n> >  extern \"C\" {\n> > +    pub fn libgit_setup_git_directory() -> *const c_char;\n> > +\n> > +    // From config.c\n> > +    pub fn libgit_config_get_int(key: *const c_char, dest: *mut c_int) -> c_int;\n> \n> Perhaps add a comment above libgit_setup_git_directory() stating its\n> origin, as you do for the other functions mentioned here?\n> \n>     // From setup.c\n>     pub fn libgit_setup_git_directory() -> *const c_char;\n> \n> (Nit: I would probably drop the word \"From\" from these comments, as it\n> doesn't seem to add value and ends up being noise. Even better, drop\n> the comments altogether since they don't really add value and can\n> easily become outdated if code is ever moved around.)\n\nRemoved in V4.\n\n> > +\n> > +    // From common-init.c\n> > +    pub fn libgit_init_git(argv: *const *const c_char);\n> > +\n> > +    // From parse.c\n> > +    pub fn libgit_parse_maybe_bool(val: *const c_char) -> c_int;\n"},{"id":"503031","messageId":"by74rpfxx2wv5nicazhhzel6hivogmajcro6736dj2zzpwadbv@h6rzhjrvd6bq","threadId":"61917","inReplyTo":"xmqq8qw4uz1q.fsf@gitster.g","subject":"Re: [PATCH v3 3/6] libgit-sys: add repo initialization and config access","fromName":"Josh Steadmon","fromEmail":"steadmon@google.com","sentAt":"2024-09-18T21:33:59Z","receivedAt":"2024-09-18T21:34:06Z","isPatch":true,"sender":{"key":"steadmon@google.com","avatar":"https://avatars.githubusercontent.com/u/2654920?v=4"},"body":"On 2024.09.06 16:45, Junio C Hamano wrote:\n> Calvin Wan <calvinwan@google.com> writes:\n> \n> > Wrap a few repo setup and config access functions in libgit-sys. These\n> > were selected as proof-of-concept items to show that we can access local\n> > config from Rust.\n> >\n> > Co-authored-by: Josh Steadmon <steadmon@google.com>\n> > Signed-off-by: Calvin Wan <calvinwan@google.com>\n> > Change-Id: I6dd886af8c63e1f0f3251064cd8903aecdf768bb\n> > ---\n> \n> \n> Common to all other steps, I suspect that you meant to but forgot to\n> strip out the Change-Id: thing here?\n> \n> Also there are a few whitespace breakages in this step.\n> \n> Applying: libgit-sys: add repo initialization and config access\n> .git/rebase-apply/patch:129: trailing whitespace.\n>          * for the existence of a key rather than a specific value\n> .git/rebase-apply/patch:138: trailing whitespace.\n>         unsafe {\n> .git/rebase-apply/patch:143: trailing whitespace.\n>         unsafe {\n> warning: 3 lines add whitespace errors.\n> \n> \n> There is another one in a later step.\n> \n> Applying: libgit: add higher-level libgit crate\n> .git/rebase-apply/patch:325: trailing whitespace.\n>         // ConfigSet retrieves correct value\n> warning: 1 line adds whitespace errors.\n\nFixed the whitespace errors in V4.\n"},{"id":"503983","messageId":"lx73jl2jxnkvr3ldx577374bhyagowet6zpcart46mxyd2ovlr@utwdnyzcsxv3","threadId":"61917","inReplyTo":"5720d5b9-a850-4024-a1fd-54acc6b15a74@gmail.com","subject":"Re: [PATCH v2 4/5] config: add git_configset_alloc() and git_configset_clear_and_free()","fromName":"Josh Steadmon","fromEmail":"steadmon@google.com","sentAt":"2024-10-02T22:12:54Z","receivedAt":"2024-10-02T22:13:03Z","isPatch":true,"sender":{"key":"steadmon@google.com","avatar":"https://avatars.githubusercontent.com/u/2654920?v=4"},"body":"On 2024.08.13 10:48, phillip.wood123@gmail.com wrote:\n> Hi Josh\n> \n> On 12/08/2024 22:39, Josh Steadmon wrote:\n> > On 2024.08.12 10:10, Phillip Wood wrote:\n> > > Hi Josh\n> > > \n> > > Do we really need to add this code to config.c rather than handling it in\n> > > the wrapper layer in the next patch?\n> > > \n> > > Looking ahead I wonder how useful it is to users of the library to separate\n> > > out allocation from initialization. A function that allocates and\n> > > initializes a configset would be more convenient and harder to misuse.\n> > > Calling release functions *_free() rather than *_clear_and_free() would be\n> > > more convenient as well. I also noticed that the data types are not\n> > > namespaced when they are exported. So perhaps we could drop this patch and\n> > > add the following to the next patch.\n> > > \n> > > /*\n> > >   * Namespace data types as well as functions and ensure consistent\n> > >   * naming of data types and the functions that operate on them.\n> > >   */\n> > > struct libgit_configset {\n> > > \tstruct config_set set;\n> > > };\n> > > \n> > > struct libgit_configset *libgit_configset_new() {\n> > > \tstruct libgit_configset *set = xmalloc(sizeof(*set));\n> > > \n> > > \tgit_configset_init(&set->set);\n> > > \treturn set;\n> > > }\n> > > \n> > > void libgit_configset_free(struct libgit_configset *set) {\n> > > \tgit_configset_clear(&set->set);\n> > > \tfree(set);\n> > > }\n> > \n> > Hmmm I see your point, but I am also hoping to keep the symbol export\n> > shim as small as possible, so that we can try to autogenerate it rather\n> > than add entries by hand.\n> \n> That's a nice aim - how do you plan to address namespacing data types with\n> that approach? The autogenerator would need to know \"struct config_set\"\n> wants to be wrapped as \"struct libgit_configset\" to ensure we end up with\n> consistent naming for the data type and its functions. We'd also still want\n> to make sure we end up with an ergonomic api which means one function to\n> allocate and initialize each data type, not separate functions for\n> allocation and initialization.\n> \n> > However, if people feel strongly that we don't\n> > want to add helper functions like *_alloc() or *_free() for types that don't\n> > already have them upstream, perhaps we can just put them in a separate\n> > rust-helpers.c file or something.\n> \n> If we're not using them upstream they're just clutter as far as git is\n> concerned. Having said that if you get the autogeneration working well so\n> the output is usable without a lot of manual tweaking I can see an argument\n> for having these functions upstream.\n> \n> Best Wishes\n> \n> Phillip\n\nFor V4 I'm going to drop the ambition to autogenerate the shim library,\nand as such there's no longer a reason to keep helper functions out of\nthe shim library. So I've moved the _alloc and _free functions into the\nshim.\n\nFor namespacing data types: for now I'm just letting the compiler infer\nan opaque \"struct libgit_config_set\" and manually cast back to \"struct\nconfig_set\" when we cross the boundary from the shim library back to\nlibgit.a. I'm not sure this is the right approach, but it avoids having\nto wrap a single pointer in a separate struct.\n"},{"id":"503985","messageId":"ypu7w5bptyiporstbffps5xsdc4qusum3vlcmysqznegfr67gr@sqm2mi2xx572","threadId":"61917","inReplyTo":"Zr827Bd5jrQwhIEp@tanuki","subject":"Re: [PATCH v2 4/5] config: add git_configset_alloc() and git_configset_clear_and_free()","fromName":"Josh Steadmon","fromEmail":"steadmon@google.com","sentAt":"2024-10-02T22:17:59Z","receivedAt":"2024-10-02T22:18:06Z","isPatch":true,"sender":{"key":"steadmon@google.com","avatar":"https://avatars.githubusercontent.com/u/2654920?v=4"},"body":"On 2024.08.16 13:24, Patrick Steinhardt wrote:\n> On Mon, Aug 12, 2024 at 10:10:48AM +0100, Phillip Wood wrote:\n> > Hi Josh\n> > \n> > On 09/08/2024 23:41, Josh Steadmon wrote:\n> > > Add git_configset_alloc() and git_configset_clear_and_free() functions\n> > > so that callers can manage config_set structs on the heap. This also\n> > > allows non-C external consumers to treat config_sets as opaque structs.\n> > \n> > Do we really need to add this code to config.c rather than handling it in\n> > the wrapper layer in the next patch?\n> > \n> > Looking ahead I wonder how useful it is to users of the library to separate\n> > out allocation from initialization. A function that allocates and\n> > initializes a configset would be more convenient and harder to misuse.\n> > Calling release functions *_free() rather than *_clear_and_free() would be\n> > more convenient as well. I also noticed that the data types are not\n> > namespaced when they are exported. So perhaps we could drop this patch and\n> > add the following to the next patch.\n> \n> Agreed. This is also part of our coding guidelines as of 10f0723c8d\n> (Documentation: document idiomatic function names, 2024-07-30).\n> \n> Patrick\n\nFixed in V4.\n"},{"id":"503987","messageId":"sh6cwo4uzgp5bvlctmdkhwhjum2zc36cpe74ygy725nr3invxe@z6ee2shr4cf3","threadId":"61917","inReplyTo":"9c6d8fea-997e-49d5-9195-e1721a750a36@gmail.com","subject":"Re: [PATCH v2 5/5] cgit: add higher-level cgit crate","fromName":"Josh Steadmon","fromEmail":"steadmon@google.com","sentAt":"2024-10-02T22:31:33Z","receivedAt":"2024-10-02T22:31:39Z","isPatch":true,"sender":{"key":"steadmon@google.com","avatar":"https://avatars.githubusercontent.com/u/2654920?v=4"},"body":"On 2024.08.22 10:12, Phillip Wood wrote:\n> Hi Calvin\n> \n> On 21/08/2024 19:46, Calvin Wan wrote:\n> > Phillip Wood <phillip.wood123@gmail.com> writes:\n> >\n> > > before the function declarations. As I said in my comments on the last\n> > > patch I think we'd be better to namespace our types as well as our\n> > > functions in this library layer so this can be resued by other language\n> > > bindings.\n> > \n> > Are you suggesting something like \"#define libgit_config_set\n> > config_set\"? I wouldn't be comfortable renaming config_set in git.git\n> > just yet until config/config_set can be a standalone library by itself.\n> \n> I was suggesting[1] adding\n> \n>     struct libgit_configset {\n> \t    struct config_set set;\n>     };\n> \n> to public_symbol_export.c and rewriting the wrappers in that file to use\n> this struct e.g.\n> \n>     int libgit_configset_get_int(struct libgit_configset *cs,\n> \t\t\t\t const char *key, int *dest)\n>     {\n> \t    return git_configset_get_int(&cs.set, key, dest);\n>     }\n> \n> In public_symbol_export.h we'd then have\n> \n>     struct libgit_configset;\n> \n>     int libgit_configset_get_int(struct libgit_configset *,\n> \t\t\t\t const char *, int *);\n> \n> If we want the symbol exports to be useful outside of the rust bindings I\n> think we need to namespace our types as well as our functions.\n> \n> [1]\n> https://lore.kernel.org/git/5720d5b9-a850-4024-a1fd-54acc6b15a74@gmail.com\n> \n> > > > +    pub fn get_str(&mut self, key: &str) -> Option<CString> {\n> > > \n> > > If we're adding an ergonomic api then having return CString isn't ideal.\n> > > I think the equivalent function in libgit2-rs has variants that return a\n> > > String which is convinent if the caller is expecting utf8 values or\n> > > Vec<u8> for non-utf8 values.\n> > \n> > Having both get_cstr() and get_str() makes sense to me.\n> \n> Just to be clear get_cstr() would return Vec<u8>? I'm far from a rust expert\n> but my understanding was that crates that wrap C libraries generally avoid\n> using CString in their API.\n> \n> Best Wishes\n> \n> Phillip\n\nYeah let's just go with \"get_string()\" and return an Option<String>.\n"},{"id":"503997","messageId":"g5h3agfmrsves4lazgohzihkqmao47obr2deh2t3nxlauwxj4s@p5fqzqwfcvpo","threadId":"61917","inReplyTo":"CAPig+cRKbvWT3Bx65-ULAYRSrrKTCL0C7r6xpNSdN5JjLBPjCQ@mail.gmail.com","subject":"Re: [PATCH v3 4/6] config: add git_configset_alloc() and git_configset_clear_and_free()","fromName":"Josh Steadmon","fromEmail":"steadmon@google.com","sentAt":"2024-10-02T22:45:29Z","receivedAt":"2024-10-02T22:45:36Z","isPatch":true,"sender":{"key":"steadmon@google.com","avatar":"https://avatars.githubusercontent.com/u/2654920?v=4"},"body":"On 2024.09.06 19:24, Eric Sunshine wrote:\n> On Fri, Sep 6, 2024 at 6:21 PM Calvin Wan <calvinwan@google.com> wrote:\n> > From: Josh Steadmon <steadmon@google.com>\n> >\n> > Add git_configset_alloc() and git_configset_clear_and_free() functions\n> > so that callers can manage config_set structs on the heap. This also\n> > allows non-C external consumers to treat config_sets as opaque structs.\n> >\n> > Co-authored-by: Calvin Wan <calvinwan@google.com>\n> > Signed-off-by: Calvin Wan <calvinwan@google.com>\n> \n> Almost all these patches suffer from some sort of missing Josh\n> trailer, so I'll stop mentioning it now.\n> \n> > diff --git a/config.h b/config.h\n> > @@ -472,6 +472,11 @@ struct config_set {\n> > +/**\n> > + * Alloc a config_set\n> > + */\n> > +struct config_set *git_configset_alloc(void);\n> \n> Should this documentation string mention that git_configset_alloc()\n> does _not_ initialize the configset? Alternatively, should this\n> function also initialize it as a convenience (and mention so in the\n> documentation)?\n\nYes, in V4 this will both alloc and init the configset (and the function\nhas been moved to the shim library).\n"},{"id":"503999","messageId":"ccfkvquzswf62gymxcbq3iubkac7y4ttmlekw3xx5rjcho24bo@423w2fb5ogzw","threadId":"61917","inReplyTo":"m0seubo5q7.fsf@epic96565.epic.com","subject":"Re: [PATCH v3 6/6] Makefile: add option to build and test libgit-rs and libgit-rs-sys","fromName":"Josh Steadmon","fromEmail":"steadmon@google.com","sentAt":"2024-10-02T22:49:57Z","receivedAt":"2024-10-02T22:50:04Z","isPatch":true,"sender":{"key":"steadmon@google.com","avatar":"https://avatars.githubusercontent.com/u/2654920?v=4"},"body":"On 2024.09.07 10:15, Sean Allred wrote:\n> Calvin Wan <calvinwan@google.com> writes:\n> > Add libgitrs, libgitrs-sys, libgitrs-test, and libgitrs-sys-test targets\n> > to their respective Makefiles so they can be built and tested without\n> > having to run cargo build/test.\n> \n> I feel like clippy should be run as part of these somehow, but I'm not\n> sure where.\n> \n> > +libgitrs-sys:\n> > +\t$(QUIET)(\\\n> > +\t\tcd contrib/libgit-rs/libgit-sys && \\\n> > +\t\tcargo build \\\n> > +\t)\n> > +.PHONY: libgitrs\n> > +libgitrs:\n> > +\t$(QUIET)(\\\n> > +\t\tcd contrib/libgit-rs && \\\n> > +\t\tcargo build \\\n> > +\t)\n> \n> We should definitely be setting `RUSTFLAGS=-Dwarnings` as an analog to\n> `-Wall` in the C world, no? These crates should build without warnings.\n> \n> Very excited to see this work; thank you for putting the time into it\n> :-)\n> \n> -- \n> Sean Allred\n\nDone in V4, thanks for the suggestion.\n"},{"id":"504363","messageId":"bjgxdo7sjueppt2y4i2psifuvgnld47jrusgpsao6fjyqfyld7@7hybmrhbdli5","threadId":"61917","inReplyTo":"Zt_qOrrWubdU_yvx@pks.im","subject":"Re: [PATCH v3 3/6] libgit-sys: add repo initialization and config access","fromName":"Josh Steadmon","fromEmail":"steadmon@google.com","sentAt":"2024-10-07T21:21:51Z","receivedAt":"2024-10-07T21:22:00Z","isPatch":true,"sender":{"key":"steadmon@google.com","avatar":"https://avatars.githubusercontent.com/u/2654920?v=4"},"body":"On 2024.09.10 08:42, Patrick Steinhardt wrote:\n> On Fri, Sep 06, 2024 at 10:21:13PM +0000, Calvin Wan wrote:\n> > diff --git a/contrib/libgit-rs/libgit-sys/public_symbol_export.c b/contrib/libgit-rs/libgit-sys/public_symbol_export.c\n> > index 39c27d9c1a..65d1620d28 100644\n> > --- a/contrib/libgit-rs/libgit-sys/public_symbol_export.c\n> > +++ b/contrib/libgit-rs/libgit-sys/public_symbol_export.c\n> > @@ -2,11 +2,37 @@\n> >  // original symbols can be hidden. Renaming these with a \"libgit_\" prefix also\n> >  // avoid conflicts with other libraries such as libgit2.\n> >  \n> > +#include \"git-compat-util.h\"\n> >  #include \"contrib/libgit-rs/libgit-sys/public_symbol_export.h\"\n> > +#include \"common-init.h\"\n> > +#include \"config.h\"\n> > +#include \"setup.h\"\n> >  #include \"version.h\"\n> >  \n> > +extern struct repository *the_repository;\n> > +\n> >  #pragma GCC visibility push(default)\n> >  \n> > +const char *libgit_setup_git_directory(void)\n> > +{\n> > +\treturn setup_git_directory();\n> > +}\n> > +\n> > +int libgit_config_get_int(const char *key, int *dest)\n> > +{\n> > +\treturn repo_config_get_int(the_repository, key, dest);\n> > +}\n> > +\n> > +void libgit_init_git(const char **argv)\n> > +{\n> > +\tinit_git(argv);\n> > +}\n> > +\n> > +int libgit_parse_maybe_bool(const char *val)\n> > +{\n> > +\treturn git_parse_maybe_bool(val);\n> > +}\n> > +\n> \n> I don't quite get why we expose functionality that is inherently not\n> libified. Exposing the current state to library users certainly does not\n> feel right to me, doubly so because `the_repository` is deprecated and\n> will eventually go away. So we already know that we'll have to break the\n> API here once that has happened. I'd rather want to see that introducing\n> the library makes us double down on providing properly encapsulated\n> interfaces from hereon.\n> \n> Also, we already have ways to initialize a repository and read their\n> config without relying on `the_repository`. So shouldn't we expose that\n> instead?\n> \n> Patrick\n\nSpecifically in this case, yeah, we should have started with the\nlibified version. This is an artifact due to the way we are figuring out\nC/Rust interop as we go, and it was easier to convert it this way\nwithout making the Rust side care about handling repository pointers.\nBut you're right, and I'll fix this soon for V4.\n\nIn general though, we're treating the initial version of libgit-rs as a\nproof-of-concept that we can call from JJ into Git without blowing\nthings up. We might not always have the option of exposing\nfully-libified code paths, and for our $DAYJOB purposes, we may have to\ndeal with non-ideal interfaces for the early versions. However, we do\nwant to use this as a way to motivate development of better, libified\nAPIs when we find real-world use cases for them.\n\nWe've said before (even before we started libgit-rs) that we're not\ngoing to be able to make guarantees about early libified APIs, because\nwe're learning as we go along. I don't want to use that as an excuse to\ncover up bad design, but I do want to be upfront that we can't commit to\nfully libifying any given code path before we expose it through the shim\nlibrary or through libgit-rs.\n"},{"id":"504364","messageId":"ynw3s2kqurwsdrghdfqq7k2i7bonyylhp6ddoxcitgvvoloha5@p4fc7hfkknvu","threadId":"61917","inReplyTo":"ZuSMFAyoD0CIfE3H@tapette.crustytoothpaste.net","subject":"Re: [PATCH v3 6/6] Makefile: add option to build and test libgit-rs and libgit-rs-sys","fromName":"Josh Steadmon","fromEmail":"steadmon@google.com","sentAt":"2024-10-07T21:23:47Z","receivedAt":"2024-10-07T21:23:53Z","isPatch":true,"sender":{"key":"steadmon@google.com","avatar":"https://avatars.githubusercontent.com/u/2654920?v=4"},"body":"On 2024.09.13 19:01, brian m. carlson wrote:\n> On 2024-09-07 at 15:15:12, Sean Allred wrote:\n> > Calvin Wan <calvinwan@google.com> writes:\n> > > Add libgitrs, libgitrs-sys, libgitrs-test, and libgitrs-sys-test targets\n> > > to their respective Makefiles so they can be built and tested without\n> > > having to run cargo build/test.\n> > \n> > I feel like clippy should be run as part of these somehow, but I'm not\n> > sure where.\n> \n> Yes, that seems like a good idea in CI.\n> \n> > > +libgitrs-sys:\n> > > +\t$(QUIET)(\\\n> > > +\t\tcd contrib/libgit-rs/libgit-sys && \\\n> > > +\t\tcargo build \\\n> > > +\t)\n> > > +.PHONY: libgitrs\n> > > +libgitrs:\n> > > +\t$(QUIET)(\\\n> > > +\t\tcd contrib/libgit-rs && \\\n> > > +\t\tcargo build \\\n> > > +\t)\n> > \n> > We should definitely be setting `RUSTFLAGS=-Dwarnings` as an analog to\n> > `-Wall` in the C world, no? These crates should build without warnings.\n> \n> I believe -Dwarnings turns warnings into errors (at least it does in my\n> tests), which is equivalent to -Werror.  We don't want that because it\n> breaks compiling older code with newer versions of the compiler, which\n> makes it harder to bisect changes or compiler on the system compiler (or\n> sometimes, other architectures or OSes).\n> \n> That would be fine for clippy, however, because that would only run in\n> CI, where we _would_ want to catch newer changes, but we want to\n> compile nonetheless.\n> -- \n> brian m. carlson (they/them or he/him)\n> Toronto, Ontario, CA\n\nSorry, I missed your message before replying on this point last week.\nI'll remove this from our build.rs in V4.\n"},{"id":"504369","messageId":"paya2jpm67x3zfkwtttcjrkfe7ai63ez3scrtarbdolkcmxvbq@yant6zn63p4z","threadId":"61917","inReplyTo":"xmqqo74kj4ys.fsf@gitster.g","subject":"Re: [PATCH v3 5/6] libgit: add higher-level libgit crate","fromName":"Josh Steadmon","fromEmail":"steadmon@google.com","sentAt":"2024-10-07T23:31:00Z","receivedAt":"2024-10-07T23:31:07Z","isPatch":true,"sender":{"key":"steadmon@google.com","avatar":"https://avatars.githubusercontent.com/u/2654920?v=4"},"body":"On 2024.09.18 09:34, Junio C Hamano wrote:\n> Josh Steadmon <steadmon@google.com> writes:\n> \n> > We want to namespace types as well as functions, as Phillip pointed out\n> > in 47b18fa4-f01b-4f42-8d04-9e145515ccc1@gmail.com.\n> >\n> > Is there a reason why we need the shim struct from your\n> > xmqqcylcpnah.fsf@gitster.g and can't just cast directly like so:\n> > ...\n> >  int libgit_configset_get_string(struct libgit_config_set *cs, const char *key, char **dest)\n> >  {\n> > -       return git_configset_get_string(cs, key, dest);\n> > +       return git_configset_get_string((struct config_set *) cs, key, dest);\n> >  }\n> \n> Not at all.  I just didn't see your intentions in the patch if all\n> you wanted to do was merely to flip names, or wanted to leave the\n> possibility to allow the wrapped ones to optionally have different\n> shape (e.g. for bookkeeping purposes for either the host environment\n> or the shim layer).  If it is merely \"we do not want to expose these\n> names but we want bit-for-bit identical data\", then you do not need\n> extra logic at all---the casts would be suffficient[*].\n> \n> PS. I am not feeling well today, so please expect delayed and/or\n> sparse responses.\n> \n> \n> [Footnote]\n> \n> * Building objects that go to libgit.a, partially linking them to\n>   resolve internal references, and then rewriting the symbol table\n>   of the resulting relocatable object file to expose only the entry\n>   points and data you want to show to the rust world to whatever\n>   names you want, would be a less gross solution, I would imagine.\n>   You only then need to write a (fake) public_symbol_export.h file\n>   that will never has to be seen on the C side, but to be seen as\n>   the header to describe that C library to the rust side (and\n>   obviously you do not need public_symbol_export.c file only to keep\n>   these casts).\n\nI don't think I'll have time to experiment with this approach for V4,\nbut I'll put it on our list of future work. For now I'm just keeping\npublic_symbol_export.{c,h} as-is and casting to avoid exposing the\nunmangled struct name.\n"},{"id":"504490","messageId":"zmdze3hbxfzuupx32hbnvyghzihkgjjhmh3prlwgby2hatgvuo@s6kdajzcagnb","threadId":"61917","inReplyTo":"bjgxdo7sjueppt2y4i2psifuvgnld47jrusgpsao6fjyqfyld7@7hybmrhbdli5","subject":"Re: [PATCH v3 3/6] libgit-sys: add repo initialization and config access","fromName":"Josh Steadmon","fromEmail":"steadmon@google.com","sentAt":"2024-10-08T20:59:23Z","receivedAt":"2024-10-08T20:59:29Z","isPatch":true,"sender":{"key":"steadmon@google.com","avatar":"https://avatars.githubusercontent.com/u/2654920?v=4"},"body":"On 2024.10.07 14:21, Josh Steadmon wrote:\n> On 2024.09.10 08:42, Patrick Steinhardt wrote:\n> > I don't quite get why we expose functionality that is inherently not\n> > libified. Exposing the current state to library users certainly does not\n> > feel right to me, doubly so because `the_repository` is deprecated and\n> > will eventually go away. So we already know that we'll have to break the\n> > API here once that has happened. I'd rather want to see that introducing\n> > the library makes us double down on providing properly encapsulated\n> > interfaces from hereon.\n> > \n> > Also, we already have ways to initialize a repository and read their\n> > config without relying on `the_repository`. So shouldn't we expose that\n> > instead?\n> > \n> > Patrick\n> \n> Specifically in this case, yeah, we should have started with the\n> libified version. This is an artifact due to the way we are figuring out\n> C/Rust interop as we go, and it was easier to convert it this way\n> without making the Rust side care about handling repository pointers.\n> But you're right, and I'll fix this soon for V4.\n> \n> In general though, we're treating the initial version of libgit-rs as a\n> proof-of-concept that we can call from JJ into Git without blowing\n> things up. We might not always have the option of exposing\n> fully-libified code paths, and for our $DAYJOB purposes, we may have to\n> deal with non-ideal interfaces for the early versions. However, we do\n> want to use this as a way to motivate development of better, libified\n> APIs when we find real-world use cases for them.\n> \n> We've said before (even before we started libgit-rs) that we're not\n> going to be able to make guarantees about early libified APIs, because\n> we're learning as we go along. I don't want to use that as an excuse to\n> cover up bad design, but I do want to be upfront that we can't commit to\n> fully libifying any given code path before we expose it through the shim\n> library or through libgit-rs.\n\nIn fact, since the JJ proof-of-concept doesn't rely on repository\ninitialization or repository-level config access, I think we can just\ndrop this patch for now and worry about getting a better interface for\nrepo initialization later.\n"},{"id":"504498","messageId":"cover.1728429158.git.steadmon@google.com","threadId":"61917","inReplyTo":"cover.1723054623.git.steadmon@google.com","subject":"[PATCH v4 0/5] Introduce libgit-rs, a Rust wrapper around libgit.a","fromName":"Josh Steadmon","fromEmail":"steadmon@google.com","sentAt":"2024-10-08T23:19:30Z","receivedAt":"2024-10-08T23:19:38Z","isPatch":true,"sender":{"key":"steadmon@google.com","avatar":"https://avatars.githubusercontent.com/u/2654920?v=4"},"body":"This series provides two small Rust wrapper libraries around parts of\nGit: \"libgit-sys\", which exposes a few functions from libgit.a, and\n\"libgit\", which provides a more Rust-friendly interface to some of those\nfunctions. In addition to included unit tests, at $DAYJOB we have tested\nbuilding JJ[1] with our library and used it to replace some of the\nlibgit2-rs uses.\n\n[1] https://github.com/martinvonz/jj\n\nPlease find V1 cover letter here:\nhttps://lore.kernel.org/git/cover.1723054623.git.steadmon@google.com/\n\nChanges in V4:\n* Drop V3 patch #3, which added wrappers around repository\n  initialization and config access. These are not well-libified, and\n  they are not necessary for JJ's proof-of-concept use case, so let's\n  avoid exporting them for now.\n\n* Set a minimum supported Rust version of 1.63. Autodetect whether our\n  Rust version has c_int and c_char types; if not, define them\n  ourselves.\n\n* When building libgitpub.a via build.rs, set DEVELOPER=1 to catch\n  additional errors at build time.\n\n* In build.rs, use the make_cmd crate to portable select the correct\n  invocation of GNU Make.\n\n* Follow naming standards for _alloc() and _free() functions.\n\n* Use String instead of CString in higher-level API.\n\n* Move libgit_configset_alloc() and libgit_configset_free() out of\n  upstream Git, to the libgitpub shim library.\n\n* In libgitpub, initialize libgit_config_set structs in the _alloc()\n  function rather than with a separate _init() function.\n\n* Remove unnecessary comments in libgit-sys showing where the wrapped\n  functions were originally defined.\n\n* Fix clippy lint: don't reborrow configfile path references.\n\n* Various typo fixes and `cargo fmt` fixes.\n\nChanges in V3:\n* Renamed cgit-rs to libgit-rs and cgit-sys to libgit-sys\n\n* Makefile cleanup, particularly adding config.mak options that\n  developers can set to run Rust builds and tests by default (Patch 6)\n\n* Provide testdata configs for unit tests\n\n* ConfigSet API now uses &Path instead of &str -- more ergonomic for\n  Rust users to pass in and errors out if the path string isn't UTF-8\n\n* Fixed unresolved dependency on libz in Cargo.toml\n\nKnown NEEDSWORK:\n\n* Investigate alternative methods of managing symbol visibility &\n  renaming.\n\n* Figure out symbol versioning\n\n\nCalvin Wan (2):\n  libgit: add higher-level libgit crate\n  Makefile: add option to build and test libgit-rs and libgit-rs-sys\n\nJosh Steadmon (3):\n  common-main: split init and exit code into new files\n  libgit-sys: introduce Rust wrapper for libgit.a\n  libgit-sys: also export some config_set functions\n\n .gitignore                                    |  2 +\n Makefile                                      | 31 ++++++\n common-exit.c                                 | 26 +++++\n common-init.c                                 | 63 ++++++++++++\n common-init.h                                 |  6 ++\n common-main.c                                 | 83 +---------------\n contrib/libgit-rs/Cargo.lock                  | 77 +++++++++++++++\n contrib/libgit-rs/Cargo.toml                  | 15 +++\n contrib/libgit-rs/build.rs                    |  4 +\n contrib/libgit-rs/libgit-sys/Cargo.lock       | 69 ++++++++++++++\n contrib/libgit-rs/libgit-sys/Cargo.toml       | 17 ++++\n contrib/libgit-rs/libgit-sys/README.md        | 15 +++\n contrib/libgit-rs/libgit-sys/build.rs         | 35 +++++++\n .../libgit-sys/public_symbol_export.c         | 50 ++++++++++\n .../libgit-sys/public_symbol_export.h         | 18 ++++\n contrib/libgit-rs/libgit-sys/src/lib.rs       | 79 +++++++++++++++\n contrib/libgit-rs/src/lib.rs                  | 95 +++++++++++++++++++\n contrib/libgit-rs/testdata/config1            |  2 +\n contrib/libgit-rs/testdata/config2            |  2 +\n contrib/libgit-rs/testdata/config3            |  2 +\n t/Makefile                                    | 16 ++++\n 21 files changed, 626 insertions(+), 81 deletions(-)\n create mode 100644 common-exit.c\n create mode 100644 common-init.c\n create mode 100644 common-init.h\n create mode 100644 contrib/libgit-rs/Cargo.lock\n create mode 100644 contrib/libgit-rs/Cargo.toml\n create mode 100644 contrib/libgit-rs/build.rs\n create mode 100644 contrib/libgit-rs/libgit-sys/Cargo.lock\n create mode 100644 contrib/libgit-rs/libgit-sys/Cargo.toml\n create mode 100644 contrib/libgit-rs/libgit-sys/README.md\n create mode 100644 contrib/libgit-rs/libgit-sys/build.rs\n create mode 100644 contrib/libgit-rs/libgit-sys/public_symbol_export.c\n create mode 100644 contrib/libgit-rs/libgit-sys/public_symbol_export.h\n create mode 100644 contrib/libgit-rs/libgit-sys/src/lib.rs\n create mode 100644 contrib/libgit-rs/src/lib.rs\n create mode 100644 contrib/libgit-rs/testdata/config1\n create mode 100644 contrib/libgit-rs/testdata/config2\n create mode 100644 contrib/libgit-rs/testdata/config3\n\nRange-diff against v3:\n1:  83afd7f4a2 ! 1:  1ae14207f6 common-main: split init and exit code into new files\n    @@ Commit message\n         [2] https://lore.kernel.org/git/20230517-unit-tests-v2-v2-1-21b5b60f4b32@google.com/\n     \n    -    Signed-off-by: Calvin Wan <calvinwan@google.com>\n    +    Signed-off-by: Josh Steadmon <steadmon@google.com>\n     \n      ## Makefile ##\n     @@ Makefile: LIB_OBJS += combine-diff.o\n2:  354b498494 ! 2:  ed925d6a33 libgit-sys: introduce Rust wrapper for libgit.a\n    @@ Commit message\n         Co-authored-by: Calvin Wan <calvinwan@google.com>\n         Signed-off-by: Calvin Wan <calvinwan@google.com>\n         Signed-off-by: Kyle Lippincott <spectral@google.com>\n    +    Signed-off-by: Josh Steadmon <steadmon@google.com>\n     \n      ## .gitignore ##\n    @@ contrib/libgit-rs/libgit-sys/Cargo.lock (new)\n     +version = 3\n     +\n     +[[package]]\n    ++name = \"autocfg\"\n    ++version = \"1.4.0\"\n    ++source = \"registry+https://github.com/rust-lang/crates.io-index\"\n    ++checksum = \"ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26\"\n    ++\n    ++[[package]]\n     +name = \"cc\"\n     +version = \"1.1.15\"\n     +source = \"registry+https://github.com/rust-lang/crates.io-index\"\n    @@ contrib/libgit-rs/libgit-sys/Cargo.lock (new)\n     +name = \"libgit-sys\"\n     +version = \"0.1.0\"\n     +dependencies = [\n    ++ \"autocfg\",\n     + \"libz-sys\",\n    ++ \"make-cmd\",\n     +]\n     +\n     +[[package]]\n    @@ contrib/libgit-rs/libgit-sys/Cargo.lock (new)\n     +]\n     +\n     +[[package]]\n    ++name = \"make-cmd\"\n    ++version = \"0.1.0\"\n    ++source = \"registry+https://github.com/rust-lang/crates.io-index\"\n    ++checksum = \"a8ca8afbe8af1785e09636acb5a41e08a765f5f0340568716c18a8700ba3c0d3\"\n    ++\n    ++[[package]]\n     +name = \"pkg-config\"\n     +version = \"0.3.30\"\n     +source = \"registry+https://github.com/rust-lang/crates.io-index\"\n    @@ contrib/libgit-rs/libgit-sys/Cargo.toml (new)\n     +edition = \"2021\"\n     +build = \"build.rs\"\n     +links = \"gitpub\"\n    ++rust-version = \"1.63\"\n     +\n     +[lib]\n     +path = \"src/lib.rs\"\n     +\n     +[dependencies]\n     +libz-sys = \"1.1.19\"\n    - \\ No newline at end of file\n    ++\n    ++[build-dependencies]\n    ++autocfg = \"1.4.0\"\n    ++make-cmd = \"0.1.0\"\n     \n      ## contrib/libgit-rs/libgit-sys/README.md (new) ##\n     @@\n    @@ contrib/libgit-rs/libgit-sys/build.rs (new)\n     +use std::path::PathBuf;\n     +\n     +pub fn main() -> std::io::Result<()> {\n    ++    let ac = autocfg::new();\n    ++    ac.emit_has_path(\"std::ffi::c_char\");\n    ++\n     +    let crate_root = PathBuf::from(env::var_os(\"CARGO_MANIFEST_DIR\").unwrap());\n     +    let git_root = crate_root.join(\"../../..\");\n     +    let dst = PathBuf::from(env::var_os(\"OUT_DIR\").unwrap());\n     +\n    -+    let make_output = std::process::Command::new(\"make\")\n    ++    let make_output = make_cmd::gnu_make()\n    ++        .env(\"DEVELOPER\", \"1\")\n     +        .env_remove(\"PROFILE\")\n     +        .current_dir(git_root.clone())\n     +        .args([\n    @@ contrib/libgit-rs/libgit-sys/build.rs (new)\n     +        );\n     +    }\n     +    std::fs::copy(crate_root.join(\"libgitpub.a\"), dst.join(\"libgitpub.a\"))?;\n    -+    println!(\"cargo::rustc-link-search=native={}\", dst.display());\n    -+    println!(\"cargo::rustc-link-lib=gitpub\");\n    -+    println!(\"cargo::rerun-if-changed={}\", git_root.display());\n    ++    println!(\"cargo:rustc-link-search=native={}\", dst.display());\n    ++    println!(\"cargo:rustc-link-lib=gitpub\");\n    ++    println!(\"cargo:rerun-if-changed={}\", git_root.display());\n     +\n     +    Ok(())\n     +}\n    @@ contrib/libgit-rs/libgit-sys/public_symbol_export.c (new)\n     @@\n     +// Shim to publicly export Git symbols. These must be renamed so that the\n     +// original symbols can be hidden. Renaming these with a \"libgit_\" prefix also\n    -+// avoid conflicts with other libraries such as libgit2.\n    ++// avoids conflicts with other libraries such as libgit2.\n     +\n    ++#include \"git-compat-util.h\"\n     +#include \"contrib/libgit-rs/libgit-sys/public_symbol_export.h\"\n     +#include \"version.h\"\n     +\n    @@ contrib/libgit-rs/libgit-sys/public_symbol_export.h (new)\n     \n      ## contrib/libgit-rs/libgit-sys/src/lib.rs (new) ##\n     @@\n    ++#[cfg(has_std__ffi__c_char)]\n     +use std::ffi::c_char;\n     +\n    ++#[cfg(not(has_std__ffi__c_char))]\n    ++#[allow(non_camel_case_types)]\n    ++pub type c_char = i8;\n    ++\n     +extern crate libz_sys;\n     +\n     +extern \"C\" {\n    -+    // From version.c\n     +    pub fn libgit_user_agent() -> *const c_char;\n     +    pub fn libgit_user_agent_sanitized() -> *const c_char;\n     +}\n3:  2ebff081f8 < -:  ---------- libgit-sys: add repo initialization and config access\n4:  6b4b7e6ced ! 3:  8eeab7b418 config: add git_configset_alloc() and git_configset_clear_and_free()\n    @@ Metadata\n     Author: Josh Steadmon <steadmon@google.com>\n     \n      ## Commit message ##\n    -    config: add git_configset_alloc() and git_configset_clear_and_free()\n    +    libgit-sys: also export some config_set functions\n     \n    -    Add git_configset_alloc() and git_configset_clear_and_free() functions\n    -    so that callers can manage config_set structs on the heap. This also\n    -    allows non-C external consumers to treat config_sets as opaque structs.\n    +    In preparation for implementing a higher-level Rust API for accessing\n    +    Git configs, export some of the upstream configset API via libgitpub and\n    +    libgit-sys. Since this will be exercised as part of the higher-level API\n    +    in the next commit, no tests have been added for libgit-sys.\n    +\n    +    While we're at it, add git_configset_alloc() and git_configset_free()\n    +    functions in libgitpub so that callers can manage config_set structs on\n    +    the heap. This also allows non-C external consumers to treat config_sets\n    +    as opaque structs.\n     \n         Co-authored-by: Calvin Wan <calvinwan@google.com>\n         Signed-off-by: Calvin Wan <calvinwan@google.com>\n    +    Signed-off-by: Josh Steadmon <steadmon@google.com>\n     \n    - ## config.c ##\n    -@@ config.c: static int config_set_element_cmp(const void *cmp_data UNUSED,\n    - \treturn strcmp(e1->key, e2->key);\n    - }\n    + ## contrib/libgit-rs/libgit-sys/public_symbol_export.c ##\n    +@@\n    + // avoids conflicts with other libraries such as libgit2.\n    + \n    + #include \"git-compat-util.h\"\n    ++#include \"config.h\"\n    + #include \"contrib/libgit-rs/libgit-sys/public_symbol_export.h\"\n    + #include \"version.h\"\n      \n    -+struct config_set *git_configset_alloc(void)\n    + #pragma GCC visibility push(default)\n    + \n    ++struct libgit_config_set *libgit_configset_alloc(void)\n     +{\n    -+\treturn xmalloc(sizeof(struct config_set));\n    ++\tstruct config_set *cs = xmalloc(sizeof(struct config_set));\n    ++\tgit_configset_init(cs);\n    ++\treturn (struct libgit_config_set *) cs;\n     +}\n     +\n    - void git_configset_init(struct config_set *set)\n    - {\n    - \thashmap_init(&set->config_hash, config_set_element_cmp, NULL, 0);\n    -@@ config.c: void git_configset_clear(struct config_set *set)\n    - \tset->list.items = NULL;\n    - }\n    - \n    -+void git_configset_clear_and_free(struct config_set *set)\n    ++void libgit_configset_free(struct libgit_config_set *cs)\n    ++{\n    ++\tgit_configset_clear((struct config_set *) cs);\n    ++\tfree((struct config_set *) cs);\n    ++}\n    ++\n    ++int libgit_configset_add_file(struct libgit_config_set *cs, const char *filename)\n     +{\n    -+\tgit_configset_clear(set);\n    -+\tfree(set);\n    ++\treturn git_configset_add_file((struct config_set *) cs, filename);\n     +}\n     +\n    - static int config_set_callback(const char *key, const char *value,\n    - \t\t\t       const struct config_context *ctx,\n    - \t\t\t       void *cb)\n    ++int libgit_configset_get_int(struct libgit_config_set *cs, const char *key, int *dest)\n    ++{\n    ++\treturn git_configset_get_int((struct config_set *) cs, key, dest);\n    ++}\n    ++\n    ++int libgit_configset_get_string(struct libgit_config_set *cs, const char *key, char **dest)\n    ++{\n    ++\treturn git_configset_get_string((struct config_set *) cs, key, dest);\n    ++}\n    ++\n    + const char *libgit_user_agent(void)\n    + {\n    + \treturn git_user_agent();\n    +\n    + ## contrib/libgit-rs/libgit-sys/public_symbol_export.h ##\n    +@@\n    + #ifndef PUBLIC_SYMBOL_EXPORT_H\n    + #define PUBLIC_SYMBOL_EXPORT_H\n    + \n    ++struct libgit_config_set *libgit_configset_alloc(void);\n    ++\n    ++void libgit_configset_free(struct libgit_config_set *cs);\n    ++\n    ++int libgit_configset_add_file(struct libgit_config_set *cs, const char *filename);\n    ++\n    ++int libgit_configset_get_int(struct libgit_config_set *cs, const char *key, int *dest);\n    ++\n    ++int libgit_configset_get_string(struct libgit_config_set *cs, const char *key, char **dest);\n    ++\n    + const char *libgit_user_agent(void);\n    + \n    + const char *libgit_user_agent_sanitized(void);\n     \n    - ## config.h ##\n    -@@ config.h: struct config_set {\n    - \tstruct configset_list list;\n    - };\n    + ## contrib/libgit-rs/libgit-sys/src/lib.rs ##\n    +@@\n    + #[cfg(has_std__ffi__c_char)]\n    +-use std::ffi::c_char;\n    ++use std::ffi::{c_char, c_int};\n    + \n    + #[cfg(not(has_std__ffi__c_char))]\n    + #[allow(non_camel_case_types)]\n    + pub type c_char = i8;\n    + \n    ++#[cfg(not(has_std__ffi__c_char))]\n    ++#[allow(non_camel_case_types)]\n    ++pub type c_int = i32;\n    ++\n    + extern crate libz_sys;\n      \n    -+/**\n    -+ * Alloc a config_set\n    -+ */\n    -+struct config_set *git_configset_alloc(void);\n    -+\n    - /**\n    -  * Initializes the config_set `cs`.\n    -  */\n    -@@ config.h: int git_configset_get_string_multi(struct config_set *cs, const char *key,\n    -  */\n    - void git_configset_clear(struct config_set *cs);\n    ++#[allow(non_camel_case_types)]\n    ++#[repr(C)]\n    ++pub struct libgit_config_set {\n    ++    _data: [u8; 0],\n    ++    _marker: core::marker::PhantomData<(*mut u8, core::marker::PhantomPinned)>,\n    ++}\n    ++\n    + extern \"C\" {\n    +     pub fn libgit_user_agent() -> *const c_char;\n    +     pub fn libgit_user_agent_sanitized() -> *const c_char;\n    ++\n    ++    pub fn libgit_configset_alloc() -> *mut libgit_config_set;\n    ++    pub fn libgit_configset_free(cs: *mut libgit_config_set);\n    ++\n    ++    pub fn libgit_configset_add_file(cs: *mut libgit_config_set, filename: *const c_char) -> c_int;\n    ++\n    ++    pub fn libgit_configset_get_int(\n    ++        cs: *mut libgit_config_set,\n    ++        key: *const c_char,\n    ++        int: *mut c_int,\n    ++    ) -> c_int;\n    ++\n    ++    pub fn libgit_configset_get_string(\n    ++        cs: *mut libgit_config_set,\n    ++        key: *const c_char,\n    ++        dest: *mut *mut c_char,\n    ++    ) -> c_int;\n    ++\n    + }\n      \n    -+/**\n    -+ * Clears and frees a heap-allocated `config_set` structure.\n    -+ */\n    -+void git_configset_clear_and_free(struct config_set *cs);\n    -+\n    - /*\n    -  * These functions return 1 if not found, and 0 if found, leaving the found\n    -  * value in the 'dest' pointer.\n    + #[cfg(test)]\n5:  18fea79783 ! 4:  29599e9c7b libgit: add higher-level libgit crate\n    @@ Commit message\n         a more Rust-friendly interface to config_set structs.\n     \n         Co-authored-by: Josh Steadmon <steadmon@google.com>\n    +    Signed-off-by: Josh Steadmon <steadmon@google.com>\n         Signed-off-by: Calvin Wan <calvinwan@google.com>\n     \n    @@ contrib/libgit-rs/Cargo.lock (new)\n     +version = 3\n     +\n     +[[package]]\n    ++name = \"autocfg\"\n    ++version = \"1.4.0\"\n    ++source = \"registry+https://github.com/rust-lang/crates.io-index\"\n    ++checksum = \"ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26\"\n    ++\n    ++[[package]]\n     +name = \"cc\"\n     +version = \"1.1.15\"\n     +source = \"registry+https://github.com/rust-lang/crates.io-index\"\n    @@ contrib/libgit-rs/Cargo.lock (new)\n     +name = \"libgit\"\n     +version = \"0.1.0\"\n     +dependencies = [\n    ++ \"autocfg\",\n     + \"libgit-sys\",\n     +]\n     +\n    @@ contrib/libgit-rs/Cargo.lock (new)\n     +name = \"libgit-sys\"\n     +version = \"0.1.0\"\n     +dependencies = [\n    ++ \"autocfg\",\n     + \"libz-sys\",\n    ++ \"make-cmd\",\n     +]\n     +\n     +[[package]]\n    @@ contrib/libgit-rs/Cargo.lock (new)\n     +]\n     +\n     +[[package]]\n    ++name = \"make-cmd\"\n    ++version = \"0.1.0\"\n    ++source = \"registry+https://github.com/rust-lang/crates.io-index\"\n    ++checksum = \"a8ca8afbe8af1785e09636acb5a41e08a765f5f0340568716c18a8700ba3c0d3\"\n    ++\n    ++[[package]]\n     +name = \"pkg-config\"\n     +version = \"0.3.30\"\n     +source = \"registry+https://github.com/rust-lang/crates.io-index\"\n    @@ contrib/libgit-rs/Cargo.toml (new)\n     +name = \"libgit\"\n     +version = \"0.1.0\"\n     +edition = \"2021\"\n    ++build = \"build.rs\"\n    ++rust-version = \"1.63\"\n     +\n     +[lib]\n     +path = \"src/lib.rs\"\n     +\n     +[dependencies]\n     +libgit-sys = { version = \"0.1.0\", path = \"libgit-sys\" }\n    -\n    - ## contrib/libgit-rs/libgit-sys/public_symbol_export.c ##\n    -@@ contrib/libgit-rs/libgit-sys/public_symbol_export.c: int libgit_parse_maybe_bool(const char *val)\n    - \treturn git_parse_maybe_bool(val);\n    - }\n    - \n    -+struct libgit_config_set *libgit_configset_alloc(void)\n    -+{\n    -+\treturn git_configset_alloc();\n    -+}\n    -+\n    -+void libgit_configset_clear_and_free(struct libgit_config_set *cs)\n    -+{\n    -+\tgit_configset_clear_and_free(cs);\n    -+}\n     +\n    -+void libgit_configset_init(struct libgit_config_set *cs)\n    -+{\n    -+\tgit_configset_init(cs);\n    -+}\n    -+\n    -+int libgit_configset_add_file(struct libgit_config_set *cs, const char *filename)\n    -+{\n    -+\treturn git_configset_add_file(cs, filename);\n    -+}\n    -+\n    -+int libgit_configset_get_int(struct libgit_config_set *cs, const char *key, int *dest)\n    -+{\n    -+\treturn git_configset_get_int(cs, key, dest);\n    -+}\n    -+\n    -+int libgit_configset_get_string(struct libgit_config_set *cs, const char *key, char **dest)\n    -+{\n    -+\treturn git_configset_get_string(cs, key, dest);\n    -+}\n    -+\n    - const char *libgit_user_agent(void)\n    - {\n    - \treturn git_user_agent();\n    ++[build-dependencies]\n    ++autocfg = \"1.4.0\"\n     \n    - ## contrib/libgit-rs/libgit-sys/public_symbol_export.h ##\n    -@@ contrib/libgit-rs/libgit-sys/public_symbol_export.h: void libgit_init_git(const char **argv);\n    - \n    - int libgit_parse_maybe_bool(const char *val);\n    - \n    -+struct libgit_config_set *libgit_configset_alloc(void);\n    -+\n    -+void libgit_configset_clear_and_free(struct libgit_config_set *cs);\n    -+\n    -+void libgit_configset_init(struct libgit_config_set *cs);\n    -+\n    -+int libgit_configset_add_file(struct libgit_config_set *cs, const char *filename);\n    -+\n    -+int libgit_configset_get_int(struct libgit_config_set *cs, const char *key, int *dest);\n    -+\n    -+int libgit_configset_get_string(struct libgit_config_set *cs, const char *key, char **dest);\n    -+\n    - const char *libgit_user_agent(void);\n    - \n    - const char *libgit_user_agent_sanitized(void);\n    + ## contrib/libgit-rs/build.rs (new) ##\n    +@@\n    ++pub fn main() {\n    ++    let ac = autocfg::new();\n    ++    ac.emit_has_path(\"std::ffi::c_char\");\n    ++}\n     \n      ## contrib/libgit-rs/libgit-sys/src/lib.rs ##\n     @@\n    --use std::ffi::{c_char, c_int};\n    -+use std::ffi::{c_char, c_int, c_void};\n    ++use std::ffi::c_void;\n     +\n    -+#[allow(non_camel_case_types)]\n    -+#[repr(C)]\n    -+pub struct libgit_config_set {\n    -+    _data: [u8; 0],\n    -+    _marker: core::marker::PhantomData<(*mut u8, core::marker::PhantomPinned)>,\n    -+}\n    + #[cfg(has_std__ffi__c_char)]\n    + use std::ffi::{c_char, c_int};\n      \n    - extern crate libz_sys;\n    +@@ contrib/libgit-rs/libgit-sys/src/lib.rs: pub struct libgit_config_set {\n    + }\n      \n      extern \"C\" {\n     +    pub fn free(ptr: *mut c_void);\n     +\n    -     pub fn libgit_setup_git_directory() -> *const c_char;\n    - \n    -     // From config.c\n    -@@ contrib/libgit-rs/libgit-sys/src/lib.rs: extern \"C\" {\n    -     // From version.c\n          pub fn libgit_user_agent() -> *const c_char;\n          pub fn libgit_user_agent_sanitized() -> *const c_char;\n    -+\n    -+    pub fn libgit_configset_alloc() -> *mut libgit_config_set;\n    -+    pub fn libgit_configset_clear_and_free(cs: *mut libgit_config_set);\n    -+\n    -+    pub fn libgit_configset_init(cs: *mut libgit_config_set);\n    -+\n    -+    pub fn libgit_configset_add_file(cs: *mut libgit_config_set, filename: *const c_char) -> c_int;\n    -+\n    -+    pub fn libgit_configset_get_int(\n    -+        cs: *mut libgit_config_set,\n    -+        key: *const c_char,\n    -+        int: *mut c_int,\n    -+    ) -> c_int;\n    -+\n    -+    pub fn libgit_configset_get_string(\n    -+        cs: *mut libgit_config_set,\n    -+        key: *const c_char,\n    -+        dest: *mut *mut c_char,\n    -+    ) -> c_int;\n    -+\n    - }\n      \n    - #[cfg(test)]\n     \n      ## contrib/libgit-rs/src/lib.rs (new) ##\n     @@\n    -+use std::ffi::{c_char, c_int, c_void, CStr, CString};\n    ++use std::ffi::{c_void, CStr, CString};\n     +use std::path::Path;\n     +\n    ++#[cfg(has_std__ffi__c_char)]\n    ++use std::ffi::{c_char, c_int};\n    ++\n    ++#[cfg(not(has_std__ffi__c_char))]\n    ++#[allow(non_camel_case_types)]\n    ++pub type c_char = i8;\n    ++\n    ++#[cfg(not(has_std__ffi__c_char))]\n    ++#[allow(non_camel_case_types)]\n    ++pub type c_int = i32;\n    ++\n     +use libgit_sys::*;\n     +\n     +pub struct ConfigSet(*mut libgit_config_set);\n     +impl ConfigSet {\n     +    pub fn new() -> Self {\n    -+        unsafe {\n    -+            let ptr = libgit_configset_alloc();\n    -+            libgit_configset_init(ptr);\n    -+            ConfigSet(ptr)\n    -+        }\n    ++        unsafe { ConfigSet(libgit_configset_alloc()) }\n     +    }\n     +\n     +    pub fn add_files(&mut self, files: &[&Path]) {\n     +        for file in files {\n    -+            let pstr: &str = file.to_str().expect(\"Invalid UTF-8\");\n    -+            let rs = CString::new(&*pstr).expect(\"Couldn't convert to CString\");\n    ++            let pstr = file.to_str().expect(\"Invalid UTF-8\");\n    ++            let rs = CString::new(pstr).expect(\"Couldn't convert to CString\");\n     +            unsafe {\n     +                libgit_configset_add_file(self.0, rs.as_ptr());\n     +            }\n    @@ contrib/libgit-rs/src/lib.rs (new)\n     +        Some(val)\n     +    }\n     +\n    -+    pub fn get_str(&mut self, key: &str) -> Option<CString> {\n    -+        let key = CString::new(key).expect(\"Couldn't convert to CString\");\n    ++    pub fn get_string(&mut self, key: &str) -> Option<String> {\n    ++        let key = CString::new(key).expect(\"Couldn't convert key to CString\");\n     +        let mut val: *mut c_char = std::ptr::null_mut();\n     +        unsafe {\n     +            if libgit_configset_get_string(self.0, key.as_ptr(), &mut val as *mut *mut c_char) != 0\n    @@ contrib/libgit-rs/src/lib.rs (new)\n     +                return None;\n     +            }\n     +            let borrowed_str = CStr::from_ptr(val);\n    -+            let owned_str = CString::from_vec_with_nul(borrowed_str.to_bytes_with_nul().to_vec());\n    ++            let owned_str =\n    ++                String::from(borrowed_str.to_str().expect(\"Couldn't convert val to str\"));\n     +            free(val as *mut c_void); // Free the xstrdup()ed pointer from the C side\n    -+            Some(owned_str.unwrap())\n    ++            Some(owned_str)\n     +        }\n     +    }\n     +}\n    @@ contrib/libgit-rs/src/lib.rs (new)\n     +impl Drop for ConfigSet {\n     +    fn drop(&mut self) {\n     +        unsafe {\n    -+            libgit_configset_clear_and_free(self.0);\n    ++            libgit_configset_free(self.0);\n     +        }\n     +    }\n     +}\n    @@ contrib/libgit-rs/src/lib.rs (new)\n     +    #[test]\n     +    fn load_configs_via_configset() {\n     +        let mut cs = ConfigSet::new();\n    -+        cs.add_files(&[Path::new(\"testdata/config1\"),\n    -+                       Path::new(\"testdata/config2\"),\n    -+                       Path::new(\"testdata/config3\")]);\n    -+        // ConfigSet retrieves correct value \n    ++        cs.add_files(&[\n    ++            Path::new(\"testdata/config1\"),\n    ++            Path::new(\"testdata/config2\"),\n    ++            Path::new(\"testdata/config3\"),\n    ++        ]);\n    ++        // ConfigSet retrieves correct value\n     +        assert_eq!(cs.get_int(\"trace2.eventTarget\"), Some(1));\n     +        // ConfigSet respects last config value set\n     +        assert_eq!(cs.get_int(\"trace2.eventNesting\"), Some(3));\n     +        // ConfigSet returns None for missing key\n    -+        assert_eq!(cs.get_str(\"foo.bar\"), None);\n    ++        assert_eq!(cs.get_string(\"foo.bar\"), None);\n     +    }\n     +}\n     \n6:  67c11dd143 ! 5:  2ed503216f Makefile: add option to build and test libgit-rs and libgit-rs-sys\n    @@ Commit message\n         automatically builds and tests libgit-rs and libgit-rs-sys when `make\n         all` is ran.\n     \n         Signed-off-by: Calvin Wan <calvinwan@google.com>\n     \n      ## Makefile ##\n\nbase-commit: 557ae147e6cdc9db121269b058c757ac5092f9c9\n-- \n2.47.0.rc0.187.ge670bccf7e-goog\n\n"},{"id":"504499","messageId":"1ae14207f69af3d1c6fe19dfd779062a69f16122.1728429158.git.steadmon@google.com","threadId":"61917","inReplyTo":"cover.1728429158.git.steadmon@google.com","subject":"[PATCH v4 1/5] common-main: split init and exit code into new files","fromName":"Josh Steadmon","fromEmail":"steadmon@google.com","sentAt":"2024-10-08T23:19:31Z","receivedAt":"2024-10-08T23:19:40Z","isPatch":true,"sender":{"key":"steadmon@google.com","avatar":"https://avatars.githubusercontent.com/u/2654920?v=4"},"body":"Currently, object files in libgit.a reference common_exit(), which is\ncontained in common-main.o. However, common-main.o also includes main(),\nwhich references cmd_main() in git.o, which in turn depends on all the\nbuiltin/*.o objects.\n\nWe would like to allow external users to link libgit.a without needing\nto include so many extra objects. Enable this by splitting common_exit()\nand check_bug_if_BUG() into a new file common-exit.c, and add\ncommon-exit.o to LIB_OBJS so that these are included in libgit.a.\n\nThis split has previously been proposed ([1], [2]) to support fuzz tests\nand unit tests by avoiding conflicting definitions for main(). However,\nboth of those issues were resolved by other methods of avoiding symbol\nconflicts. Now we are trying to make libgit.a more self-contained, so\nhopefully we can revisit this approach.\n\nAdditionally, move the initialization code out of main() into a new\ninit_git() function in its own file. Include this in libgit.a as well,\nso that external users can share our setup code without calling our\nmain().\n\n[1] https://lore.kernel.org/git/Yp+wjCPhqieTku3X@google.com/\n[2] https://lore.kernel.org/git/20230517-unit-tests-v2-v2-1-21b5b60f4b32@google.com/\n\nSigned-off-by: Josh Steadmon <steadmon@google.com>\n---\n Makefile      |  2 ++\n common-exit.c | 26 ++++++++++++++++\n common-init.c | 63 ++++++++++++++++++++++++++++++++++++++\n common-init.h |  6 ++++\n common-main.c | 83 ++-------------------------------------------------\n 5 files changed, 99 insertions(+), 81 deletions(-)\n create mode 100644 common-exit.c\n create mode 100644 common-init.c\n create mode 100644 common-init.h\n\ndiff --git a/Makefile b/Makefile\nindex 3eab701b10..7caeb3c872 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -979,6 +979,8 @@ LIB_OBJS += combine-diff.o\n LIB_OBJS += commit-graph.o\n LIB_OBJS += commit-reach.o\n LIB_OBJS += commit.o\n+LIB_OBJS += common-exit.o\n+LIB_OBJS += common-init.o\n LIB_OBJS += compat/nonblock.o\n LIB_OBJS += compat/obstack.o\n LIB_OBJS += compat/terminal.o\ndiff --git a/common-exit.c b/common-exit.c\nnew file mode 100644\nindex 0000000000..1aaa538be3\n--- /dev/null\n+++ b/common-exit.c\n@@ -0,0 +1,26 @@\n+#include \"git-compat-util.h\"\n+#include \"trace2.h\"\n+\n+static void check_bug_if_BUG(void)\n+{\n+\tif (!bug_called_must_BUG)\n+\t\treturn;\n+\tBUG(\"on exit(): had bug() call(s) in this process without explicit BUG_if_bug()\");\n+}\n+\n+/* We wrap exit() to call common_exit() in git-compat-util.h */\n+int common_exit(const char *file, int line, int code)\n+{\n+\t/*\n+\t * For non-POSIX systems: Take the lowest 8 bits of the \"code\"\n+\t * to e.g. turn -1 into 255. On a POSIX system this is\n+\t * redundant, see exit(3) and wait(2), but as it doesn't harm\n+\t * anything there we don't need to guard this with an \"ifdef\".\n+\t */\n+\tcode &= 0xff;\n+\n+\tcheck_bug_if_BUG();\n+\ttrace2_cmd_exit_fl(file, line, code);\n+\n+\treturn code;\n+}\ndiff --git a/common-init.c b/common-init.c\nnew file mode 100644\nindex 0000000000..5cc73f058c\n--- /dev/null\n+++ b/common-init.c\n@@ -0,0 +1,63 @@\n+#define USE_THE_REPOSITORY_VARIABLE\n+\n+#include \"git-compat-util.h\"\n+#include \"common-init.h\"\n+#include \"exec-cmd.h\"\n+#include \"gettext.h\"\n+#include \"attr.h\"\n+#include \"repository.h\"\n+#include \"setup.h\"\n+#include \"strbuf.h\"\n+#include \"trace2.h\"\n+\n+/*\n+ * Many parts of Git have subprograms communicate via pipe, expect the\n+ * upstream of a pipe to die with SIGPIPE when the downstream of a\n+ * pipe does not need to read all that is written.  Some third-party\n+ * programs that ignore or block SIGPIPE for their own reason forget\n+ * to restore SIGPIPE handling to the default before spawning Git and\n+ * break this carefully orchestrated machinery.\n+ *\n+ * Restore the way SIGPIPE is handled to default, which is what we\n+ * expect.\n+ */\n+static void restore_sigpipe_to_default(void)\n+{\n+\tsigset_t unblock;\n+\n+\tsigemptyset(&unblock);\n+\tsigaddset(&unblock, SIGPIPE);\n+\tsigprocmask(SIG_UNBLOCK, &unblock, NULL);\n+\tsignal(SIGPIPE, SIG_DFL);\n+}\n+\n+void init_git(const char **argv)\n+{\n+\tstruct strbuf tmp = STRBUF_INIT;\n+\n+\ttrace2_initialize_clock();\n+\n+\t/*\n+\t * Always open file descriptors 0/1/2 to avoid clobbering files\n+\t * in die().  It also avoids messing up when the pipes are dup'ed\n+\t * onto stdin/stdout/stderr in the child processes we spawn.\n+\t */\n+\tsanitize_stdfds();\n+\trestore_sigpipe_to_default();\n+\n+\tgit_resolve_executable_dir(argv[0]);\n+\n+\tsetlocale(LC_CTYPE, \"\");\n+\tgit_setup_gettext();\n+\n+\tinitialize_repository(the_repository);\n+\n+\tattr_start();\n+\n+\ttrace2_initialize();\n+\ttrace2_cmd_start(argv);\n+\ttrace2_collect_process_info(TRACE2_PROCESS_INFO_STARTUP);\n+\n+\tif (!strbuf_getcwd(&tmp))\n+\t\ttmp_original_cwd = strbuf_detach(&tmp, NULL);\n+}\ndiff --git a/common-init.h b/common-init.h\nnew file mode 100644\nindex 0000000000..3e6db20cae\n--- /dev/null\n+++ b/common-init.h\n@@ -0,0 +1,6 @@\n+#ifndef COMMON_INIT_H\n+#define COMMON_INIT_H\n+\n+void init_git(const char **argv);\n+\n+#endif /* COMMON_INIT_H */\ndiff --git a/common-main.c b/common-main.c\nindex 8e68ac9e42..6b7ab077b0 100644\n--- a/common-main.c\n+++ b/common-main.c\n@@ -1,92 +1,13 @@\n-#define USE_THE_REPOSITORY_VARIABLE\n-\n #include \"git-compat-util.h\"\n-#include \"exec-cmd.h\"\n-#include \"gettext.h\"\n-#include \"attr.h\"\n-#include \"repository.h\"\n-#include \"setup.h\"\n-#include \"strbuf.h\"\n-#include \"trace2.h\"\n-\n-/*\n- * Many parts of Git have subprograms communicate via pipe, expect the\n- * upstream of a pipe to die with SIGPIPE when the downstream of a\n- * pipe does not need to read all that is written.  Some third-party\n- * programs that ignore or block SIGPIPE for their own reason forget\n- * to restore SIGPIPE handling to the default before spawning Git and\n- * break this carefully orchestrated machinery.\n- *\n- * Restore the way SIGPIPE is handled to default, which is what we\n- * expect.\n- */\n-static void restore_sigpipe_to_default(void)\n-{\n-\tsigset_t unblock;\n-\n-\tsigemptyset(&unblock);\n-\tsigaddset(&unblock, SIGPIPE);\n-\tsigprocmask(SIG_UNBLOCK, &unblock, NULL);\n-\tsignal(SIGPIPE, SIG_DFL);\n-}\n+#include \"common-init.h\"\n \n int main(int argc, const char **argv)\n {\n \tint result;\n-\tstruct strbuf tmp = STRBUF_INIT;\n-\n-\ttrace2_initialize_clock();\n-\n-\t/*\n-\t * Always open file descriptors 0/1/2 to avoid clobbering files\n-\t * in die().  It also avoids messing up when the pipes are dup'ed\n-\t * onto stdin/stdout/stderr in the child processes we spawn.\n-\t */\n-\tsanitize_stdfds();\n-\trestore_sigpipe_to_default();\n-\n-\tgit_resolve_executable_dir(argv[0]);\n-\n-\tsetlocale(LC_CTYPE, \"\");\n-\tgit_setup_gettext();\n-\n-\tinitialize_repository(the_repository);\n-\n-\tattr_start();\n-\n-\ttrace2_initialize();\n-\ttrace2_cmd_start(argv);\n-\ttrace2_collect_process_info(TRACE2_PROCESS_INFO_STARTUP);\n-\n-\tif (!strbuf_getcwd(&tmp))\n-\t\ttmp_original_cwd = strbuf_detach(&tmp, NULL);\n \n+\tinit_git(argv);\n \tresult = cmd_main(argc, argv);\n \n \t/* Not exit(3), but a wrapper calling our common_exit() */\n \texit(result);\n }\n-\n-static void check_bug_if_BUG(void)\n-{\n-\tif (!bug_called_must_BUG)\n-\t\treturn;\n-\tBUG(\"on exit(): had bug() call(s) in this process without explicit BUG_if_bug()\");\n-}\n-\n-/* We wrap exit() to call common_exit() in git-compat-util.h */\n-int common_exit(const char *file, int line, int code)\n-{\n-\t/*\n-\t * For non-POSIX systems: Take the lowest 8 bits of the \"code\"\n-\t * to e.g. turn -1 into 255. On a POSIX system this is\n-\t * redundant, see exit(3) and wait(2), but as it doesn't harm\n-\t * anything there we don't need to guard this with an \"ifdef\".\n-\t */\n-\tcode &= 0xff;\n-\n-\tcheck_bug_if_BUG();\n-\ttrace2_cmd_exit_fl(file, line, code);\n-\n-\treturn code;\n-}\n-- \n2.47.0.rc0.187.ge670bccf7e-goog\n\n"},{"id":"504500","messageId":"ed925d6a334b1df43e879699efee21b5a5d435e3.1728429158.git.steadmon@google.com","threadId":"61917","inReplyTo":"cover.1728429158.git.steadmon@google.com","subject":"[PATCH v4 2/5] libgit-sys: introduce Rust wrapper for libgit.a","fromName":"Josh Steadmon","fromEmail":"steadmon@google.com","sentAt":"2024-10-08T23:19:32Z","receivedAt":"2024-10-08T23:19:41Z","isPatch":true,"sender":{"key":"steadmon@google.com","avatar":"https://avatars.githubusercontent.com/u/2654920?v=4"},"body":"Introduce libgit-sys, a Rust wrapper crate that allows Rust code to call\nfunctions in libgit.a. This initial patch defines build rules and an\ninterface that exposes user agent string getter functions as a proof of\nconcept. This library can be tested with `cargo test`. In later commits,\na higher-level library containing a more Rust-friendly interface will be\nadded at `contrib/libgit-rs`.\n\nSymbols in libgit can collide with symbols from other libraries such as\nlibgit2. We avoid this by first exposing library symbols in\npublic_symbol_export.[ch]. These symbols are prepended with \"libgit_\" to\navoid collisions and set to visible using a visibility pragma. In\nbuild.rs, Rust builds contrib/libgit-rs/libgit-sys/libgitpub.a, which also\ncontains libgit.a and other dependent libraries, with\n-fvisibility=hidden to hide all symbols within those libraries that\nhaven't been exposed with a visibility pragma.\n\nCo-authored-by: Kyle Lippincott <spectral@google.com>\nCo-authored-by: Calvin Wan <calvinwan@google.com>\nSigned-off-by: Calvin Wan <calvinwan@google.com>\nSigned-off-by: Kyle Lippincott <spectral@google.com>\nSigned-off-by: Josh Steadmon <steadmon@google.com>\n---\n .gitignore                                    |  1 +\n Makefile                                      | 13 ++++\n contrib/libgit-rs/libgit-sys/Cargo.lock       | 69 +++++++++++++++++++\n contrib/libgit-rs/libgit-sys/Cargo.toml       | 17 +++++\n contrib/libgit-rs/libgit-sys/README.md        | 15 ++++\n contrib/libgit-rs/libgit-sys/build.rs         | 35 ++++++++++\n .../libgit-sys/public_symbol_export.c         | 21 ++++++\n .../libgit-sys/public_symbol_export.h         |  8 +++\n contrib/libgit-rs/libgit-sys/src/lib.rs       | 46 +++++++++++++\n 9 files changed, 225 insertions(+)\n create mode 100644 contrib/libgit-rs/libgit-sys/Cargo.lock\n create mode 100644 contrib/libgit-rs/libgit-sys/Cargo.toml\n create mode 100644 contrib/libgit-rs/libgit-sys/README.md\n create mode 100644 contrib/libgit-rs/libgit-sys/build.rs\n create mode 100644 contrib/libgit-rs/libgit-sys/public_symbol_export.c\n create mode 100644 contrib/libgit-rs/libgit-sys/public_symbol_export.h\n create mode 100644 contrib/libgit-rs/libgit-sys/src/lib.rs\n\ndiff --git a/.gitignore b/.gitignore\nindex 8caf3700c2..dfd72820fb 100644\n--- a/.gitignore\n+++ b/.gitignore\n@@ -248,3 +248,4 @@ Release/\n /git.VC.db\n *.dSYM\n /contrib/buildsystems/out\n+/contrib/libgit-rs/libgit-sys/target\ndiff --git a/Makefile b/Makefile\nindex 7caeb3c872..0090514e55 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -653,6 +653,8 @@ CURL_CONFIG = curl-config\n GCOV = gcov\n STRIP = strip\n SPATCH = spatch\n+LD = ld\n+OBJCOPY = objcopy\n \n export TCL_PATH TCLTK_PATH\n \n@@ -2713,6 +2715,7 @@ OBJECTS += $(XDIFF_OBJS)\n OBJECTS += $(FUZZ_OBJS)\n OBJECTS += $(REFTABLE_OBJS) $(REFTABLE_TEST_OBJS)\n OBJECTS += $(UNIT_TEST_OBJS)\n+OBJECTS += contrib/libgit-rs/libgit-sys/public_symbol_export.o\n \n ifndef NO_CURL\n \tOBJECTS += http.o http-walker.o remote-curl.o\n@@ -3720,6 +3723,7 @@ clean: profile-clean coverage-clean cocciclean\n \t$(RM) $(htmldocs).tar.gz $(manpages).tar.gz\n \t$(MAKE) -C Documentation/ clean\n \t$(RM) Documentation/GIT-EXCLUDED-PROGRAMS\n+\t$(RM) -r contrib/libgit-rs/libgit-sys/target\n ifndef NO_PERL\n \t$(RM) -r perl/build/\n endif\n@@ -3865,3 +3869,12 @@ $(UNIT_TEST_PROGS): $(UNIT_TEST_BIN)/%$X: $(UNIT_TEST_DIR)/%.o \\\n build-unit-tests: $(UNIT_TEST_PROGS)\n unit-tests: $(UNIT_TEST_PROGS) t/helper/test-tool$X\n \t$(MAKE) -C t/ unit-tests\n+\n+contrib/libgit-rs/libgit-sys/partial_symbol_export.o: contrib/libgit-rs/libgit-sys/public_symbol_export.o libgit.a reftable/libreftable.a xdiff/lib.a\n+\t$(LD) -r $^ -o $@\n+\n+contrib/libgit-rs/libgit-sys/hidden_symbol_export.o: contrib/libgit-rs/libgit-sys/partial_symbol_export.o\n+\t$(OBJCOPY) --localize-hidden $^ $@\n+\n+contrib/libgit-rs/libgit-sys/libgitpub.a: contrib/libgit-rs/libgit-sys/hidden_symbol_export.o\n+\t$(AR) $(ARFLAGS) $@ $^\ndiff --git a/contrib/libgit-rs/libgit-sys/Cargo.lock b/contrib/libgit-rs/libgit-sys/Cargo.lock\nnew file mode 100644\nindex 0000000000..427a4c66b7\n--- /dev/null\n+++ b/contrib/libgit-rs/libgit-sys/Cargo.lock\n@@ -0,0 +1,69 @@\n+# This file is automatically @generated by Cargo.\n+# It is not intended for manual editing.\n+version = 3\n+\n+[[package]]\n+name = \"autocfg\"\n+version = \"1.4.0\"\n+source = \"registry+https://github.com/rust-lang/crates.io-index\"\n+checksum = \"ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26\"\n+\n+[[package]]\n+name = \"cc\"\n+version = \"1.1.15\"\n+source = \"registry+https://github.com/rust-lang/crates.io-index\"\n+checksum = \"57b6a275aa2903740dc87da01c62040406b8812552e97129a63ea8850a17c6e6\"\n+dependencies = [\n+ \"shlex\",\n+]\n+\n+[[package]]\n+name = \"libc\"\n+version = \"0.2.158\"\n+source = \"registry+https://github.com/rust-lang/crates.io-index\"\n+checksum = \"d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439\"\n+\n+[[package]]\n+name = \"libgit-sys\"\n+version = \"0.1.0\"\n+dependencies = [\n+ \"autocfg\",\n+ \"libz-sys\",\n+ \"make-cmd\",\n+]\n+\n+[[package]]\n+name = \"libz-sys\"\n+version = \"1.1.20\"\n+source = \"registry+https://github.com/rust-lang/crates.io-index\"\n+checksum = \"d2d16453e800a8cf6dd2fc3eb4bc99b786a9b90c663b8559a5b1a041bf89e472\"\n+dependencies = [\n+ \"cc\",\n+ \"libc\",\n+ \"pkg-config\",\n+ \"vcpkg\",\n+]\n+\n+[[package]]\n+name = \"make-cmd\"\n+version = \"0.1.0\"\n+source = \"registry+https://github.com/rust-lang/crates.io-index\"\n+checksum = \"a8ca8afbe8af1785e09636acb5a41e08a765f5f0340568716c18a8700ba3c0d3\"\n+\n+[[package]]\n+name = \"pkg-config\"\n+version = \"0.3.30\"\n+source = \"registry+https://github.com/rust-lang/crates.io-index\"\n+checksum = \"d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec\"\n+\n+[[package]]\n+name = \"shlex\"\n+version = \"1.3.0\"\n+source = \"registry+https://github.com/rust-lang/crates.io-index\"\n+checksum = \"0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64\"\n+\n+[[package]]\n+name = \"vcpkg\"\n+version = \"0.2.15\"\n+source = \"registry+https://github.com/rust-lang/crates.io-index\"\n+checksum = \"accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426\"\ndiff --git a/contrib/libgit-rs/libgit-sys/Cargo.toml b/contrib/libgit-rs/libgit-sys/Cargo.toml\nnew file mode 100644\nindex 0000000000..7a230a4437\n--- /dev/null\n+++ b/contrib/libgit-rs/libgit-sys/Cargo.toml\n@@ -0,0 +1,17 @@\n+[package]\n+name = \"libgit-sys\"\n+version = \"0.1.0\"\n+edition = \"2021\"\n+build = \"build.rs\"\n+links = \"gitpub\"\n+rust-version = \"1.63\"\n+\n+[lib]\n+path = \"src/lib.rs\"\n+\n+[dependencies]\n+libz-sys = \"1.1.19\"\n+\n+[build-dependencies]\n+autocfg = \"1.4.0\"\n+make-cmd = \"0.1.0\"\ndiff --git a/contrib/libgit-rs/libgit-sys/README.md b/contrib/libgit-rs/libgit-sys/README.md\nnew file mode 100644\nindex 0000000000..7a59602c30\n--- /dev/null\n+++ b/contrib/libgit-rs/libgit-sys/README.md\n@@ -0,0 +1,15 @@\n+# cgit-info\n+\n+A small hacky proof-of-concept showing how to provide a Rust FFI for the Git\n+library.\n+\n+## Building\n+\n+`cargo build` automatically builds and picks up on changes made to both\n+the Rust wrapper and git.git code so there is no need to run `make`\n+beforehand.\n+\n+## Running\n+\n+Assuming you don't make any changes to the Git source, you can just work from\n+`contrib/cgit-rs` and use `cargo build` or `cargo run` as usual.\ndiff --git a/contrib/libgit-rs/libgit-sys/build.rs b/contrib/libgit-rs/libgit-sys/build.rs\nnew file mode 100644\nindex 0000000000..8d74120191\n--- /dev/null\n+++ b/contrib/libgit-rs/libgit-sys/build.rs\n@@ -0,0 +1,35 @@\n+use std::env;\n+use std::path::PathBuf;\n+\n+pub fn main() -> std::io::Result<()> {\n+    let ac = autocfg::new();\n+    ac.emit_has_path(\"std::ffi::c_char\");\n+\n+    let crate_root = PathBuf::from(env::var_os(\"CARGO_MANIFEST_DIR\").unwrap());\n+    let git_root = crate_root.join(\"../../..\");\n+    let dst = PathBuf::from(env::var_os(\"OUT_DIR\").unwrap());\n+\n+    let make_output = make_cmd::gnu_make()\n+        .env(\"DEVELOPER\", \"1\")\n+        .env_remove(\"PROFILE\")\n+        .current_dir(git_root.clone())\n+        .args([\n+            \"CFLAGS=-fvisibility=hidden\",\n+            \"contrib/libgit-rs/libgit-sys/libgitpub.a\",\n+        ])\n+        .output()\n+        .expect(\"Make failed to run\");\n+    if !make_output.status.success() {\n+        panic!(\n+            \"Make failed:\\n  stdout = {}\\n  stderr = {}\\n\",\n+            String::from_utf8(make_output.stdout).unwrap(),\n+            String::from_utf8(make_output.stderr).unwrap()\n+        );\n+    }\n+    std::fs::copy(crate_root.join(\"libgitpub.a\"), dst.join(\"libgitpub.a\"))?;\n+    println!(\"cargo:rustc-link-search=native={}\", dst.display());\n+    println!(\"cargo:rustc-link-lib=gitpub\");\n+    println!(\"cargo:rerun-if-changed={}\", git_root.display());\n+\n+    Ok(())\n+}\ndiff --git a/contrib/libgit-rs/libgit-sys/public_symbol_export.c b/contrib/libgit-rs/libgit-sys/public_symbol_export.c\nnew file mode 100644\nindex 0000000000..8e4b94d821\n--- /dev/null\n+++ b/contrib/libgit-rs/libgit-sys/public_symbol_export.c\n@@ -0,0 +1,21 @@\n+// Shim to publicly export Git symbols. These must be renamed so that the\n+// original symbols can be hidden. Renaming these with a \"libgit_\" prefix also\n+// avoids conflicts with other libraries such as libgit2.\n+\n+#include \"git-compat-util.h\"\n+#include \"contrib/libgit-rs/libgit-sys/public_symbol_export.h\"\n+#include \"version.h\"\n+\n+#pragma GCC visibility push(default)\n+\n+const char *libgit_user_agent(void)\n+{\n+\treturn git_user_agent();\n+}\n+\n+const char *libgit_user_agent_sanitized(void)\n+{\n+\treturn git_user_agent_sanitized();\n+}\n+\n+#pragma GCC visibility pop\ndiff --git a/contrib/libgit-rs/libgit-sys/public_symbol_export.h b/contrib/libgit-rs/libgit-sys/public_symbol_export.h\nnew file mode 100644\nindex 0000000000..a3372f93fa\n--- /dev/null\n+++ b/contrib/libgit-rs/libgit-sys/public_symbol_export.h\n@@ -0,0 +1,8 @@\n+#ifndef PUBLIC_SYMBOL_EXPORT_H\n+#define PUBLIC_SYMBOL_EXPORT_H\n+\n+const char *libgit_user_agent(void);\n+\n+const char *libgit_user_agent_sanitized(void);\n+\n+#endif /* PUBLIC_SYMBOL_EXPORT_H */\ndiff --git a/contrib/libgit-rs/libgit-sys/src/lib.rs b/contrib/libgit-rs/libgit-sys/src/lib.rs\nnew file mode 100644\nindex 0000000000..d4853f3074\n--- /dev/null\n+++ b/contrib/libgit-rs/libgit-sys/src/lib.rs\n@@ -0,0 +1,46 @@\n+#[cfg(has_std__ffi__c_char)]\n+use std::ffi::c_char;\n+\n+#[cfg(not(has_std__ffi__c_char))]\n+#[allow(non_camel_case_types)]\n+pub type c_char = i8;\n+\n+extern crate libz_sys;\n+\n+extern \"C\" {\n+    pub fn libgit_user_agent() -> *const c_char;\n+    pub fn libgit_user_agent_sanitized() -> *const c_char;\n+}\n+\n+#[cfg(test)]\n+mod tests {\n+    use std::ffi::CStr;\n+\n+    use super::*;\n+\n+    #[test]\n+    fn user_agent_starts_with_git() {\n+        let c_str = unsafe { CStr::from_ptr(libgit_user_agent()) };\n+        let agent = c_str\n+            .to_str()\n+            .expect(\"User agent contains invalid UTF-8 data\");\n+        assert!(\n+            agent.starts_with(\"git/\"),\n+            r#\"Expected user agent to start with \"git/\", got: {}\"#,\n+            agent\n+        );\n+    }\n+\n+    #[test]\n+    fn sanitized_user_agent_starts_with_git() {\n+        let c_str = unsafe { CStr::from_ptr(libgit_user_agent_sanitized()) };\n+        let agent = c_str\n+            .to_str()\n+            .expect(\"Sanitized user agent contains invalid UTF-8 data\");\n+        assert!(\n+            agent.starts_with(\"git/\"),\n+            r#\"Expected user agent to start with \"git/\", got: {}\"#,\n+            agent\n+        );\n+    }\n+}\n-- \n2.47.0.rc0.187.ge670bccf7e-goog\n\n"},{"id":"504501","messageId":"8eeab7b418b99d34a575a293cfac7bdea1e60102.1728429158.git.steadmon@google.com","threadId":"61917","inReplyTo":"cover.1728429158.git.steadmon@google.com","subject":"[PATCH v4 3/5] libgit-sys: also export some config_set functions","fromName":"Josh Steadmon","fromEmail":"steadmon@google.com","sentAt":"2024-10-08T23:19:33Z","receivedAt":"2024-10-08T23:19:44Z","isPatch":true,"sender":{"key":"steadmon@google.com","avatar":"https://avatars.githubusercontent.com/u/2654920?v=4"},"body":"In preparation for implementing a higher-level Rust API for accessing\nGit configs, export some of the upstream configset API via libgitpub and\nlibgit-sys. Since this will be exercised as part of the higher-level API\nin the next commit, no tests have been added for libgit-sys.\n\nWhile we're at it, add git_configset_alloc() and git_configset_free()\nfunctions in libgitpub so that callers can manage config_set structs on\nthe heap. This also allows non-C external consumers to treat config_sets\nas opaque structs.\n\nCo-authored-by: Calvin Wan <calvinwan@google.com>\nSigned-off-by: Calvin Wan <calvinwan@google.com>\nSigned-off-by: Josh Steadmon <steadmon@google.com>\n---\n .../libgit-sys/public_symbol_export.c         | 29 +++++++++++++++++\n .../libgit-sys/public_symbol_export.h         | 10 ++++++\n contrib/libgit-rs/libgit-sys/src/lib.rs       | 31 ++++++++++++++++++-\n 3 files changed, 69 insertions(+), 1 deletion(-)\n\ndiff --git a/contrib/libgit-rs/libgit-sys/public_symbol_export.c b/contrib/libgit-rs/libgit-sys/public_symbol_export.c\nindex 8e4b94d821..29e8a5363e 100644\n--- a/contrib/libgit-rs/libgit-sys/public_symbol_export.c\n+++ b/contrib/libgit-rs/libgit-sys/public_symbol_export.c\n@@ -3,11 +3,40 @@\n // avoids conflicts with other libraries such as libgit2.\n \n #include \"git-compat-util.h\"\n+#include \"config.h\"\n #include \"contrib/libgit-rs/libgit-sys/public_symbol_export.h\"\n #include \"version.h\"\n \n #pragma GCC visibility push(default)\n \n+struct libgit_config_set *libgit_configset_alloc(void)\n+{\n+\tstruct config_set *cs = xmalloc(sizeof(struct config_set));\n+\tgit_configset_init(cs);\n+\treturn (struct libgit_config_set *) cs;\n+}\n+\n+void libgit_configset_free(struct libgit_config_set *cs)\n+{\n+\tgit_configset_clear((struct config_set *) cs);\n+\tfree((struct config_set *) cs);\n+}\n+\n+int libgit_configset_add_file(struct libgit_config_set *cs, const char *filename)\n+{\n+\treturn git_configset_add_file((struct config_set *) cs, filename);\n+}\n+\n+int libgit_configset_get_int(struct libgit_config_set *cs, const char *key, int *dest)\n+{\n+\treturn git_configset_get_int((struct config_set *) cs, key, dest);\n+}\n+\n+int libgit_configset_get_string(struct libgit_config_set *cs, const char *key, char **dest)\n+{\n+\treturn git_configset_get_string((struct config_set *) cs, key, dest);\n+}\n+\n const char *libgit_user_agent(void)\n {\n \treturn git_user_agent();\ndiff --git a/contrib/libgit-rs/libgit-sys/public_symbol_export.h b/contrib/libgit-rs/libgit-sys/public_symbol_export.h\nindex a3372f93fa..701db92d53 100644\n--- a/contrib/libgit-rs/libgit-sys/public_symbol_export.h\n+++ b/contrib/libgit-rs/libgit-sys/public_symbol_export.h\n@@ -1,6 +1,16 @@\n #ifndef PUBLIC_SYMBOL_EXPORT_H\n #define PUBLIC_SYMBOL_EXPORT_H\n \n+struct libgit_config_set *libgit_configset_alloc(void);\n+\n+void libgit_configset_free(struct libgit_config_set *cs);\n+\n+int libgit_configset_add_file(struct libgit_config_set *cs, const char *filename);\n+\n+int libgit_configset_get_int(struct libgit_config_set *cs, const char *key, int *dest);\n+\n+int libgit_configset_get_string(struct libgit_config_set *cs, const char *key, char **dest);\n+\n const char *libgit_user_agent(void);\n \n const char *libgit_user_agent_sanitized(void);\ndiff --git a/contrib/libgit-rs/libgit-sys/src/lib.rs b/contrib/libgit-rs/libgit-sys/src/lib.rs\nindex d4853f3074..dadb4e5f40 100644\n--- a/contrib/libgit-rs/libgit-sys/src/lib.rs\n+++ b/contrib/libgit-rs/libgit-sys/src/lib.rs\n@@ -1,15 +1,44 @@\n #[cfg(has_std__ffi__c_char)]\n-use std::ffi::c_char;\n+use std::ffi::{c_char, c_int};\n \n #[cfg(not(has_std__ffi__c_char))]\n #[allow(non_camel_case_types)]\n pub type c_char = i8;\n \n+#[cfg(not(has_std__ffi__c_char))]\n+#[allow(non_camel_case_types)]\n+pub type c_int = i32;\n+\n extern crate libz_sys;\n \n+#[allow(non_camel_case_types)]\n+#[repr(C)]\n+pub struct libgit_config_set {\n+    _data: [u8; 0],\n+    _marker: core::marker::PhantomData<(*mut u8, core::marker::PhantomPinned)>,\n+}\n+\n extern \"C\" {\n     pub fn libgit_user_agent() -> *const c_char;\n     pub fn libgit_user_agent_sanitized() -> *const c_char;\n+\n+    pub fn libgit_configset_alloc() -> *mut libgit_config_set;\n+    pub fn libgit_configset_free(cs: *mut libgit_config_set);\n+\n+    pub fn libgit_configset_add_file(cs: *mut libgit_config_set, filename: *const c_char) -> c_int;\n+\n+    pub fn libgit_configset_get_int(\n+        cs: *mut libgit_config_set,\n+        key: *const c_char,\n+        int: *mut c_int,\n+    ) -> c_int;\n+\n+    pub fn libgit_configset_get_string(\n+        cs: *mut libgit_config_set,\n+        key: *const c_char,\n+        dest: *mut *mut c_char,\n+    ) -> c_int;\n+\n }\n \n #[cfg(test)]\n-- \n2.47.0.rc0.187.ge670bccf7e-goog\n\n"},{"id":"504502","messageId":"29599e9c7be1737bcf0de0541c9635212a1b691d.1728429158.git.steadmon@google.com","threadId":"61917","inReplyTo":"cover.1728429158.git.steadmon@google.com","subject":"[PATCH v4 4/5] libgit: add higher-level libgit crate","fromName":"Josh Steadmon","fromEmail":"steadmon@google.com","sentAt":"2024-10-08T23:19:34Z","receivedAt":"2024-10-08T23:19:46Z","isPatch":true,"sender":{"key":"steadmon@google.com","avatar":"https://avatars.githubusercontent.com/u/2654920?v=4"},"body":"From: Calvin Wan <calvinwan@google.com>\n\nWrap `struct config_set` and a few of its associated functions in\nlibgit-sys. Also introduce a higher-level \"libgit\" crate which provides\na more Rust-friendly interface to config_set structs.\n\nCo-authored-by: Josh Steadmon <steadmon@google.com>\nSigned-off-by: Calvin Wan <calvinwan@google.com>\nSigned-off-by: Josh Steadmon <steadmon@google.com>\n---\n .gitignore                              |  1 +\n Makefile                                |  2 +-\n contrib/libgit-rs/Cargo.lock            | 77 ++++++++++++++++++++\n contrib/libgit-rs/Cargo.toml            | 15 ++++\n contrib/libgit-rs/build.rs              |  4 ++\n contrib/libgit-rs/libgit-sys/src/lib.rs |  4 ++\n contrib/libgit-rs/src/lib.rs            | 95 +++++++++++++++++++++++++\n contrib/libgit-rs/testdata/config1      |  2 +\n contrib/libgit-rs/testdata/config2      |  2 +\n contrib/libgit-rs/testdata/config3      |  2 +\n 10 files changed, 203 insertions(+), 1 deletion(-)\n create mode 100644 contrib/libgit-rs/Cargo.lock\n create mode 100644 contrib/libgit-rs/Cargo.toml\n create mode 100644 contrib/libgit-rs/build.rs\n create mode 100644 contrib/libgit-rs/src/lib.rs\n create mode 100644 contrib/libgit-rs/testdata/config1\n create mode 100644 contrib/libgit-rs/testdata/config2\n create mode 100644 contrib/libgit-rs/testdata/config3\n\ndiff --git a/.gitignore b/.gitignore\nindex dfd72820fb..0a42f27117 100644\n--- a/.gitignore\n+++ b/.gitignore\n@@ -248,4 +248,5 @@ Release/\n /git.VC.db\n *.dSYM\n /contrib/buildsystems/out\n+/contrib/libgit-rs/target\n /contrib/libgit-rs/libgit-sys/target\ndiff --git a/Makefile b/Makefile\nindex 0090514e55..abeee01d9e 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -3723,7 +3723,7 @@ clean: profile-clean coverage-clean cocciclean\n \t$(RM) $(htmldocs).tar.gz $(manpages).tar.gz\n \t$(MAKE) -C Documentation/ clean\n \t$(RM) Documentation/GIT-EXCLUDED-PROGRAMS\n-\t$(RM) -r contrib/libgit-rs/libgit-sys/target\n+\t$(RM) -r contrib/libgit-rs/target contrib/libgit-rs/libgit-sys/target\n ifndef NO_PERL\n \t$(RM) -r perl/build/\n endif\ndiff --git a/contrib/libgit-rs/Cargo.lock b/contrib/libgit-rs/Cargo.lock\nnew file mode 100644\nindex 0000000000..a30c7c8d33\n--- /dev/null\n+++ b/contrib/libgit-rs/Cargo.lock\n@@ -0,0 +1,77 @@\n+# This file is automatically @generated by Cargo.\n+# It is not intended for manual editing.\n+version = 3\n+\n+[[package]]\n+name = \"autocfg\"\n+version = \"1.4.0\"\n+source = \"registry+https://github.com/rust-lang/crates.io-index\"\n+checksum = \"ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26\"\n+\n+[[package]]\n+name = \"cc\"\n+version = \"1.1.15\"\n+source = \"registry+https://github.com/rust-lang/crates.io-index\"\n+checksum = \"57b6a275aa2903740dc87da01c62040406b8812552e97129a63ea8850a17c6e6\"\n+dependencies = [\n+ \"shlex\",\n+]\n+\n+[[package]]\n+name = \"libc\"\n+version = \"0.2.158\"\n+source = \"registry+https://github.com/rust-lang/crates.io-index\"\n+checksum = \"d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439\"\n+\n+[[package]]\n+name = \"libgit\"\n+version = \"0.1.0\"\n+dependencies = [\n+ \"autocfg\",\n+ \"libgit-sys\",\n+]\n+\n+[[package]]\n+name = \"libgit-sys\"\n+version = \"0.1.0\"\n+dependencies = [\n+ \"autocfg\",\n+ \"libz-sys\",\n+ \"make-cmd\",\n+]\n+\n+[[package]]\n+name = \"libz-sys\"\n+version = \"1.1.20\"\n+source = \"registry+https://github.com/rust-lang/crates.io-index\"\n+checksum = \"d2d16453e800a8cf6dd2fc3eb4bc99b786a9b90c663b8559a5b1a041bf89e472\"\n+dependencies = [\n+ \"cc\",\n+ \"libc\",\n+ \"pkg-config\",\n+ \"vcpkg\",\n+]\n+\n+[[package]]\n+name = \"make-cmd\"\n+version = \"0.1.0\"\n+source = \"registry+https://github.com/rust-lang/crates.io-index\"\n+checksum = \"a8ca8afbe8af1785e09636acb5a41e08a765f5f0340568716c18a8700ba3c0d3\"\n+\n+[[package]]\n+name = \"pkg-config\"\n+version = \"0.3.30\"\n+source = \"registry+https://github.com/rust-lang/crates.io-index\"\n+checksum = \"d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec\"\n+\n+[[package]]\n+name = \"shlex\"\n+version = \"1.3.0\"\n+source = \"registry+https://github.com/rust-lang/crates.io-index\"\n+checksum = \"0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64\"\n+\n+[[package]]\n+name = \"vcpkg\"\n+version = \"0.2.15\"\n+source = \"registry+https://github.com/rust-lang/crates.io-index\"\n+checksum = \"accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426\"\ndiff --git a/contrib/libgit-rs/Cargo.toml b/contrib/libgit-rs/Cargo.toml\nnew file mode 100644\nindex 0000000000..6b4387b9e7\n--- /dev/null\n+++ b/contrib/libgit-rs/Cargo.toml\n@@ -0,0 +1,15 @@\n+[package]\n+name = \"libgit\"\n+version = \"0.1.0\"\n+edition = \"2021\"\n+build = \"build.rs\"\n+rust-version = \"1.63\"\n+\n+[lib]\n+path = \"src/lib.rs\"\n+\n+[dependencies]\n+libgit-sys = { version = \"0.1.0\", path = \"libgit-sys\" }\n+\n+[build-dependencies]\n+autocfg = \"1.4.0\"\ndiff --git a/contrib/libgit-rs/build.rs b/contrib/libgit-rs/build.rs\nnew file mode 100644\nindex 0000000000..f8bd01a690\n--- /dev/null\n+++ b/contrib/libgit-rs/build.rs\n@@ -0,0 +1,4 @@\n+pub fn main() {\n+    let ac = autocfg::new();\n+    ac.emit_has_path(\"std::ffi::c_char\");\n+}\ndiff --git a/contrib/libgit-rs/libgit-sys/src/lib.rs b/contrib/libgit-rs/libgit-sys/src/lib.rs\nindex dadb4e5f40..4bfc650450 100644\n--- a/contrib/libgit-rs/libgit-sys/src/lib.rs\n+++ b/contrib/libgit-rs/libgit-sys/src/lib.rs\n@@ -1,3 +1,5 @@\n+use std::ffi::c_void;\n+\n #[cfg(has_std__ffi__c_char)]\n use std::ffi::{c_char, c_int};\n \n@@ -19,6 +21,8 @@ pub struct libgit_config_set {\n }\n \n extern \"C\" {\n+    pub fn free(ptr: *mut c_void);\n+\n     pub fn libgit_user_agent() -> *const c_char;\n     pub fn libgit_user_agent_sanitized() -> *const c_char;\n \ndiff --git a/contrib/libgit-rs/src/lib.rs b/contrib/libgit-rs/src/lib.rs\nnew file mode 100644\nindex 0000000000..d228091f3d\n--- /dev/null\n+++ b/contrib/libgit-rs/src/lib.rs\n@@ -0,0 +1,95 @@\n+use std::ffi::{c_void, CStr, CString};\n+use std::path::Path;\n+\n+#[cfg(has_std__ffi__c_char)]\n+use std::ffi::{c_char, c_int};\n+\n+#[cfg(not(has_std__ffi__c_char))]\n+#[allow(non_camel_case_types)]\n+pub type c_char = i8;\n+\n+#[cfg(not(has_std__ffi__c_char))]\n+#[allow(non_camel_case_types)]\n+pub type c_int = i32;\n+\n+use libgit_sys::*;\n+\n+pub struct ConfigSet(*mut libgit_config_set);\n+impl ConfigSet {\n+    pub fn new() -> Self {\n+        unsafe { ConfigSet(libgit_configset_alloc()) }\n+    }\n+\n+    pub fn add_files(&mut self, files: &[&Path]) {\n+        for file in files {\n+            let pstr = file.to_str().expect(\"Invalid UTF-8\");\n+            let rs = CString::new(pstr).expect(\"Couldn't convert to CString\");\n+            unsafe {\n+                libgit_configset_add_file(self.0, rs.as_ptr());\n+            }\n+        }\n+    }\n+\n+    pub fn get_int(&mut self, key: &str) -> Option<c_int> {\n+        let key = CString::new(key).expect(\"Couldn't convert to CString\");\n+        let mut val: c_int = 0;\n+        unsafe {\n+            if libgit_configset_get_int(self.0, key.as_ptr(), &mut val as *mut c_int) != 0 {\n+                return None;\n+            }\n+        }\n+\n+        Some(val)\n+    }\n+\n+    pub fn get_string(&mut self, key: &str) -> Option<String> {\n+        let key = CString::new(key).expect(\"Couldn't convert key to CString\");\n+        let mut val: *mut c_char = std::ptr::null_mut();\n+        unsafe {\n+            if libgit_configset_get_string(self.0, key.as_ptr(), &mut val as *mut *mut c_char) != 0\n+            {\n+                return None;\n+            }\n+            let borrowed_str = CStr::from_ptr(val);\n+            let owned_str =\n+                String::from(borrowed_str.to_str().expect(\"Couldn't convert val to str\"));\n+            free(val as *mut c_void); // Free the xstrdup()ed pointer from the C side\n+            Some(owned_str)\n+        }\n+    }\n+}\n+\n+impl Default for ConfigSet {\n+    fn default() -> Self {\n+        Self::new()\n+    }\n+}\n+\n+impl Drop for ConfigSet {\n+    fn drop(&mut self) {\n+        unsafe {\n+            libgit_configset_free(self.0);\n+        }\n+    }\n+}\n+\n+#[cfg(test)]\n+mod tests {\n+    use super::*;\n+\n+    #[test]\n+    fn load_configs_via_configset() {\n+        let mut cs = ConfigSet::new();\n+        cs.add_files(&[\n+            Path::new(\"testdata/config1\"),\n+            Path::new(\"testdata/config2\"),\n+            Path::new(\"testdata/config3\"),\n+        ]);\n+        // ConfigSet retrieves correct value\n+        assert_eq!(cs.get_int(\"trace2.eventTarget\"), Some(1));\n+        // ConfigSet respects last config value set\n+        assert_eq!(cs.get_int(\"trace2.eventNesting\"), Some(3));\n+        // ConfigSet returns None for missing key\n+        assert_eq!(cs.get_string(\"foo.bar\"), None);\n+    }\n+}\ndiff --git a/contrib/libgit-rs/testdata/config1 b/contrib/libgit-rs/testdata/config1\nnew file mode 100644\nindex 0000000000..4e9a9d25d1\n--- /dev/null\n+++ b/contrib/libgit-rs/testdata/config1\n@@ -0,0 +1,2 @@\n+[trace2]\n+\teventNesting = 1\ndiff --git a/contrib/libgit-rs/testdata/config2 b/contrib/libgit-rs/testdata/config2\nnew file mode 100644\nindex 0000000000..b8d1eca423\n--- /dev/null\n+++ b/contrib/libgit-rs/testdata/config2\n@@ -0,0 +1,2 @@\n+[trace2]\n+\teventTarget = 1\ndiff --git a/contrib/libgit-rs/testdata/config3 b/contrib/libgit-rs/testdata/config3\nnew file mode 100644\nindex 0000000000..ca7b9a7c38\n--- /dev/null\n+++ b/contrib/libgit-rs/testdata/config3\n@@ -0,0 +1,2 @@\n+[trace2]\n+\teventNesting = 3\n-- \n2.47.0.rc0.187.ge670bccf7e-goog\n\n"},{"id":"504503","messageId":"2ed503216f8e14d7b516c488caf3c76ffcb15697.1728429158.git.steadmon@google.com","threadId":"61917","inReplyTo":"cover.1728429158.git.steadmon@google.com","subject":"[PATCH v4 5/5] Makefile: add option to build and test libgit-rs and libgit-rs-sys","fromName":"Josh Steadmon","fromEmail":"steadmon@google.com","sentAt":"2024-10-08T23:19:35Z","receivedAt":"2024-10-08T23:19:48Z","isPatch":true,"sender":{"key":"steadmon@google.com","avatar":"https://avatars.githubusercontent.com/u/2654920?v=4"},"body":"From: Calvin Wan <calvinwan@google.com>\n\nAdd libgitrs, libgitrs-sys, libgitrs-test, and libgitrs-sys-test targets\nto their respective Makefiles so they can be built and tested without\nhaving to run cargo build/test.\n\nAdd environment variable, INCLUDE_LIBGIT_RS, that when set,\nautomatically builds and tests libgit-rs and libgit-rs-sys when `make\nall` is ran.\n\nSigned-off-by: Calvin Wan <calvinwan@google.com>\nSigned-off-by: Josh Steadmon <steadmon@google.com>\n---\n Makefile   | 16 ++++++++++++++++\n t/Makefile | 16 ++++++++++++++++\n 2 files changed, 32 insertions(+)\n\ndiff --git a/Makefile b/Makefile\nindex abeee01d9e..41ad458aef 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -3870,6 +3870,22 @@ build-unit-tests: $(UNIT_TEST_PROGS)\n unit-tests: $(UNIT_TEST_PROGS) t/helper/test-tool$X\n \t$(MAKE) -C t/ unit-tests\n \n+.PHONY: libgitrs-sys\n+libgitrs-sys:\n+\t$(QUIET)(\\\n+\t\tcd contrib/libgit-rs/libgit-sys && \\\n+\t\tcargo build \\\n+\t)\n+.PHONY: libgitrs\n+libgitrs:\n+\t$(QUIET)(\\\n+\t\tcd contrib/libgit-rs && \\\n+\t\tcargo build \\\n+\t)\n+ifdef INCLUDE_LIBGIT_RS\n+all:: libgitrs\n+endif\n+\n contrib/libgit-rs/libgit-sys/partial_symbol_export.o: contrib/libgit-rs/libgit-sys/public_symbol_export.o libgit.a reftable/libreftable.a xdiff/lib.a\n \t$(LD) -r $^ -o $@\n \ndiff --git a/t/Makefile b/t/Makefile\nindex b2eb9f770b..066cb5c2b4 100644\n--- a/t/Makefile\n+++ b/t/Makefile\n@@ -169,3 +169,19 @@ perf:\n \n .PHONY: pre-clean $(T) aggregate-results clean valgrind perf \\\n \tcheck-chainlint clean-chainlint test-chainlint $(UNIT_TESTS)\n+\n+.PHONY: libgitrs-sys-test\n+libgitrs-sys-test:\n+\t$(QUIET)(\\\n+\t\tcd ../contrib/libgit-rs/libgit-sys && \\\n+\t\tcargo test \\\n+\t)\n+.PHONY: libgitrs-test\n+libgitrs-test:\n+\t$(QUIET)(\\\n+\t\tcd ../contrib/libgit-rs && \\\n+\t\tcargo test \\\n+\t)\n+ifdef INCLUDE_LIBGIT_RS\n+all:: libgitrs-sys-test libgitrs-test\n+endif\n-- \n2.47.0.rc0.187.ge670bccf7e-goog\n\n"},{"id":"504504","messageId":"xmqqed4qxior.fsf@gitster.g","threadId":"61917","inReplyTo":"2ed503216f8e14d7b516c488caf3c76ffcb15697.1728429158.git.steadmon@google.com","subject":"Re: [PATCH v4 5/5] Makefile: add option to build and test libgit-rs and libgit-rs-sys","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2024-10-08T23:45:40Z","receivedAt":"2024-10-08T23:45:42Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Josh Steadmon <steadmon@google.com> writes:\n\n> Add environment variable, INCLUDE_LIBGIT_RS, that when set,\n> automatically builds and tests libgit-rs and libgit-rs-sys when `make\n> all` is ran.\n\nIs this unusual, or is it just like how other makefile macros like\nsay USE_NSEC (to cause the resulting Git to use subsecond mtimes)\nare meant to be used to control the build?  IOW, shouldn't this be\ndocumented near the top of the Makefile, e.g.\n\n    diff --git i/Makefile w/Makefile\n    index 41ad458aef..2b55fe9672 100644\n    --- i/Makefile\n    +++ w/Makefile\n    @@ -392,6 +392,9 @@ include shared.mak\n     # INSTALL_STRIP can be set to \"-s\" to strip binaries during installation,\n     # if your $(INSTALL) command supports the option.\n     #\n    +# Define INCLUDE_LIBGIT_RS if you want your gostak to distim\n    +# the doshes and ...\n    +#\n     # Define GENERATE_COMPILATION_DATABASE to \"yes\" to generate JSON compilation\n     # database entries during compilation if your compiler supports it, using the\n     # `-MJ` flag. The JSON entries will be placed in the `compile_commands/`\n\nIt might make sense to follow naming convention to call it NO_RUST\nand flip its polarity.  Those who do not have or want libgit-rs and\nfriends can say NO_RUST but otherwise it gets built by default.  It\nwould give you a wider developer population coverage.\n\nThanks.\n\n\n\n"},{"id":"504505","messageId":"xmqq7caixhz0.fsf@gitster.g","threadId":"61917","inReplyTo":"2ed503216f8e14d7b516c488caf3c76ffcb15697.1728429158.git.steadmon@google.com","subject":"Re: [PATCH v4 5/5] Makefile: add option to build and test libgit-rs and libgit-rs-sys","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2024-10-09T00:01:07Z","receivedAt":"2024-10-09T00:01:10Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Josh Steadmon <steadmon@google.com> writes:\n\n> From: Calvin Wan <calvinwan@google.com>\n>\n> Add libgitrs, libgitrs-sys, libgitrs-test, and libgitrs-sys-test targets\n> to their respective Makefiles so they can be built and tested without\n> having to run cargo build/test.\n>\n> Add environment variable, INCLUDE_LIBGIT_RS, that when set,\n> automatically builds and tests libgit-rs and libgit-rs-sys when `make\n> all` is ran.\n>\n> Signed-off-by: Calvin Wan <calvinwan@google.com>\n> Signed-off-by: Josh Steadmon <steadmon@google.com>\n> ---\n>  Makefile   | 16 ++++++++++++++++\n>  t/Makefile | 16 ++++++++++++++++\n>  2 files changed, 32 insertions(+)\n\nInteresting.\n\nI tried\n\n\t$ make INCLUDE_LIBGIT_RS=YesPlease\n\nwhich did not fail, and then did the same\n\n\t$ make INCLUDE_LIBGIT_RS=YesPlease\n\nand was surprised to see that not only the libgit-sys part but\neverything was recompiled and rebuilt.\n\n> diff --git a/Makefile b/Makefile\n> ...\n> +.PHONY: libgitrs\n> +libgitrs:\n> +\t$(QUIET)(\\\n> +\t\tcd contrib/libgit-rs && \\\n> +\t\tcargo build \\\n> +\t)\n> +ifdef INCLUDE_LIBGIT_RS\n> +all:: libgitrs\n> +endif\n> +\n>  contrib/libgit-rs/libgit-sys/partial_symbol_export.o: contrib/libgit-rs/libgit-sys/public_symbol_export.o libgit.a reftable/libreftable.a xdiff/lib.a\n>  \t$(LD) -r $^ -o $@\n\nI can see libgitrs is a phony target designed to run every time it\ngets triggered, and I would imagine \"cargo build\" itself would avoid\nrepeating unnecessary work, but I do not see this patch screwing up\nwith the dependencies for other object files.\n\nIs it fair to say this is still a WIP?  Showing a WIP to others and\nasking for help is OK, but it is fair to make sure that others know\nwhat is expected of them.\n\nThanks.\n"},{"id":"504506","messageId":"xmqq1q0qxhj2.fsf@gitster.g","threadId":"61917","inReplyTo":"2ed503216f8e14d7b516c488caf3c76ffcb15697.1728429158.git.steadmon@google.com","subject":"Re: [PATCH v4 5/5] Makefile: add option to build and test libgit-rs and libgit-rs-sys","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2024-10-09T00:10:41Z","receivedAt":"2024-10-09T00:10:43Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Josh Steadmon <steadmon@google.com> writes:\n\n> From: Calvin Wan <calvinwan@google.com>\n>\n> Add libgitrs, libgitrs-sys, libgitrs-test, and libgitrs-sys-test targets\n> to their respective Makefiles so they can be built and tested without\n> having to run cargo build/test.\n>\n> Add environment variable, INCLUDE_LIBGIT_RS, that when set,\n> automatically builds and tests libgit-rs and libgit-rs-sys when `make\n> all` is ran.\n>\n> Signed-off-by: Calvin Wan <calvinwan@google.com>\n> Signed-off-by: Josh Steadmon <steadmon@google.com>\n> ---\n>  Makefile   | 16 ++++++++++++++++\n>  t/Makefile | 16 ++++++++++++++++\n>  2 files changed, 32 insertions(+)\n\nAfter \n\n    $ make INCLUDE_LIBGIT_RS=YesPlease\n\nrunning either\n\n    $ make INCLUDE_LIBGIT_RS=YesPlease distclean\n    $ make distclean\n\nleaves\n\n    $ git clean -n -x\n    Would remove contrib/libgit-rs/libgit-sys/libgitpub.a\n\nbehind.  We'd need to add a bit more to the Makefile, it seems.\n\n\n\n Makefile | 9 ++++++++-\n 1 file changed, 8 insertions(+), 1 deletion(-)\n\ndiff --git i/Makefile w/Makefile\nindex 41ad458aef..2acb5353d1 100644\n--- i/Makefile\n+++ w/Makefile\n@@ -392,6 +392,9 @@ include shared.mak\n # INSTALL_STRIP can be set to \"-s\" to strip binaries during installation,\n # if your $(INSTALL) command supports the option.\n #\n+# Define INCLUDE_LIBGIT_RS if you want your gostak to distim\n+# the doshes.\n+#\n # Define GENERATE_COMPILATION_DATABASE to \"yes\" to generate JSON compilation\n # database entries during compilation if your compiler supports it, using the\n # `-MJ` flag. The JSON entries will be placed in the `compile_commands/`\n@@ -771,6 +774,9 @@ PROGRAM_OBJS += shell.o\n .PHONY: program-objs\n program-objs: $(PROGRAM_OBJS)\n \n+# libgit-rs stuff\n+LIBGITPUB_A = contrib/libgit-rs/libgit-sys/libgitpub.a\n+\n # Binary suffix, set to .exe for Windows builds\n X =\n \n@@ -3708,6 +3714,7 @@ clean: profile-clean coverage-clean cocciclean\n \t$(RM) po/git.pot po/git-core.pot\n \t$(RM) git.res\n \t$(RM) $(OBJECTS)\n+\t$(RM) $(LIBGITPUB_A)\n \t$(RM) headless-git.o\n \t$(RM) $(LIB_FILE) $(XDIFF_LIB) $(REFTABLE_LIB) $(REFTABLE_TEST_LIB)\n \t$(RM) $(ALL_PROGRAMS) $(SCRIPT_LIB) $(BUILT_INS) $(OTHER_PROGRAMS)\n@@ -3892,5 +3899,5 @@ contrib/libgit-rs/libgit-sys/partial_symbol_export.o: contrib/libgit-rs/libgit-s\n contrib/libgit-rs/libgit-sys/hidden_symbol_export.o: contrib/libgit-rs/libgit-sys/partial_symbol_export.o\n \t$(OBJCOPY) --localize-hidden $^ $@\n \n-contrib/libgit-rs/libgit-sys/libgitpub.a: contrib/libgit-rs/libgit-sys/hidden_symbol_export.o\n+$(LIBGITPUB_A): contrib/libgit-rs/libgit-sys/hidden_symbol_export.o\n \t$(AR) $(ARFLAGS) $@ $^\n"},{"id":"504507","messageId":"02a401db19df$eaca75e0$c05f61a0$@nexbridge.com","threadId":"61917","inReplyTo":"xmqqed4qxior.fsf@gitster.g","subject":"RE: [PATCH v4 5/5] Makefile: add option to build and test libgit-rs and libgit-rs-sys","fromName":"","fromEmail":"rsbecker@nexbridge.com","sentAt":"2024-10-09T00:12:18Z","receivedAt":"2024-10-09T00:19:17Z","isPatch":true,"sender":{"key":"randall.becker@nexbridge.ca","avatar":"https://avatars.githubusercontent.com/u/28956764?v=4"},"body":"On October 8, 2024 7:46 PM, Junio C Hamano wrote:\n>Josh Steadmon <steadmon@google.com> writes:\n>\n>> Add environment variable, INCLUDE_LIBGIT_RS, that when set,\n>> automatically builds and tests libgit-rs and libgit-rs-sys when `make\n>> all` is ran.\n>\n>Is this unusual, or is it just like how other makefile macros like say\nUSE_NSEC (to\n>cause the resulting Git to use subsecond mtimes) are meant to be used to\ncontrol\n>the build?  IOW, shouldn't this be documented near the top of the Makefile,\ne.g.\n>\n>    diff --git i/Makefile w/Makefile\n>    index 41ad458aef..2b55fe9672 100644\n>    --- i/Makefile\n>    +++ w/Makefile\n>    @@ -392,6 +392,9 @@ include shared.mak\n>     # INSTALL_STRIP can be set to \"-s\" to strip binaries during\ninstallation,\n>     # if your $(INSTALL) command supports the option.\n>     #\n>    +# Define INCLUDE_LIBGIT_RS if you want your gostak to distim\n>    +# the doshes and ...\n>    +#\n>     # Define GENERATE_COMPILATION_DATABASE to \"yes\" to generate JSON\n>compilation\n>     # database entries during compilation if your compiler supports it,\nusing the\n>     # `-MJ` flag. The JSON entries will be placed in the\n`compile_commands/`\n>\n>It might make sense to follow naming convention to call it NO_RUST and flip\nits\n>polarity.  Those who do not have or want libgit-rs and friends can say\nNO_RUST but\n>otherwise it gets built by default.  It would give you a wider developer\npopulation\n>coverage.\n\nSome of us who do not have Rust (yet) approve this message. I hope our\nsituation\nwill change on having Rust on NonStop.\n\n"},{"id":"504516","messageId":"CAPig+cQEE=rOxb49rHHY5sDvSFm4+nU6RdFgXmQyt8EhMpFMLg@mail.gmail.com","threadId":"61917","inReplyTo":"29599e9c7be1737bcf0de0541c9635212a1b691d.1728429158.git.steadmon@google.com","subject":"Re: [PATCH v4 4/5] libgit: add higher-level libgit crate","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2024-10-09T06:18:24Z","receivedAt":"2024-10-09T06:18:36Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Tue, Oct 8, 2024 at 7:19 PM Josh Steadmon <steadmon@google.com> wrote:\n> Wrap `struct config_set` and a few of its associated functions in\n> libgit-sys. Also introduce a higher-level \"libgit\" crate which provides\n> a more Rust-friendly interface to config_set structs.\n>\n> Signed-off-by: Calvin Wan <calvinwan@google.com>\n> Signed-off-by: Josh Steadmon <steadmon@google.com>\n> ---\n> diff --git a/contrib/libgit-rs/src/lib.rs b/contrib/libgit-rs/src/lib.rs\n> @@ -0,0 +1,95 @@\n> +pub struct ConfigSet(*mut libgit_config_set);\n> +impl ConfigSet {\n> +    pub fn get_int(&mut self, key: &str) -> Option<c_int> {\n> +        let key = CString::new(key).expect(\"Couldn't convert to CString\");\n> +        let mut val: c_int = 0;\n> +        unsafe {\n> +            if libgit_configset_get_int(self.0, key.as_ptr(), &mut val as *mut c_int) != 0 {\n> +                return None;\n> +            }\n> +        }\n> +        Some(val)\n> +    }\n\nConsidering that v4 finally gets around to swapping out `CString` for\n`String` in order to make this high-level crate more\nRust-programmer-friendly, I was more than a little surprised to see\nthat this function is still exposing `c_int` rather than, say, `i64`\nor such.\n"},{"id":"504654","messageId":"qxlotenjjgmoal7qnxiutxknrcf4sa7mgwxcepya3luuilxdhq@c3qqr7uftz3u","threadId":"61917","inReplyTo":"CAPig+cQEE=rOxb49rHHY5sDvSFm4+nU6RdFgXmQyt8EhMpFMLg@mail.gmail.com","subject":"Re: [PATCH v4 4/5] libgit: add higher-level libgit crate","fromName":"Josh Steadmon","fromEmail":"steadmon@google.com","sentAt":"2024-10-09T21:21:28Z","receivedAt":"2024-10-09T21:21:34Z","isPatch":true,"sender":{"key":"steadmon@google.com","avatar":"https://avatars.githubusercontent.com/u/2654920?v=4"},"body":"On 2024.10.09 02:18, Eric Sunshine wrote:\n> On Tue, Oct 8, 2024 at 7:19 PM Josh Steadmon <steadmon@google.com> wrote:\n> > Wrap `struct config_set` and a few of its associated functions in\n> > libgit-sys. Also introduce a higher-level \"libgit\" crate which provides\n> > a more Rust-friendly interface to config_set structs.\n> >\n> > Signed-off-by: Calvin Wan <calvinwan@google.com>\n> > Signed-off-by: Josh Steadmon <steadmon@google.com>\n> > ---\n> > diff --git a/contrib/libgit-rs/src/lib.rs b/contrib/libgit-rs/src/lib.rs\n> > @@ -0,0 +1,95 @@\n> > +pub struct ConfigSet(*mut libgit_config_set);\n> > +impl ConfigSet {\n> > +    pub fn get_int(&mut self, key: &str) -> Option<c_int> {\n> > +        let key = CString::new(key).expect(\"Couldn't convert to CString\");\n> > +        let mut val: c_int = 0;\n> > +        unsafe {\n> > +            if libgit_configset_get_int(self.0, key.as_ptr(), &mut val as *mut c_int) != 0 {\n> > +                return None;\n> > +            }\n> > +        }\n> > +        Some(val)\n> > +    }\n> \n> Considering that v4 finally gets around to swapping out `CString` for\n> `String` in order to make this high-level crate more\n> Rust-programmer-friendly, I was more than a little surprised to see\n> that this function is still exposing `c_int` rather than, say, `i64`\n> or such.\n\nYes, definitely an oversight. I'll fix in V5.\n"},{"id":"504656","messageId":"yynolodpcv43doqcab4bin7z7gembixqfhvqdzpcoeipnyjwl4@ek3gr4l2ycrr","threadId":"61917","inReplyTo":"xmqq7caixhz0.fsf@gitster.g","subject":"Re: [PATCH v4 5/5] Makefile: add option to build and test libgit-rs and libgit-rs-sys","fromName":"Josh Steadmon","fromEmail":"steadmon@google.com","sentAt":"2024-10-09T21:53:17Z","receivedAt":"2024-10-09T21:53:23Z","isPatch":true,"sender":{"key":"steadmon@google.com","avatar":"https://avatars.githubusercontent.com/u/2654920?v=4"},"body":"On 2024.10.08 17:01, Junio C Hamano wrote:\n> Josh Steadmon <steadmon@google.com> writes:\n> \n> > From: Calvin Wan <calvinwan@google.com>\n> >\n> > Add libgitrs, libgitrs-sys, libgitrs-test, and libgitrs-sys-test targets\n> > to their respective Makefiles so they can be built and tested without\n> > having to run cargo build/test.\n> >\n> > Add environment variable, INCLUDE_LIBGIT_RS, that when set,\n> > automatically builds and tests libgit-rs and libgit-rs-sys when `make\n> > all` is ran.\n> >\n> > Signed-off-by: Calvin Wan <calvinwan@google.com>\n> > Signed-off-by: Josh Steadmon <steadmon@google.com>\n> > ---\n> >  Makefile   | 16 ++++++++++++++++\n> >  t/Makefile | 16 ++++++++++++++++\n> >  2 files changed, 32 insertions(+)\n> \n> Interesting.\n> \n> I tried\n> \n> \t$ make INCLUDE_LIBGIT_RS=YesPlease\n> \n> which did not fail, and then did the same\n> \n> \t$ make INCLUDE_LIBGIT_RS=YesPlease\n> \n> and was surprised to see that not only the libgit-sys part but\n> everything was recompiled and rebuilt.\n> \n> > diff --git a/Makefile b/Makefile\n> > ...\n> > +.PHONY: libgitrs\n> > +libgitrs:\n> > +\t$(QUIET)(\\\n> > +\t\tcd contrib/libgit-rs && \\\n> > +\t\tcargo build \\\n> > +\t)\n> > +ifdef INCLUDE_LIBGIT_RS\n> > +all:: libgitrs\n> > +endif\n> > +\n> >  contrib/libgit-rs/libgit-sys/partial_symbol_export.o: contrib/libgit-rs/libgit-sys/public_symbol_export.o libgit.a reftable/libreftable.a xdiff/lib.a\n> >  \t$(LD) -r $^ -o $@\n> \n> I can see libgitrs is a phony target designed to run every time it\n> gets triggered, and I would imagine \"cargo build\" itself would avoid\n> repeating unnecessary work, but I do not see this patch screwing up\n> with the dependencies for other object files.\n> \n> Is it fair to say this is still a WIP?  Showing a WIP to others and\n> asking for help is OK, but it is fair to make sure that others know\n> what is expected of them.\n\nHmm, I think this may be an unfortunate interaction between Git's `make\nall`, followed by libgit-sys's `build.rs` calling make again (with\ndifferent CFLAGS, specifically '-fvisibility=hidden') to build\nlibgitpub.a. So then the second `make all` has to rebuild everything due\nto changing the CFLAGS back, and then libgit-sys has to rebuild\nlibgitpub.a once again.\n\nUnfortunately, I don't see a way around this problem, at least with our\ncurrent approach for building libgitpub.a. We have to pass\n'-fvisibility=hidden' when compiling each source file, not just at link\ntime, so I think the object files created when building vanilla Git will\nnecessarily differ from those created when building libgit-rs.\n\nI think that means we'll need to drop this patch for now, and revisit\nif/when we change our libgitpub.a strategy.\n"},{"id":"504658","messageId":"bn4qspsmqtbbr6mi2tx3gjbldopc5qf7iaqjudnahrhllwecqe@rtk6inujylzc","threadId":"61917","inReplyTo":"xmqq1q0qxhj2.fsf@gitster.g","subject":"Re: [PATCH v4 5/5] Makefile: add option to build and test libgit-rs and libgit-rs-sys","fromName":"Josh Steadmon","fromEmail":"steadmon@google.com","sentAt":"2024-10-09T22:24:36Z","receivedAt":"2024-10-09T22:24:43Z","isPatch":true,"sender":{"key":"steadmon@google.com","avatar":"https://avatars.githubusercontent.com/u/2654920?v=4"},"body":"On 2024.10.08 17:10, Junio C Hamano wrote:\n> Josh Steadmon <steadmon@google.com> writes:\n> \n> > From: Calvin Wan <calvinwan@google.com>\n> >\n> > Add libgitrs, libgitrs-sys, libgitrs-test, and libgitrs-sys-test targets\n> > to their respective Makefiles so they can be built and tested without\n> > having to run cargo build/test.\n> >\n> > Add environment variable, INCLUDE_LIBGIT_RS, that when set,\n> > automatically builds and tests libgit-rs and libgit-rs-sys when `make\n> > all` is ran.\n> >\n> > Signed-off-by: Calvin Wan <calvinwan@google.com>\n> > Signed-off-by: Josh Steadmon <steadmon@google.com>\n> > ---\n> >  Makefile   | 16 ++++++++++++++++\n> >  t/Makefile | 16 ++++++++++++++++\n> >  2 files changed, 32 insertions(+)\n> \n> After \n> \n>     $ make INCLUDE_LIBGIT_RS=YesPlease\n> \n> running either\n> \n>     $ make INCLUDE_LIBGIT_RS=YesPlease distclean\n>     $ make distclean\n> \n> leaves\n> \n>     $ git clean -n -x\n>     Would remove contrib/libgit-rs/libgit-sys/libgitpub.a\n> \n> behind.  We'd need to add a bit more to the Makefile, it seems.\n> \n> \n> \n>  Makefile | 9 ++++++++-\n>  1 file changed, 8 insertions(+), 1 deletion(-)\n> \n> diff --git i/Makefile w/Makefile\n> index 41ad458aef..2acb5353d1 100644\n> --- i/Makefile\n> +++ w/Makefile\n> @@ -392,6 +392,9 @@ include shared.mak\n>  # INSTALL_STRIP can be set to \"-s\" to strip binaries during installation,\n>  # if your $(INSTALL) command supports the option.\n>  #\n> +# Define INCLUDE_LIBGIT_RS if you want your gostak to distim\n> +# the doshes.\n> +#\n>  # Define GENERATE_COMPILATION_DATABASE to \"yes\" to generate JSON compilation\n>  # database entries during compilation if your compiler supports it, using the\n>  # `-MJ` flag. The JSON entries will be placed in the `compile_commands/`\n> @@ -771,6 +774,9 @@ PROGRAM_OBJS += shell.o\n>  .PHONY: program-objs\n>  program-objs: $(PROGRAM_OBJS)\n>  \n> +# libgit-rs stuff\n> +LIBGITPUB_A = contrib/libgit-rs/libgit-sys/libgitpub.a\n> +\n>  # Binary suffix, set to .exe for Windows builds\n>  X =\n>  \n> @@ -3708,6 +3714,7 @@ clean: profile-clean coverage-clean cocciclean\n>  \t$(RM) po/git.pot po/git-core.pot\n>  \t$(RM) git.res\n>  \t$(RM) $(OBJECTS)\n> +\t$(RM) $(LIBGITPUB_A)\n>  \t$(RM) headless-git.o\n>  \t$(RM) $(LIB_FILE) $(XDIFF_LIB) $(REFTABLE_LIB) $(REFTABLE_TEST_LIB)\n>  \t$(RM) $(ALL_PROGRAMS) $(SCRIPT_LIB) $(BUILT_INS) $(OTHER_PROGRAMS)\n> @@ -3892,5 +3899,5 @@ contrib/libgit-rs/libgit-sys/partial_symbol_export.o: contrib/libgit-rs/libgit-s\n>  contrib/libgit-rs/libgit-sys/hidden_symbol_export.o: contrib/libgit-rs/libgit-sys/partial_symbol_export.o\n>  \t$(OBJCOPY) --localize-hidden $^ $@\n>  \n> -contrib/libgit-rs/libgit-sys/libgitpub.a: contrib/libgit-rs/libgit-sys/hidden_symbol_export.o\n> +$(LIBGITPUB_A): contrib/libgit-rs/libgit-sys/hidden_symbol_export.o\n>  \t$(AR) $(ARFLAGS) $@ $^\n\nDone in V5.\n"},{"id":"504659","messageId":"sojvhdl2jfsvngaixtccfsezlp7mpoj3ag62q66zphragf7o2l@3auva7nngupm","threadId":"61917","inReplyTo":"29599e9c7be1737bcf0de0541c9635212a1b691d.1728429158.git.steadmon@google.com","subject":"Re: [PATCH v4 4/5] libgit: add higher-level libgit crate","fromName":"Josh Steadmon","fromEmail":"steadmon@google.com","sentAt":"2024-10-09T22:25:29Z","receivedAt":"2024-10-09T22:25:36Z","isPatch":true,"sender":{"key":"steadmon@google.com","avatar":"https://avatars.githubusercontent.com/u/2654920?v=4"},"body":"On 2024.10.08 16:19, Josh Steadmon wrote:\n> From: Calvin Wan <calvinwan@google.com>\n> \n> Wrap `struct config_set` and a few of its associated functions in\n> libgit-sys. Also introduce a higher-level \"libgit\" crate which provides\n> a more Rust-friendly interface to config_set structs.\n> \n> Co-authored-by: Josh Steadmon <steadmon@google.com>\n> Signed-off-by: Calvin Wan <calvinwan@google.com>\n> Signed-off-by: Josh Steadmon <steadmon@google.com>\n> ---\n>  .gitignore                              |  1 +\n>  Makefile                                |  2 +-\n>  contrib/libgit-rs/Cargo.lock            | 77 ++++++++++++++++++++\n>  contrib/libgit-rs/Cargo.toml            | 15 ++++\n>  contrib/libgit-rs/build.rs              |  4 ++\n>  contrib/libgit-rs/libgit-sys/src/lib.rs |  4 ++\n>  contrib/libgit-rs/src/lib.rs            | 95 +++++++++++++++++++++++++\n>  contrib/libgit-rs/testdata/config1      |  2 +\n>  contrib/libgit-rs/testdata/config2      |  2 +\n>  contrib/libgit-rs/testdata/config3      |  2 +\n>  10 files changed, 203 insertions(+), 1 deletion(-)\n>  create mode 100644 contrib/libgit-rs/Cargo.lock\n>  create mode 100644 contrib/libgit-rs/Cargo.toml\n>  create mode 100644 contrib/libgit-rs/build.rs\n>  create mode 100644 contrib/libgit-rs/src/lib.rs\n>  create mode 100644 contrib/libgit-rs/testdata/config1\n>  create mode 100644 contrib/libgit-rs/testdata/config2\n>  create mode 100644 contrib/libgit-rs/testdata/config3\n\nJust realized that this commit message is not accurate anymore (and\ncould provide more useful info anyway). I've reworded it in V5.\n"},{"id":"504665","messageId":"xmqq7cagok3j.fsf@gitster.g","threadId":"61917","inReplyTo":"yynolodpcv43doqcab4bin7z7gembixqfhvqdzpcoeipnyjwl4@ek3gr4l2ycrr","subject":"Re: [PATCH v4 5/5] Makefile: add option to build and test libgit-rs and libgit-rs-sys","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2024-10-10T00:52:16Z","receivedAt":"2024-10-10T00:52:19Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Josh Steadmon <steadmon@google.com> writes:\n\n> Hmm, I think this may be an unfortunate interaction between Git's `make\n> all`, followed by libgit-sys's `build.rs` calling make again (with\n> different CFLAGS, specifically '-fvisibility=hidden') to build\n> libgitpub.a. So then the second `make all` has to rebuild everything due\n> to changing the CFLAGS back, and then libgit-sys has to rebuild\n> libgitpub.a once again.\n\nAh, OK, if we need to compile in two different ways, then it is a\nmatter of giving a dedicated *.o build directory to each, and until\nthat happens, the object files for Git proper and libgit-sys would\ntry to stomp on each other.\n\nI thought Patrick's build procedure update has out-of-tree build as\none of its goals, in which case we may be able to piggy-back on the\neffort once it starts to stabilize.\n\nThanks.\n"},{"id":"505047","messageId":"jpzziq6a2sepvrrpltahlgvx3kibestargeettnty3q7dk6njn@vizhrfj72g7a","threadId":"61917","inReplyTo":"xmqq7cagok3j.fsf@gitster.g","subject":"Re: [PATCH v4 5/5] Makefile: add option to build and test libgit-rs and libgit-rs-sys","fromName":"Josh Steadmon","fromEmail":"steadmon@google.com","sentAt":"2024-10-14T20:13:26Z","receivedAt":"2024-10-14T20:13:34Z","isPatch":true,"sender":{"key":"steadmon@google.com","avatar":"https://avatars.githubusercontent.com/u/2654920?v=4"},"body":"On 2024.10.09 17:52, Junio C Hamano wrote:\n> Josh Steadmon <steadmon@google.com> writes:\n> \n> > Hmm, I think this may be an unfortunate interaction between Git's `make\n> > all`, followed by libgit-sys's `build.rs` calling make again (with\n> > different CFLAGS, specifically '-fvisibility=hidden') to build\n> > libgitpub.a. So then the second `make all` has to rebuild everything due\n> > to changing the CFLAGS back, and then libgit-sys has to rebuild\n> > libgitpub.a once again.\n> \n> Ah, OK, if we need to compile in two different ways, then it is a\n> matter of giving a dedicated *.o build directory to each, and until\n> that happens, the object files for Git proper and libgit-sys would\n> try to stomp on each other.\n> \n> I thought Patrick's build procedure update has out-of-tree build as\n> one of its goals, in which case we may be able to piggy-back on the\n> effort once it starts to stabilize.\n> \n> Thanks.\n\nActually, including '-fvisibility=hidden' by default doesn't break the\nmain build, so I think we can just add this to BASIC_CFLAGS if\nINCLUDE_LIBGIT_RS is set. I'll do that (and add documentation as\nrequested) and restore this patch in V5.\n"},{"id":"505048","messageId":"bqrwfci6jcjvvlgbuoohl2dw5ui63p42lgc3ilmbxtkcssvuae@xsq65nk5cypr","threadId":"61917","inReplyTo":"xmqqed4qxior.fsf@gitster.g","subject":"Re: [PATCH v4 5/5] Makefile: add option to build and test libgit-rs and libgit-rs-sys","fromName":"Josh Steadmon","fromEmail":"steadmon@google.com","sentAt":"2024-10-14T20:19:54Z","receivedAt":"2024-10-14T20:20:00Z","isPatch":true,"sender":{"key":"steadmon@google.com","avatar":"https://avatars.githubusercontent.com/u/2654920?v=4"},"body":"On 2024.10.08 16:45, Junio C Hamano wrote:\n> Josh Steadmon <steadmon@google.com> writes:\n> \n> > Add environment variable, INCLUDE_LIBGIT_RS, that when set,\n> > automatically builds and tests libgit-rs and libgit-rs-sys when `make\n> > all` is ran.\n> \n> Is this unusual, or is it just like how other makefile macros like\n> say USE_NSEC (to cause the resulting Git to use subsecond mtimes)\n> are meant to be used to control the build?  IOW, shouldn't this be\n> documented near the top of the Makefile, e.g.\n> \n>     diff --git i/Makefile w/Makefile\n>     index 41ad458aef..2b55fe9672 100644\n>     --- i/Makefile\n>     +++ w/Makefile\n>     @@ -392,6 +392,9 @@ include shared.mak\n>      # INSTALL_STRIP can be set to \"-s\" to strip binaries during installation,\n>      # if your $(INSTALL) command supports the option.\n>      #\n>     +# Define INCLUDE_LIBGIT_RS if you want your gostak to distim\n>     +# the doshes and ...\n>     +#\n>      # Define GENERATE_COMPILATION_DATABASE to \"yes\" to generate JSON compilation\n>      # database entries during compilation if your compiler supports it, using the\n>      # `-MJ` flag. The JSON entries will be placed in the `compile_commands/`\n> \n> It might make sense to follow naming convention to call it NO_RUST\n> and flip its polarity.  Those who do not have or want libgit-rs and\n> friends can say NO_RUST but otherwise it gets built by default.  It\n> would give you a wider developer population coverage.\n> \n> Thanks.\n\nFor now I'd be more comfortable keeping it off by default. I don't want\nto force those not interested in Rust to work around our in-progress\nprojects. Once it's more stable and we have CI I'd feel better about\nturning it on by default (and maybe moving it out of contrib/ at that\npoint?).\n"},{"id":"505209","messageId":"cover.1729032373.git.steadmon@google.com","threadId":"61917","inReplyTo":"cover.1723054623.git.steadmon@google.com","subject":"[PATCH v5 0/5] Introduce libgit-rs, a Rust wrapper around libgit.a","fromName":"Josh Steadmon","fromEmail":"steadmon@google.com","sentAt":"2024-10-15T22:50:52Z","receivedAt":"2024-10-15T22:51:01Z","isPatch":true,"sender":{"key":"steadmon@google.com","avatar":"https://avatars.githubusercontent.com/u/2654920?v=4"},"body":"A quick note: normally I'd wait longer for more feedback before sending\nout a new reroll, but due to some firefighting at $DAYJOB, I will not\nhave time to focus on this series for the next several weeks, possibly\nup to a month. I wanted to get V5 out before then, but please understand\nthat I will not be able to address new feedback for a while.\n\nThis series provides two small Rust wrapper libraries around parts of\nGit: \"libgit-sys\", which exposes a few functions from libgit.a, and\n\"libgit\", which provides a more Rust-friendly interface to some of those\nfunctions. In addition to included unit tests, at $DAYJOB we have tested\nbuilding JJ[1] with our library and used it to replace some of the\nlibgit2-rs uses.\n\n[1] https://github.com/martinvonz/jj\n\nPlease find V1 cover letter here:\nhttps://lore.kernel.org/git/cover.1723054623.git.steadmon@google.com/\n\nKnown NEEDSWORK:\n\n* Investigate alternative methods of managing symbol visibility &\n  renaming.\n\n* Figure out symbol versioning\n\nChanges in V5:\n* When building with INCLUDE_LIBGIT_RS defined, add\n  \"-fvisibility=hidden\" to CFLAGS. This allows us to manage symbol\n  visibility in libgitpub.a without causing `make all` to rebuild from\n  scratch due to changing CFLAGS.\n\n* Avoid using c_int in the higher-level Rust API.\n\n* Remove libgitpub.a and intermediate files with `make clean`.\n\n\nChanges in V4:\n* Drop V3 patch #3, which added wrappers around repository\n  initialization and config access. These are not well-libified, and\n  they are not necessary for JJ's proof-of-concept use case, so let's\n  avoid exporting them for now.\n\n* Set a minimum supported Rust version of 1.63. Autodetect whether our\n  Rust version has c_int and c_char types; if not, define them\n  ourselves.\n\n* When building libgitpub.a via build.rs, set DEVELOPER=1 to catch\n  additional errors at build time.\n\n* In build.rs, use the make_cmd crate to portable select the correct\n  invocation of GNU Make.\n\n* Follow naming standards for _alloc() and _free() functions.\n\n* Use String instead of CString in higher-level API.\n\n* Move libgit_configset_alloc() and libgit_configset_free() out of\n  upstream Git, to the libgitpub shim library.\n\n* In libgitpub, initialize libgit_config_set structs in the _alloc()\n  function rather than with a separate _init() function.\n\n* Remove unnecessary comments in libgit-sys showing where the wrapped\n  functions were originally defined.\n\n* Fix clippy lint: don't reborrow configfile path references.\n\n* Various typo fixes and `cargo fmt` fixes.\n\nChanges in V3:\n* Renamed cgit-rs to libgit-rs and cgit-sys to libgit-sys\n\n* Makefile cleanup, particularly adding config.mak options that\n  developers can set to run Rust builds and tests by default (Patch 6)\n\n* Provide testdata configs for unit tests\n\n* ConfigSet API now uses &Path instead of &str -- more ergonomic for\n  Rust users to pass in and errors out if the path string isn't UTF-8\n\n* Fixed unresolved dependency on libz in Cargo.toml\n\n\nCalvin Wan (2):\n  libgit: add higher-level libgit crate\n  Makefile: add option to build and test libgit-rs and libgit-rs-sys\n\nJosh Steadmon (3):\n  common-main: split init and exit code into new files\n  libgit-sys: introduce Rust wrapper for libgit.a\n  libgit-sys: also export some config_set functions\n\n .gitignore                                    |  2 +\n Makefile                                      | 44 +++++++++\n common-exit.c                                 | 26 +++++\n common-init.c                                 | 63 ++++++++++++\n common-init.h                                 |  6 ++\n common-main.c                                 | 83 +---------------\n contrib/libgit-rs/Cargo.lock                  | 77 +++++++++++++++\n contrib/libgit-rs/Cargo.toml                  | 15 +++\n contrib/libgit-rs/build.rs                    |  4 +\n contrib/libgit-rs/libgit-sys/Cargo.lock       | 69 ++++++++++++++\n contrib/libgit-rs/libgit-sys/Cargo.toml       | 17 ++++\n contrib/libgit-rs/libgit-sys/README.md        | 15 +++\n contrib/libgit-rs/libgit-sys/build.rs         | 35 +++++++\n .../libgit-sys/public_symbol_export.c         | 50 ++++++++++\n .../libgit-sys/public_symbol_export.h         | 18 ++++\n contrib/libgit-rs/libgit-sys/src/lib.rs       | 79 +++++++++++++++\n contrib/libgit-rs/src/lib.rs                  | 95 +++++++++++++++++++\n contrib/libgit-rs/testdata/config1            |  2 +\n contrib/libgit-rs/testdata/config2            |  2 +\n contrib/libgit-rs/testdata/config3            |  2 +\n t/Makefile                                    | 16 ++++\n 21 files changed, 639 insertions(+), 81 deletions(-)\n create mode 100644 common-exit.c\n create mode 100644 common-init.c\n create mode 100644 common-init.h\n create mode 100644 contrib/libgit-rs/Cargo.lock\n create mode 100644 contrib/libgit-rs/Cargo.toml\n create mode 100644 contrib/libgit-rs/build.rs\n create mode 100644 contrib/libgit-rs/libgit-sys/Cargo.lock\n create mode 100644 contrib/libgit-rs/libgit-sys/Cargo.toml\n create mode 100644 contrib/libgit-rs/libgit-sys/README.md\n create mode 100644 contrib/libgit-rs/libgit-sys/build.rs\n create mode 100644 contrib/libgit-rs/libgit-sys/public_symbol_export.c\n create mode 100644 contrib/libgit-rs/libgit-sys/public_symbol_export.h\n create mode 100644 contrib/libgit-rs/libgit-sys/src/lib.rs\n create mode 100644 contrib/libgit-rs/src/lib.rs\n create mode 100644 contrib/libgit-rs/testdata/config1\n create mode 100644 contrib/libgit-rs/testdata/config2\n create mode 100644 contrib/libgit-rs/testdata/config3\n\nRange-diff against v4:\n4:  2ed503216f ! 1:  1ae14207f6 Makefile: add option to build and test libgit-rs and libgit-rs-sys\n    @@\n      ## Metadata ##\n    -Author: Calvin Wan <calvinwan@google.com>\n    +Author: Josh Steadmon <steadmon@google.com>\n     \n      ## Commit message ##\n    -    Makefile: add option to build and test libgit-rs and libgit-rs-sys\n    +    common-main: split init and exit code into new files\n     \n    -    Add libgitrs, libgitrs-sys, libgitrs-test, and libgitrs-sys-test targets\n    -    to their respective Makefiles so they can be built and tested without\n    -    having to run cargo build/test.\n    +    Currently, object files in libgit.a reference common_exit(), which is\n    +    contained in common-main.o. However, common-main.o also includes main(),\n    +    which references cmd_main() in git.o, which in turn depends on all the\n    +    builtin/*.o objects.\n     \n    -    Add environment variable, INCLUDE_LIBGIT_RS, that when set,\n    -    automatically builds and tests libgit-rs and libgit-rs-sys when `make\n    -    all` is ran.\n    +    We would like to allow external users to link libgit.a without needing\n    +    to include so many extra objects. Enable this by splitting common_exit()\n    +    and check_bug_if_BUG() into a new file common-exit.c, and add\n    +    common-exit.o to LIB_OBJS so that these are included in libgit.a.\n     \n    -    Signed-off-by: Calvin Wan <calvinwan@google.com>\n    +    This split has previously been proposed ([1], [2]) to support fuzz tests\n    +    and unit tests by avoiding conflicting definitions for main(). However,\n    +    both of those issues were resolved by other methods of avoiding symbol\n    +    conflicts. Now we are trying to make libgit.a more self-contained, so\n    +    hopefully we can revisit this approach.\n    +\n    +    Additionally, move the initialization code out of main() into a new\n    +    init_git() function in its own file. Include this in libgit.a as well,\n    +    so that external users can share our setup code without calling our\n    +    main().\n    +\n    +    [1] https://lore.kernel.org/git/Yp+wjCPhqieTku3X@google.com/\n    +    [2] https://lore.kernel.org/git/20230517-unit-tests-v2-v2-1-21b5b60f4b32@google.com/\n    +\n    +    Signed-off-by: Josh Steadmon <steadmon@google.com>\n     \n      ## Makefile ##\n    -@@ Makefile: build-unit-tests: $(UNIT_TEST_PROGS)\n    - unit-tests: $(UNIT_TEST_PROGS) t/helper/test-tool$X\n    - \t$(MAKE) -C t/ unit-tests\n    +@@ Makefile: LIB_OBJS += combine-diff.o\n    + LIB_OBJS += commit-graph.o\n    + LIB_OBJS += commit-reach.o\n    + LIB_OBJS += commit.o\n    ++LIB_OBJS += common-exit.o\n    ++LIB_OBJS += common-init.o\n    + LIB_OBJS += compat/nonblock.o\n    + LIB_OBJS += compat/obstack.o\n    + LIB_OBJS += compat/terminal.o\n    +\n    + ## common-exit.c (new) ##\n    +@@\n    ++#include \"git-compat-util.h\"\n    ++#include \"trace2.h\"\n    ++\n    ++static void check_bug_if_BUG(void)\n    ++{\n    ++\tif (!bug_called_must_BUG)\n    ++\t\treturn;\n    ++\tBUG(\"on exit(): had bug() call(s) in this process without explicit BUG_if_bug()\");\n    ++}\n    ++\n    ++/* We wrap exit() to call common_exit() in git-compat-util.h */\n    ++int common_exit(const char *file, int line, int code)\n    ++{\n    ++\t/*\n    ++\t * For non-POSIX systems: Take the lowest 8 bits of the \"code\"\n    ++\t * to e.g. turn -1 into 255. On a POSIX system this is\n    ++\t * redundant, see exit(3) and wait(2), but as it doesn't harm\n    ++\t * anything there we don't need to guard this with an \"ifdef\".\n    ++\t */\n    ++\tcode &= 0xff;\n    ++\n    ++\tcheck_bug_if_BUG();\n    ++\ttrace2_cmd_exit_fl(file, line, code);\n    ++\n    ++\treturn code;\n    ++}\n    +\n    + ## common-init.c (new) ##\n    +@@\n    ++#define USE_THE_REPOSITORY_VARIABLE\n    ++\n    ++#include \"git-compat-util.h\"\n    ++#include \"common-init.h\"\n    ++#include \"exec-cmd.h\"\n    ++#include \"gettext.h\"\n    ++#include \"attr.h\"\n    ++#include \"repository.h\"\n    ++#include \"setup.h\"\n    ++#include \"strbuf.h\"\n    ++#include \"trace2.h\"\n    ++\n    ++/*\n    ++ * Many parts of Git have subprograms communicate via pipe, expect the\n    ++ * upstream of a pipe to die with SIGPIPE when the downstream of a\n    ++ * pipe does not need to read all that is written.  Some third-party\n    ++ * programs that ignore or block SIGPIPE for their own reason forget\n    ++ * to restore SIGPIPE handling to the default before spawning Git and\n    ++ * break this carefully orchestrated machinery.\n    ++ *\n    ++ * Restore the way SIGPIPE is handled to default, which is what we\n    ++ * expect.\n    ++ */\n    ++static void restore_sigpipe_to_default(void)\n    ++{\n    ++\tsigset_t unblock;\n    ++\n    ++\tsigemptyset(&unblock);\n    ++\tsigaddset(&unblock, SIGPIPE);\n    ++\tsigprocmask(SIG_UNBLOCK, &unblock, NULL);\n    ++\tsignal(SIGPIPE, SIG_DFL);\n    ++}\n    ++\n    ++void init_git(const char **argv)\n    ++{\n    ++\tstruct strbuf tmp = STRBUF_INIT;\n    ++\n    ++\ttrace2_initialize_clock();\n    ++\n    ++\t/*\n    ++\t * Always open file descriptors 0/1/2 to avoid clobbering files\n    ++\t * in die().  It also avoids messing up when the pipes are dup'ed\n    ++\t * onto stdin/stdout/stderr in the child processes we spawn.\n    ++\t */\n    ++\tsanitize_stdfds();\n    ++\trestore_sigpipe_to_default();\n    ++\n    ++\tgit_resolve_executable_dir(argv[0]);\n    ++\n    ++\tsetlocale(LC_CTYPE, \"\");\n    ++\tgit_setup_gettext();\n    ++\n    ++\tinitialize_repository(the_repository);\n    ++\n    ++\tattr_start();\n    ++\n    ++\ttrace2_initialize();\n    ++\ttrace2_cmd_start(argv);\n    ++\ttrace2_collect_process_info(TRACE2_PROCESS_INFO_STARTUP);\n    ++\n    ++\tif (!strbuf_getcwd(&tmp))\n    ++\t\ttmp_original_cwd = strbuf_detach(&tmp, NULL);\n    ++}\n    +\n    + ## common-init.h (new) ##\n    +@@\n    ++#ifndef COMMON_INIT_H\n    ++#define COMMON_INIT_H\n    ++\n    ++void init_git(const char **argv);\n    ++\n    ++#endif /* COMMON_INIT_H */\n    +\n    + ## common-main.c ##\n    +@@\n    +-#define USE_THE_REPOSITORY_VARIABLE\n    +-\n    + #include \"git-compat-util.h\"\n    +-#include \"exec-cmd.h\"\n    +-#include \"gettext.h\"\n    +-#include \"attr.h\"\n    +-#include \"repository.h\"\n    +-#include \"setup.h\"\n    +-#include \"strbuf.h\"\n    +-#include \"trace2.h\"\n    +-\n    +-/*\n    +- * Many parts of Git have subprograms communicate via pipe, expect the\n    +- * upstream of a pipe to die with SIGPIPE when the downstream of a\n    +- * pipe does not need to read all that is written.  Some third-party\n    +- * programs that ignore or block SIGPIPE for their own reason forget\n    +- * to restore SIGPIPE handling to the default before spawning Git and\n    +- * break this carefully orchestrated machinery.\n    +- *\n    +- * Restore the way SIGPIPE is handled to default, which is what we\n    +- * expect.\n    +- */\n    +-static void restore_sigpipe_to_default(void)\n    +-{\n    +-\tsigset_t unblock;\n    +-\n    +-\tsigemptyset(&unblock);\n    +-\tsigaddset(&unblock, SIGPIPE);\n    +-\tsigprocmask(SIG_UNBLOCK, &unblock, NULL);\n    +-\tsignal(SIGPIPE, SIG_DFL);\n    +-}\n    ++#include \"common-init.h\"\n      \n    -+.PHONY: libgitrs-sys\n    -+libgitrs-sys:\n    -+\t$(QUIET)(\\\n    -+\t\tcd contrib/libgit-rs/libgit-sys && \\\n    -+\t\tcargo build \\\n    -+\t)\n    -+.PHONY: libgitrs\n    -+libgitrs:\n    -+\t$(QUIET)(\\\n    -+\t\tcd contrib/libgit-rs && \\\n    -+\t\tcargo build \\\n    -+\t)\n    -+ifdef INCLUDE_LIBGIT_RS\n    -+all:: libgitrs\n    -+endif\n    -+\n    - contrib/libgit-rs/libgit-sys/partial_symbol_export.o: contrib/libgit-rs/libgit-sys/public_symbol_export.o libgit.a reftable/libreftable.a xdiff/lib.a\n    - \t$(LD) -r $^ -o $@\n    + int main(int argc, const char **argv)\n    + {\n    + \tint result;\n    +-\tstruct strbuf tmp = STRBUF_INIT;\n    +-\n    +-\ttrace2_initialize_clock();\n    +-\n    +-\t/*\n    +-\t * Always open file descriptors 0/1/2 to avoid clobbering files\n    +-\t * in die().  It also avoids messing up when the pipes are dup'ed\n    +-\t * onto stdin/stdout/stderr in the child processes we spawn.\n    +-\t */\n    +-\tsanitize_stdfds();\n    +-\trestore_sigpipe_to_default();\n    +-\n    +-\tgit_resolve_executable_dir(argv[0]);\n    +-\n    +-\tsetlocale(LC_CTYPE, \"\");\n    +-\tgit_setup_gettext();\n    +-\n    +-\tinitialize_repository(the_repository);\n    +-\n    +-\tattr_start();\n    +-\n    +-\ttrace2_initialize();\n    +-\ttrace2_cmd_start(argv);\n    +-\ttrace2_collect_process_info(TRACE2_PROCESS_INFO_STARTUP);\n    +-\n    +-\tif (!strbuf_getcwd(&tmp))\n    +-\t\ttmp_original_cwd = strbuf_detach(&tmp, NULL);\n      \n    -\n    - ## t/Makefile ##\n    -@@ t/Makefile: perf:\n    ++\tinit_git(argv);\n    + \tresult = cmd_main(argc, argv);\n      \n    - .PHONY: pre-clean $(T) aggregate-results clean valgrind perf \\\n    - \tcheck-chainlint clean-chainlint test-chainlint $(UNIT_TESTS)\n    -+\n    -+.PHONY: libgitrs-sys-test\n    -+libgitrs-sys-test:\n    -+\t$(QUIET)(\\\n    -+\t\tcd ../contrib/libgit-rs/libgit-sys && \\\n    -+\t\tcargo test \\\n    -+\t)\n    -+.PHONY: libgitrs-test\n    -+libgitrs-test:\n    -+\t$(QUIET)(\\\n    -+\t\tcd ../contrib/libgit-rs && \\\n    -+\t\tcargo test \\\n    -+\t)\n    -+ifdef INCLUDE_LIBGIT_RS\n    -+all:: libgitrs-sys-test libgitrs-test\n    -+endif\n    + \t/* Not exit(3), but a wrapper calling our common_exit() */\n    + \texit(result);\n    + }\n    +-\n    +-static void check_bug_if_BUG(void)\n    +-{\n    +-\tif (!bug_called_must_BUG)\n    +-\t\treturn;\n    +-\tBUG(\"on exit(): had bug() call(s) in this process without explicit BUG_if_bug()\");\n    +-}\n    +-\n    +-/* We wrap exit() to call common_exit() in git-compat-util.h */\n    +-int common_exit(const char *file, int line, int code)\n    +-{\n    +-\t/*\n    +-\t * For non-POSIX systems: Take the lowest 8 bits of the \"code\"\n    +-\t * to e.g. turn -1 into 255. On a POSIX system this is\n    +-\t * redundant, see exit(3) and wait(2), but as it doesn't harm\n    +-\t * anything there we don't need to guard this with an \"ifdef\".\n    +-\t */\n    +-\tcode &= 0xff;\n    +-\n    +-\tcheck_bug_if_BUG();\n    +-\ttrace2_cmd_exit_fl(file, line, code);\n    +-\n    +-\treturn code;\n    +-}\n1:  ed925d6a33 ! 2:  1ed010c378 libgit-sys: introduce Rust wrapper for libgit.a\n    @@ Makefile: clean: profile-clean coverage-clean cocciclean\n      \t$(MAKE) -C Documentation/ clean\n      \t$(RM) Documentation/GIT-EXCLUDED-PROGRAMS\n     +\t$(RM) -r contrib/libgit-rs/libgit-sys/target\n    ++\t$(RM) -r contrib/libgit-rs/libgit-sys/partial_symbol_export.o\n    ++\t$(RM) -r contrib/libgit-rs/libgit-sys/hidden_symbol_export.o\n    ++\t$(RM) -r contrib/libgit-rs/libgit-sys/libgitpub.a\n      ifndef NO_PERL\n      \t$(RM) -r perl/build/\n      endif\n2:  8eeab7b418 = 3:  00762b57d0 libgit-sys: also export some config_set functions\n3:  29599e9c7b ! 4:  4e5360931b libgit: add higher-level libgit crate\n    @@ Metadata\n      ## Commit message ##\n         libgit: add higher-level libgit crate\n     \n    -    Wrap `struct config_set` and a few of its associated functions in\n    -    libgit-sys. Also introduce a higher-level \"libgit\" crate which provides\n    -    a more Rust-friendly interface to config_set structs.\n    +    The C functions exported by libgit-sys do not provide an idiomatic Rust\n    +    interface. To make it easier to use these functions via Rust, add a\n    +    higher-level \"libgit\" crate, that wraps the lower-level configset API\n    +    with an interface that is more Rust-y.\n    +\n    +    This combination of $X and $X-sys crates is a common pattern for FFI in\n    +    Rust, as documented in \"The Cargo Book\" [1].\n    +\n    +    [1] https://doc.rust-lang.org/cargo/reference/build-scripts.html#-sys-packages\n     \n         Co-authored-by: Josh Steadmon <steadmon@google.com>\n         Signed-off-by: Josh Steadmon <steadmon@google.com>\n    @@ Makefile: clean: profile-clean coverage-clean cocciclean\n      \t$(RM) Documentation/GIT-EXCLUDED-PROGRAMS\n     -\t$(RM) -r contrib/libgit-rs/libgit-sys/target\n     +\t$(RM) -r contrib/libgit-rs/target contrib/libgit-rs/libgit-sys/target\n    - ifndef NO_PERL\n    - \t$(RM) -r perl/build/\n    - endif\n    + \t$(RM) -r contrib/libgit-rs/libgit-sys/partial_symbol_export.o\n    + \t$(RM) -r contrib/libgit-rs/libgit-sys/hidden_symbol_export.o\n    + \t$(RM) -r contrib/libgit-rs/libgit-sys/libgitpub.a\n     \n      ## contrib/libgit-rs/Cargo.lock (new) ##\n     @@\n    @@ contrib/libgit-rs/src/lib.rs (new)\n     +        }\n     +    }\n     +\n    -+    pub fn get_int(&mut self, key: &str) -> Option<c_int> {\n    ++    pub fn get_int(&mut self, key: &str) -> Option<i32> {\n     +        let key = CString::new(key).expect(\"Couldn't convert to CString\");\n     +        let mut val: c_int = 0;\n     +        unsafe {\n    @@ contrib/libgit-rs/src/lib.rs (new)\n     +            }\n     +        }\n     +\n    -+        Some(val)\n    ++        Some(val.into())\n     +    }\n     +\n     +    pub fn get_string(&mut self, key: &str) -> Option<String> {\n-:  ---------- > 5:  15ce989de8 Makefile: add option to build and test libgit-rs and libgit-rs-sys\n\nbase-commit: 557ae147e6cdc9db121269b058c757ac5092f9c9\n-- \n2.47.0.rc1.288.g06298d1525-goog\n\n"},{"id":"505210","messageId":"1ae14207f69af3d1c6fe19dfd779062a69f16122.1729032373.git.steadmon@google.com","threadId":"61917","inReplyTo":"cover.1729032373.git.steadmon@google.com","subject":"[PATCH v5 1/5] common-main: split init and exit code into new files","fromName":"Josh Steadmon","fromEmail":"steadmon@google.com","sentAt":"2024-10-15T22:50:53Z","receivedAt":"2024-10-15T22:51:02Z","isPatch":true,"sender":{"key":"steadmon@google.com","avatar":"https://avatars.githubusercontent.com/u/2654920?v=4"},"body":"Currently, object files in libgit.a reference common_exit(), which is\ncontained in common-main.o. However, common-main.o also includes main(),\nwhich references cmd_main() in git.o, which in turn depends on all the\nbuiltin/*.o objects.\n\nWe would like to allow external users to link libgit.a without needing\nto include so many extra objects. Enable this by splitting common_exit()\nand check_bug_if_BUG() into a new file common-exit.c, and add\ncommon-exit.o to LIB_OBJS so that these are included in libgit.a.\n\nThis split has previously been proposed ([1], [2]) to support fuzz tests\nand unit tests by avoiding conflicting definitions for main(). However,\nboth of those issues were resolved by other methods of avoiding symbol\nconflicts. Now we are trying to make libgit.a more self-contained, so\nhopefully we can revisit this approach.\n\nAdditionally, move the initialization code out of main() into a new\ninit_git() function in its own file. Include this in libgit.a as well,\nso that external users can share our setup code without calling our\nmain().\n\n[1] https://lore.kernel.org/git/Yp+wjCPhqieTku3X@google.com/\n[2] https://lore.kernel.org/git/20230517-unit-tests-v2-v2-1-21b5b60f4b32@google.com/\n\nSigned-off-by: Josh Steadmon <steadmon@google.com>\n---\n Makefile      |  2 ++\n common-exit.c | 26 ++++++++++++++++\n common-init.c | 63 ++++++++++++++++++++++++++++++++++++++\n common-init.h |  6 ++++\n common-main.c | 83 ++-------------------------------------------------\n 5 files changed, 99 insertions(+), 81 deletions(-)\n create mode 100644 common-exit.c\n create mode 100644 common-init.c\n create mode 100644 common-init.h\n\ndiff --git a/Makefile b/Makefile\nindex 3eab701b10..7caeb3c872 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -979,6 +979,8 @@ LIB_OBJS += combine-diff.o\n LIB_OBJS += commit-graph.o\n LIB_OBJS += commit-reach.o\n LIB_OBJS += commit.o\n+LIB_OBJS += common-exit.o\n+LIB_OBJS += common-init.o\n LIB_OBJS += compat/nonblock.o\n LIB_OBJS += compat/obstack.o\n LIB_OBJS += compat/terminal.o\ndiff --git a/common-exit.c b/common-exit.c\nnew file mode 100644\nindex 0000000000..1aaa538be3\n--- /dev/null\n+++ b/common-exit.c\n@@ -0,0 +1,26 @@\n+#include \"git-compat-util.h\"\n+#include \"trace2.h\"\n+\n+static void check_bug_if_BUG(void)\n+{\n+\tif (!bug_called_must_BUG)\n+\t\treturn;\n+\tBUG(\"on exit(): had bug() call(s) in this process without explicit BUG_if_bug()\");\n+}\n+\n+/* We wrap exit() to call common_exit() in git-compat-util.h */\n+int common_exit(const char *file, int line, int code)\n+{\n+\t/*\n+\t * For non-POSIX systems: Take the lowest 8 bits of the \"code\"\n+\t * to e.g. turn -1 into 255. On a POSIX system this is\n+\t * redundant, see exit(3) and wait(2), but as it doesn't harm\n+\t * anything there we don't need to guard this with an \"ifdef\".\n+\t */\n+\tcode &= 0xff;\n+\n+\tcheck_bug_if_BUG();\n+\ttrace2_cmd_exit_fl(file, line, code);\n+\n+\treturn code;\n+}\ndiff --git a/common-init.c b/common-init.c\nnew file mode 100644\nindex 0000000000..5cc73f058c\n--- /dev/null\n+++ b/common-init.c\n@@ -0,0 +1,63 @@\n+#define USE_THE_REPOSITORY_VARIABLE\n+\n+#include \"git-compat-util.h\"\n+#include \"common-init.h\"\n+#include \"exec-cmd.h\"\n+#include \"gettext.h\"\n+#include \"attr.h\"\n+#include \"repository.h\"\n+#include \"setup.h\"\n+#include \"strbuf.h\"\n+#include \"trace2.h\"\n+\n+/*\n+ * Many parts of Git have subprograms communicate via pipe, expect the\n+ * upstream of a pipe to die with SIGPIPE when the downstream of a\n+ * pipe does not need to read all that is written.  Some third-party\n+ * programs that ignore or block SIGPIPE for their own reason forget\n+ * to restore SIGPIPE handling to the default before spawning Git and\n+ * break this carefully orchestrated machinery.\n+ *\n+ * Restore the way SIGPIPE is handled to default, which is what we\n+ * expect.\n+ */\n+static void restore_sigpipe_to_default(void)\n+{\n+\tsigset_t unblock;\n+\n+\tsigemptyset(&unblock);\n+\tsigaddset(&unblock, SIGPIPE);\n+\tsigprocmask(SIG_UNBLOCK, &unblock, NULL);\n+\tsignal(SIGPIPE, SIG_DFL);\n+}\n+\n+void init_git(const char **argv)\n+{\n+\tstruct strbuf tmp = STRBUF_INIT;\n+\n+\ttrace2_initialize_clock();\n+\n+\t/*\n+\t * Always open file descriptors 0/1/2 to avoid clobbering files\n+\t * in die().  It also avoids messing up when the pipes are dup'ed\n+\t * onto stdin/stdout/stderr in the child processes we spawn.\n+\t */\n+\tsanitize_stdfds();\n+\trestore_sigpipe_to_default();\n+\n+\tgit_resolve_executable_dir(argv[0]);\n+\n+\tsetlocale(LC_CTYPE, \"\");\n+\tgit_setup_gettext();\n+\n+\tinitialize_repository(the_repository);\n+\n+\tattr_start();\n+\n+\ttrace2_initialize();\n+\ttrace2_cmd_start(argv);\n+\ttrace2_collect_process_info(TRACE2_PROCESS_INFO_STARTUP);\n+\n+\tif (!strbuf_getcwd(&tmp))\n+\t\ttmp_original_cwd = strbuf_detach(&tmp, NULL);\n+}\ndiff --git a/common-init.h b/common-init.h\nnew file mode 100644\nindex 0000000000..3e6db20cae\n--- /dev/null\n+++ b/common-init.h\n@@ -0,0 +1,6 @@\n+#ifndef COMMON_INIT_H\n+#define COMMON_INIT_H\n+\n+void init_git(const char **argv);\n+\n+#endif /* COMMON_INIT_H */\ndiff --git a/common-main.c b/common-main.c\nindex 8e68ac9e42..6b7ab077b0 100644\n--- a/common-main.c\n+++ b/common-main.c\n@@ -1,92 +1,13 @@\n-#define USE_THE_REPOSITORY_VARIABLE\n-\n #include \"git-compat-util.h\"\n-#include \"exec-cmd.h\"\n-#include \"gettext.h\"\n-#include \"attr.h\"\n-#include \"repository.h\"\n-#include \"setup.h\"\n-#include \"strbuf.h\"\n-#include \"trace2.h\"\n-\n-/*\n- * Many parts of Git have subprograms communicate via pipe, expect the\n- * upstream of a pipe to die with SIGPIPE when the downstream of a\n- * pipe does not need to read all that is written.  Some third-party\n- * programs that ignore or block SIGPIPE for their own reason forget\n- * to restore SIGPIPE handling to the default before spawning Git and\n- * break this carefully orchestrated machinery.\n- *\n- * Restore the way SIGPIPE is handled to default, which is what we\n- * expect.\n- */\n-static void restore_sigpipe_to_default(void)\n-{\n-\tsigset_t unblock;\n-\n-\tsigemptyset(&unblock);\n-\tsigaddset(&unblock, SIGPIPE);\n-\tsigprocmask(SIG_UNBLOCK, &unblock, NULL);\n-\tsignal(SIGPIPE, SIG_DFL);\n-}\n+#include \"common-init.h\"\n \n int main(int argc, const char **argv)\n {\n \tint result;\n-\tstruct strbuf tmp = STRBUF_INIT;\n-\n-\ttrace2_initialize_clock();\n-\n-\t/*\n-\t * Always open file descriptors 0/1/2 to avoid clobbering files\n-\t * in die().  It also avoids messing up when the pipes are dup'ed\n-\t * onto stdin/stdout/stderr in the child processes we spawn.\n-\t */\n-\tsanitize_stdfds();\n-\trestore_sigpipe_to_default();\n-\n-\tgit_resolve_executable_dir(argv[0]);\n-\n-\tsetlocale(LC_CTYPE, \"\");\n-\tgit_setup_gettext();\n-\n-\tinitialize_repository(the_repository);\n-\n-\tattr_start();\n-\n-\ttrace2_initialize();\n-\ttrace2_cmd_start(argv);\n-\ttrace2_collect_process_info(TRACE2_PROCESS_INFO_STARTUP);\n-\n-\tif (!strbuf_getcwd(&tmp))\n-\t\ttmp_original_cwd = strbuf_detach(&tmp, NULL);\n \n+\tinit_git(argv);\n \tresult = cmd_main(argc, argv);\n \n \t/* Not exit(3), but a wrapper calling our common_exit() */\n \texit(result);\n }\n-\n-static void check_bug_if_BUG(void)\n-{\n-\tif (!bug_called_must_BUG)\n-\t\treturn;\n-\tBUG(\"on exit(): had bug() call(s) in this process without explicit BUG_if_bug()\");\n-}\n-\n-/* We wrap exit() to call common_exit() in git-compat-util.h */\n-int common_exit(const char *file, int line, int code)\n-{\n-\t/*\n-\t * For non-POSIX systems: Take the lowest 8 bits of the \"code\"\n-\t * to e.g. turn -1 into 255. On a POSIX system this is\n-\t * redundant, see exit(3) and wait(2), but as it doesn't harm\n-\t * anything there we don't need to guard this with an \"ifdef\".\n-\t */\n-\tcode &= 0xff;\n-\n-\tcheck_bug_if_BUG();\n-\ttrace2_cmd_exit_fl(file, line, code);\n-\n-\treturn code;\n-}\n-- \n2.47.0.rc1.288.g06298d1525-goog\n\n"},{"id":"505211","messageId":"1ed010c37852255e646b6dbb4beeac9c75c4cdde.1729032373.git.steadmon@google.com","threadId":"61917","inReplyTo":"cover.1729032373.git.steadmon@google.com","subject":"[PATCH v5 2/5] libgit-sys: introduce Rust wrapper for libgit.a","fromName":"Josh Steadmon","fromEmail":"steadmon@google.com","sentAt":"2024-10-15T22:50:54Z","receivedAt":"2024-10-15T22:51:04Z","isPatch":true,"sender":{"key":"steadmon@google.com","avatar":"https://avatars.githubusercontent.com/u/2654920?v=4"},"body":"Introduce libgit-sys, a Rust wrapper crate that allows Rust code to call\nfunctions in libgit.a. This initial patch defines build rules and an\ninterface that exposes user agent string getter functions as a proof of\nconcept. This library can be tested with `cargo test`. In later commits,\na higher-level library containing a more Rust-friendly interface will be\nadded at `contrib/libgit-rs`.\n\nSymbols in libgit can collide with symbols from other libraries such as\nlibgit2. We avoid this by first exposing library symbols in\npublic_symbol_export.[ch]. These symbols are prepended with \"libgit_\" to\navoid collisions and set to visible using a visibility pragma. In\nbuild.rs, Rust builds contrib/libgit-rs/libgit-sys/libgitpub.a, which also\ncontains libgit.a and other dependent libraries, with\n-fvisibility=hidden to hide all symbols within those libraries that\nhaven't been exposed with a visibility pragma.\n\nCo-authored-by: Kyle Lippincott <spectral@google.com>\nCo-authored-by: Calvin Wan <calvinwan@google.com>\nSigned-off-by: Calvin Wan <calvinwan@google.com>\nSigned-off-by: Kyle Lippincott <spectral@google.com>\nSigned-off-by: Josh Steadmon <steadmon@google.com>\n---\n .gitignore                                    |  1 +\n Makefile                                      | 16 +++++\n contrib/libgit-rs/libgit-sys/Cargo.lock       | 69 +++++++++++++++++++\n contrib/libgit-rs/libgit-sys/Cargo.toml       | 17 +++++\n contrib/libgit-rs/libgit-sys/README.md        | 15 ++++\n contrib/libgit-rs/libgit-sys/build.rs         | 35 ++++++++++\n .../libgit-sys/public_symbol_export.c         | 21 ++++++\n .../libgit-sys/public_symbol_export.h         |  8 +++\n contrib/libgit-rs/libgit-sys/src/lib.rs       | 46 +++++++++++++\n 9 files changed, 228 insertions(+)\n create mode 100644 contrib/libgit-rs/libgit-sys/Cargo.lock\n create mode 100644 contrib/libgit-rs/libgit-sys/Cargo.toml\n create mode 100644 contrib/libgit-rs/libgit-sys/README.md\n create mode 100644 contrib/libgit-rs/libgit-sys/build.rs\n create mode 100644 contrib/libgit-rs/libgit-sys/public_symbol_export.c\n create mode 100644 contrib/libgit-rs/libgit-sys/public_symbol_export.h\n create mode 100644 contrib/libgit-rs/libgit-sys/src/lib.rs\n\ndiff --git a/.gitignore b/.gitignore\nindex 8caf3700c2..dfd72820fb 100644\n--- a/.gitignore\n+++ b/.gitignore\n@@ -248,3 +248,4 @@ Release/\n /git.VC.db\n *.dSYM\n /contrib/buildsystems/out\n+/contrib/libgit-rs/libgit-sys/target\ndiff --git a/Makefile b/Makefile\nindex 7caeb3c872..4f7467a8c2 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -653,6 +653,8 @@ CURL_CONFIG = curl-config\n GCOV = gcov\n STRIP = strip\n SPATCH = spatch\n+LD = ld\n+OBJCOPY = objcopy\n \n export TCL_PATH TCLTK_PATH\n \n@@ -2713,6 +2715,7 @@ OBJECTS += $(XDIFF_OBJS)\n OBJECTS += $(FUZZ_OBJS)\n OBJECTS += $(REFTABLE_OBJS) $(REFTABLE_TEST_OBJS)\n OBJECTS += $(UNIT_TEST_OBJS)\n+OBJECTS += contrib/libgit-rs/libgit-sys/public_symbol_export.o\n \n ifndef NO_CURL\n \tOBJECTS += http.o http-walker.o remote-curl.o\n@@ -3720,6 +3723,10 @@ clean: profile-clean coverage-clean cocciclean\n \t$(RM) $(htmldocs).tar.gz $(manpages).tar.gz\n \t$(MAKE) -C Documentation/ clean\n \t$(RM) Documentation/GIT-EXCLUDED-PROGRAMS\n+\t$(RM) -r contrib/libgit-rs/libgit-sys/target\n+\t$(RM) -r contrib/libgit-rs/libgit-sys/partial_symbol_export.o\n+\t$(RM) -r contrib/libgit-rs/libgit-sys/hidden_symbol_export.o\n+\t$(RM) -r contrib/libgit-rs/libgit-sys/libgitpub.a\n ifndef NO_PERL\n \t$(RM) -r perl/build/\n endif\n@@ -3865,3 +3872,12 @@ $(UNIT_TEST_PROGS): $(UNIT_TEST_BIN)/%$X: $(UNIT_TEST_DIR)/%.o \\\n build-unit-tests: $(UNIT_TEST_PROGS)\n unit-tests: $(UNIT_TEST_PROGS) t/helper/test-tool$X\n \t$(MAKE) -C t/ unit-tests\n+\n+contrib/libgit-rs/libgit-sys/partial_symbol_export.o: contrib/libgit-rs/libgit-sys/public_symbol_export.o libgit.a reftable/libreftable.a xdiff/lib.a\n+\t$(LD) -r $^ -o $@\n+\n+contrib/libgit-rs/libgit-sys/hidden_symbol_export.o: contrib/libgit-rs/libgit-sys/partial_symbol_export.o\n+\t$(OBJCOPY) --localize-hidden $^ $@\n+\n+contrib/libgit-rs/libgit-sys/libgitpub.a: contrib/libgit-rs/libgit-sys/hidden_symbol_export.o\n+\t$(AR) $(ARFLAGS) $@ $^\ndiff --git a/contrib/libgit-rs/libgit-sys/Cargo.lock b/contrib/libgit-rs/libgit-sys/Cargo.lock\nnew file mode 100644\nindex 0000000000..427a4c66b7\n--- /dev/null\n+++ b/contrib/libgit-rs/libgit-sys/Cargo.lock\n@@ -0,0 +1,69 @@\n+# This file is automatically @generated by Cargo.\n+# It is not intended for manual editing.\n+version = 3\n+\n+[[package]]\n+name = \"autocfg\"\n+version = \"1.4.0\"\n+source = \"registry+https://github.com/rust-lang/crates.io-index\"\n+checksum = \"ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26\"\n+\n+[[package]]\n+name = \"cc\"\n+version = \"1.1.15\"\n+source = \"registry+https://github.com/rust-lang/crates.io-index\"\n+checksum = \"57b6a275aa2903740dc87da01c62040406b8812552e97129a63ea8850a17c6e6\"\n+dependencies = [\n+ \"shlex\",\n+]\n+\n+[[package]]\n+name = \"libc\"\n+version = \"0.2.158\"\n+source = \"registry+https://github.com/rust-lang/crates.io-index\"\n+checksum = \"d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439\"\n+\n+[[package]]\n+name = \"libgit-sys\"\n+version = \"0.1.0\"\n+dependencies = [\n+ \"autocfg\",\n+ \"libz-sys\",\n+ \"make-cmd\",\n+]\n+\n+[[package]]\n+name = \"libz-sys\"\n+version = \"1.1.20\"\n+source = \"registry+https://github.com/rust-lang/crates.io-index\"\n+checksum = \"d2d16453e800a8cf6dd2fc3eb4bc99b786a9b90c663b8559a5b1a041bf89e472\"\n+dependencies = [\n+ \"cc\",\n+ \"libc\",\n+ \"pkg-config\",\n+ \"vcpkg\",\n+]\n+\n+[[package]]\n+name = \"make-cmd\"\n+version = \"0.1.0\"\n+source = \"registry+https://github.com/rust-lang/crates.io-index\"\n+checksum = \"a8ca8afbe8af1785e09636acb5a41e08a765f5f0340568716c18a8700ba3c0d3\"\n+\n+[[package]]\n+name = \"pkg-config\"\n+version = \"0.3.30\"\n+source = \"registry+https://github.com/rust-lang/crates.io-index\"\n+checksum = \"d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec\"\n+\n+[[package]]\n+name = \"shlex\"\n+version = \"1.3.0\"\n+source = \"registry+https://github.com/rust-lang/crates.io-index\"\n+checksum = \"0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64\"\n+\n+[[package]]\n+name = \"vcpkg\"\n+version = \"0.2.15\"\n+source = \"registry+https://github.com/rust-lang/crates.io-index\"\n+checksum = \"accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426\"\ndiff --git a/contrib/libgit-rs/libgit-sys/Cargo.toml b/contrib/libgit-rs/libgit-sys/Cargo.toml\nnew file mode 100644\nindex 0000000000..7a230a4437\n--- /dev/null\n+++ b/contrib/libgit-rs/libgit-sys/Cargo.toml\n@@ -0,0 +1,17 @@\n+[package]\n+name = \"libgit-sys\"\n+version = \"0.1.0\"\n+edition = \"2021\"\n+build = \"build.rs\"\n+links = \"gitpub\"\n+rust-version = \"1.63\"\n+\n+[lib]\n+path = \"src/lib.rs\"\n+\n+[dependencies]\n+libz-sys = \"1.1.19\"\n+\n+[build-dependencies]\n+autocfg = \"1.4.0\"\n+make-cmd = \"0.1.0\"\ndiff --git a/contrib/libgit-rs/libgit-sys/README.md b/contrib/libgit-rs/libgit-sys/README.md\nnew file mode 100644\nindex 0000000000..7a59602c30\n--- /dev/null\n+++ b/contrib/libgit-rs/libgit-sys/README.md\n@@ -0,0 +1,15 @@\n+# cgit-info\n+\n+A small hacky proof-of-concept showing how to provide a Rust FFI for the Git\n+library.\n+\n+## Building\n+\n+`cargo build` automatically builds and picks up on changes made to both\n+the Rust wrapper and git.git code so there is no need to run `make`\n+beforehand.\n+\n+## Running\n+\n+Assuming you don't make any changes to the Git source, you can just work from\n+`contrib/cgit-rs` and use `cargo build` or `cargo run` as usual.\ndiff --git a/contrib/libgit-rs/libgit-sys/build.rs b/contrib/libgit-rs/libgit-sys/build.rs\nnew file mode 100644\nindex 0000000000..8d74120191\n--- /dev/null\n+++ b/contrib/libgit-rs/libgit-sys/build.rs\n@@ -0,0 +1,35 @@\n+use std::env;\n+use std::path::PathBuf;\n+\n+pub fn main() -> std::io::Result<()> {\n+    let ac = autocfg::new();\n+    ac.emit_has_path(\"std::ffi::c_char\");\n+\n+    let crate_root = PathBuf::from(env::var_os(\"CARGO_MANIFEST_DIR\").unwrap());\n+    let git_root = crate_root.join(\"../../..\");\n+    let dst = PathBuf::from(env::var_os(\"OUT_DIR\").unwrap());\n+\n+    let make_output = make_cmd::gnu_make()\n+        .env(\"DEVELOPER\", \"1\")\n+        .env_remove(\"PROFILE\")\n+        .current_dir(git_root.clone())\n+        .args([\n+            \"CFLAGS=-fvisibility=hidden\",\n+            \"contrib/libgit-rs/libgit-sys/libgitpub.a\",\n+        ])\n+        .output()\n+        .expect(\"Make failed to run\");\n+    if !make_output.status.success() {\n+        panic!(\n+            \"Make failed:\\n  stdout = {}\\n  stderr = {}\\n\",\n+            String::from_utf8(make_output.stdout).unwrap(),\n+            String::from_utf8(make_output.stderr).unwrap()\n+        );\n+    }\n+    std::fs::copy(crate_root.join(\"libgitpub.a\"), dst.join(\"libgitpub.a\"))?;\n+    println!(\"cargo:rustc-link-search=native={}\", dst.display());\n+    println!(\"cargo:rustc-link-lib=gitpub\");\n+    println!(\"cargo:rerun-if-changed={}\", git_root.display());\n+\n+    Ok(())\n+}\ndiff --git a/contrib/libgit-rs/libgit-sys/public_symbol_export.c b/contrib/libgit-rs/libgit-sys/public_symbol_export.c\nnew file mode 100644\nindex 0000000000..8e4b94d821\n--- /dev/null\n+++ b/contrib/libgit-rs/libgit-sys/public_symbol_export.c\n@@ -0,0 +1,21 @@\n+// Shim to publicly export Git symbols. These must be renamed so that the\n+// original symbols can be hidden. Renaming these with a \"libgit_\" prefix also\n+// avoids conflicts with other libraries such as libgit2.\n+\n+#include \"git-compat-util.h\"\n+#include \"contrib/libgit-rs/libgit-sys/public_symbol_export.h\"\n+#include \"version.h\"\n+\n+#pragma GCC visibility push(default)\n+\n+const char *libgit_user_agent(void)\n+{\n+\treturn git_user_agent();\n+}\n+\n+const char *libgit_user_agent_sanitized(void)\n+{\n+\treturn git_user_agent_sanitized();\n+}\n+\n+#pragma GCC visibility pop\ndiff --git a/contrib/libgit-rs/libgit-sys/public_symbol_export.h b/contrib/libgit-rs/libgit-sys/public_symbol_export.h\nnew file mode 100644\nindex 0000000000..a3372f93fa\n--- /dev/null\n+++ b/contrib/libgit-rs/libgit-sys/public_symbol_export.h\n@@ -0,0 +1,8 @@\n+#ifndef PUBLIC_SYMBOL_EXPORT_H\n+#define PUBLIC_SYMBOL_EXPORT_H\n+\n+const char *libgit_user_agent(void);\n+\n+const char *libgit_user_agent_sanitized(void);\n+\n+#endif /* PUBLIC_SYMBOL_EXPORT_H */\ndiff --git a/contrib/libgit-rs/libgit-sys/src/lib.rs b/contrib/libgit-rs/libgit-sys/src/lib.rs\nnew file mode 100644\nindex 0000000000..d4853f3074\n--- /dev/null\n+++ b/contrib/libgit-rs/libgit-sys/src/lib.rs\n@@ -0,0 +1,46 @@\n+#[cfg(has_std__ffi__c_char)]\n+use std::ffi::c_char;\n+\n+#[cfg(not(has_std__ffi__c_char))]\n+#[allow(non_camel_case_types)]\n+pub type c_char = i8;\n+\n+extern crate libz_sys;\n+\n+extern \"C\" {\n+    pub fn libgit_user_agent() -> *const c_char;\n+    pub fn libgit_user_agent_sanitized() -> *const c_char;\n+}\n+\n+#[cfg(test)]\n+mod tests {\n+    use std::ffi::CStr;\n+\n+    use super::*;\n+\n+    #[test]\n+    fn user_agent_starts_with_git() {\n+        let c_str = unsafe { CStr::from_ptr(libgit_user_agent()) };\n+        let agent = c_str\n+            .to_str()\n+            .expect(\"User agent contains invalid UTF-8 data\");\n+        assert!(\n+            agent.starts_with(\"git/\"),\n+            r#\"Expected user agent to start with \"git/\", got: {}\"#,\n+            agent\n+        );\n+    }\n+\n+    #[test]\n+    fn sanitized_user_agent_starts_with_git() {\n+        let c_str = unsafe { CStr::from_ptr(libgit_user_agent_sanitized()) };\n+        let agent = c_str\n+            .to_str()\n+            .expect(\"Sanitized user agent contains invalid UTF-8 data\");\n+        assert!(\n+            agent.starts_with(\"git/\"),\n+            r#\"Expected user agent to start with \"git/\", got: {}\"#,\n+            agent\n+        );\n+    }\n+}\n-- \n2.47.0.rc1.288.g06298d1525-goog\n\n"},{"id":"505212","messageId":"00762b57d032ddb80fb47ef277a164a46f1fc10f.1729032373.git.steadmon@google.com","threadId":"61917","inReplyTo":"cover.1729032373.git.steadmon@google.com","subject":"[PATCH v5 3/5] libgit-sys: also export some config_set functions","fromName":"Josh Steadmon","fromEmail":"steadmon@google.com","sentAt":"2024-10-15T22:50:55Z","receivedAt":"2024-10-15T22:51:06Z","isPatch":true,"sender":{"key":"steadmon@google.com","avatar":"https://avatars.githubusercontent.com/u/2654920?v=4"},"body":"In preparation for implementing a higher-level Rust API for accessing\nGit configs, export some of the upstream configset API via libgitpub and\nlibgit-sys. Since this will be exercised as part of the higher-level API\nin the next commit, no tests have been added for libgit-sys.\n\nWhile we're at it, add git_configset_alloc() and git_configset_free()\nfunctions in libgitpub so that callers can manage config_set structs on\nthe heap. This also allows non-C external consumers to treat config_sets\nas opaque structs.\n\nCo-authored-by: Calvin Wan <calvinwan@google.com>\nSigned-off-by: Calvin Wan <calvinwan@google.com>\nSigned-off-by: Josh Steadmon <steadmon@google.com>\n---\n .../libgit-sys/public_symbol_export.c         | 29 +++++++++++++++++\n .../libgit-sys/public_symbol_export.h         | 10 ++++++\n contrib/libgit-rs/libgit-sys/src/lib.rs       | 31 ++++++++++++++++++-\n 3 files changed, 69 insertions(+), 1 deletion(-)\n\ndiff --git a/contrib/libgit-rs/libgit-sys/public_symbol_export.c b/contrib/libgit-rs/libgit-sys/public_symbol_export.c\nindex 8e4b94d821..29e8a5363e 100644\n--- a/contrib/libgit-rs/libgit-sys/public_symbol_export.c\n+++ b/contrib/libgit-rs/libgit-sys/public_symbol_export.c\n@@ -3,11 +3,40 @@\n // avoids conflicts with other libraries such as libgit2.\n \n #include \"git-compat-util.h\"\n+#include \"config.h\"\n #include \"contrib/libgit-rs/libgit-sys/public_symbol_export.h\"\n #include \"version.h\"\n \n #pragma GCC visibility push(default)\n \n+struct libgit_config_set *libgit_configset_alloc(void)\n+{\n+\tstruct config_set *cs = xmalloc(sizeof(struct config_set));\n+\tgit_configset_init(cs);\n+\treturn (struct libgit_config_set *) cs;\n+}\n+\n+void libgit_configset_free(struct libgit_config_set *cs)\n+{\n+\tgit_configset_clear((struct config_set *) cs);\n+\tfree((struct config_set *) cs);\n+}\n+\n+int libgit_configset_add_file(struct libgit_config_set *cs, const char *filename)\n+{\n+\treturn git_configset_add_file((struct config_set *) cs, filename);\n+}\n+\n+int libgit_configset_get_int(struct libgit_config_set *cs, const char *key, int *dest)\n+{\n+\treturn git_configset_get_int((struct config_set *) cs, key, dest);\n+}\n+\n+int libgit_configset_get_string(struct libgit_config_set *cs, const char *key, char **dest)\n+{\n+\treturn git_configset_get_string((struct config_set *) cs, key, dest);\n+}\n+\n const char *libgit_user_agent(void)\n {\n \treturn git_user_agent();\ndiff --git a/contrib/libgit-rs/libgit-sys/public_symbol_export.h b/contrib/libgit-rs/libgit-sys/public_symbol_export.h\nindex a3372f93fa..701db92d53 100644\n--- a/contrib/libgit-rs/libgit-sys/public_symbol_export.h\n+++ b/contrib/libgit-rs/libgit-sys/public_symbol_export.h\n@@ -1,6 +1,16 @@\n #ifndef PUBLIC_SYMBOL_EXPORT_H\n #define PUBLIC_SYMBOL_EXPORT_H\n \n+struct libgit_config_set *libgit_configset_alloc(void);\n+\n+void libgit_configset_free(struct libgit_config_set *cs);\n+\n+int libgit_configset_add_file(struct libgit_config_set *cs, const char *filename);\n+\n+int libgit_configset_get_int(struct libgit_config_set *cs, const char *key, int *dest);\n+\n+int libgit_configset_get_string(struct libgit_config_set *cs, const char *key, char **dest);\n+\n const char *libgit_user_agent(void);\n \n const char *libgit_user_agent_sanitized(void);\ndiff --git a/contrib/libgit-rs/libgit-sys/src/lib.rs b/contrib/libgit-rs/libgit-sys/src/lib.rs\nindex d4853f3074..dadb4e5f40 100644\n--- a/contrib/libgit-rs/libgit-sys/src/lib.rs\n+++ b/contrib/libgit-rs/libgit-sys/src/lib.rs\n@@ -1,15 +1,44 @@\n #[cfg(has_std__ffi__c_char)]\n-use std::ffi::c_char;\n+use std::ffi::{c_char, c_int};\n \n #[cfg(not(has_std__ffi__c_char))]\n #[allow(non_camel_case_types)]\n pub type c_char = i8;\n \n+#[cfg(not(has_std__ffi__c_char))]\n+#[allow(non_camel_case_types)]\n+pub type c_int = i32;\n+\n extern crate libz_sys;\n \n+#[allow(non_camel_case_types)]\n+#[repr(C)]\n+pub struct libgit_config_set {\n+    _data: [u8; 0],\n+    _marker: core::marker::PhantomData<(*mut u8, core::marker::PhantomPinned)>,\n+}\n+\n extern \"C\" {\n     pub fn libgit_user_agent() -> *const c_char;\n     pub fn libgit_user_agent_sanitized() -> *const c_char;\n+\n+    pub fn libgit_configset_alloc() -> *mut libgit_config_set;\n+    pub fn libgit_configset_free(cs: *mut libgit_config_set);\n+\n+    pub fn libgit_configset_add_file(cs: *mut libgit_config_set, filename: *const c_char) -> c_int;\n+\n+    pub fn libgit_configset_get_int(\n+        cs: *mut libgit_config_set,\n+        key: *const c_char,\n+        int: *mut c_int,\n+    ) -> c_int;\n+\n+    pub fn libgit_configset_get_string(\n+        cs: *mut libgit_config_set,\n+        key: *const c_char,\n+        dest: *mut *mut c_char,\n+    ) -> c_int;\n+\n }\n \n #[cfg(test)]\n-- \n2.47.0.rc1.288.g06298d1525-goog\n\n"},{"id":"505213","messageId":"4e5360931b74a6c3ee60e3b648a9757d1f9c94f5.1729032373.git.steadmon@google.com","threadId":"61917","inReplyTo":"cover.1729032373.git.steadmon@google.com","subject":"[PATCH v5 4/5] libgit: add higher-level libgit crate","fromName":"Josh Steadmon","fromEmail":"steadmon@google.com","sentAt":"2024-10-15T22:50:56Z","receivedAt":"2024-10-15T22:51:08Z","isPatch":true,"sender":{"key":"steadmon@google.com","avatar":"https://avatars.githubusercontent.com/u/2654920?v=4"},"body":"From: Calvin Wan <calvinwan@google.com>\n\nThe C functions exported by libgit-sys do not provide an idiomatic Rust\ninterface. To make it easier to use these functions via Rust, add a\nhigher-level \"libgit\" crate, that wraps the lower-level configset API\nwith an interface that is more Rust-y.\n\nThis combination of $X and $X-sys crates is a common pattern for FFI in\nRust, as documented in \"The Cargo Book\" [1].\n\n[1] https://doc.rust-lang.org/cargo/reference/build-scripts.html#-sys-packages\n\nCo-authored-by: Josh Steadmon <steadmon@google.com>\nSigned-off-by: Josh Steadmon <steadmon@google.com>\nSigned-off-by: Calvin Wan <calvinwan@google.com>\n---\n .gitignore                              |  1 +\n Makefile                                |  2 +-\n contrib/libgit-rs/Cargo.lock            | 77 ++++++++++++++++++++\n contrib/libgit-rs/Cargo.toml            | 15 ++++\n contrib/libgit-rs/build.rs              |  4 ++\n contrib/libgit-rs/libgit-sys/src/lib.rs |  4 ++\n contrib/libgit-rs/src/lib.rs            | 95 +++++++++++++++++++++++++\n contrib/libgit-rs/testdata/config1      |  2 +\n contrib/libgit-rs/testdata/config2      |  2 +\n contrib/libgit-rs/testdata/config3      |  2 +\n 10 files changed, 203 insertions(+), 1 deletion(-)\n create mode 100644 contrib/libgit-rs/Cargo.lock\n create mode 100644 contrib/libgit-rs/Cargo.toml\n create mode 100644 contrib/libgit-rs/build.rs\n create mode 100644 contrib/libgit-rs/src/lib.rs\n create mode 100644 contrib/libgit-rs/testdata/config1\n create mode 100644 contrib/libgit-rs/testdata/config2\n create mode 100644 contrib/libgit-rs/testdata/config3\n\ndiff --git a/.gitignore b/.gitignore\nindex dfd72820fb..0a42f27117 100644\n--- a/.gitignore\n+++ b/.gitignore\n@@ -248,4 +248,5 @@ Release/\n /git.VC.db\n *.dSYM\n /contrib/buildsystems/out\n+/contrib/libgit-rs/target\n /contrib/libgit-rs/libgit-sys/target\ndiff --git a/Makefile b/Makefile\nindex 4f7467a8c2..1790c737bd 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -3723,7 +3723,7 @@ clean: profile-clean coverage-clean cocciclean\n \t$(RM) $(htmldocs).tar.gz $(manpages).tar.gz\n \t$(MAKE) -C Documentation/ clean\n \t$(RM) Documentation/GIT-EXCLUDED-PROGRAMS\n-\t$(RM) -r contrib/libgit-rs/libgit-sys/target\n+\t$(RM) -r contrib/libgit-rs/target contrib/libgit-rs/libgit-sys/target\n \t$(RM) -r contrib/libgit-rs/libgit-sys/partial_symbol_export.o\n \t$(RM) -r contrib/libgit-rs/libgit-sys/hidden_symbol_export.o\n \t$(RM) -r contrib/libgit-rs/libgit-sys/libgitpub.a\ndiff --git a/contrib/libgit-rs/Cargo.lock b/contrib/libgit-rs/Cargo.lock\nnew file mode 100644\nindex 0000000000..a30c7c8d33\n--- /dev/null\n+++ b/contrib/libgit-rs/Cargo.lock\n@@ -0,0 +1,77 @@\n+# This file is automatically @generated by Cargo.\n+# It is not intended for manual editing.\n+version = 3\n+\n+[[package]]\n+name = \"autocfg\"\n+version = \"1.4.0\"\n+source = \"registry+https://github.com/rust-lang/crates.io-index\"\n+checksum = \"ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26\"\n+\n+[[package]]\n+name = \"cc\"\n+version = \"1.1.15\"\n+source = \"registry+https://github.com/rust-lang/crates.io-index\"\n+checksum = \"57b6a275aa2903740dc87da01c62040406b8812552e97129a63ea8850a17c6e6\"\n+dependencies = [\n+ \"shlex\",\n+]\n+\n+[[package]]\n+name = \"libc\"\n+version = \"0.2.158\"\n+source = \"registry+https://github.com/rust-lang/crates.io-index\"\n+checksum = \"d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439\"\n+\n+[[package]]\n+name = \"libgit\"\n+version = \"0.1.0\"\n+dependencies = [\n+ \"autocfg\",\n+ \"libgit-sys\",\n+]\n+\n+[[package]]\n+name = \"libgit-sys\"\n+version = \"0.1.0\"\n+dependencies = [\n+ \"autocfg\",\n+ \"libz-sys\",\n+ \"make-cmd\",\n+]\n+\n+[[package]]\n+name = \"libz-sys\"\n+version = \"1.1.20\"\n+source = \"registry+https://github.com/rust-lang/crates.io-index\"\n+checksum = \"d2d16453e800a8cf6dd2fc3eb4bc99b786a9b90c663b8559a5b1a041bf89e472\"\n+dependencies = [\n+ \"cc\",\n+ \"libc\",\n+ \"pkg-config\",\n+ \"vcpkg\",\n+]\n+\n+[[package]]\n+name = \"make-cmd\"\n+version = \"0.1.0\"\n+source = \"registry+https://github.com/rust-lang/crates.io-index\"\n+checksum = \"a8ca8afbe8af1785e09636acb5a41e08a765f5f0340568716c18a8700ba3c0d3\"\n+\n+[[package]]\n+name = \"pkg-config\"\n+version = \"0.3.30\"\n+source = \"registry+https://github.com/rust-lang/crates.io-index\"\n+checksum = \"d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec\"\n+\n+[[package]]\n+name = \"shlex\"\n+version = \"1.3.0\"\n+source = \"registry+https://github.com/rust-lang/crates.io-index\"\n+checksum = \"0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64\"\n+\n+[[package]]\n+name = \"vcpkg\"\n+version = \"0.2.15\"\n+source = \"registry+https://github.com/rust-lang/crates.io-index\"\n+checksum = \"accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426\"\ndiff --git a/contrib/libgit-rs/Cargo.toml b/contrib/libgit-rs/Cargo.toml\nnew file mode 100644\nindex 0000000000..6b4387b9e7\n--- /dev/null\n+++ b/contrib/libgit-rs/Cargo.toml\n@@ -0,0 +1,15 @@\n+[package]\n+name = \"libgit\"\n+version = \"0.1.0\"\n+edition = \"2021\"\n+build = \"build.rs\"\n+rust-version = \"1.63\"\n+\n+[lib]\n+path = \"src/lib.rs\"\n+\n+[dependencies]\n+libgit-sys = { version = \"0.1.0\", path = \"libgit-sys\" }\n+\n+[build-dependencies]\n+autocfg = \"1.4.0\"\ndiff --git a/contrib/libgit-rs/build.rs b/contrib/libgit-rs/build.rs\nnew file mode 100644\nindex 0000000000..f8bd01a690\n--- /dev/null\n+++ b/contrib/libgit-rs/build.rs\n@@ -0,0 +1,4 @@\n+pub fn main() {\n+    let ac = autocfg::new();\n+    ac.emit_has_path(\"std::ffi::c_char\");\n+}\ndiff --git a/contrib/libgit-rs/libgit-sys/src/lib.rs b/contrib/libgit-rs/libgit-sys/src/lib.rs\nindex dadb4e5f40..4bfc650450 100644\n--- a/contrib/libgit-rs/libgit-sys/src/lib.rs\n+++ b/contrib/libgit-rs/libgit-sys/src/lib.rs\n@@ -1,3 +1,5 @@\n+use std::ffi::c_void;\n+\n #[cfg(has_std__ffi__c_char)]\n use std::ffi::{c_char, c_int};\n \n@@ -19,6 +21,8 @@ pub struct libgit_config_set {\n }\n \n extern \"C\" {\n+    pub fn free(ptr: *mut c_void);\n+\n     pub fn libgit_user_agent() -> *const c_char;\n     pub fn libgit_user_agent_sanitized() -> *const c_char;\n \ndiff --git a/contrib/libgit-rs/src/lib.rs b/contrib/libgit-rs/src/lib.rs\nnew file mode 100644\nindex 0000000000..27b6fd63f1\n--- /dev/null\n+++ b/contrib/libgit-rs/src/lib.rs\n@@ -0,0 +1,95 @@\n+use std::ffi::{c_void, CStr, CString};\n+use std::path::Path;\n+\n+#[cfg(has_std__ffi__c_char)]\n+use std::ffi::{c_char, c_int};\n+\n+#[cfg(not(has_std__ffi__c_char))]\n+#[allow(non_camel_case_types)]\n+pub type c_char = i8;\n+\n+#[cfg(not(has_std__ffi__c_char))]\n+#[allow(non_camel_case_types)]\n+pub type c_int = i32;\n+\n+use libgit_sys::*;\n+\n+pub struct ConfigSet(*mut libgit_config_set);\n+impl ConfigSet {\n+    pub fn new() -> Self {\n+        unsafe { ConfigSet(libgit_configset_alloc()) }\n+    }\n+\n+    pub fn add_files(&mut self, files: &[&Path]) {\n+        for file in files {\n+            let pstr = file.to_str().expect(\"Invalid UTF-8\");\n+            let rs = CString::new(pstr).expect(\"Couldn't convert to CString\");\n+            unsafe {\n+                libgit_configset_add_file(self.0, rs.as_ptr());\n+            }\n+        }\n+    }\n+\n+    pub fn get_int(&mut self, key: &str) -> Option<i32> {\n+        let key = CString::new(key).expect(\"Couldn't convert to CString\");\n+        let mut val: c_int = 0;\n+        unsafe {\n+            if libgit_configset_get_int(self.0, key.as_ptr(), &mut val as *mut c_int) != 0 {\n+                return None;\n+            }\n+        }\n+\n+        Some(val.into())\n+    }\n+\n+    pub fn get_string(&mut self, key: &str) -> Option<String> {\n+        let key = CString::new(key).expect(\"Couldn't convert key to CString\");\n+        let mut val: *mut c_char = std::ptr::null_mut();\n+        unsafe {\n+            if libgit_configset_get_string(self.0, key.as_ptr(), &mut val as *mut *mut c_char) != 0\n+            {\n+                return None;\n+            }\n+            let borrowed_str = CStr::from_ptr(val);\n+            let owned_str =\n+                String::from(borrowed_str.to_str().expect(\"Couldn't convert val to str\"));\n+            free(val as *mut c_void); // Free the xstrdup()ed pointer from the C side\n+            Some(owned_str)\n+        }\n+    }\n+}\n+\n+impl Default for ConfigSet {\n+    fn default() -> Self {\n+        Self::new()\n+    }\n+}\n+\n+impl Drop for ConfigSet {\n+    fn drop(&mut self) {\n+        unsafe {\n+            libgit_configset_free(self.0);\n+        }\n+    }\n+}\n+\n+#[cfg(test)]\n+mod tests {\n+    use super::*;\n+\n+    #[test]\n+    fn load_configs_via_configset() {\n+        let mut cs = ConfigSet::new();\n+        cs.add_files(&[\n+            Path::new(\"testdata/config1\"),\n+            Path::new(\"testdata/config2\"),\n+            Path::new(\"testdata/config3\"),\n+        ]);\n+        // ConfigSet retrieves correct value\n+        assert_eq!(cs.get_int(\"trace2.eventTarget\"), Some(1));\n+        // ConfigSet respects last config value set\n+        assert_eq!(cs.get_int(\"trace2.eventNesting\"), Some(3));\n+        // ConfigSet returns None for missing key\n+        assert_eq!(cs.get_string(\"foo.bar\"), None);\n+    }\n+}\ndiff --git a/contrib/libgit-rs/testdata/config1 b/contrib/libgit-rs/testdata/config1\nnew file mode 100644\nindex 0000000000..4e9a9d25d1\n--- /dev/null\n+++ b/contrib/libgit-rs/testdata/config1\n@@ -0,0 +1,2 @@\n+[trace2]\n+\teventNesting = 1\ndiff --git a/contrib/libgit-rs/testdata/config2 b/contrib/libgit-rs/testdata/config2\nnew file mode 100644\nindex 0000000000..b8d1eca423\n--- /dev/null\n+++ b/contrib/libgit-rs/testdata/config2\n@@ -0,0 +1,2 @@\n+[trace2]\n+\teventTarget = 1\ndiff --git a/contrib/libgit-rs/testdata/config3 b/contrib/libgit-rs/testdata/config3\nnew file mode 100644\nindex 0000000000..ca7b9a7c38\n--- /dev/null\n+++ b/contrib/libgit-rs/testdata/config3\n@@ -0,0 +1,2 @@\n+[trace2]\n+\teventNesting = 3\n-- \n2.47.0.rc1.288.g06298d1525-goog\n\n"},{"id":"505214","messageId":"15ce989de8bff1080a018fd9dee678856180a45f.1729032373.git.steadmon@google.com","threadId":"61917","inReplyTo":"cover.1729032373.git.steadmon@google.com","subject":"[PATCH v5 5/5] Makefile: add option to build and test libgit-rs and libgit-rs-sys","fromName":"Josh Steadmon","fromEmail":"steadmon@google.com","sentAt":"2024-10-15T22:50:57Z","receivedAt":"2024-10-15T22:51:10Z","isPatch":true,"sender":{"key":"steadmon@google.com","avatar":"https://avatars.githubusercontent.com/u/2654920?v=4"},"body":"From: Calvin Wan <calvinwan@google.com>\n\nAdd libgitrs, libgitrs-sys, libgitrs-test, and libgitrs-sys-test targets\nto their respective Makefiles so they can be built and tested without\nhaving to run cargo build/test.\n\nAdd build variable, INCLUDE_LIBGIT_RS, that when set, automatically\nbuilds and tests libgit-rs and libgit-rs-sys when `make all` is run.\n\nCo-authored-by: Josh Steadmon <steadmon@google.com>\nSigned-off-by: Calvin Wan <calvinwan@google.com>\nSigned-off-by: Josh Steadmon <steadmon@google.com>\n---\n Makefile                              | 26 ++++++++++++++++++++++++++\n contrib/libgit-rs/libgit-sys/build.rs |  2 +-\n t/Makefile                            | 16 ++++++++++++++++\n 3 files changed, 43 insertions(+), 1 deletion(-)\n\ndiff --git a/Makefile b/Makefile\nindex 1790c737bd..f191ee32bb 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -412,6 +412,9 @@ include shared.mak\n # Define LINK_FUZZ_PROGRAMS if you want `make all` to also build the fuzz test\n # programs in oss-fuzz/.\n #\n+# Define INCLUDE_LIBGIT_RS if you want `make all` and `make test` to build and\n+# test the Rust crates in contrib/libgit-rs/ and contrib/libgit-rs/libgit-sys/.\n+#\n # === Optional library: libintl ===\n #\n # Define NO_GETTEXT if you don't want Git output to be translated.\n@@ -2181,6 +2184,13 @@ ifdef FSMONITOR_OS_SETTINGS\n \tCOMPAT_OBJS += compat/fsmonitor/fsm-path-utils-$(FSMONITOR_OS_SETTINGS).o\n endif\n \n+ifdef INCLUDE_LIBGIT_RS\n+\t# Enable symbol hiding in contrib/libgit-rs/libgit-sys/libgitpub.a\n+\t# without making us rebuild the whole tree every time we run a Rust\n+\t# build.\n+\tBASIC_CFLAGS += -fvisibility=hidden\n+endif\n+\n ifeq ($(TCLTK_PATH),)\n NO_TCLTK = NoThanks\n endif\n@@ -3873,6 +3883,22 @@ build-unit-tests: $(UNIT_TEST_PROGS)\n unit-tests: $(UNIT_TEST_PROGS) t/helper/test-tool$X\n \t$(MAKE) -C t/ unit-tests\n \n+.PHONY: libgitrs-sys\n+libgitrs-sys:\n+\t$(QUIET)(\\\n+\t\tcd contrib/libgit-rs/libgit-sys && \\\n+\t\tcargo build \\\n+\t)\n+.PHONY: libgitrs\n+libgitrs:\n+\t$(QUIET)(\\\n+\t\tcd contrib/libgit-rs && \\\n+\t\tcargo build \\\n+\t)\n+ifdef INCLUDE_LIBGIT_RS\n+all:: libgitrs\n+endif\n+\n contrib/libgit-rs/libgit-sys/partial_symbol_export.o: contrib/libgit-rs/libgit-sys/public_symbol_export.o libgit.a reftable/libreftable.a xdiff/lib.a\n \t$(LD) -r $^ -o $@\n \ndiff --git a/contrib/libgit-rs/libgit-sys/build.rs b/contrib/libgit-rs/libgit-sys/build.rs\nindex 8d74120191..d6d6210ab2 100644\n--- a/contrib/libgit-rs/libgit-sys/build.rs\n+++ b/contrib/libgit-rs/libgit-sys/build.rs\n@@ -14,7 +14,7 @@ pub fn main() -> std::io::Result<()> {\n         .env_remove(\"PROFILE\")\n         .current_dir(git_root.clone())\n         .args([\n-            \"CFLAGS=-fvisibility=hidden\",\n+            \"INCLUDE_LIBGIT_RS=YesPlease\",\n             \"contrib/libgit-rs/libgit-sys/libgitpub.a\",\n         ])\n         .output()\ndiff --git a/t/Makefile b/t/Makefile\nindex b2eb9f770b..066cb5c2b4 100644\n--- a/t/Makefile\n+++ b/t/Makefile\n@@ -169,3 +169,19 @@ perf:\n \n .PHONY: pre-clean $(T) aggregate-results clean valgrind perf \\\n \tcheck-chainlint clean-chainlint test-chainlint $(UNIT_TESTS)\n+\n+.PHONY: libgitrs-sys-test\n+libgitrs-sys-test:\n+\t$(QUIET)(\\\n+\t\tcd ../contrib/libgit-rs/libgit-sys && \\\n+\t\tcargo test \\\n+\t)\n+.PHONY: libgitrs-test\n+libgitrs-test:\n+\t$(QUIET)(\\\n+\t\tcd ../contrib/libgit-rs && \\\n+\t\tcargo test \\\n+\t)\n+ifdef INCLUDE_LIBGIT_RS\n+all:: libgitrs-sys-test libgitrs-test\n+endif\n-- \n2.47.0.rc1.288.g06298d1525-goog\n\n"},{"id":"508516","messageId":"xmqqjzchcopu.fsf@gitster.g","threadId":"61917","inReplyTo":"cover.1729032373.git.steadmon@google.com","subject":"Re: [PATCH v5 0/5] Introduce libgit-rs, a Rust wrapper around libgit.a","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2024-12-03T05:36:29Z","receivedAt":"2024-12-03T05:36:33Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Josh Steadmon <steadmon@google.com> writes:\n\n> A quick note: normally I'd wait longer for more feedback before sending\n> out a new reroll, but due to some firefighting at $DAYJOB, I will not\n> have time to focus on this series for the next several weeks, possibly\n> up to a month. I wanted to get V5 out before then, but please understand\n> that I will not be able to address new feedback for a while.\n>\n> This series provides two small Rust wrapper libraries around parts of\n> Git: \"libgit-sys\", which exposes a few functions from libgit.a, and\n> \"libgit\", which provides a more Rust-friendly interface to some of those\n> functions. In addition to included unit tests, at $DAYJOB we have tested\n> building JJ[1] with our library and used it to replace some of the\n> libgit2-rs uses.\n>\n> [1] https://github.com/martinvonz/jj\n>\n> Please find V1 cover letter here:\n> https://lore.kernel.org/git/cover.1723054623.git.steadmon@google.com/\n\nHaven't seen any traffic on this one since it was posted.\n\nIt happened during my absense, but that is just one potential\nreviewer, and it is unnerving that nobody showed any interested in\nthe topic, unlike any of the previous iterations with active\ndiscussion subthreads.\n\nAnybody interested in reviewing and championing the topic, or should\nwe wait until the topic gets refined and resubmit?\n\nThanks.\n\n\n"},{"id":"510597","messageId":"cover.1736971328.git.steadmon@google.com","threadId":"61917","inReplyTo":"cover.1723054623.git.steadmon@google.com","subject":"[PATCH v6 0/5] Introduce libgit-rs, a Rust wrapper around libgit.a","fromName":"Josh Steadmon","fromEmail":"steadmon@google.com","sentAt":"2025-01-15T20:05:39Z","receivedAt":"2025-01-15T20:05:47Z","isPatch":true,"sender":{"key":"steadmon@google.com","avatar":"https://avatars.githubusercontent.com/u/2654920?v=4"},"body":"Apologies for the long delay on V6; I am finally back after several\nmonths of $DAYJOB firefighting, holidays, and sick leave. I should have\ntime to devote to this series again, but given the lack of feedback on\nV5 I am hopeful that this will be the final iteration of this series.\n\nThis series provides two small Rust wrapper libraries around parts of\nGit: \"libgit-sys\", which exposes a few functions from libgit.a, and\n\"libgit\", which provides a more Rust-friendly interface to some of those\nfunctions. In addition to included unit tests, at $DAYJOB we have tested\nbuilding JJ[1] with our library and used it to replace some of the\nlibgit2-rs uses.\n\n[1] https://github.com/martinvonz/jj\n\nThere is known NEEDSWORK, but I feel that they can be addressed in\nfollow-up changes, rather than in this series. If you feel otherwise,\nplease let me know:\n\n* Investigate alternative methods of managing symbol visibility &\n  renaming.\n\n* Figure out symbol versioning\n\nChanges in V6:\n* Rebased onto current master, since V5 was several months old.\n\n* Move libgit-sys out of libgit-rs; while this sort of nesting is common\n  in Rust crates with standalone repositories, it doesn't make as much\n  sense when they're contained in the larger Git project's repo.\n\n* Standardize the naming of some of the Makefile targets to always\n  include a dash in the \"-rs\" or \"-sys\" suffixes.\n\n* Clean up READMEs and crate descriptions in preparation for\n  uploading to crates.io.\n\nChanges in V5:\n* When building with INCLUDE_LIBGIT_RS defined, add\n  \"-fvisibility=hidden\" to CFLAGS. This allows us to manage symbol\n  visibility in libgitpub.a without causing `make all` to rebuild from\n  scratch due to changing CFLAGS.\n\n* Avoid using c_int in the higher-level Rust API.\n\n* Remove libgitpub.a and intermediate files with `make clean`.\n\nChanges in V4:\n* Drop V3 patch #3, which added wrappers around repository\n  initialization and config access. These are not well-libified, and\n  they are not necessary for JJ's proof-of-concept use case, so let's\n  avoid exporting them for now.\n\n* Set a minimum supported Rust version of 1.63. Autodetect whether our\n  Rust version has c_int and c_char types; if not, define them\n  ourselves.\n\n* When building libgitpub.a via build.rs, set DEVELOPER=1 to catch\n  additional errors at build time.\n\n* In build.rs, use the make_cmd crate to portable select the correct\n  invocation of GNU Make.\n\n* Follow naming standards for _alloc() and _free() functions.\n\n* Use String instead of CString in higher-level API.\n\n* Move libgit_configset_alloc() and libgit_configset_free() out of\n  upstream Git, to the libgitpub shim library.\n\n* In libgitpub, initialize libgit_config_set structs in the _alloc()\n  function rather than with a separate _init() function.\n\n* Remove unnecessary comments in libgit-sys showing where the wrapped\n  functions were originally defined.\n\n* Fix clippy lint: don't reborrow configfile path references.\n\n* Various typo fixes and `cargo fmt` fixes.\n\nChanges in V3:\n* Renamed cgit-rs to libgit-rs and cgit-sys to libgit-sys\n\n* Makefile cleanup, particularly adding config.mak options that\n  developers can set to run Rust builds and tests by default (Patch 6)\n\n* Provide testdata configs for unit tests\n\n* ConfigSet API now uses &Path instead of &str -- more ergonomic for\n  Rust users to pass in and errors out if the path string isn't UTF-8\n\n* Fixed unresolved dependency on libz in Cargo.toml\n\n\nCalvin Wan (2):\n  libgit: add higher-level libgit crate\n  Makefile: add option to build and test libgit-rs and libgit-rs-sys\n\nJosh Steadmon (3):\n  common-main: split init and exit code into new files\n  libgit-sys: introduce Rust wrapper for libgit.a\n  libgit-sys: also export some config_set functions\n\n .gitignore                                |  2 +\n Makefile                                  | 44 +++++++++++\n common-exit.c                             | 26 +++++++\n common-init.c                             | 63 +++++++++++++++\n common-init.h                             |  6 ++\n common-main.c                             | 83 +-------------------\n contrib/libgit-rs/Cargo.lock              | 77 ++++++++++++++++++\n contrib/libgit-rs/Cargo.toml              | 15 ++++\n contrib/libgit-rs/README.md               | 13 ++++\n contrib/libgit-rs/build.rs                |  4 +\n contrib/libgit-rs/src/lib.rs              | 95 +++++++++++++++++++++++\n contrib/libgit-rs/testdata/config1        |  2 +\n contrib/libgit-rs/testdata/config2        |  2 +\n contrib/libgit-rs/testdata/config3        |  2 +\n contrib/libgit-sys/Cargo.lock             | 69 ++++++++++++++++\n contrib/libgit-sys/Cargo.toml             | 18 +++++\n contrib/libgit-sys/README.md              |  4 +\n contrib/libgit-sys/build.rs               | 35 +++++++++\n contrib/libgit-sys/public_symbol_export.c | 50 ++++++++++++\n contrib/libgit-sys/public_symbol_export.h | 18 +++++\n contrib/libgit-sys/src/lib.rs             | 79 +++++++++++++++++++\n t/Makefile                                | 16 ++++\n 22 files changed, 642 insertions(+), 81 deletions(-)\n create mode 100644 common-exit.c\n create mode 100644 common-init.c\n create mode 100644 common-init.h\n create mode 100644 contrib/libgit-rs/Cargo.lock\n create mode 100644 contrib/libgit-rs/Cargo.toml\n create mode 100644 contrib/libgit-rs/README.md\n create mode 100644 contrib/libgit-rs/build.rs\n create mode 100644 contrib/libgit-rs/src/lib.rs\n create mode 100644 contrib/libgit-rs/testdata/config1\n create mode 100644 contrib/libgit-rs/testdata/config2\n create mode 100644 contrib/libgit-rs/testdata/config3\n create mode 100644 contrib/libgit-sys/Cargo.lock\n create mode 100644 contrib/libgit-sys/Cargo.toml\n create mode 100644 contrib/libgit-sys/README.md\n create mode 100644 contrib/libgit-sys/build.rs\n create mode 100644 contrib/libgit-sys/public_symbol_export.c\n create mode 100644 contrib/libgit-sys/public_symbol_export.h\n create mode 100644 contrib/libgit-sys/src/lib.rs\n\nRange-diff against v5:\n1:  1ae14207f6 = 1:  ff6cd62397 common-main: split init and exit code into new files\n2:  1ed010c378 ! 2:  5fc66cdb16 libgit-sys: introduce Rust wrapper for libgit.a\n    @@ .gitignore: Release/\n      /git.VC.db\n      *.dSYM\n      /contrib/buildsystems/out\n    -+/contrib/libgit-rs/libgit-sys/target\n    ++/contrib/libgit-sys/target\n     \n      ## Makefile ##\n     @@ Makefile: CURL_CONFIG = curl-config\n    @@ Makefile: CURL_CONFIG = curl-config\n      \n      export TCL_PATH TCLTK_PATH\n      \n    -@@ Makefile: OBJECTS += $(XDIFF_OBJS)\n    - OBJECTS += $(FUZZ_OBJS)\n    - OBJECTS += $(REFTABLE_OBJS) $(REFTABLE_TEST_OBJS)\n    +@@ Makefile: OBJECTS += $(REFTABLE_OBJS) $(REFTABLE_TEST_OBJS)\n      OBJECTS += $(UNIT_TEST_OBJS)\n    -+OBJECTS += contrib/libgit-rs/libgit-sys/public_symbol_export.o\n    + OBJECTS += $(CLAR_TEST_OBJS)\n    + OBJECTS += $(patsubst %,$(UNIT_TEST_DIR)/%.o,$(UNIT_TEST_PROGRAMS))\n    ++OBJECTS += contrib/libgit-sys/public_symbol_export.o\n      \n      ifndef NO_CURL\n      \tOBJECTS += http.o http-walker.o remote-curl.o\n    @@ Makefile: clean: profile-clean coverage-clean cocciclean\n      \t$(RM) $(htmldocs).tar.gz $(manpages).tar.gz\n      \t$(MAKE) -C Documentation/ clean\n      \t$(RM) Documentation/GIT-EXCLUDED-PROGRAMS\n    -+\t$(RM) -r contrib/libgit-rs/libgit-sys/target\n    -+\t$(RM) -r contrib/libgit-rs/libgit-sys/partial_symbol_export.o\n    -+\t$(RM) -r contrib/libgit-rs/libgit-sys/hidden_symbol_export.o\n    -+\t$(RM) -r contrib/libgit-rs/libgit-sys/libgitpub.a\n    ++\t$(RM) -r contrib/libgit-sys/target\n    ++\t$(RM) -r contrib/libgit-sys/partial_symbol_export.o\n    ++\t$(RM) -r contrib/libgit-sys/hidden_symbol_export.o\n    ++\t$(RM) -r contrib/libgit-sys/libgitpub.a\n      ifndef NO_PERL\n      \t$(RM) -r perl/build/\n      endif\n    -@@ Makefile: $(UNIT_TEST_PROGS): $(UNIT_TEST_BIN)/%$X: $(UNIT_TEST_DIR)/%.o \\\n    - build-unit-tests: $(UNIT_TEST_PROGS)\n    - unit-tests: $(UNIT_TEST_PROGS) t/helper/test-tool$X\n    +@@ Makefile: $(CLAR_TEST_PROG): $(UNIT_TEST_DIR)/clar.suite $(CLAR_TEST_OBJS) $(GITLIBS) GIT-\n    + build-unit-tests: $(UNIT_TEST_PROGS) $(CLAR_TEST_PROG)\n    + unit-tests: $(UNIT_TEST_PROGS) $(CLAR_TEST_PROG) t/helper/test-tool$X\n      \t$(MAKE) -C t/ unit-tests\n     +\n    -+contrib/libgit-rs/libgit-sys/partial_symbol_export.o: contrib/libgit-rs/libgit-sys/public_symbol_export.o libgit.a reftable/libreftable.a xdiff/lib.a\n    ++contrib/libgit-sys/partial_symbol_export.o: contrib/libgit-sys/public_symbol_export.o libgit.a reftable/libreftable.a xdiff/lib.a\n     +\t$(LD) -r $^ -o $@\n     +\n    -+contrib/libgit-rs/libgit-sys/hidden_symbol_export.o: contrib/libgit-rs/libgit-sys/partial_symbol_export.o\n    ++contrib/libgit-sys/hidden_symbol_export.o: contrib/libgit-sys/partial_symbol_export.o\n     +\t$(OBJCOPY) --localize-hidden $^ $@\n     +\n    -+contrib/libgit-rs/libgit-sys/libgitpub.a: contrib/libgit-rs/libgit-sys/hidden_symbol_export.o\n    ++contrib/libgit-sys/libgitpub.a: contrib/libgit-sys/hidden_symbol_export.o\n     +\t$(AR) $(ARFLAGS) $@ $^\n     \n    - ## contrib/libgit-rs/libgit-sys/Cargo.lock (new) ##\n    + ## contrib/libgit-sys/Cargo.lock (new) ##\n     @@\n     +# This file is automatically @generated by Cargo.\n     +# It is not intended for manual editing.\n    @@ contrib/libgit-rs/libgit-sys/Cargo.lock (new)\n     +source = \"registry+https://github.com/rust-lang/crates.io-index\"\n     +checksum = \"accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426\"\n     \n    - ## contrib/libgit-rs/libgit-sys/Cargo.toml (new) ##\n    + ## contrib/libgit-sys/Cargo.toml (new) ##\n     @@\n     +[package]\n     +name = \"libgit-sys\"\n    @@ contrib/libgit-rs/libgit-sys/Cargo.toml (new)\n     +build = \"build.rs\"\n     +links = \"gitpub\"\n     +rust-version = \"1.63\"\n    ++description = \"Native bindings to a portion of libgit\"\n     +\n     +[lib]\n     +path = \"src/lib.rs\"\n    @@ contrib/libgit-rs/libgit-sys/Cargo.toml (new)\n     +autocfg = \"1.4.0\"\n     +make-cmd = \"0.1.0\"\n     \n    - ## contrib/libgit-rs/libgit-sys/README.md (new) ##\n    + ## contrib/libgit-sys/README.md (new) ##\n     @@\n    -+# cgit-info\n    ++# libgit-sys\n     +\n    -+A small hacky proof-of-concept showing how to provide a Rust FFI for the Git\n    -+library.\n    -+\n    -+## Building\n    -+\n    -+`cargo build` automatically builds and picks up on changes made to both\n    -+the Rust wrapper and git.git code so there is no need to run `make`\n    -+beforehand.\n    -+\n    -+## Running\n    -+\n    -+Assuming you don't make any changes to the Git source, you can just work from\n    -+`contrib/cgit-rs` and use `cargo build` or `cargo run` as usual.\n    ++A small proof-of-concept crate showing how to provide a Rust FFI to Git\n    ++internals.\n     \n    - ## contrib/libgit-rs/libgit-sys/build.rs (new) ##\n    + ## contrib/libgit-sys/build.rs (new) ##\n     @@\n     +use std::env;\n     +use std::path::PathBuf;\n    @@ contrib/libgit-rs/libgit-sys/build.rs (new)\n     +    ac.emit_has_path(\"std::ffi::c_char\");\n     +\n     +    let crate_root = PathBuf::from(env::var_os(\"CARGO_MANIFEST_DIR\").unwrap());\n    -+    let git_root = crate_root.join(\"../../..\");\n    ++    let git_root = crate_root.join(\"../..\");\n     +    let dst = PathBuf::from(env::var_os(\"OUT_DIR\").unwrap());\n     +\n     +    let make_output = make_cmd::gnu_make()\n    @@ contrib/libgit-rs/libgit-sys/build.rs (new)\n     +        .current_dir(git_root.clone())\n     +        .args([\n     +            \"CFLAGS=-fvisibility=hidden\",\n    -+            \"contrib/libgit-rs/libgit-sys/libgitpub.a\",\n    ++            \"contrib/libgit-sys/libgitpub.a\",\n     +        ])\n     +        .output()\n     +        .expect(\"Make failed to run\");\n    @@ contrib/libgit-rs/libgit-sys/build.rs (new)\n     +    Ok(())\n     +}\n     \n    - ## contrib/libgit-rs/libgit-sys/public_symbol_export.c (new) ##\n    + ## contrib/libgit-sys/public_symbol_export.c (new) ##\n     @@\n     +// Shim to publicly export Git symbols. These must be renamed so that the\n     +// original symbols can be hidden. Renaming these with a \"libgit_\" prefix also\n     +// avoids conflicts with other libraries such as libgit2.\n     +\n     +#include \"git-compat-util.h\"\n    -+#include \"contrib/libgit-rs/libgit-sys/public_symbol_export.h\"\n    ++#include \"contrib/libgit-sys/public_symbol_export.h\"\n     +#include \"version.h\"\n     +\n     +#pragma GCC visibility push(default)\n    @@ contrib/libgit-rs/libgit-sys/public_symbol_export.c (new)\n     +\n     +#pragma GCC visibility pop\n     \n    - ## contrib/libgit-rs/libgit-sys/public_symbol_export.h (new) ##\n    + ## contrib/libgit-sys/public_symbol_export.h (new) ##\n     @@\n     +#ifndef PUBLIC_SYMBOL_EXPORT_H\n     +#define PUBLIC_SYMBOL_EXPORT_H\n    @@ contrib/libgit-rs/libgit-sys/public_symbol_export.h (new)\n     +\n     +#endif /* PUBLIC_SYMBOL_EXPORT_H */\n     \n    - ## contrib/libgit-rs/libgit-sys/src/lib.rs (new) ##\n    + ## contrib/libgit-sys/src/lib.rs (new) ##\n     @@\n     +#[cfg(has_std__ffi__c_char)]\n     +use std::ffi::c_char;\n3:  00762b57d0 ! 3:  03f39b6c3a libgit-sys: also export some config_set functions\n    @@ Commit message\n         Signed-off-by: Calvin Wan <calvinwan@google.com>\n         Signed-off-by: Josh Steadmon <steadmon@google.com>\n     \n    - ## contrib/libgit-rs/libgit-sys/public_symbol_export.c ##\n    + ## contrib/libgit-sys/public_symbol_export.c ##\n     @@\n      // avoids conflicts with other libraries such as libgit2.\n      \n      #include \"git-compat-util.h\"\n     +#include \"config.h\"\n    - #include \"contrib/libgit-rs/libgit-sys/public_symbol_export.h\"\n    + #include \"contrib/libgit-sys/public_symbol_export.h\"\n      #include \"version.h\"\n      \n      #pragma GCC visibility push(default)\n    @@ contrib/libgit-rs/libgit-sys/public_symbol_export.c\n      {\n      \treturn git_user_agent();\n     \n    - ## contrib/libgit-rs/libgit-sys/public_symbol_export.h ##\n    + ## contrib/libgit-sys/public_symbol_export.h ##\n     @@\n      #ifndef PUBLIC_SYMBOL_EXPORT_H\n      #define PUBLIC_SYMBOL_EXPORT_H\n    @@ contrib/libgit-rs/libgit-sys/public_symbol_export.h\n      \n      const char *libgit_user_agent_sanitized(void);\n     \n    - ## contrib/libgit-rs/libgit-sys/src/lib.rs ##\n    + ## contrib/libgit-sys/src/lib.rs ##\n     @@\n      #[cfg(has_std__ffi__c_char)]\n     -use std::ffi::c_char;\n4:  4e5360931b ! 4:  65166ea0c0 libgit: add higher-level libgit crate\n    @@ .gitignore: Release/\n      *.dSYM\n      /contrib/buildsystems/out\n     +/contrib/libgit-rs/target\n    - /contrib/libgit-rs/libgit-sys/target\n    + /contrib/libgit-sys/target\n     \n      ## Makefile ##\n     @@ Makefile: clean: profile-clean coverage-clean cocciclean\n      \t$(RM) $(htmldocs).tar.gz $(manpages).tar.gz\n      \t$(MAKE) -C Documentation/ clean\n      \t$(RM) Documentation/GIT-EXCLUDED-PROGRAMS\n    --\t$(RM) -r contrib/libgit-rs/libgit-sys/target\n    -+\t$(RM) -r contrib/libgit-rs/target contrib/libgit-rs/libgit-sys/target\n    - \t$(RM) -r contrib/libgit-rs/libgit-sys/partial_symbol_export.o\n    - \t$(RM) -r contrib/libgit-rs/libgit-sys/hidden_symbol_export.o\n    - \t$(RM) -r contrib/libgit-rs/libgit-sys/libgitpub.a\n    +-\t$(RM) -r contrib/libgit-sys/target\n    ++\t$(RM) -r contrib/libgit-rs/target contrib/libgit-sys/target\n    + \t$(RM) -r contrib/libgit-sys/partial_symbol_export.o\n    + \t$(RM) -r contrib/libgit-sys/hidden_symbol_export.o\n    + \t$(RM) -r contrib/libgit-sys/libgitpub.a\n     \n      ## contrib/libgit-rs/Cargo.lock (new) ##\n     @@\n    @@ contrib/libgit-rs/Cargo.toml (new)\n     +path = \"src/lib.rs\"\n     +\n     +[dependencies]\n    -+libgit-sys = { version = \"0.1.0\", path = \"libgit-sys\" }\n    ++libgit-sys = { version = \"0.1.0\", path = \"../libgit-sys\" }\n     +\n     +[build-dependencies]\n     +autocfg = \"1.4.0\"\n     \n    + ## contrib/libgit-rs/README.md (new) ##\n    +@@\n    ++# libgit-rs\n    ++\n    ++Proof-of-concept Git bindings for Rust.\n    ++\n    ++```toml\n    ++[dependencies]\n    ++libgit = \"0.1.0\"\n    ++```\n    ++\n    ++## Rust version requirements\n    ++\n    ++libgit-rs should support Rust versions at least as old as the version included\n    ++in Debian stable (currently 1.63).\n    +\n      ## contrib/libgit-rs/build.rs (new) ##\n     @@\n     +pub fn main() {\n    @@ contrib/libgit-rs/build.rs (new)\n     +    ac.emit_has_path(\"std::ffi::c_char\");\n     +}\n     \n    - ## contrib/libgit-rs/libgit-sys/src/lib.rs ##\n    -@@\n    -+use std::ffi::c_void;\n    -+\n    - #[cfg(has_std__ffi__c_char)]\n    - use std::ffi::{c_char, c_int};\n    - \n    -@@ contrib/libgit-rs/libgit-sys/src/lib.rs: pub struct libgit_config_set {\n    - }\n    - \n    - extern \"C\" {\n    -+    pub fn free(ptr: *mut c_void);\n    -+\n    -     pub fn libgit_user_agent() -> *const c_char;\n    -     pub fn libgit_user_agent_sanitized() -> *const c_char;\n    - \n    -\n      ## contrib/libgit-rs/src/lib.rs (new) ##\n     @@\n     +use std::ffi::{c_void, CStr, CString};\n    @@ contrib/libgit-rs/testdata/config3 (new)\n     @@\n     +[trace2]\n     +\teventNesting = 3\n    +\n    + ## contrib/libgit-sys/src/lib.rs ##\n    +@@\n    ++use std::ffi::c_void;\n    ++\n    + #[cfg(has_std__ffi__c_char)]\n    + use std::ffi::{c_char, c_int};\n    + \n    +@@ contrib/libgit-sys/src/lib.rs: pub struct libgit_config_set {\n    + }\n    + \n    + extern \"C\" {\n    ++    pub fn free(ptr: *mut c_void);\n    ++\n    +     pub fn libgit_user_agent() -> *const c_char;\n    +     pub fn libgit_user_agent_sanitized() -> *const c_char;\n    + \n5:  15ce989de8 ! 5:  84706f0db7 Makefile: add option to build and test libgit-rs and libgit-rs-sys\n    @@ Makefile: ifdef FSMONITOR_OS_SETTINGS\n      ifeq ($(TCLTK_PATH),)\n      NO_TCLTK = NoThanks\n      endif\n    -@@ Makefile: build-unit-tests: $(UNIT_TEST_PROGS)\n    - unit-tests: $(UNIT_TEST_PROGS) t/helper/test-tool$X\n    +@@ Makefile: build-unit-tests: $(UNIT_TEST_PROGS) $(CLAR_TEST_PROG)\n    + unit-tests: $(UNIT_TEST_PROGS) $(CLAR_TEST_PROG) t/helper/test-tool$X\n      \t$(MAKE) -C t/ unit-tests\n      \n    -+.PHONY: libgitrs-sys\n    -+libgitrs-sys:\n    ++.PHONY: libgit-sys\n    ++libgit-sys:\n     +\t$(QUIET)(\\\n    -+\t\tcd contrib/libgit-rs/libgit-sys && \\\n    ++\t\tcd contrib/libgit-sys && \\\n     +\t\tcargo build \\\n     +\t)\n    -+.PHONY: libgitrs\n    -+libgitrs:\n    ++.PHONY: libgit-rs\n    ++libgit-rs:\n     +\t$(QUIET)(\\\n     +\t\tcd contrib/libgit-rs && \\\n     +\t\tcargo build \\\n     +\t)\n     +ifdef INCLUDE_LIBGIT_RS\n    -+all:: libgitrs\n    ++all:: libgit-rs\n     +endif\n     +\n    - contrib/libgit-rs/libgit-sys/partial_symbol_export.o: contrib/libgit-rs/libgit-sys/public_symbol_export.o libgit.a reftable/libreftable.a xdiff/lib.a\n    + contrib/libgit-sys/partial_symbol_export.o: contrib/libgit-sys/public_symbol_export.o libgit.a reftable/libreftable.a xdiff/lib.a\n      \t$(LD) -r $^ -o $@\n      \n     \n    - ## contrib/libgit-rs/libgit-sys/build.rs ##\n    -@@ contrib/libgit-rs/libgit-sys/build.rs: pub fn main() -> std::io::Result<()> {\n    + ## contrib/libgit-sys/build.rs ##\n    +@@ contrib/libgit-sys/build.rs: pub fn main() -> std::io::Result<()> {\n              .env_remove(\"PROFILE\")\n              .current_dir(git_root.clone())\n              .args([\n     -            \"CFLAGS=-fvisibility=hidden\",\n     +            \"INCLUDE_LIBGIT_RS=YesPlease\",\n    -             \"contrib/libgit-rs/libgit-sys/libgitpub.a\",\n    +             \"contrib/libgit-sys/libgitpub.a\",\n              ])\n              .output()\n     \n    @@ t/Makefile: perf:\n      .PHONY: pre-clean $(T) aggregate-results clean valgrind perf \\\n      \tcheck-chainlint clean-chainlint test-chainlint $(UNIT_TESTS)\n     +\n    -+.PHONY: libgitrs-sys-test\n    -+libgitrs-sys-test:\n    ++.PHONY: libgit-sys-test\n    ++libgit-sys-test:\n     +\t$(QUIET)(\\\n    -+\t\tcd ../contrib/libgit-rs/libgit-sys && \\\n    ++\t\tcd ../contrib/libgit-sys && \\\n     +\t\tcargo test \\\n     +\t)\n    -+.PHONY: libgitrs-test\n    -+libgitrs-test:\n    ++.PHONY: libgit-rs-test\n    ++libgit-rs-test:\n     +\t$(QUIET)(\\\n     +\t\tcd ../contrib/libgit-rs && \\\n     +\t\tcargo test \\\n     +\t)\n     +ifdef INCLUDE_LIBGIT_RS\n    -+all:: libgitrs-sys-test libgitrs-test\n    ++all:: libgit-sys-test libgit-rs-test\n     +endif\n\nbase-commit: 757161efcca150a9a96b312d9e780a071e601a03\n-- \n2.48.0.rc2.279.g1de40edade-goog\n\n"},{"id":"510598","messageId":"ff6cd62397ec2755d15e9d76f9af8a84b54a36c1.1736971328.git.steadmon@google.com","threadId":"61917","inReplyTo":"cover.1736971328.git.steadmon@google.com","subject":"[PATCH v6 1/5] common-main: split init and exit code into new files","fromName":"Josh Steadmon","fromEmail":"steadmon@google.com","sentAt":"2025-01-15T20:05:40Z","receivedAt":"2025-01-15T20:05:49Z","isPatch":true,"sender":{"key":"steadmon@google.com","avatar":"https://avatars.githubusercontent.com/u/2654920?v=4"},"body":"Currently, object files in libgit.a reference common_exit(), which is\ncontained in common-main.o. However, common-main.o also includes main(),\nwhich references cmd_main() in git.o, which in turn depends on all the\nbuiltin/*.o objects.\n\nWe would like to allow external users to link libgit.a without needing\nto include so many extra objects. Enable this by splitting common_exit()\nand check_bug_if_BUG() into a new file common-exit.c, and add\ncommon-exit.o to LIB_OBJS so that these are included in libgit.a.\n\nThis split has previously been proposed ([1], [2]) to support fuzz tests\nand unit tests by avoiding conflicting definitions for main(). However,\nboth of those issues were resolved by other methods of avoiding symbol\nconflicts. Now we are trying to make libgit.a more self-contained, so\nhopefully we can revisit this approach.\n\nAdditionally, move the initialization code out of main() into a new\ninit_git() function in its own file. Include this in libgit.a as well,\nso that external users can share our setup code without calling our\nmain().\n\n[1] https://lore.kernel.org/git/Yp+wjCPhqieTku3X@google.com/\n[2] https://lore.kernel.org/git/20230517-unit-tests-v2-v2-1-21b5b60f4b32@google.com/\n\nSigned-off-by: Josh Steadmon <steadmon@google.com>\n---\n Makefile      |  2 ++\n common-exit.c | 26 ++++++++++++++++\n common-init.c | 63 ++++++++++++++++++++++++++++++++++++++\n common-init.h |  6 ++++\n common-main.c | 83 ++-------------------------------------------------\n 5 files changed, 99 insertions(+), 81 deletions(-)\n create mode 100644 common-exit.c\n create mode 100644 common-init.c\n create mode 100644 common-init.h\n\ndiff --git a/Makefile b/Makefile\nindex 97e8385b66..27e68ac039 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -981,6 +981,8 @@ LIB_OBJS += combine-diff.o\n LIB_OBJS += commit-graph.o\n LIB_OBJS += commit-reach.o\n LIB_OBJS += commit.o\n+LIB_OBJS += common-exit.o\n+LIB_OBJS += common-init.o\n LIB_OBJS += compat/nonblock.o\n LIB_OBJS += compat/obstack.o\n LIB_OBJS += compat/terminal.o\ndiff --git a/common-exit.c b/common-exit.c\nnew file mode 100644\nindex 0000000000..1aaa538be3\n--- /dev/null\n+++ b/common-exit.c\n@@ -0,0 +1,26 @@\n+#include \"git-compat-util.h\"\n+#include \"trace2.h\"\n+\n+static void check_bug_if_BUG(void)\n+{\n+\tif (!bug_called_must_BUG)\n+\t\treturn;\n+\tBUG(\"on exit(): had bug() call(s) in this process without explicit BUG_if_bug()\");\n+}\n+\n+/* We wrap exit() to call common_exit() in git-compat-util.h */\n+int common_exit(const char *file, int line, int code)\n+{\n+\t/*\n+\t * For non-POSIX systems: Take the lowest 8 bits of the \"code\"\n+\t * to e.g. turn -1 into 255. On a POSIX system this is\n+\t * redundant, see exit(3) and wait(2), but as it doesn't harm\n+\t * anything there we don't need to guard this with an \"ifdef\".\n+\t */\n+\tcode &= 0xff;\n+\n+\tcheck_bug_if_BUG();\n+\ttrace2_cmd_exit_fl(file, line, code);\n+\n+\treturn code;\n+}\ndiff --git a/common-init.c b/common-init.c\nnew file mode 100644\nindex 0000000000..5cc73f058c\n--- /dev/null\n+++ b/common-init.c\n@@ -0,0 +1,63 @@\n+#define USE_THE_REPOSITORY_VARIABLE\n+\n+#include \"git-compat-util.h\"\n+#include \"common-init.h\"\n+#include \"exec-cmd.h\"\n+#include \"gettext.h\"\n+#include \"attr.h\"\n+#include \"repository.h\"\n+#include \"setup.h\"\n+#include \"strbuf.h\"\n+#include \"trace2.h\"\n+\n+/*\n+ * Many parts of Git have subprograms communicate via pipe, expect the\n+ * upstream of a pipe to die with SIGPIPE when the downstream of a\n+ * pipe does not need to read all that is written.  Some third-party\n+ * programs that ignore or block SIGPIPE for their own reason forget\n+ * to restore SIGPIPE handling to the default before spawning Git and\n+ * break this carefully orchestrated machinery.\n+ *\n+ * Restore the way SIGPIPE is handled to default, which is what we\n+ * expect.\n+ */\n+static void restore_sigpipe_to_default(void)\n+{\n+\tsigset_t unblock;\n+\n+\tsigemptyset(&unblock);\n+\tsigaddset(&unblock, SIGPIPE);\n+\tsigprocmask(SIG_UNBLOCK, &unblock, NULL);\n+\tsignal(SIGPIPE, SIG_DFL);\n+}\n+\n+void init_git(const char **argv)\n+{\n+\tstruct strbuf tmp = STRBUF_INIT;\n+\n+\ttrace2_initialize_clock();\n+\n+\t/*\n+\t * Always open file descriptors 0/1/2 to avoid clobbering files\n+\t * in die().  It also avoids messing up when the pipes are dup'ed\n+\t * onto stdin/stdout/stderr in the child processes we spawn.\n+\t */\n+\tsanitize_stdfds();\n+\trestore_sigpipe_to_default();\n+\n+\tgit_resolve_executable_dir(argv[0]);\n+\n+\tsetlocale(LC_CTYPE, \"\");\n+\tgit_setup_gettext();\n+\n+\tinitialize_repository(the_repository);\n+\n+\tattr_start();\n+\n+\ttrace2_initialize();\n+\ttrace2_cmd_start(argv);\n+\ttrace2_collect_process_info(TRACE2_PROCESS_INFO_STARTUP);\n+\n+\tif (!strbuf_getcwd(&tmp))\n+\t\ttmp_original_cwd = strbuf_detach(&tmp, NULL);\n+}\ndiff --git a/common-init.h b/common-init.h\nnew file mode 100644\nindex 0000000000..3e6db20cae\n--- /dev/null\n+++ b/common-init.h\n@@ -0,0 +1,6 @@\n+#ifndef COMMON_INIT_H\n+#define COMMON_INIT_H\n+\n+void init_git(const char **argv);\n+\n+#endif /* COMMON_INIT_H */\ndiff --git a/common-main.c b/common-main.c\nindex 8e68ac9e42..6b7ab077b0 100644\n--- a/common-main.c\n+++ b/common-main.c\n@@ -1,92 +1,13 @@\n-#define USE_THE_REPOSITORY_VARIABLE\n-\n #include \"git-compat-util.h\"\n-#include \"exec-cmd.h\"\n-#include \"gettext.h\"\n-#include \"attr.h\"\n-#include \"repository.h\"\n-#include \"setup.h\"\n-#include \"strbuf.h\"\n-#include \"trace2.h\"\n-\n-/*\n- * Many parts of Git have subprograms communicate via pipe, expect the\n- * upstream of a pipe to die with SIGPIPE when the downstream of a\n- * pipe does not need to read all that is written.  Some third-party\n- * programs that ignore or block SIGPIPE for their own reason forget\n- * to restore SIGPIPE handling to the default before spawning Git and\n- * break this carefully orchestrated machinery.\n- *\n- * Restore the way SIGPIPE is handled to default, which is what we\n- * expect.\n- */\n-static void restore_sigpipe_to_default(void)\n-{\n-\tsigset_t unblock;\n-\n-\tsigemptyset(&unblock);\n-\tsigaddset(&unblock, SIGPIPE);\n-\tsigprocmask(SIG_UNBLOCK, &unblock, NULL);\n-\tsignal(SIGPIPE, SIG_DFL);\n-}\n+#include \"common-init.h\"\n \n int main(int argc, const char **argv)\n {\n \tint result;\n-\tstruct strbuf tmp = STRBUF_INIT;\n-\n-\ttrace2_initialize_clock();\n-\n-\t/*\n-\t * Always open file descriptors 0/1/2 to avoid clobbering files\n-\t * in die().  It also avoids messing up when the pipes are dup'ed\n-\t * onto stdin/stdout/stderr in the child processes we spawn.\n-\t */\n-\tsanitize_stdfds();\n-\trestore_sigpipe_to_default();\n-\n-\tgit_resolve_executable_dir(argv[0]);\n-\n-\tsetlocale(LC_CTYPE, \"\");\n-\tgit_setup_gettext();\n-\n-\tinitialize_repository(the_repository);\n-\n-\tattr_start();\n-\n-\ttrace2_initialize();\n-\ttrace2_cmd_start(argv);\n-\ttrace2_collect_process_info(TRACE2_PROCESS_INFO_STARTUP);\n-\n-\tif (!strbuf_getcwd(&tmp))\n-\t\ttmp_original_cwd = strbuf_detach(&tmp, NULL);\n \n+\tinit_git(argv);\n \tresult = cmd_main(argc, argv);\n \n \t/* Not exit(3), but a wrapper calling our common_exit() */\n \texit(result);\n }\n-\n-static void check_bug_if_BUG(void)\n-{\n-\tif (!bug_called_must_BUG)\n-\t\treturn;\n-\tBUG(\"on exit(): had bug() call(s) in this process without explicit BUG_if_bug()\");\n-}\n-\n-/* We wrap exit() to call common_exit() in git-compat-util.h */\n-int common_exit(const char *file, int line, int code)\n-{\n-\t/*\n-\t * For non-POSIX systems: Take the lowest 8 bits of the \"code\"\n-\t * to e.g. turn -1 into 255. On a POSIX system this is\n-\t * redundant, see exit(3) and wait(2), but as it doesn't harm\n-\t * anything there we don't need to guard this with an \"ifdef\".\n-\t */\n-\tcode &= 0xff;\n-\n-\tcheck_bug_if_BUG();\n-\ttrace2_cmd_exit_fl(file, line, code);\n-\n-\treturn code;\n-}\n-- \n2.48.0.rc2.279.g1de40edade-goog\n\n"},{"id":"510599","messageId":"5fc66cdb1628e0c9e420f3f0455779d7471f46ee.1736971328.git.steadmon@google.com","threadId":"61917","inReplyTo":"cover.1736971328.git.steadmon@google.com","subject":"[PATCH v6 2/5] libgit-sys: introduce Rust wrapper for libgit.a","fromName":"Josh Steadmon","fromEmail":"steadmon@google.com","sentAt":"2025-01-15T20:05:41Z","receivedAt":"2025-01-15T20:05:51Z","isPatch":true,"sender":{"key":"steadmon@google.com","avatar":"https://avatars.githubusercontent.com/u/2654920?v=4"},"body":"Introduce libgit-sys, a Rust wrapper crate that allows Rust code to call\nfunctions in libgit.a. This initial patch defines build rules and an\ninterface that exposes user agent string getter functions as a proof of\nconcept. This library can be tested with `cargo test`. In later commits,\na higher-level library containing a more Rust-friendly interface will be\nadded at `contrib/libgit-rs`.\n\nSymbols in libgit can collide with symbols from other libraries such as\nlibgit2. We avoid this by first exposing library symbols in\npublic_symbol_export.[ch]. These symbols are prepended with \"libgit_\" to\navoid collisions and set to visible using a visibility pragma. In\nbuild.rs, Rust builds contrib/libgit-rs/libgit-sys/libgitpub.a, which also\ncontains libgit.a and other dependent libraries, with\n-fvisibility=hidden to hide all symbols within those libraries that\nhaven't been exposed with a visibility pragma.\n\nCo-authored-by: Kyle Lippincott <spectral@google.com>\nCo-authored-by: Calvin Wan <calvinwan@google.com>\nSigned-off-by: Calvin Wan <calvinwan@google.com>\nSigned-off-by: Kyle Lippincott <spectral@google.com>\nSigned-off-by: Josh Steadmon <steadmon@google.com>\n---\n .gitignore                                |  1 +\n Makefile                                  | 16 ++++++\n contrib/libgit-sys/Cargo.lock             | 69 +++++++++++++++++++++++\n contrib/libgit-sys/Cargo.toml             | 18 ++++++\n contrib/libgit-sys/README.md              |  4 ++\n contrib/libgit-sys/build.rs               | 35 ++++++++++++\n contrib/libgit-sys/public_symbol_export.c | 21 +++++++\n contrib/libgit-sys/public_symbol_export.h |  8 +++\n contrib/libgit-sys/src/lib.rs             | 46 +++++++++++++++\n 9 files changed, 218 insertions(+)\n create mode 100644 contrib/libgit-sys/Cargo.lock\n create mode 100644 contrib/libgit-sys/Cargo.toml\n create mode 100644 contrib/libgit-sys/README.md\n create mode 100644 contrib/libgit-sys/build.rs\n create mode 100644 contrib/libgit-sys/public_symbol_export.c\n create mode 100644 contrib/libgit-sys/public_symbol_export.h\n create mode 100644 contrib/libgit-sys/src/lib.rs\n\ndiff --git a/.gitignore b/.gitignore\nindex e82aa19df0..31d7e64287 100644\n--- a/.gitignore\n+++ b/.gitignore\n@@ -250,3 +250,4 @@ Release/\n /git.VC.db\n *.dSYM\n /contrib/buildsystems/out\n+/contrib/libgit-sys/target\ndiff --git a/Makefile b/Makefile\nindex 27e68ac039..47e864a861 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -657,6 +657,8 @@ CURL_CONFIG = curl-config\n GCOV = gcov\n STRIP = strip\n SPATCH = spatch\n+LD = ld\n+OBJCOPY = objcopy\n \n export TCL_PATH TCLTK_PATH\n \n@@ -2731,6 +2733,7 @@ OBJECTS += $(REFTABLE_OBJS) $(REFTABLE_TEST_OBJS)\n OBJECTS += $(UNIT_TEST_OBJS)\n OBJECTS += $(CLAR_TEST_OBJS)\n OBJECTS += $(patsubst %,$(UNIT_TEST_DIR)/%.o,$(UNIT_TEST_PROGRAMS))\n+OBJECTS += contrib/libgit-sys/public_symbol_export.o\n \n ifndef NO_CURL\n \tOBJECTS += http.o http-walker.o remote-curl.o\n@@ -3726,6 +3729,10 @@ clean: profile-clean coverage-clean cocciclean\n \t$(RM) $(htmldocs).tar.gz $(manpages).tar.gz\n \t$(MAKE) -C Documentation/ clean\n \t$(RM) Documentation/GIT-EXCLUDED-PROGRAMS\n+\t$(RM) -r contrib/libgit-sys/target\n+\t$(RM) -r contrib/libgit-sys/partial_symbol_export.o\n+\t$(RM) -r contrib/libgit-sys/hidden_symbol_export.o\n+\t$(RM) -r contrib/libgit-sys/libgitpub.a\n ifndef NO_PERL\n \t$(RM) -r perl/build/\n endif\n@@ -3887,3 +3894,12 @@ $(CLAR_TEST_PROG): $(UNIT_TEST_DIR)/clar.suite $(CLAR_TEST_OBJS) $(GITLIBS) GIT-\n build-unit-tests: $(UNIT_TEST_PROGS) $(CLAR_TEST_PROG)\n unit-tests: $(UNIT_TEST_PROGS) $(CLAR_TEST_PROG) t/helper/test-tool$X\n \t$(MAKE) -C t/ unit-tests\n+\n+contrib/libgit-sys/partial_symbol_export.o: contrib/libgit-sys/public_symbol_export.o libgit.a reftable/libreftable.a xdiff/lib.a\n+\t$(LD) -r $^ -o $@\n+\n+contrib/libgit-sys/hidden_symbol_export.o: contrib/libgit-sys/partial_symbol_export.o\n+\t$(OBJCOPY) --localize-hidden $^ $@\n+\n+contrib/libgit-sys/libgitpub.a: contrib/libgit-sys/hidden_symbol_export.o\n+\t$(AR) $(ARFLAGS) $@ $^\ndiff --git a/contrib/libgit-sys/Cargo.lock b/contrib/libgit-sys/Cargo.lock\nnew file mode 100644\nindex 0000000000..427a4c66b7\n--- /dev/null\n+++ b/contrib/libgit-sys/Cargo.lock\n@@ -0,0 +1,69 @@\n+# This file is automatically @generated by Cargo.\n+# It is not intended for manual editing.\n+version = 3\n+\n+[[package]]\n+name = \"autocfg\"\n+version = \"1.4.0\"\n+source = \"registry+https://github.com/rust-lang/crates.io-index\"\n+checksum = \"ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26\"\n+\n+[[package]]\n+name = \"cc\"\n+version = \"1.1.15\"\n+source = \"registry+https://github.com/rust-lang/crates.io-index\"\n+checksum = \"57b6a275aa2903740dc87da01c62040406b8812552e97129a63ea8850a17c6e6\"\n+dependencies = [\n+ \"shlex\",\n+]\n+\n+[[package]]\n+name = \"libc\"\n+version = \"0.2.158\"\n+source = \"registry+https://github.com/rust-lang/crates.io-index\"\n+checksum = \"d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439\"\n+\n+[[package]]\n+name = \"libgit-sys\"\n+version = \"0.1.0\"\n+dependencies = [\n+ \"autocfg\",\n+ \"libz-sys\",\n+ \"make-cmd\",\n+]\n+\n+[[package]]\n+name = \"libz-sys\"\n+version = \"1.1.20\"\n+source = \"registry+https://github.com/rust-lang/crates.io-index\"\n+checksum = \"d2d16453e800a8cf6dd2fc3eb4bc99b786a9b90c663b8559a5b1a041bf89e472\"\n+dependencies = [\n+ \"cc\",\n+ \"libc\",\n+ \"pkg-config\",\n+ \"vcpkg\",\n+]\n+\n+[[package]]\n+name = \"make-cmd\"\n+version = \"0.1.0\"\n+source = \"registry+https://github.com/rust-lang/crates.io-index\"\n+checksum = \"a8ca8afbe8af1785e09636acb5a41e08a765f5f0340568716c18a8700ba3c0d3\"\n+\n+[[package]]\n+name = \"pkg-config\"\n+version = \"0.3.30\"\n+source = \"registry+https://github.com/rust-lang/crates.io-index\"\n+checksum = \"d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec\"\n+\n+[[package]]\n+name = \"shlex\"\n+version = \"1.3.0\"\n+source = \"registry+https://github.com/rust-lang/crates.io-index\"\n+checksum = \"0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64\"\n+\n+[[package]]\n+name = \"vcpkg\"\n+version = \"0.2.15\"\n+source = \"registry+https://github.com/rust-lang/crates.io-index\"\n+checksum = \"accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426\"\ndiff --git a/contrib/libgit-sys/Cargo.toml b/contrib/libgit-sys/Cargo.toml\nnew file mode 100644\nindex 0000000000..15b28c3585\n--- /dev/null\n+++ b/contrib/libgit-sys/Cargo.toml\n@@ -0,0 +1,18 @@\n+[package]\n+name = \"libgit-sys\"\n+version = \"0.1.0\"\n+edition = \"2021\"\n+build = \"build.rs\"\n+links = \"gitpub\"\n+rust-version = \"1.63\"\n+description = \"Native bindings to a portion of libgit\"\n+\n+[lib]\n+path = \"src/lib.rs\"\n+\n+[dependencies]\n+libz-sys = \"1.1.19\"\n+\n+[build-dependencies]\n+autocfg = \"1.4.0\"\n+make-cmd = \"0.1.0\"\ndiff --git a/contrib/libgit-sys/README.md b/contrib/libgit-sys/README.md\nnew file mode 100644\nindex 0000000000..c061cfcaf5\n--- /dev/null\n+++ b/contrib/libgit-sys/README.md\n@@ -0,0 +1,4 @@\n+# libgit-sys\n+\n+A small proof-of-concept crate showing how to provide a Rust FFI to Git\n+internals.\ndiff --git a/contrib/libgit-sys/build.rs b/contrib/libgit-sys/build.rs\nnew file mode 100644\nindex 0000000000..b6c65193bc\n--- /dev/null\n+++ b/contrib/libgit-sys/build.rs\n@@ -0,0 +1,35 @@\n+use std::env;\n+use std::path::PathBuf;\n+\n+pub fn main() -> std::io::Result<()> {\n+    let ac = autocfg::new();\n+    ac.emit_has_path(\"std::ffi::c_char\");\n+\n+    let crate_root = PathBuf::from(env::var_os(\"CARGO_MANIFEST_DIR\").unwrap());\n+    let git_root = crate_root.join(\"../..\");\n+    let dst = PathBuf::from(env::var_os(\"OUT_DIR\").unwrap());\n+\n+    let make_output = make_cmd::gnu_make()\n+        .env(\"DEVELOPER\", \"1\")\n+        .env_remove(\"PROFILE\")\n+        .current_dir(git_root.clone())\n+        .args([\n+            \"CFLAGS=-fvisibility=hidden\",\n+            \"contrib/libgit-sys/libgitpub.a\",\n+        ])\n+        .output()\n+        .expect(\"Make failed to run\");\n+    if !make_output.status.success() {\n+        panic!(\n+            \"Make failed:\\n  stdout = {}\\n  stderr = {}\\n\",\n+            String::from_utf8(make_output.stdout).unwrap(),\n+            String::from_utf8(make_output.stderr).unwrap()\n+        );\n+    }\n+    std::fs::copy(crate_root.join(\"libgitpub.a\"), dst.join(\"libgitpub.a\"))?;\n+    println!(\"cargo:rustc-link-search=native={}\", dst.display());\n+    println!(\"cargo:rustc-link-lib=gitpub\");\n+    println!(\"cargo:rerun-if-changed={}\", git_root.display());\n+\n+    Ok(())\n+}\ndiff --git a/contrib/libgit-sys/public_symbol_export.c b/contrib/libgit-sys/public_symbol_export.c\nnew file mode 100644\nindex 0000000000..7cd5007902\n--- /dev/null\n+++ b/contrib/libgit-sys/public_symbol_export.c\n@@ -0,0 +1,21 @@\n+// Shim to publicly export Git symbols. These must be renamed so that the\n+// original symbols can be hidden. Renaming these with a \"libgit_\" prefix also\n+// avoids conflicts with other libraries such as libgit2.\n+\n+#include \"git-compat-util.h\"\n+#include \"contrib/libgit-sys/public_symbol_export.h\"\n+#include \"version.h\"\n+\n+#pragma GCC visibility push(default)\n+\n+const char *libgit_user_agent(void)\n+{\n+\treturn git_user_agent();\n+}\n+\n+const char *libgit_user_agent_sanitized(void)\n+{\n+\treturn git_user_agent_sanitized();\n+}\n+\n+#pragma GCC visibility pop\ndiff --git a/contrib/libgit-sys/public_symbol_export.h b/contrib/libgit-sys/public_symbol_export.h\nnew file mode 100644\nindex 0000000000..a3372f93fa\n--- /dev/null\n+++ b/contrib/libgit-sys/public_symbol_export.h\n@@ -0,0 +1,8 @@\n+#ifndef PUBLIC_SYMBOL_EXPORT_H\n+#define PUBLIC_SYMBOL_EXPORT_H\n+\n+const char *libgit_user_agent(void);\n+\n+const char *libgit_user_agent_sanitized(void);\n+\n+#endif /* PUBLIC_SYMBOL_EXPORT_H */\ndiff --git a/contrib/libgit-sys/src/lib.rs b/contrib/libgit-sys/src/lib.rs\nnew file mode 100644\nindex 0000000000..d4853f3074\n--- /dev/null\n+++ b/contrib/libgit-sys/src/lib.rs\n@@ -0,0 +1,46 @@\n+#[cfg(has_std__ffi__c_char)]\n+use std::ffi::c_char;\n+\n+#[cfg(not(has_std__ffi__c_char))]\n+#[allow(non_camel_case_types)]\n+pub type c_char = i8;\n+\n+extern crate libz_sys;\n+\n+extern \"C\" {\n+    pub fn libgit_user_agent() -> *const c_char;\n+    pub fn libgit_user_agent_sanitized() -> *const c_char;\n+}\n+\n+#[cfg(test)]\n+mod tests {\n+    use std::ffi::CStr;\n+\n+    use super::*;\n+\n+    #[test]\n+    fn user_agent_starts_with_git() {\n+        let c_str = unsafe { CStr::from_ptr(libgit_user_agent()) };\n+        let agent = c_str\n+            .to_str()\n+            .expect(\"User agent contains invalid UTF-8 data\");\n+        assert!(\n+            agent.starts_with(\"git/\"),\n+            r#\"Expected user agent to start with \"git/\", got: {}\"#,\n+            agent\n+        );\n+    }\n+\n+    #[test]\n+    fn sanitized_user_agent_starts_with_git() {\n+        let c_str = unsafe { CStr::from_ptr(libgit_user_agent_sanitized()) };\n+        let agent = c_str\n+            .to_str()\n+            .expect(\"Sanitized user agent contains invalid UTF-8 data\");\n+        assert!(\n+            agent.starts_with(\"git/\"),\n+            r#\"Expected user agent to start with \"git/\", got: {}\"#,\n+            agent\n+        );\n+    }\n+}\n-- \n2.48.0.rc2.279.g1de40edade-goog\n\n"},{"id":"510600","messageId":"03f39b6c3ae8a36b3cedaa76d592af00491be1da.1736971328.git.steadmon@google.com","threadId":"61917","inReplyTo":"cover.1736971328.git.steadmon@google.com","subject":"[PATCH v6 3/5] libgit-sys: also export some config_set functions","fromName":"Josh Steadmon","fromEmail":"steadmon@google.com","sentAt":"2025-01-15T20:05:42Z","receivedAt":"2025-01-15T20:05:53Z","isPatch":true,"sender":{"key":"steadmon@google.com","avatar":"https://avatars.githubusercontent.com/u/2654920?v=4"},"body":"In preparation for implementing a higher-level Rust API for accessing\nGit configs, export some of the upstream configset API via libgitpub and\nlibgit-sys. Since this will be exercised as part of the higher-level API\nin the next commit, no tests have been added for libgit-sys.\n\nWhile we're at it, add git_configset_alloc() and git_configset_free()\nfunctions in libgitpub so that callers can manage config_set structs on\nthe heap. This also allows non-C external consumers to treat config_sets\nas opaque structs.\n\nCo-authored-by: Calvin Wan <calvinwan@google.com>\nSigned-off-by: Calvin Wan <calvinwan@google.com>\nSigned-off-by: Josh Steadmon <steadmon@google.com>\n---\n contrib/libgit-sys/public_symbol_export.c | 29 +++++++++++++++++++++\n contrib/libgit-sys/public_symbol_export.h | 10 ++++++++\n contrib/libgit-sys/src/lib.rs             | 31 ++++++++++++++++++++++-\n 3 files changed, 69 insertions(+), 1 deletion(-)\n\ndiff --git a/contrib/libgit-sys/public_symbol_export.c b/contrib/libgit-sys/public_symbol_export.c\nindex 7cd5007902..1799fc6fde 100644\n--- a/contrib/libgit-sys/public_symbol_export.c\n+++ b/contrib/libgit-sys/public_symbol_export.c\n@@ -3,11 +3,40 @@\n // avoids conflicts with other libraries such as libgit2.\n \n #include \"git-compat-util.h\"\n+#include \"config.h\"\n #include \"contrib/libgit-sys/public_symbol_export.h\"\n #include \"version.h\"\n \n #pragma GCC visibility push(default)\n \n+struct libgit_config_set *libgit_configset_alloc(void)\n+{\n+\tstruct config_set *cs = xmalloc(sizeof(struct config_set));\n+\tgit_configset_init(cs);\n+\treturn (struct libgit_config_set *) cs;\n+}\n+\n+void libgit_configset_free(struct libgit_config_set *cs)\n+{\n+\tgit_configset_clear((struct config_set *) cs);\n+\tfree((struct config_set *) cs);\n+}\n+\n+int libgit_configset_add_file(struct libgit_config_set *cs, const char *filename)\n+{\n+\treturn git_configset_add_file((struct config_set *) cs, filename);\n+}\n+\n+int libgit_configset_get_int(struct libgit_config_set *cs, const char *key, int *dest)\n+{\n+\treturn git_configset_get_int((struct config_set *) cs, key, dest);\n+}\n+\n+int libgit_configset_get_string(struct libgit_config_set *cs, const char *key, char **dest)\n+{\n+\treturn git_configset_get_string((struct config_set *) cs, key, dest);\n+}\n+\n const char *libgit_user_agent(void)\n {\n \treturn git_user_agent();\ndiff --git a/contrib/libgit-sys/public_symbol_export.h b/contrib/libgit-sys/public_symbol_export.h\nindex a3372f93fa..701db92d53 100644\n--- a/contrib/libgit-sys/public_symbol_export.h\n+++ b/contrib/libgit-sys/public_symbol_export.h\n@@ -1,6 +1,16 @@\n #ifndef PUBLIC_SYMBOL_EXPORT_H\n #define PUBLIC_SYMBOL_EXPORT_H\n \n+struct libgit_config_set *libgit_configset_alloc(void);\n+\n+void libgit_configset_free(struct libgit_config_set *cs);\n+\n+int libgit_configset_add_file(struct libgit_config_set *cs, const char *filename);\n+\n+int libgit_configset_get_int(struct libgit_config_set *cs, const char *key, int *dest);\n+\n+int libgit_configset_get_string(struct libgit_config_set *cs, const char *key, char **dest);\n+\n const char *libgit_user_agent(void);\n \n const char *libgit_user_agent_sanitized(void);\ndiff --git a/contrib/libgit-sys/src/lib.rs b/contrib/libgit-sys/src/lib.rs\nindex d4853f3074..dadb4e5f40 100644\n--- a/contrib/libgit-sys/src/lib.rs\n+++ b/contrib/libgit-sys/src/lib.rs\n@@ -1,15 +1,44 @@\n #[cfg(has_std__ffi__c_char)]\n-use std::ffi::c_char;\n+use std::ffi::{c_char, c_int};\n \n #[cfg(not(has_std__ffi__c_char))]\n #[allow(non_camel_case_types)]\n pub type c_char = i8;\n \n+#[cfg(not(has_std__ffi__c_char))]\n+#[allow(non_camel_case_types)]\n+pub type c_int = i32;\n+\n extern crate libz_sys;\n \n+#[allow(non_camel_case_types)]\n+#[repr(C)]\n+pub struct libgit_config_set {\n+    _data: [u8; 0],\n+    _marker: core::marker::PhantomData<(*mut u8, core::marker::PhantomPinned)>,\n+}\n+\n extern \"C\" {\n     pub fn libgit_user_agent() -> *const c_char;\n     pub fn libgit_user_agent_sanitized() -> *const c_char;\n+\n+    pub fn libgit_configset_alloc() -> *mut libgit_config_set;\n+    pub fn libgit_configset_free(cs: *mut libgit_config_set);\n+\n+    pub fn libgit_configset_add_file(cs: *mut libgit_config_set, filename: *const c_char) -> c_int;\n+\n+    pub fn libgit_configset_get_int(\n+        cs: *mut libgit_config_set,\n+        key: *const c_char,\n+        int: *mut c_int,\n+    ) -> c_int;\n+\n+    pub fn libgit_configset_get_string(\n+        cs: *mut libgit_config_set,\n+        key: *const c_char,\n+        dest: *mut *mut c_char,\n+    ) -> c_int;\n+\n }\n \n #[cfg(test)]\n-- \n2.48.0.rc2.279.g1de40edade-goog\n\n"},{"id":"510601","messageId":"65166ea0c077665c350a1a7b00dc3175be889d55.1736971328.git.steadmon@google.com","threadId":"61917","inReplyTo":"cover.1736971328.git.steadmon@google.com","subject":"[PATCH v6 4/5] libgit: add higher-level libgit crate","fromName":"Josh Steadmon","fromEmail":"steadmon@google.com","sentAt":"2025-01-15T20:05:43Z","receivedAt":"2025-01-15T20:05:55Z","isPatch":true,"sender":{"key":"steadmon@google.com","avatar":"https://avatars.githubusercontent.com/u/2654920?v=4"},"body":"From: Calvin Wan <calvinwan@google.com>\n\nThe C functions exported by libgit-sys do not provide an idiomatic Rust\ninterface. To make it easier to use these functions via Rust, add a\nhigher-level \"libgit\" crate, that wraps the lower-level configset API\nwith an interface that is more Rust-y.\n\nThis combination of $X and $X-sys crates is a common pattern for FFI in\nRust, as documented in \"The Cargo Book\" [1].\n\n[1] https://doc.rust-lang.org/cargo/reference/build-scripts.html#-sys-packages\n\nCo-authored-by: Josh Steadmon <steadmon@google.com>\nSigned-off-by: Calvin Wan <calvinwan@google.com>\nSigned-off-by: Josh Steadmon <steadmon@google.com>\n---\n .gitignore                         |  1 +\n Makefile                           |  2 +-\n contrib/libgit-rs/Cargo.lock       | 77 ++++++++++++++++++++++++\n contrib/libgit-rs/Cargo.toml       | 15 +++++\n contrib/libgit-rs/README.md        | 13 ++++\n contrib/libgit-rs/build.rs         |  4 ++\n contrib/libgit-rs/src/lib.rs       | 95 ++++++++++++++++++++++++++++++\n contrib/libgit-rs/testdata/config1 |  2 +\n contrib/libgit-rs/testdata/config2 |  2 +\n contrib/libgit-rs/testdata/config3 |  2 +\n contrib/libgit-sys/src/lib.rs      |  4 ++\n 11 files changed, 216 insertions(+), 1 deletion(-)\n create mode 100644 contrib/libgit-rs/Cargo.lock\n create mode 100644 contrib/libgit-rs/Cargo.toml\n create mode 100644 contrib/libgit-rs/README.md\n create mode 100644 contrib/libgit-rs/build.rs\n create mode 100644 contrib/libgit-rs/src/lib.rs\n create mode 100644 contrib/libgit-rs/testdata/config1\n create mode 100644 contrib/libgit-rs/testdata/config2\n create mode 100644 contrib/libgit-rs/testdata/config3\n\ndiff --git a/.gitignore b/.gitignore\nindex 31d7e64287..acdd8ce7c7 100644\n--- a/.gitignore\n+++ b/.gitignore\n@@ -250,4 +250,5 @@ Release/\n /git.VC.db\n *.dSYM\n /contrib/buildsystems/out\n+/contrib/libgit-rs/target\n /contrib/libgit-sys/target\ndiff --git a/Makefile b/Makefile\nindex 47e864a861..230d366457 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -3729,7 +3729,7 @@ clean: profile-clean coverage-clean cocciclean\n \t$(RM) $(htmldocs).tar.gz $(manpages).tar.gz\n \t$(MAKE) -C Documentation/ clean\n \t$(RM) Documentation/GIT-EXCLUDED-PROGRAMS\n-\t$(RM) -r contrib/libgit-sys/target\n+\t$(RM) -r contrib/libgit-rs/target contrib/libgit-sys/target\n \t$(RM) -r contrib/libgit-sys/partial_symbol_export.o\n \t$(RM) -r contrib/libgit-sys/hidden_symbol_export.o\n \t$(RM) -r contrib/libgit-sys/libgitpub.a\ndiff --git a/contrib/libgit-rs/Cargo.lock b/contrib/libgit-rs/Cargo.lock\nnew file mode 100644\nindex 0000000000..a30c7c8d33\n--- /dev/null\n+++ b/contrib/libgit-rs/Cargo.lock\n@@ -0,0 +1,77 @@\n+# This file is automatically @generated by Cargo.\n+# It is not intended for manual editing.\n+version = 3\n+\n+[[package]]\n+name = \"autocfg\"\n+version = \"1.4.0\"\n+source = \"registry+https://github.com/rust-lang/crates.io-index\"\n+checksum = \"ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26\"\n+\n+[[package]]\n+name = \"cc\"\n+version = \"1.1.15\"\n+source = \"registry+https://github.com/rust-lang/crates.io-index\"\n+checksum = \"57b6a275aa2903740dc87da01c62040406b8812552e97129a63ea8850a17c6e6\"\n+dependencies = [\n+ \"shlex\",\n+]\n+\n+[[package]]\n+name = \"libc\"\n+version = \"0.2.158\"\n+source = \"registry+https://github.com/rust-lang/crates.io-index\"\n+checksum = \"d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439\"\n+\n+[[package]]\n+name = \"libgit\"\n+version = \"0.1.0\"\n+dependencies = [\n+ \"autocfg\",\n+ \"libgit-sys\",\n+]\n+\n+[[package]]\n+name = \"libgit-sys\"\n+version = \"0.1.0\"\n+dependencies = [\n+ \"autocfg\",\n+ \"libz-sys\",\n+ \"make-cmd\",\n+]\n+\n+[[package]]\n+name = \"libz-sys\"\n+version = \"1.1.20\"\n+source = \"registry+https://github.com/rust-lang/crates.io-index\"\n+checksum = \"d2d16453e800a8cf6dd2fc3eb4bc99b786a9b90c663b8559a5b1a041bf89e472\"\n+dependencies = [\n+ \"cc\",\n+ \"libc\",\n+ \"pkg-config\",\n+ \"vcpkg\",\n+]\n+\n+[[package]]\n+name = \"make-cmd\"\n+version = \"0.1.0\"\n+source = \"registry+https://github.com/rust-lang/crates.io-index\"\n+checksum = \"a8ca8afbe8af1785e09636acb5a41e08a765f5f0340568716c18a8700ba3c0d3\"\n+\n+[[package]]\n+name = \"pkg-config\"\n+version = \"0.3.30\"\n+source = \"registry+https://github.com/rust-lang/crates.io-index\"\n+checksum = \"d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec\"\n+\n+[[package]]\n+name = \"shlex\"\n+version = \"1.3.0\"\n+source = \"registry+https://github.com/rust-lang/crates.io-index\"\n+checksum = \"0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64\"\n+\n+[[package]]\n+name = \"vcpkg\"\n+version = \"0.2.15\"\n+source = \"registry+https://github.com/rust-lang/crates.io-index\"\n+checksum = \"accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426\"\ndiff --git a/contrib/libgit-rs/Cargo.toml b/contrib/libgit-rs/Cargo.toml\nnew file mode 100644\nindex 0000000000..634435cd6c\n--- /dev/null\n+++ b/contrib/libgit-rs/Cargo.toml\n@@ -0,0 +1,15 @@\n+[package]\n+name = \"libgit\"\n+version = \"0.1.0\"\n+edition = \"2021\"\n+build = \"build.rs\"\n+rust-version = \"1.63\"\n+\n+[lib]\n+path = \"src/lib.rs\"\n+\n+[dependencies]\n+libgit-sys = { version = \"0.1.0\", path = \"../libgit-sys\" }\n+\n+[build-dependencies]\n+autocfg = \"1.4.0\"\ndiff --git a/contrib/libgit-rs/README.md b/contrib/libgit-rs/README.md\nnew file mode 100644\nindex 0000000000..ff945e1ce2\n--- /dev/null\n+++ b/contrib/libgit-rs/README.md\n@@ -0,0 +1,13 @@\n+# libgit-rs\n+\n+Proof-of-concept Git bindings for Rust.\n+\n+```toml\n+[dependencies]\n+libgit = \"0.1.0\"\n+```\n+\n+## Rust version requirements\n+\n+libgit-rs should support Rust versions at least as old as the version included\n+in Debian stable (currently 1.63).\ndiff --git a/contrib/libgit-rs/build.rs b/contrib/libgit-rs/build.rs\nnew file mode 100644\nindex 0000000000..f8bd01a690\n--- /dev/null\n+++ b/contrib/libgit-rs/build.rs\n@@ -0,0 +1,4 @@\n+pub fn main() {\n+    let ac = autocfg::new();\n+    ac.emit_has_path(\"std::ffi::c_char\");\n+}\ndiff --git a/contrib/libgit-rs/src/lib.rs b/contrib/libgit-rs/src/lib.rs\nnew file mode 100644\nindex 0000000000..27b6fd63f1\n--- /dev/null\n+++ b/contrib/libgit-rs/src/lib.rs\n@@ -0,0 +1,95 @@\n+use std::ffi::{c_void, CStr, CString};\n+use std::path::Path;\n+\n+#[cfg(has_std__ffi__c_char)]\n+use std::ffi::{c_char, c_int};\n+\n+#[cfg(not(has_std__ffi__c_char))]\n+#[allow(non_camel_case_types)]\n+pub type c_char = i8;\n+\n+#[cfg(not(has_std__ffi__c_char))]\n+#[allow(non_camel_case_types)]\n+pub type c_int = i32;\n+\n+use libgit_sys::*;\n+\n+pub struct ConfigSet(*mut libgit_config_set);\n+impl ConfigSet {\n+    pub fn new() -> Self {\n+        unsafe { ConfigSet(libgit_configset_alloc()) }\n+    }\n+\n+    pub fn add_files(&mut self, files: &[&Path]) {\n+        for file in files {\n+            let pstr = file.to_str().expect(\"Invalid UTF-8\");\n+            let rs = CString::new(pstr).expect(\"Couldn't convert to CString\");\n+            unsafe {\n+                libgit_configset_add_file(self.0, rs.as_ptr());\n+            }\n+        }\n+    }\n+\n+    pub fn get_int(&mut self, key: &str) -> Option<i32> {\n+        let key = CString::new(key).expect(\"Couldn't convert to CString\");\n+        let mut val: c_int = 0;\n+        unsafe {\n+            if libgit_configset_get_int(self.0, key.as_ptr(), &mut val as *mut c_int) != 0 {\n+                return None;\n+            }\n+        }\n+\n+        Some(val.into())\n+    }\n+\n+    pub fn get_string(&mut self, key: &str) -> Option<String> {\n+        let key = CString::new(key).expect(\"Couldn't convert key to CString\");\n+        let mut val: *mut c_char = std::ptr::null_mut();\n+        unsafe {\n+            if libgit_configset_get_string(self.0, key.as_ptr(), &mut val as *mut *mut c_char) != 0\n+            {\n+                return None;\n+            }\n+            let borrowed_str = CStr::from_ptr(val);\n+            let owned_str =\n+                String::from(borrowed_str.to_str().expect(\"Couldn't convert val to str\"));\n+            free(val as *mut c_void); // Free the xstrdup()ed pointer from the C side\n+            Some(owned_str)\n+        }\n+    }\n+}\n+\n+impl Default for ConfigSet {\n+    fn default() -> Self {\n+        Self::new()\n+    }\n+}\n+\n+impl Drop for ConfigSet {\n+    fn drop(&mut self) {\n+        unsafe {\n+            libgit_configset_free(self.0);\n+        }\n+    }\n+}\n+\n+#[cfg(test)]\n+mod tests {\n+    use super::*;\n+\n+    #[test]\n+    fn load_configs_via_configset() {\n+        let mut cs = ConfigSet::new();\n+        cs.add_files(&[\n+            Path::new(\"testdata/config1\"),\n+            Path::new(\"testdata/config2\"),\n+            Path::new(\"testdata/config3\"),\n+        ]);\n+        // ConfigSet retrieves correct value\n+        assert_eq!(cs.get_int(\"trace2.eventTarget\"), Some(1));\n+        // ConfigSet respects last config value set\n+        assert_eq!(cs.get_int(\"trace2.eventNesting\"), Some(3));\n+        // ConfigSet returns None for missing key\n+        assert_eq!(cs.get_string(\"foo.bar\"), None);\n+    }\n+}\ndiff --git a/contrib/libgit-rs/testdata/config1 b/contrib/libgit-rs/testdata/config1\nnew file mode 100644\nindex 0000000000..4e9a9d25d1\n--- /dev/null\n+++ b/contrib/libgit-rs/testdata/config1\n@@ -0,0 +1,2 @@\n+[trace2]\n+\teventNesting = 1\ndiff --git a/contrib/libgit-rs/testdata/config2 b/contrib/libgit-rs/testdata/config2\nnew file mode 100644\nindex 0000000000..b8d1eca423\n--- /dev/null\n+++ b/contrib/libgit-rs/testdata/config2\n@@ -0,0 +1,2 @@\n+[trace2]\n+\teventTarget = 1\ndiff --git a/contrib/libgit-rs/testdata/config3 b/contrib/libgit-rs/testdata/config3\nnew file mode 100644\nindex 0000000000..ca7b9a7c38\n--- /dev/null\n+++ b/contrib/libgit-rs/testdata/config3\n@@ -0,0 +1,2 @@\n+[trace2]\n+\teventNesting = 3\ndiff --git a/contrib/libgit-sys/src/lib.rs b/contrib/libgit-sys/src/lib.rs\nindex dadb4e5f40..4bfc650450 100644\n--- a/contrib/libgit-sys/src/lib.rs\n+++ b/contrib/libgit-sys/src/lib.rs\n@@ -1,3 +1,5 @@\n+use std::ffi::c_void;\n+\n #[cfg(has_std__ffi__c_char)]\n use std::ffi::{c_char, c_int};\n \n@@ -19,6 +21,8 @@ pub struct libgit_config_set {\n }\n \n extern \"C\" {\n+    pub fn free(ptr: *mut c_void);\n+\n     pub fn libgit_user_agent() -> *const c_char;\n     pub fn libgit_user_agent_sanitized() -> *const c_char;\n \n-- \n2.48.0.rc2.279.g1de40edade-goog\n\n"},{"id":"510602","messageId":"84706f0db79fd83d93ea7b40e226300e69658d17.1736971328.git.steadmon@google.com","threadId":"61917","inReplyTo":"cover.1736971328.git.steadmon@google.com","subject":"[PATCH v6 5/5] Makefile: add option to build and test libgit-rs and libgit-rs-sys","fromName":"Josh Steadmon","fromEmail":"steadmon@google.com","sentAt":"2025-01-15T20:05:44Z","receivedAt":"2025-01-15T20:05:57Z","isPatch":true,"sender":{"key":"steadmon@google.com","avatar":"https://avatars.githubusercontent.com/u/2654920?v=4"},"body":"From: Calvin Wan <calvinwan@google.com>\n\nAdd libgitrs, libgitrs-sys, libgitrs-test, and libgitrs-sys-test targets\nto their respective Makefiles so they can be built and tested without\nhaving to run cargo build/test.\n\nAdd build variable, INCLUDE_LIBGIT_RS, that when set, automatically\nbuilds and tests libgit-rs and libgit-rs-sys when `make all` is run.\n\nCo-authored-by: Josh Steadmon <steadmon@google.com>\nSigned-off-by: Calvin Wan <calvinwan@google.com>\nSigned-off-by: Josh Steadmon <steadmon@google.com>\n---\n Makefile                    | 26 ++++++++++++++++++++++++++\n contrib/libgit-sys/build.rs |  2 +-\n t/Makefile                  | 16 ++++++++++++++++\n 3 files changed, 43 insertions(+), 1 deletion(-)\n\ndiff --git a/Makefile b/Makefile\nindex 230d366457..133090a145 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -416,6 +416,9 @@ include shared.mak\n # Define LINK_FUZZ_PROGRAMS if you want `make all` to also build the fuzz test\n # programs in oss-fuzz/.\n #\n+# Define INCLUDE_LIBGIT_RS if you want `make all` and `make test` to build and\n+# test the Rust crates in contrib/libgit-rs/ and contrib/libgit-rs/libgit-sys/.\n+#\n # === Optional library: libintl ===\n #\n # Define NO_GETTEXT if you don't want Git output to be translated.\n@@ -2238,6 +2241,13 @@ ifdef FSMONITOR_OS_SETTINGS\n \tCOMPAT_OBJS += compat/fsmonitor/fsm-path-utils-$(FSMONITOR_OS_SETTINGS).o\n endif\n \n+ifdef INCLUDE_LIBGIT_RS\n+\t# Enable symbol hiding in contrib/libgit-rs/libgit-sys/libgitpub.a\n+\t# without making us rebuild the whole tree every time we run a Rust\n+\t# build.\n+\tBASIC_CFLAGS += -fvisibility=hidden\n+endif\n+\n ifeq ($(TCLTK_PATH),)\n NO_TCLTK = NoThanks\n endif\n@@ -3895,6 +3905,22 @@ build-unit-tests: $(UNIT_TEST_PROGS) $(CLAR_TEST_PROG)\n unit-tests: $(UNIT_TEST_PROGS) $(CLAR_TEST_PROG) t/helper/test-tool$X\n \t$(MAKE) -C t/ unit-tests\n \n+.PHONY: libgit-sys\n+libgit-sys:\n+\t$(QUIET)(\\\n+\t\tcd contrib/libgit-sys && \\\n+\t\tcargo build \\\n+\t)\n+.PHONY: libgit-rs\n+libgit-rs:\n+\t$(QUIET)(\\\n+\t\tcd contrib/libgit-rs && \\\n+\t\tcargo build \\\n+\t)\n+ifdef INCLUDE_LIBGIT_RS\n+all:: libgit-rs\n+endif\n+\n contrib/libgit-sys/partial_symbol_export.o: contrib/libgit-sys/public_symbol_export.o libgit.a reftable/libreftable.a xdiff/lib.a\n \t$(LD) -r $^ -o $@\n \ndiff --git a/contrib/libgit-sys/build.rs b/contrib/libgit-sys/build.rs\nindex b6c65193bc..3ffd80ad91 100644\n--- a/contrib/libgit-sys/build.rs\n+++ b/contrib/libgit-sys/build.rs\n@@ -14,7 +14,7 @@ pub fn main() -> std::io::Result<()> {\n         .env_remove(\"PROFILE\")\n         .current_dir(git_root.clone())\n         .args([\n-            \"CFLAGS=-fvisibility=hidden\",\n+            \"INCLUDE_LIBGIT_RS=YesPlease\",\n             \"contrib/libgit-sys/libgitpub.a\",\n         ])\n         .output()\ndiff --git a/t/Makefile b/t/Makefile\nindex daa5fcae86..45fe6089b2 100644\n--- a/t/Makefile\n+++ b/t/Makefile\n@@ -177,3 +177,19 @@ perf:\n \n .PHONY: pre-clean $(T) aggregate-results clean valgrind perf \\\n \tcheck-chainlint clean-chainlint test-chainlint $(UNIT_TESTS)\n+\n+.PHONY: libgit-sys-test\n+libgit-sys-test:\n+\t$(QUIET)(\\\n+\t\tcd ../contrib/libgit-sys && \\\n+\t\tcargo test \\\n+\t)\n+.PHONY: libgit-rs-test\n+libgit-rs-test:\n+\t$(QUIET)(\\\n+\t\tcd ../contrib/libgit-rs && \\\n+\t\tcargo test \\\n+\t)\n+ifdef INCLUDE_LIBGIT_RS\n+all:: libgit-sys-test libgit-rs-test\n+endif\n-- \n2.48.0.rc2.279.g1de40edade-goog\n\n"},{"id":"510616","messageId":"xmqqwmevwvy5.fsf@gitster.g","threadId":"61917","inReplyTo":"cover.1736971328.git.steadmon@google.com","subject":"Re: [PATCH v6 0/5] Introduce libgit-rs, a Rust wrapper around libgit.a","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-01-15T22:31:30Z","receivedAt":"2025-01-15T22:31:33Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Josh Steadmon <steadmon@google.com> writes:\n\n> Apologies for the long delay on V6; I am finally back after several\n> months of $DAYJOB firefighting, holidays, and sick leave. I should have\n> time to devote to this series again, but given the lack of feedback on\n> V5 I am hopeful that this will be the final iteration of this series.\n\nThanks and welcome back ;-)\n\nGiven the lack of feedback on the previous round, I hope we will see\nenthused support on the topic.  Otherwise it is hard to tell if the\nprevious lack of feedback was merely lack of interest, or lack of\nanything lacking in the series.\n\n> There is known NEEDSWORK, but I feel that they can be addressed in\n> follow-up changes, rather than in this series. If you feel otherwise,\n> please let me know:\n>\n> * Investigate alternative methods of managing symbol visibility &\n>   renaming.\n>\n> * Figure out symbol versioning\n\nOK.  Let's see what people find out.\n\n"},{"id":"510617","messageId":"xmqqr053wvip.fsf@gitster.g","threadId":"61917","inReplyTo":"ff6cd62397ec2755d15e9d76f9af8a84b54a36c1.1736971328.git.steadmon@google.com","subject":"Re: [PATCH v6 1/5] common-main: split init and exit code into new files","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-01-15T22:40:46Z","receivedAt":"2025-01-15T22:40:49Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Josh Steadmon <steadmon@google.com> writes:\n\n> This split has previously been proposed ([1], [2]) to support fuzz tests\n> and unit tests by avoiding conflicting definitions for main(). However,\n> both of those issues were resolved by other methods of avoiding symbol\n> conflicts. Now we are trying to make libgit.a more self-contained, so\n> hopefully we can revisit this approach.\n\nYay!\n\n> Additionally, move the initialization code out of main() into a new\n> init_git() function in its own file. Include this in libgit.a as well,\n> so that external users can share our setup code without calling our\n> main().\n\nSounds good.\n\n> diff --git a/common-main.c b/common-main.c\n> index 8e68ac9e42..6b7ab077b0 100644\n> --- a/common-main.c\n> +++ b/common-main.c\n> @@ -1,92 +1,13 @@\n> ...\n> +#include \"common-init.h\"\n>  \n>  int main(int argc, const char **argv)\n>  {\n>  \tint result;\n>  \n> +\tinit_git(argv);\n>  \tresult = cmd_main(argc, argv);\n>  \n>  \t/* Not exit(3), but a wrapper calling our common_exit() */\n>  \texit(result);\n>  }\n\nNice.  Very nice.  I wasn't too carefully validating what we lost\nhere is identical to what we added to init_git(), but hopefully\nothers would spot and stop us if that is not the case.\n\nThanks.\n"},{"id":"510618","messageId":"xmqqa5brwtzq.fsf@gitster.g","threadId":"61917","inReplyTo":"5fc66cdb1628e0c9e420f3f0455779d7471f46ee.1736971328.git.steadmon@google.com","subject":"Re: [PATCH v6 2/5] libgit-sys: introduce Rust wrapper for libgit.a","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-01-15T23:13:45Z","receivedAt":"2025-01-15T23:13:49Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Josh Steadmon <steadmon@google.com> writes:\n\n> diff --git a/Makefile b/Makefile\n> index 27e68ac039..47e864a861 100644\n> --- a/Makefile\n> +++ b/Makefile\n> @@ -657,6 +657,8 @@ CURL_CONFIG = curl-config\n>  GCOV = gcov\n>  STRIP = strip\n>  SPATCH = spatch\n> +LD = ld\n> +OBJCOPY = objcopy\n\nThis assumes GNU binutils is available.  As long as our intention is\nto start the Rust support as an optional feature, that is OK.\nHopefully the piece that requires $(OBJCOPY) is arranged to be\neasily opted out.  Let's keep reading.\n\n> @@ -2731,6 +2733,7 @@ OBJECTS += $(REFTABLE_OBJS) $(REFTABLE_TEST_OBJS)\n>  OBJECTS += $(UNIT_TEST_OBJS)\n>  OBJECTS += $(CLAR_TEST_OBJS)\n>  OBJECTS += $(patsubst %,$(UNIT_TEST_DIR)/%.o,$(UNIT_TEST_PROGRAMS))\n> +OBJECTS += contrib/libgit-sys/public_symbol_export.o\n\nThis is compiled for everybody, even for those whose platform cannot\nsupport Rust interface (or those who choose not to build it).  As\nlong as what is in the file is written portably, it is fine to have\nstubs and entry points that their build will not use.\n\n>  ifndef NO_CURL\n>  \tOBJECTS += http.o http-walker.o remote-curl.o\n> @@ -3726,6 +3729,10 @@ clean: profile-clean coverage-clean cocciclean\n>  \t$(RM) $(htmldocs).tar.gz $(manpages).tar.gz\n>  \t$(MAKE) -C Documentation/ clean\n>  \t$(RM) Documentation/GIT-EXCLUDED-PROGRAMS\n> +\t$(RM) -r contrib/libgit-sys/target\n> +\t$(RM) -r contrib/libgit-sys/partial_symbol_export.o\n> +\t$(RM) -r contrib/libgit-sys/hidden_symbol_export.o\n> +\t$(RM) -r contrib/libgit-sys/libgitpub.a\n\nWhich one of the above is a directory?  The latter three smells like\na regular file, so we shouldn't say \"-r\" there.\n\n> @@ -3887,3 +3894,12 @@ $(CLAR_TEST_PROG): $(UNIT_TEST_DIR)/clar.suite $(CLAR_TEST_OBJS) $(GITLIBS) GIT-\n>  build-unit-tests: $(UNIT_TEST_PROGS) $(CLAR_TEST_PROG)\n>  unit-tests: $(UNIT_TEST_PROGS) $(CLAR_TEST_PROG) t/helper/test-tool$X\n>  \t$(MAKE) -C t/ unit-tests\n> +\n> +contrib/libgit-sys/partial_symbol_export.o: contrib/libgit-sys/public_symbol_export.o libgit.a reftable/libreftable.a xdiff/lib.a\n> +\t$(LD) -r $^ -o $@\n\nOK.  We build a \"relocatable\" object, which is unconditionally made\nas part of $(OBJECTS) above.  Even without GNU binutils \"ld\", people\nhopefully can convince their linker to do the equivalent.  I am not\nsure if it is healthy to assume that such a linker also uses \"-r\"\nfor the feature, so we may have to make this rule more customizable,\nor make partial_symbol_export.o only conditionally part of $(OBJECTS)\nto allow them to opt out.\n\n> +contrib/libgit-sys/hidden_symbol_export.o: contrib/libgit-sys/partial_symbol_export.o\n> +\t$(OBJCOPY) --localize-hidden $^ $@\n\nUnlike the \"public\" thing, hidden_symbol_export.o was not made part\nof $(OBJECTS), so this part is arranged to allow people without\n$(OBJCOPY) to easily opt out of this part of the system, which is\ngood.\n\n> +contrib/libgit-sys/libgitpub.a: contrib/libgit-sys/hidden_symbol_export.o\n> +\t$(AR) $(ARFLAGS) $@ $^\n\nLikewise, people can easily opt out of building \"libgitpub.a\", which\nis good (these targets are triggered only from build.rs).\n\n> diff --git a/contrib/libgit-sys/README.md b/contrib/libgit-sys/README.md\n> new file mode 100644\n> index 0000000000..c061cfcaf5\n> --- /dev/null\n> +++ b/contrib/libgit-sys/README.md\n> @@ -0,0 +1,4 @@\n> +# libgit-sys\n> +\n> +A small proof-of-concept crate showing how to provide a Rust FFI to Git\n> +internals.\n\nOK.\n\n> diff --git a/contrib/libgit-sys/public_symbol_export.c b/contrib/libgit-sys/public_symbol_export.c\n> new file mode 100644\n> index 0000000000..7cd5007902\n> --- /dev/null\n> +++ b/contrib/libgit-sys/public_symbol_export.c\n> @@ -0,0 +1,21 @@\n> +// Shim to publicly export Git symbols. These must be renamed so that the\n> +// original symbols can be hidden. Renaming these with a \"libgit_\" prefix also\n> +// avoids conflicts with other libraries such as libgit2.\n\nStyle.\n\n> +#include \"git-compat-util.h\"\n> +#include \"contrib/libgit-sys/public_symbol_export.h\"\n> +#include \"version.h\"\n> +\n> +#pragma GCC visibility push(default)\n> +\n> +const char *libgit_user_agent(void)\n> +{\n> +\treturn git_user_agent();\n> +}\n> +\n> +const char *libgit_user_agent_sanitized(void)\n> +{\n> +\treturn git_user_agent_sanitized();\n> +}\n> +\n> +#pragma GCC visibility pop\n\nI do not think we would mind not having Rust binding support on\nplatforms without GCC (and clang---I assume it would be aware of and\nreact to that #pragma GCC the same way?).  But do we allow this file\nto be left uncompiled when the build wants to opt out of Rust\nsupport?\n\n> diff --git a/contrib/libgit-sys/public_symbol_export.h b/contrib/libgit-sys/public_symbol_export.h\n> new file mode 100644\n> index 0000000000..a3372f93fa\n> --- /dev/null\n> +++ b/contrib/libgit-sys/public_symbol_export.h\n> @@ -0,0 +1,8 @@\n> +#ifndef PUBLIC_SYMBOL_EXPORT_H\n> +#define PUBLIC_SYMBOL_EXPORT_H\n> +\n> +const char *libgit_user_agent(void);\n> +\n> +const char *libgit_user_agent_sanitized(void);\n> +\n> +#endif /* PUBLIC_SYMBOL_EXPORT_H */\n\nOK.\n"},{"id":"510634","messageId":"xmqq34hjv5kw.fsf@gitster.g","threadId":"61917","inReplyTo":"xmqqr053wvip.fsf@gitster.g","subject":"Re: [PATCH v6 1/5] common-main: split init and exit code into new files","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-01-16T02:46:23Z","receivedAt":"2025-01-16T02:46:26Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n> Josh Steadmon <steadmon@google.com> writes:\n>\n>> This split has previously been proposed ([1], [2]) to support fuzz tests\n>> and unit tests by avoiding conflicting definitions for main(). However,\n>> both of those issues were resolved by other methods of avoiding symbol\n>> conflicts. Now we are trying to make libgit.a more self-contained, so\n>> hopefully we can revisit this approach.\n>\n> Yay!\n>\n>> Additionally, move the initialization code out of main() into a new\n>> init_git() function in its own file. Include this in libgit.a as well,\n>> so that external users can share our setup code without calling our\n>> main().\n>\n> Sounds good.\n>\n>> diff --git a/common-main.c b/common-main.c\n>> index 8e68ac9e42..6b7ab077b0 100644\n>> --- a/common-main.c\n>> +++ b/common-main.c\n>> @@ -1,92 +1,13 @@\n>> ...\n>> +#include \"common-init.h\"\n>>  \n>>  int main(int argc, const char **argv)\n>>  {\n>>  \tint result;\n>>  \n>> +\tinit_git(argv);\n>>  \tresult = cmd_main(argc, argv);\n>>  \n>>  \t/* Not exit(3), but a wrapper calling our common_exit() */\n>>  \texit(result);\n>>  }\n>\n> Nice.  Very nice.  I wasn't too carefully validating what we lost\n> here is identical to what we added to init_git(), but hopefully\n> others would spot and stop us if that is not the case.\n>\n> Thanks.\n\nUnfortunately, build based on meson does not seem to like the\ninit_git() thing.  Perhaps we need to add some missing files to\nrelevant lists in meson.build file or something silly like that?\n\nhttps://github.com/git/git/actions/runs/12800227601/job/35687658673#step:8:961\n\n\n"},{"id":"510732","messageId":"xmqqtt9ypj4m.fsf@gitster.g","threadId":"61917","inReplyTo":"xmqq34hjv5kw.fsf@gitster.g","subject":"Re: [PATCH v6 1/5] common-main: split init and exit code into new files","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-01-16T21:02:33Z","receivedAt":"2025-01-16T21:02:36Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n> Unfortunately, build based on meson does not seem to like the\n> init_git() thing.  Perhaps we need to add some missing files to\n> relevant lists in meson.build file or something silly like that?\n>\n> https://github.com/git/git/actions/runs/12800227601/job/35687658673#step:8:961\n\nI needed the following to get \"meson compile\" pass in my local\nenvironment.  I suspect that Mesonized CI jobs exercise a bit more\nthan just \"meson compile\", so there is no guarantee that the\nfollowing is enough, but at least hopefully it would nudge you (and\nthose who may be interested in helping to build a working Rust\nbindings) in the right direction.\n\nI think it should be squashed into the step these files are added,\ni.e. [PATCH 1/5].\n\nThanks.\n\n meson.build | 2 ++\n 1 file changed, 2 insertions(+)\n\ndiff --git c/meson.build w/meson.build\nindex 0064eb64f5..e5ba28b47f 100644\n--- c/meson.build\n+++ w/meson.build\n@@ -245,6 +245,8 @@ libgit_sources = [\n   'commit-graph.c',\n   'commit-reach.c',\n   'commit.c',\n+  'common-exit.c',\n+  'common-init.c',\n   'compat/nonblock.c',\n   'compat/obstack.c',\n   'compat/terminal.c',\n"},{"id":"510778","messageId":"Z4omfC0v_UIBRiNM@pks.im","threadId":"61917","inReplyTo":"xmqqtt9ypj4m.fsf@gitster.g","subject":"Re: [PATCH v6 1/5] common-main: split init and exit code into new files","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-01-17T09:44:28Z","receivedAt":"2025-01-17T09:44:34Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"On Thu, Jan 16, 2025 at 01:02:33PM -0800, Junio C Hamano wrote:\n> Junio C Hamano <gitster@pobox.com> writes:\n> \n> > Unfortunately, build based on meson does not seem to like the\n> > init_git() thing.  Perhaps we need to add some missing files to\n> > relevant lists in meson.build file or something silly like that?\n> >\n> > https://github.com/git/git/actions/runs/12800227601/job/35687658673#step:8:961\n> \n> I needed the following to get \"meson compile\" pass in my local\n> environment.  I suspect that Mesonized CI jobs exercise a bit more\n> than just \"meson compile\", so there is no guarantee that the\n> following is enough, but at least hopefully it would nudge you (and\n> those who may be interested in helping to build a working Rust\n> bindings) in the right direction.\n> \n> I think it should be squashed into the step these files are added,\n> i.e. [PATCH 1/5].\n> \n> Thanks.\n> \n>  meson.build | 2 ++\n>  1 file changed, 2 insertions(+)\n> \n> diff --git c/meson.build w/meson.build\n> index 0064eb64f5..e5ba28b47f 100644\n> --- c/meson.build\n> +++ w/meson.build\n> @@ -245,6 +245,8 @@ libgit_sources = [\n>    'commit-graph.c',\n>    'commit-reach.c',\n>    'commit.c',\n> +  'common-exit.c',\n> +  'common-init.c',\n>    'compat/nonblock.c',\n>    'compat/obstack.c',\n>    'compat/terminal.c',\n\nYeah, I remember having the same hunk while Meson was still in-flight in\norder to make it compatible with \"seen\". So this should be sufficient.\n\nPatrick\n"},{"id":"510984","messageId":"Z47jgK-oMjFRSslr@tapette.crustytoothpaste.net","threadId":"61917","inReplyTo":"65166ea0c077665c350a1a7b00dc3175be889d55.1736971328.git.steadmon@google.com","subject":"Re: [PATCH v6 4/5] libgit: add higher-level libgit crate","fromName":"brian m. carlson","fromEmail":"sandals@crustytoothpaste.net","sentAt":"2025-01-21T00:00:00Z","receivedAt":"2025-01-21T00:00:03Z","isPatch":true,"sender":{"key":"sandals@crustytoothpaste.net","avatar":"https://avatars.githubusercontent.com/u/497054?v=4"},"body":"On 2025-01-15 at 20:05:43, Josh Steadmon wrote:\n> From: Calvin Wan <calvinwan@google.com>\n> diff --git a/contrib/libgit-rs/Cargo.lock b/contrib/libgit-rs/Cargo.lock\n> new file mode 100644\n> index 0000000000..a30c7c8d33\n> --- /dev/null\n> +++ b/contrib/libgit-rs/Cargo.lock\n> @@ -0,0 +1,77 @@\n> +# This file is automatically @generated by Cargo.\n> +# It is not intended for manual editing.\n> +version = 3\n> +\n> +[[package]]\n> +name = \"autocfg\"\n> +version = \"1.4.0\"\n> +source = \"registry+https://github.com/rust-lang/crates.io-index\"\n> +checksum = \"ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26\"\n> +\n> +[[package]]\n> +name = \"cc\"\n> +version = \"1.1.15\"\n> +source = \"registry+https://github.com/rust-lang/crates.io-index\"\n> +checksum = \"57b6a275aa2903740dc87da01c62040406b8812552e97129a63ea8850a17c6e6\"\n> +dependencies = [\n> + \"shlex\",\n> +]\n> +\n> +[[package]]\n> +name = \"libc\"\n> +version = \"0.2.158\"\n> +source = \"registry+https://github.com/rust-lang/crates.io-index\"\n> +checksum = \"d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439\"\n> +\n> +[[package]]\n> +name = \"libgit\"\n> +version = \"0.1.0\"\n> +dependencies = [\n> + \"autocfg\",\n> + \"libgit-sys\",\n> +]\n> +\n> +[[package]]\n> +name = \"libgit-sys\"\n> +version = \"0.1.0\"\n> +dependencies = [\n> + \"autocfg\",\n> + \"libz-sys\",\n> + \"make-cmd\",\n> +]\n> +\n> +[[package]]\n> +name = \"libz-sys\"\n> +version = \"1.1.20\"\n> +source = \"registry+https://github.com/rust-lang/crates.io-index\"\n> +checksum = \"d2d16453e800a8cf6dd2fc3eb4bc99b786a9b90c663b8559a5b1a041bf89e472\"\n> +dependencies = [\n> + \"cc\",\n> + \"libc\",\n> + \"pkg-config\",\n> + \"vcpkg\",\n> +]\n> +\n> +[[package]]\n> +name = \"make-cmd\"\n> +version = \"0.1.0\"\n> +source = \"registry+https://github.com/rust-lang/crates.io-index\"\n> +checksum = \"a8ca8afbe8af1785e09636acb5a41e08a765f5f0340568716c18a8700ba3c0d3\"\n> +\n> +[[package]]\n> +name = \"pkg-config\"\n> +version = \"0.3.30\"\n> +source = \"registry+https://github.com/rust-lang/crates.io-index\"\n> +checksum = \"d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec\"\n> +\n> +[[package]]\n> +name = \"shlex\"\n> +version = \"1.3.0\"\n> +source = \"registry+https://github.com/rust-lang/crates.io-index\"\n> +checksum = \"0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64\"\n> +\n> +[[package]]\n> +name = \"vcpkg\"\n> +version = \"0.2.15\"\n> +source = \"registry+https://github.com/rust-lang/crates.io-index\"\n> +checksum = \"accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426\"\n\nThere are two possibilities here.  The first is to check in the\nCargo.lock, in which case all users will have to use these versions.\nThat produces a more stable and reliable approach, but it has some\ndownsides.\n\nSay, for instance, that a cool new platform or architecture is added to\nlibc and we'd like to support it, but that version of libc requires a\nnewer version of Rust.  We then would have to hold off supporting that\nnew platform due to compatibility reasons.  But if we omitted the\nCargo.lock, users could install any version that meets their needs.\n\nI believe Rust just got the ability to install only versions that honour\nthe rust-version directive in 1.84, whereas older versions will try to\nuse the latest version, even if that fails.  So I think it's okay for\nnow to use Cargo.lock, because that means that things will be better out\nof the box for users on older Rust.  But we may want to drop it once\n1.84 is older than our supported version.\n\n> diff --git a/contrib/libgit-rs/src/lib.rs b/contrib/libgit-rs/src/lib.rs\n> new file mode 100644\n> index 0000000000..27b6fd63f1\n> --- /dev/null\n> +++ b/contrib/libgit-rs/src/lib.rs\n> @@ -0,0 +1,95 @@\n> +use std::ffi::{c_void, CStr, CString};\n> +use std::path::Path;\n> +\n> +#[cfg(has_std__ffi__c_char)]\n> +use std::ffi::{c_char, c_int};\n> +\n> +#[cfg(not(has_std__ffi__c_char))]\n> +#[allow(non_camel_case_types)]\n> +pub type c_char = i8;\n> +\n> +#[cfg(not(has_std__ffi__c_char))]\n> +#[allow(non_camel_case_types)]\n> +pub type c_int = i32;\n\nBy making these `pub`, you're exporting them.  We probably do not want\nto do that, since they are not part of our API.\n\nIf we need them more generally in the code, let's put them in a module\ncalled `ffi` or such that's `pub(crate)`, and then use them from there.\n\n> +use libgit_sys::*;\n> +\n> +pub struct ConfigSet(*mut libgit_config_set);\n> +impl ConfigSet {\n\nI would suggest we place these in a module, such as `config`.  We should\nexpect to have a lot more things in our crate in the future and putting\na little thought into this now will make it easier for users in the\nfuture.\n\nI'd also, of course, suggest documentation comments, since if we ever\nupload this to crates.io, people are overwhelmingly going to read only\nthe docs and not the source, and right now we've said nothing about how\nthis works or should work.\n\n> +#[cfg(test)]\n> +mod tests {\n> +    use super::*;\n> +\n> +    #[test]\n> +    fn load_configs_via_configset() {\n> +        let mut cs = ConfigSet::new();\n> +        cs.add_files(&[\n> +            Path::new(\"testdata/config1\"),\n> +            Path::new(\"testdata/config2\"),\n> +            Path::new(\"testdata/config3\"),\n> +        ]);\n> +        // ConfigSet retrieves correct value\n> +        assert_eq!(cs.get_int(\"trace2.eventTarget\"), Some(1));\n> +        // ConfigSet respects last config value set\n> +        assert_eq!(cs.get_int(\"trace2.eventNesting\"), Some(3));\n> +        // ConfigSet returns None for missing key\n> +        assert_eq!(cs.get_string(\"foo.bar\"), None);\n> +    }\n> +}\n\nI am, of course, delighted to see tests.  This is a nice improvement in\nour code that we can take advantage of, and we can test both the C code\nand Rust code at the same time.  And if, in the future, we decide that\nwe'd like to implement a Rust-based version of this API to replace the C\none, we've already written tests.\n-- \nbrian m. carlson (they/them or he/him)\nToronto, Ontario, CA\n"},{"id":"510985","messageId":"Z47kr0_fYYdaMWyA@tapette.crustytoothpaste.net","threadId":"61917","inReplyTo":"cover.1736971328.git.steadmon@google.com","subject":"Re: [PATCH v6 0/5] Introduce libgit-rs, a Rust wrapper around libgit.a","fromName":"brian m. carlson","fromEmail":"sandals@crustytoothpaste.net","sentAt":"2025-01-21T00:05:03Z","receivedAt":"2025-01-21T00:05:06Z","isPatch":true,"sender":{"key":"sandals@crustytoothpaste.net","avatar":"https://avatars.githubusercontent.com/u/497054?v=4"},"body":"On 2025-01-15 at 20:05:39, Josh Steadmon wrote:\n> Apologies for the long delay on V6; I am finally back after several\n> months of $DAYJOB firefighting, holidays, and sick leave. I should have\n> time to devote to this series again, but given the lack of feedback on\n> V5 I am hopeful that this will be the final iteration of this series.\n> \n> This series provides two small Rust wrapper libraries around parts of\n> Git: \"libgit-sys\", which exposes a few functions from libgit.a, and\n> \"libgit\", which provides a more Rust-friendly interface to some of those\n> functions. In addition to included unit tests, at $DAYJOB we have tested\n> building JJ[1] with our library and used it to replace some of the\n> libgit2-rs uses.\n> \n> [1] https://github.com/martinvonz/jj\n> \n> There is known NEEDSWORK, but I feel that they can be addressed in\n> follow-up changes, rather than in this series. If you feel otherwise,\n> please let me know:\n> \n> * Investigate alternative methods of managing symbol visibility &\n>   renaming.\n> \n> * Figure out symbol versioning\n\nIt looks like we're building a general Rust lib crate and a static\nlibrary here, so symbol versioning isn't an issue.  I expect in the\nfuture we may want to provide a shared library, in which case we will\nindeed want to do that, but I agree that can wait until later.\n\nIn any event, I overall think this series is a nice improvement, and I\nam very enthusiastic about it.  (This is mostly for the benefit of\nJunio, since I think the authors of this series already know that.)\nOnce it lands, I do plan to build on it somewhat.\n-- \nbrian m. carlson (they/them or he/him)\nToronto, Ontario, CA\n"},{"id":"510986","messageId":"xmqqikq92cfm.fsf@gitster.g","threadId":"61917","inReplyTo":"Z47kr0_fYYdaMWyA@tapette.crustytoothpaste.net","subject":"Re: [PATCH v6 0/5] Introduce libgit-rs, a Rust wrapper around libgit.a","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-01-21T01:17:01Z","receivedAt":"2025-01-21T01:17:04Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"brian m. carlson\" <sandals@crustytoothpaste.net> writes:\n\n> It looks like we're building a general Rust lib crate and a static\n> library here, so symbol versioning isn't an issue.  I expect in the\n> future we may want to provide a shared library, in which case we will\n> indeed want to do that, but I agree that can wait until later.\n>\n> In any event, I overall think this series is a nice improvement, and I\n> am very enthusiastic about it.  (This is mostly for the benefit of\n> Junio, since I think the authors of this series already know that.)\n> Once it lands, I do plan to build on it somewhat.\n\n;-)  Thanks, I heard you.\n"},{"id":"511029","messageId":"enua2yv34i26defybya2s6ds2v7ffilk2bljfdyay6pech3jjr@pvwc3iewvhj3","threadId":"61917","inReplyTo":"Z4omfC0v_UIBRiNM@pks.im","subject":"Re: [PATCH v6 1/5] common-main: split init and exit code into new files","fromName":"Josh Steadmon","fromEmail":"steadmon@google.com","sentAt":"2025-01-21T23:21:20Z","receivedAt":"2025-01-21T23:21:25Z","isPatch":true,"sender":{"key":"steadmon@google.com","avatar":"https://avatars.githubusercontent.com/u/2654920?v=4"},"body":"On 2025.01.17 10:44, Patrick Steinhardt wrote:\n> On Thu, Jan 16, 2025 at 01:02:33PM -0800, Junio C Hamano wrote:\n> > Junio C Hamano <gitster@pobox.com> writes:\n> > \n> > > Unfortunately, build based on meson does not seem to like the\n> > > init_git() thing.  Perhaps we need to add some missing files to\n> > > relevant lists in meson.build file or something silly like that?\n> > >\n> > > https://github.com/git/git/actions/runs/12800227601/job/35687658673#step:8:961\n> > \n> > I needed the following to get \"meson compile\" pass in my local\n> > environment.  I suspect that Mesonized CI jobs exercise a bit more\n> > than just \"meson compile\", so there is no guarantee that the\n> > following is enough, but at least hopefully it would nudge you (and\n> > those who may be interested in helping to build a working Rust\n> > bindings) in the right direction.\n> > \n> > I think it should be squashed into the step these files are added,\n> > i.e. [PATCH 1/5].\n> > \n> > Thanks.\n> > \n> >  meson.build | 2 ++\n> >  1 file changed, 2 insertions(+)\n> > \n> > diff --git c/meson.build w/meson.build\n> > index 0064eb64f5..e5ba28b47f 100644\n> > --- c/meson.build\n> > +++ w/meson.build\n> > @@ -245,6 +245,8 @@ libgit_sources = [\n> >    'commit-graph.c',\n> >    'commit-reach.c',\n> >    'commit.c',\n> > +  'common-exit.c',\n> > +  'common-init.c',\n> >    'compat/nonblock.c',\n> >    'compat/obstack.c',\n> >    'compat/terminal.c',\n> \n> Yeah, I remember having the same hunk while Meson was still in-flight in\n> order to make it compatible with \"seen\". So this should be sufficient.\n> \n> Patrick\n\nACK, sorry about that. I missed the Meson developments while I was\noff-list, so it wasn't on my radar. I'll make sure to squash the fix in\nto V7. Thanks both.\n"},{"id":"511030","messageId":"kcjc4a27e7qs42asw2wa3v5zqvro5jiufoqys3qwru7ykpovcl@s2nuwvahjf6s","threadId":"61917","inReplyTo":"Z47jgK-oMjFRSslr@tapette.crustytoothpaste.net","subject":"Re: [PATCH v6 4/5] libgit: add higher-level libgit crate","fromName":"Josh Steadmon","fromEmail":"steadmon@google.com","sentAt":"2025-01-22T00:01:51Z","receivedAt":"2025-01-22T00:01:57Z","isPatch":true,"sender":{"key":"steadmon@google.com","avatar":"https://avatars.githubusercontent.com/u/2654920?v=4"},"body":"On 2025.01.21 00:00, brian m. carlson wrote:\n> On 2025-01-15 at 20:05:43, Josh Steadmon wrote:\n> > From: Calvin Wan <calvinwan@google.com>\n> > diff --git a/contrib/libgit-rs/Cargo.lock b/contrib/libgit-rs/Cargo.lock\n> > new file mode 100644\n> > index 0000000000..a30c7c8d33\n> > --- /dev/null\n> > +++ b/contrib/libgit-rs/Cargo.lock\n> > @@ -0,0 +1,77 @@\n> > +# This file is automatically @generated by Cargo.\n> > +# It is not intended for manual editing.\n> > +version = 3\n> > +\n> > +[[package]]\n> > +name = \"autocfg\"\n> > +version = \"1.4.0\"\n> > +source = \"registry+https://github.com/rust-lang/crates.io-index\"\n> > +checksum = \"ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26\"\n> > +\n> > +[[package]]\n> > +name = \"cc\"\n> > +version = \"1.1.15\"\n> > +source = \"registry+https://github.com/rust-lang/crates.io-index\"\n> > +checksum = \"57b6a275aa2903740dc87da01c62040406b8812552e97129a63ea8850a17c6e6\"\n> > +dependencies = [\n> > + \"shlex\",\n> > +]\n> > +\n> > +[[package]]\n> > +name = \"libc\"\n> > +version = \"0.2.158\"\n> > +source = \"registry+https://github.com/rust-lang/crates.io-index\"\n> > +checksum = \"d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439\"\n> > +\n> > +[[package]]\n> > +name = \"libgit\"\n> > +version = \"0.1.0\"\n> > +dependencies = [\n> > + \"autocfg\",\n> > + \"libgit-sys\",\n> > +]\n> > +\n> > +[[package]]\n> > +name = \"libgit-sys\"\n> > +version = \"0.1.0\"\n> > +dependencies = [\n> > + \"autocfg\",\n> > + \"libz-sys\",\n> > + \"make-cmd\",\n> > +]\n> > +\n> > +[[package]]\n> > +name = \"libz-sys\"\n> > +version = \"1.1.20\"\n> > +source = \"registry+https://github.com/rust-lang/crates.io-index\"\n> > +checksum = \"d2d16453e800a8cf6dd2fc3eb4bc99b786a9b90c663b8559a5b1a041bf89e472\"\n> > +dependencies = [\n> > + \"cc\",\n> > + \"libc\",\n> > + \"pkg-config\",\n> > + \"vcpkg\",\n> > +]\n> > +\n> > +[[package]]\n> > +name = \"make-cmd\"\n> > +version = \"0.1.0\"\n> > +source = \"registry+https://github.com/rust-lang/crates.io-index\"\n> > +checksum = \"a8ca8afbe8af1785e09636acb5a41e08a765f5f0340568716c18a8700ba3c0d3\"\n> > +\n> > +[[package]]\n> > +name = \"pkg-config\"\n> > +version = \"0.3.30\"\n> > +source = \"registry+https://github.com/rust-lang/crates.io-index\"\n> > +checksum = \"d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec\"\n> > +\n> > +[[package]]\n> > +name = \"shlex\"\n> > +version = \"1.3.0\"\n> > +source = \"registry+https://github.com/rust-lang/crates.io-index\"\n> > +checksum = \"0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64\"\n> > +\n> > +[[package]]\n> > +name = \"vcpkg\"\n> > +version = \"0.2.15\"\n> > +source = \"registry+https://github.com/rust-lang/crates.io-index\"\n> > +checksum = \"accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426\"\n> \n> There are two possibilities here.  The first is to check in the\n> Cargo.lock, in which case all users will have to use these versions.\n> That produces a more stable and reliable approach, but it has some\n> downsides.\n> \n> Say, for instance, that a cool new platform or architecture is added to\n> libc and we'd like to support it, but that version of libc requires a\n> newer version of Rust.  We then would have to hold off supporting that\n> new platform due to compatibility reasons.  But if we omitted the\n> Cargo.lock, users could install any version that meets their needs.\n> \n> I believe Rust just got the ability to install only versions that honour\n> the rust-version directive in 1.84, whereas older versions will try to\n> use the latest version, even if that fails.  So I think it's okay for\n> now to use Cargo.lock, because that means that things will be better out\n> of the box for users on older Rust.  But we may want to drop it once\n> 1.84 is older than our supported version.\n\nOK, I'll add a TODO comment in Cargo.toml.\n\n\n> > diff --git a/contrib/libgit-rs/src/lib.rs b/contrib/libgit-rs/src/lib.rs\n> > new file mode 100644\n> > index 0000000000..27b6fd63f1\n> > --- /dev/null\n> > +++ b/contrib/libgit-rs/src/lib.rs\n> > @@ -0,0 +1,95 @@\n> > +use std::ffi::{c_void, CStr, CString};\n> > +use std::path::Path;\n> > +\n> > +#[cfg(has_std__ffi__c_char)]\n> > +use std::ffi::{c_char, c_int};\n> > +\n> > +#[cfg(not(has_std__ffi__c_char))]\n> > +#[allow(non_camel_case_types)]\n> > +pub type c_char = i8;\n> > +\n> > +#[cfg(not(has_std__ffi__c_char))]\n> > +#[allow(non_camel_case_types)]\n> > +pub type c_int = i32;\n> \n> By making these `pub`, you're exporting them.  We probably do not want\n> to do that, since they are not part of our API.\n\nFixed for V7.\n\n\n> If we need them more generally in the code, let's put them in a module\n> called `ffi` or such that's `pub(crate)`, and then use them from there.\n\nHmm, I guess we'd need to do that for libgit-sys as well? Or maybe not,\nsince they're part of the API and thus we should just keep the current\n`pub type ...` setup?\n\n> > +use libgit_sys::*;\n> > +\n> > +pub struct ConfigSet(*mut libgit_config_set);\n> > +impl ConfigSet {\n> \n> I would suggest we place these in a module, such as `config`.  We should\n> expect to have a lot more things in our crate in the future and putting\n> a little thought into this now will make it easier for users in the\n> future.\n\nACK, will do for V7.\n\n\n> I'd also, of course, suggest documentation comments, since if we ever\n> upload this to crates.io, people are overwhelmingly going to read only\n> the docs and not the source, and right now we've said nothing about how\n> this works or should work.\n\nACK.\n\n\n> > +#[cfg(test)]\n> > +mod tests {\n> > +    use super::*;\n> > +\n> > +    #[test]\n> > +    fn load_configs_via_configset() {\n> > +        let mut cs = ConfigSet::new();\n> > +        cs.add_files(&[\n> > +            Path::new(\"testdata/config1\"),\n> > +            Path::new(\"testdata/config2\"),\n> > +            Path::new(\"testdata/config3\"),\n> > +        ]);\n> > +        // ConfigSet retrieves correct value\n> > +        assert_eq!(cs.get_int(\"trace2.eventTarget\"), Some(1));\n> > +        // ConfigSet respects last config value set\n> > +        assert_eq!(cs.get_int(\"trace2.eventNesting\"), Some(3));\n> > +        // ConfigSet returns None for missing key\n> > +        assert_eq!(cs.get_string(\"foo.bar\"), None);\n> > +    }\n> > +}\n> \n> I am, of course, delighted to see tests.  This is a nice improvement in\n> our code that we can take advantage of, and we can test both the C code\n> and Rust code at the same time.  And if, in the future, we decide that\n> we'd like to implement a Rust-based version of this API to replace the C\n> one, we've already written tests.\n\nThanks for the review and advice (both for this round and all the\nprevious ones), it's much appreciated.\n\n> -- \n> brian m. carlson (they/them or he/him)\n> Toronto, Ontario, CA\n\n\n"},{"id":"511031","messageId":"d2qnphuygwjfi753pvhlhg4hunhddmqrs25wo3zmruruop6q3b@s6ustbmf4rn5","threadId":"61917","inReplyTo":"xmqqa5brwtzq.fsf@gitster.g","subject":"Re: [PATCH v6 2/5] libgit-sys: introduce Rust wrapper for libgit.a","fromName":"Josh Steadmon","fromEmail":"steadmon@google.com","sentAt":"2025-01-22T00:03:47Z","receivedAt":"2025-01-22T00:03:53Z","isPatch":true,"sender":{"key":"steadmon@google.com","avatar":"https://avatars.githubusercontent.com/u/2654920?v=4"},"body":"On 2025.01.15 15:13, Junio C Hamano wrote:\n> Josh Steadmon <steadmon@google.com> writes:\n> \n> > diff --git a/Makefile b/Makefile\n> > index 27e68ac039..47e864a861 100644\n> > --- a/Makefile\n> > +++ b/Makefile\n> > @@ -657,6 +657,8 @@ CURL_CONFIG = curl-config\n> >  GCOV = gcov\n> >  STRIP = strip\n> >  SPATCH = spatch\n> > +LD = ld\n> > +OBJCOPY = objcopy\n> \n> This assumes GNU binutils is available.  As long as our intention is\n> to start the Rust support as an optional feature, that is OK.\n> Hopefully the piece that requires $(OBJCOPY) is arranged to be\n> easily opted out.  Let's keep reading.\n> \n> > @@ -2731,6 +2733,7 @@ OBJECTS += $(REFTABLE_OBJS) $(REFTABLE_TEST_OBJS)\n> >  OBJECTS += $(UNIT_TEST_OBJS)\n> >  OBJECTS += $(CLAR_TEST_OBJS)\n> >  OBJECTS += $(patsubst %,$(UNIT_TEST_DIR)/%.o,$(UNIT_TEST_PROGRAMS))\n> > +OBJECTS += contrib/libgit-sys/public_symbol_export.o\n> \n> This is compiled for everybody, even for those whose platform cannot\n> support Rust interface (or those who choose not to build it).  As\n> long as what is in the file is written portably, it is fine to have\n> stubs and entry points that their build will not use.\n\nLater on in the series we add an INCLUDE_LIBGIT_RS variable to control\nbuilds and tests; I don't see any reason why we can't move that earlier\nin the series, so I'll do so in V7 and then look at what we need to do\nto make things more portable.\n\n> >  ifndef NO_CURL\n> >  \tOBJECTS += http.o http-walker.o remote-curl.o\n> > @@ -3726,6 +3729,10 @@ clean: profile-clean coverage-clean cocciclean\n> >  \t$(RM) $(htmldocs).tar.gz $(manpages).tar.gz\n> >  \t$(MAKE) -C Documentation/ clean\n> >  \t$(RM) Documentation/GIT-EXCLUDED-PROGRAMS\n> > +\t$(RM) -r contrib/libgit-sys/target\n> > +\t$(RM) -r contrib/libgit-sys/partial_symbol_export.o\n> > +\t$(RM) -r contrib/libgit-sys/hidden_symbol_export.o\n> > +\t$(RM) -r contrib/libgit-sys/libgitpub.a\n> \n> Which one of the above is a directory?  The latter three smells like\n> a regular file, so we shouldn't say \"-r\" there.\n> \n> > @@ -3887,3 +3894,12 @@ $(CLAR_TEST_PROG): $(UNIT_TEST_DIR)/clar.suite $(CLAR_TEST_OBJS) $(GITLIBS) GIT-\n> >  build-unit-tests: $(UNIT_TEST_PROGS) $(CLAR_TEST_PROG)\n> >  unit-tests: $(UNIT_TEST_PROGS) $(CLAR_TEST_PROG) t/helper/test-tool$X\n> >  \t$(MAKE) -C t/ unit-tests\n> > +\n> > +contrib/libgit-sys/partial_symbol_export.o: contrib/libgit-sys/public_symbol_export.o libgit.a reftable/libreftable.a xdiff/lib.a\n> > +\t$(LD) -r $^ -o $@\n> \n> OK.  We build a \"relocatable\" object, which is unconditionally made\n> as part of $(OBJECTS) above.  Even without GNU binutils \"ld\", people\n> hopefully can convince their linker to do the equivalent.  I am not\n> sure if it is healthy to assume that such a linker also uses \"-r\"\n> for the feature, so we may have to make this rule more customizable,\n> or make partial_symbol_export.o only conditionally part of $(OBJECTS)\n> to allow them to opt out.\n> \n> > +contrib/libgit-sys/hidden_symbol_export.o: contrib/libgit-sys/partial_symbol_export.o\n> > +\t$(OBJCOPY) --localize-hidden $^ $@\n> \n> Unlike the \"public\" thing, hidden_symbol_export.o was not made part\n> of $(OBJECTS), so this part is arranged to allow people without\n> $(OBJCOPY) to easily opt out of this part of the system, which is\n> good.\n> \n> > +contrib/libgit-sys/libgitpub.a: contrib/libgit-sys/hidden_symbol_export.o\n> > +\t$(AR) $(ARFLAGS) $@ $^\n> \n> Likewise, people can easily opt out of building \"libgitpub.a\", which\n> is good (these targets are triggered only from build.rs).\n> \n> > diff --git a/contrib/libgit-sys/README.md b/contrib/libgit-sys/README.md\n> > new file mode 100644\n> > index 0000000000..c061cfcaf5\n> > --- /dev/null\n> > +++ b/contrib/libgit-sys/README.md\n> > @@ -0,0 +1,4 @@\n> > +# libgit-sys\n> > +\n> > +A small proof-of-concept crate showing how to provide a Rust FFI to Git\n> > +internals.\n> \n> OK.\n> \n> > diff --git a/contrib/libgit-sys/public_symbol_export.c b/contrib/libgit-sys/public_symbol_export.c\n> > new file mode 100644\n> > index 0000000000..7cd5007902\n> > --- /dev/null\n> > +++ b/contrib/libgit-sys/public_symbol_export.c\n> > @@ -0,0 +1,21 @@\n> > +// Shim to publicly export Git symbols. These must be renamed so that the\n> > +// original symbols can be hidden. Renaming these with a \"libgit_\" prefix also\n> > +// avoids conflicts with other libraries such as libgit2.\n> \n> Style.\n> \n> > +#include \"git-compat-util.h\"\n> > +#include \"contrib/libgit-sys/public_symbol_export.h\"\n> > +#include \"version.h\"\n> > +\n> > +#pragma GCC visibility push(default)\n> > +\n> > +const char *libgit_user_agent(void)\n> > +{\n> > +\treturn git_user_agent();\n> > +}\n> > +\n> > +const char *libgit_user_agent_sanitized(void)\n> > +{\n> > +\treturn git_user_agent_sanitized();\n> > +}\n> > +\n> > +#pragma GCC visibility pop\n> \n> I do not think we would mind not having Rust binding support on\n> platforms without GCC (and clang---I assume it would be aware of and\n> react to that #pragma GCC the same way?).  But do we allow this file\n> to be left uncompiled when the build wants to opt out of Rust\n> support?\n> \n> > diff --git a/contrib/libgit-sys/public_symbol_export.h b/contrib/libgit-sys/public_symbol_export.h\n> > new file mode 100644\n> > index 0000000000..a3372f93fa\n> > --- /dev/null\n> > +++ b/contrib/libgit-sys/public_symbol_export.h\n> > @@ -0,0 +1,8 @@\n> > +#ifndef PUBLIC_SYMBOL_EXPORT_H\n> > +#define PUBLIC_SYMBOL_EXPORT_H\n> > +\n> > +const char *libgit_user_agent(void);\n> > +\n> > +const char *libgit_user_agent_sanitized(void);\n> > +\n> > +#endif /* PUBLIC_SYMBOL_EXPORT_H */\n> \n> OK.\n"},{"id":"511304","messageId":"ebqg66cp3mttyrjne2gonmjis7v6djp6jmnpzjj6o6e7sz5taw@fezpjrflojm5","threadId":"61917","inReplyTo":"xmqqa5brwtzq.fsf@gitster.g","subject":"Re: [PATCH v6 2/5] libgit-sys: introduce Rust wrapper for libgit.a","fromName":"Josh Steadmon","fromEmail":"steadmon@google.com","sentAt":"2025-01-27T23:18:57Z","receivedAt":"2025-01-27T23:19:03Z","isPatch":true,"sender":{"key":"steadmon@google.com","avatar":"https://avatars.githubusercontent.com/u/2654920?v=4"},"body":"On 2025.01.15 15:13, Junio C Hamano wrote:\n> Josh Steadmon <steadmon@google.com> writes:\n> \n> > diff --git a/contrib/libgit-sys/public_symbol_export.c b/contrib/libgit-sys/public_symbol_export.c\n> > new file mode 100644\n> > index 0000000000..7cd5007902\n> > --- /dev/null\n> > +++ b/contrib/libgit-sys/public_symbol_export.c\n> > @@ -0,0 +1,21 @@\n> > +// Shim to publicly export Git symbols. These must be renamed so that the\n> > +// original symbols can be hidden. Renaming these with a \"libgit_\" prefix also\n> > +// avoids conflicts with other libraries such as libgit2.\n> \n> Style.\n> \n> > +#include \"git-compat-util.h\"\n> > +#include \"contrib/libgit-sys/public_symbol_export.h\"\n> > +#include \"version.h\"\n> > +\n> > +#pragma GCC visibility push(default)\n> > +\n> > +const char *libgit_user_agent(void)\n> > +{\n> > +\treturn git_user_agent();\n> > +}\n> > +\n> > +const char *libgit_user_agent_sanitized(void)\n> > +{\n> > +\treturn git_user_agent_sanitized();\n> > +}\n> > +\n> > +#pragma GCC visibility pop\n> \n> I do not think we would mind not having Rust binding support on\n> platforms without GCC (and clang---I assume it would be aware of and\n> react to that #pragma GCC the same way?).\n\nYes, GCC and clang both handle this the same way.\n\n\n> But do we allow this file\n> to be left uncompiled when the build wants to opt out of Rust\n> support?\n\nYes, in V7 this will only be built if INCLUDE_LIBGIT_RS is set.\n"},{"id":"511307","messageId":"cover.1738023208.git.steadmon@google.com","threadId":"61917","inReplyTo":"cover.1723054623.git.steadmon@google.com","subject":"[PATCH v7 0/4] Introduce libgit-rs, a Rust wrapper around libgit.a","fromName":"Josh Steadmon","fromEmail":"steadmon@google.com","sentAt":"2025-01-28T00:19:01Z","receivedAt":"2025-01-28T00:19:08Z","isPatch":true,"sender":{"key":"steadmon@google.com","avatar":"https://avatars.githubusercontent.com/u/2654920?v=4"},"body":"This series provides two small Rust wrapper libraries around parts of\nGit: \"libgit-sys\", which exposes a few functions from libgit.a, and\n\"libgit\", which provides a more Rust-friendly interface to some of those\nfunctions. In addition to included unit tests, at $DAYJOB we have tested\nbuilding JJ[1] with our library and used it to replace some of the\nlibgit2-rs uses.\n\n[1] https://github.com/jj-vcs/jj\n\nThere is known NEEDSWORK, but I feel that they can be addressed in\nfollow-up changes, rather than in this series. If you feel otherwise,\nplease let me know:\n\n* Investigate alternative methods of managing symbol visibility &\n  renaming.\n\n* Figure out symbol versioning\n\nChanges in V7:\n* Moved the ConfigSet implementation in libgit-rs to a `config` module.\n\n* Added doc comments for ConfigSet and its methods.\n\n* Fix meson builds by adding new object files to `libgit_sources`\n\n* Moved INCLUDE_LIBGIT_RS Makefile changes earlier in the series, so\n  that we can make it optional to compile some of the libgitpub sources.\n  Squashed V6 patch 5/5 into this series' patch 4/4.\n\n* Don't publicly export FFI types in libgit-rs.\n\n* Removed extraneous `-r` argument to $(RM) in the clean rules.\n\n* Added TODO reminder in Cargo.toml about removing Cargo.lock once we\n  hit a certain minimum supported Rust version.\n\n* Style cleanup in public_symbol_export.c\n\nChanges in V6:\n* Rebased onto current master, since V5 was several months old.\n\n* Move libgit-sys out of libgit-rs; while this sort of nesting is common\n  in Rust crates with standalone repositories, it doesn't make as much\n  sense when they're contained in the larger Git project's repo.\n\n* Standardize the naming of some of the Makefile targets to always\n  include a dash in the \"-rs\" or \"-sys\" suffixes.\n\n* Clean up READMEs and crate descriptions in preparation for\n  uploading to crates.io.\n\nChanges in V5:\n* When building with INCLUDE_LIBGIT_RS defined, add\n  \"-fvisibility=hidden\" to CFLAGS. This allows us to manage symbol\n  visibility in libgitpub.a without causing `make all` to rebuild from\n  scratch due to changing CFLAGS.\n\n* Avoid using c_int in the higher-level Rust API.\n\n* Remove libgitpub.a and intermediate files with `make clean`.\n\nChanges in V4:\n* Drop V3 patch #3, which added wrappers around repository\n  initialization and config access. These are not well-libified, and\n  they are not necessary for JJ's proof-of-concept use case, so let's\n  avoid exporting them for now.\n\n* Set a minimum supported Rust version of 1.63. Autodetect whether our\n  Rust version has c_int and c_char types; if not, define them\n  ourselves.\n\n* When building libgitpub.a via build.rs, set DEVELOPER=1 to catch\n  additional errors at build time.\n\n* In build.rs, use the make_cmd crate to portable select the correct\n  invocation of GNU Make.\n\n* Follow naming standards for _alloc() and _free() functions.\n\n* Use String instead of CString in higher-level API.\n\n* Move libgit_configset_alloc() and libgit_configset_free() out of\n  upstream Git, to the libgitpub shim library.\n\n* In libgitpub, initialize libgit_config_set structs in the _alloc()\n  function rather than with a separate _init() function.\n\n* Remove unnecessary comments in libgit-sys showing where the wrapped\n  functions were originally defined.\n\n* Fix clippy lint: don't reborrow configfile path references.\n\n* Various typo fixes and `cargo fmt` fixes.\n\nChanges in V3:\n* Renamed cgit-rs to libgit-rs and cgit-sys to libgit-sys\n\n* Makefile cleanup, particularly adding config.mak options that\n  developers can set to run Rust builds and tests by default (Patch 6)\n\n* Provide testdata configs for unit tests\n\n* ConfigSet API now uses &Path instead of &str -- more ergonomic for\n  Rust users to pass in and errors out if the path string isn't UTF-8\n\n* Fixed unresolved dependency on libz in Cargo.toml\n\n\nCalvin Wan (1):\n  libgit: add higher-level libgit crate\n\nJosh Steadmon (3):\n  common-main: split init and exit code into new files\n  libgit-sys: introduce Rust wrapper for libgit.a\n  libgit-sys: also export some config_set functions\n\n .gitignore                                |   2 +\n Makefile                                  |  45 +++++++++\n common-exit.c                             |  26 ++++++\n common-init.c                             |  63 +++++++++++++\n common-init.h                             |   6 ++\n common-main.c                             |  83 +----------------\n contrib/libgit-rs/Cargo.lock              |  77 ++++++++++++++++\n contrib/libgit-rs/Cargo.toml              |  17 ++++\n contrib/libgit-rs/README.md               |  13 +++\n contrib/libgit-rs/build.rs                |   4 +\n contrib/libgit-rs/src/config.rs           | 106 ++++++++++++++++++++++\n contrib/libgit-rs/src/lib.rs              |   1 +\n contrib/libgit-rs/testdata/config1        |   2 +\n contrib/libgit-rs/testdata/config2        |   2 +\n contrib/libgit-rs/testdata/config3        |   2 +\n contrib/libgit-sys/Cargo.lock             |  69 ++++++++++++++\n contrib/libgit-sys/Cargo.toml             |  19 ++++\n contrib/libgit-sys/README.md              |   4 +\n contrib/libgit-sys/build.rs               |  35 +++++++\n contrib/libgit-sys/public_symbol_export.c |  51 +++++++++++\n contrib/libgit-sys/public_symbol_export.h |  18 ++++\n contrib/libgit-sys/src/lib.rs             |  79 ++++++++++++++++\n meson.build                               |   2 +\n t/Makefile                                |  15 +++\n 24 files changed, 660 insertions(+), 81 deletions(-)\n create mode 100644 common-exit.c\n create mode 100644 common-init.c\n create mode 100644 common-init.h\n create mode 100644 contrib/libgit-rs/Cargo.lock\n create mode 100644 contrib/libgit-rs/Cargo.toml\n create mode 100644 contrib/libgit-rs/README.md\n create mode 100644 contrib/libgit-rs/build.rs\n create mode 100644 contrib/libgit-rs/src/config.rs\n create mode 100644 contrib/libgit-rs/src/lib.rs\n create mode 100644 contrib/libgit-rs/testdata/config1\n create mode 100644 contrib/libgit-rs/testdata/config2\n create mode 100644 contrib/libgit-rs/testdata/config3\n create mode 100644 contrib/libgit-sys/Cargo.lock\n create mode 100644 contrib/libgit-sys/Cargo.toml\n create mode 100644 contrib/libgit-sys/README.md\n create mode 100644 contrib/libgit-sys/build.rs\n create mode 100644 contrib/libgit-sys/public_symbol_export.c\n create mode 100644 contrib/libgit-sys/public_symbol_export.h\n create mode 100644 contrib/libgit-sys/src/lib.rs\n\nRange-diff against v6:\n1:  ff6cd62397 ! 1:  cd0cb9aa04 common-main: split init and exit code into new files\n    @@ common-main.c\n     -\n     -\treturn code;\n     -}\n    +\n    + ## meson.build ##\n    +@@ meson.build: libgit_sources = [\n    +   'commit-graph.c',\n    +   'commit-reach.c',\n    +   'commit.c',\n    ++  'common-exit.c',\n    ++  'common-init.c',\n    +   'compat/nonblock.c',\n    +   'compat/obstack.c',\n    +   'compat/terminal.c',\n2:  5fc66cdb16 ! 2:  f1502b8590 libgit-sys: introduce Rust wrapper for libgit.a\n    @@ .gitignore: Release/\n     +/contrib/libgit-sys/target\n     \n      ## Makefile ##\n    +@@ Makefile: include shared.mak\n    + # Define LINK_FUZZ_PROGRAMS if you want `make all` to also build the fuzz test\n    + # programs in oss-fuzz/.\n    + #\n    ++# Define INCLUDE_LIBGIT_RS if you want `make all` and `make test` to build and\n    ++# test the Rust crate in contrib/libgit-sys.\n    ++#\n    + # === Optional library: libintl ===\n    + #\n    + # Define NO_GETTEXT if you don't want Git output to be translated.\n     @@ Makefile: CURL_CONFIG = curl-config\n      GCOV = gcov\n      STRIP = strip\n    @@ Makefile: CURL_CONFIG = curl-config\n      \n      export TCL_PATH TCLTK_PATH\n      \n    -@@ Makefile: OBJECTS += $(REFTABLE_OBJS) $(REFTABLE_TEST_OBJS)\n    - OBJECTS += $(UNIT_TEST_OBJS)\n    +@@ Makefile: ifdef FSMONITOR_OS_SETTINGS\n    + \tCOMPAT_OBJS += compat/fsmonitor/fsm-path-utils-$(FSMONITOR_OS_SETTINGS).o\n    + endif\n    + \n    ++ifdef INCLUDE_LIBGIT_RS\n    ++\t# Enable symbol hiding in contrib/libgit-sys/libgitpub.a without making\n    ++\t# us rebuild the whole tree every time we run a Rust build.\n    ++\tBASIC_CFLAGS += -fvisibility=hidden\n    ++endif\n    ++\n    + ifeq ($(TCLTK_PATH),)\n    + NO_TCLTK = NoThanks\n    + endif\n    +@@ Makefile: OBJECTS += $(UNIT_TEST_OBJS)\n      OBJECTS += $(CLAR_TEST_OBJS)\n      OBJECTS += $(patsubst %,$(UNIT_TEST_DIR)/%.o,$(UNIT_TEST_PROGRAMS))\n    -+OBJECTS += contrib/libgit-sys/public_symbol_export.o\n      \n    ++ifdef INCLUDE_LIBGIT_RS\n    ++\tOBJECTS += contrib/libgit-sys/public_symbol_export.o\n    ++endif\n    ++\n      ifndef NO_CURL\n      \tOBJECTS += http.o http-walker.o remote-curl.o\n    + endif\n     @@ Makefile: clean: profile-clean coverage-clean cocciclean\n      \t$(RM) $(htmldocs).tar.gz $(manpages).tar.gz\n      \t$(MAKE) -C Documentation/ clean\n      \t$(RM) Documentation/GIT-EXCLUDED-PROGRAMS\n     +\t$(RM) -r contrib/libgit-sys/target\n    -+\t$(RM) -r contrib/libgit-sys/partial_symbol_export.o\n    -+\t$(RM) -r contrib/libgit-sys/hidden_symbol_export.o\n    -+\t$(RM) -r contrib/libgit-sys/libgitpub.a\n    ++\t$(RM) contrib/libgit-sys/partial_symbol_export.o\n    ++\t$(RM) contrib/libgit-sys/hidden_symbol_export.o\n    ++\t$(RM) contrib/libgit-sys/libgitpub.a\n      ifndef NO_PERL\n      \t$(RM) -r perl/build/\n      endif\n    @@ Makefile: $(CLAR_TEST_PROG): $(UNIT_TEST_DIR)/clar.suite $(CLAR_TEST_OBJS) $(GIT\n      unit-tests: $(UNIT_TEST_PROGS) $(CLAR_TEST_PROG) t/helper/test-tool$X\n      \t$(MAKE) -C t/ unit-tests\n     +\n    ++.PHONY: libgit-sys\n    ++libgit-sys:\n    ++\t$(QUIET)(\\\n    ++\t\tcd contrib/libgit-sys && \\\n    ++\t\tcargo build \\\n    ++\t)\n    ++ifdef INCLUDE_LIBGIT_RS\n    ++all:: libgit-sys\n    ++endif\n    ++\n     +contrib/libgit-sys/partial_symbol_export.o: contrib/libgit-sys/public_symbol_export.o libgit.a reftable/libreftable.a xdiff/lib.a\n     +\t$(LD) -r $^ -o $@\n     +\n    @@ contrib/libgit-sys/Cargo.toml (new)\n     +edition = \"2021\"\n     +build = \"build.rs\"\n     +links = \"gitpub\"\n    -+rust-version = \"1.63\"\n    ++rust-version = \"1.63\" # TODO: Once we hit 1.84 or newer, we may want to remove Cargo.lock from\n    ++                      # version control. See https://lore.kernel.org/git/Z47jgK-oMjFRSslr@tapette.crustytoothpaste.net/\n     +description = \"Native bindings to a portion of libgit\"\n     +\n     +[lib]\n    @@ contrib/libgit-sys/build.rs (new)\n     +        .env_remove(\"PROFILE\")\n     +        .current_dir(git_root.clone())\n     +        .args([\n    -+            \"CFLAGS=-fvisibility=hidden\",\n    ++            \"INCLUDE_LIBGIT_RS=YesPlease\",\n     +            \"contrib/libgit-sys/libgitpub.a\",\n     +        ])\n     +        .output()\n    @@ contrib/libgit-sys/build.rs (new)\n     \n      ## contrib/libgit-sys/public_symbol_export.c (new) ##\n     @@\n    -+// Shim to publicly export Git symbols. These must be renamed so that the\n    -+// original symbols can be hidden. Renaming these with a \"libgit_\" prefix also\n    -+// avoids conflicts with other libraries such as libgit2.\n    ++/* Shim to publicly export Git symbols. These must be renamed so that the\n    ++ * original symbols can be hidden. Renaming these with a \"libgit_\" prefix also\n    ++ * avoids conflicts with other libraries such as libgit2.\n    ++ */\n     +\n     +#include \"git-compat-util.h\"\n     +#include \"contrib/libgit-sys/public_symbol_export.h\"\n    @@ contrib/libgit-sys/src/lib.rs (new)\n     +        );\n     +    }\n     +}\n    +\n    + ## t/Makefile ##\n    +@@ t/Makefile: perf:\n    + \n    + .PHONY: pre-clean $(T) aggregate-results clean valgrind perf \\\n    + \tcheck-chainlint clean-chainlint test-chainlint $(UNIT_TESTS)\n    ++\n    ++.PHONY: libgit-sys-test\n    ++libgit-sys-test:\n    ++\t$(QUIET)(\\\n    ++\t\tcd ../contrib/libgit-sys && \\\n    ++\t\tcargo test \\\n    ++\t)\n    ++ifdef INCLUDE_LIBGIT_RS\n    ++all:: libgit-sys-test\n    ++endif\n3:  03f39b6c3a < -:  ---------- libgit-sys: also export some config_set functions\n5:  84706f0db7 ! 3:  d67d3648d1 Makefile: add option to build and test libgit-rs and libgit-rs-sys\n    @@\n      ## Metadata ##\n    -Author: Calvin Wan <calvinwan@google.com>\n    +Author: Josh Steadmon <steadmon@google.com>\n     \n      ## Commit message ##\n    -    Makefile: add option to build and test libgit-rs and libgit-rs-sys\n    +    libgit-sys: also export some config_set functions\n     \n    -    Add libgitrs, libgitrs-sys, libgitrs-test, and libgitrs-sys-test targets\n    -    to their respective Makefiles so they can be built and tested without\n    -    having to run cargo build/test.\n    +    In preparation for implementing a higher-level Rust API for accessing\n    +    Git configs, export some of the upstream configset API via libgitpub and\n    +    libgit-sys. Since this will be exercised as part of the higher-level API\n    +    in the next commit, no tests have been added for libgit-sys.\n     \n    -    Add build variable, INCLUDE_LIBGIT_RS, that when set, automatically\n    -    builds and tests libgit-rs and libgit-rs-sys when `make all` is run.\n    +    While we're at it, add git_configset_alloc() and git_configset_free()\n    +    functions in libgitpub so that callers can manage config_set structs on\n    +    the heap. This also allows non-C external consumers to treat config_sets\n    +    as opaque structs.\n     \n    -    Co-authored-by: Josh Steadmon <steadmon@google.com>\n    +    Co-authored-by: Calvin Wan <calvinwan@google.com>\n         Signed-off-by: Calvin Wan <calvinwan@google.com>\n    +    Signed-off-by: Josh Steadmon <steadmon@google.com>\n     \n    - ## Makefile ##\n    -@@ Makefile: include shared.mak\n    - # Define LINK_FUZZ_PROGRAMS if you want `make all` to also build the fuzz test\n    - # programs in oss-fuzz/.\n    - #\n    -+# Define INCLUDE_LIBGIT_RS if you want `make all` and `make test` to build and\n    -+# test the Rust crates in contrib/libgit-rs/ and contrib/libgit-rs/libgit-sys/.\n    -+#\n    - # === Optional library: libintl ===\n    - #\n    - # Define NO_GETTEXT if you don't want Git output to be translated.\n    -@@ Makefile: ifdef FSMONITOR_OS_SETTINGS\n    - \tCOMPAT_OBJS += compat/fsmonitor/fsm-path-utils-$(FSMONITOR_OS_SETTINGS).o\n    - endif\n    + ## contrib/libgit-sys/public_symbol_export.c ##\n    +@@\n    +  */\n      \n    -+ifdef INCLUDE_LIBGIT_RS\n    -+\t# Enable symbol hiding in contrib/libgit-rs/libgit-sys/libgitpub.a\n    -+\t# without making us rebuild the whole tree every time we run a Rust\n    -+\t# build.\n    -+\tBASIC_CFLAGS += -fvisibility=hidden\n    -+endif\n    -+\n    - ifeq ($(TCLTK_PATH),)\n    - NO_TCLTK = NoThanks\n    - endif\n    -@@ Makefile: build-unit-tests: $(UNIT_TEST_PROGS) $(CLAR_TEST_PROG)\n    - unit-tests: $(UNIT_TEST_PROGS) $(CLAR_TEST_PROG) t/helper/test-tool$X\n    - \t$(MAKE) -C t/ unit-tests\n    + #include \"git-compat-util.h\"\n    ++#include \"config.h\"\n    + #include \"contrib/libgit-sys/public_symbol_export.h\"\n    + #include \"version.h\"\n      \n    -+.PHONY: libgit-sys\n    -+libgit-sys:\n    -+\t$(QUIET)(\\\n    -+\t\tcd contrib/libgit-sys && \\\n    -+\t\tcargo build \\\n    -+\t)\n    -+.PHONY: libgit-rs\n    -+libgit-rs:\n    -+\t$(QUIET)(\\\n    -+\t\tcd contrib/libgit-rs && \\\n    -+\t\tcargo build \\\n    -+\t)\n    -+ifdef INCLUDE_LIBGIT_RS\n    -+all:: libgit-rs\n    -+endif\n    -+\n    - contrib/libgit-sys/partial_symbol_export.o: contrib/libgit-sys/public_symbol_export.o libgit.a reftable/libreftable.a xdiff/lib.a\n    - \t$(LD) -r $^ -o $@\n    + #pragma GCC visibility push(default)\n      \n    ++struct libgit_config_set *libgit_configset_alloc(void)\n    ++{\n    ++\tstruct config_set *cs = xmalloc(sizeof(struct config_set));\n    ++\tgit_configset_init(cs);\n    ++\treturn (struct libgit_config_set *) cs;\n    ++}\n    ++\n    ++void libgit_configset_free(struct libgit_config_set *cs)\n    ++{\n    ++\tgit_configset_clear((struct config_set *) cs);\n    ++\tfree((struct config_set *) cs);\n    ++}\n    ++\n    ++int libgit_configset_add_file(struct libgit_config_set *cs, const char *filename)\n    ++{\n    ++\treturn git_configset_add_file((struct config_set *) cs, filename);\n    ++}\n    ++\n    ++int libgit_configset_get_int(struct libgit_config_set *cs, const char *key, int *dest)\n    ++{\n    ++\treturn git_configset_get_int((struct config_set *) cs, key, dest);\n    ++}\n    ++\n    ++int libgit_configset_get_string(struct libgit_config_set *cs, const char *key, char **dest)\n    ++{\n    ++\treturn git_configset_get_string((struct config_set *) cs, key, dest);\n    ++}\n    ++\n    + const char *libgit_user_agent(void)\n    + {\n    + \treturn git_user_agent();\n     \n    - ## contrib/libgit-sys/build.rs ##\n    -@@ contrib/libgit-sys/build.rs: pub fn main() -> std::io::Result<()> {\n    -         .env_remove(\"PROFILE\")\n    -         .current_dir(git_root.clone())\n    -         .args([\n    --            \"CFLAGS=-fvisibility=hidden\",\n    -+            \"INCLUDE_LIBGIT_RS=YesPlease\",\n    -             \"contrib/libgit-sys/libgitpub.a\",\n    -         ])\n    -         .output()\n    + ## contrib/libgit-sys/public_symbol_export.h ##\n    +@@\n    + #ifndef PUBLIC_SYMBOL_EXPORT_H\n    + #define PUBLIC_SYMBOL_EXPORT_H\n    + \n    ++struct libgit_config_set *libgit_configset_alloc(void);\n    ++\n    ++void libgit_configset_free(struct libgit_config_set *cs);\n    ++\n    ++int libgit_configset_add_file(struct libgit_config_set *cs, const char *filename);\n    ++\n    ++int libgit_configset_get_int(struct libgit_config_set *cs, const char *key, int *dest);\n    ++\n    ++int libgit_configset_get_string(struct libgit_config_set *cs, const char *key, char **dest);\n    ++\n    + const char *libgit_user_agent(void);\n    + \n    + const char *libgit_user_agent_sanitized(void);\n     \n    - ## t/Makefile ##\n    -@@ t/Makefile: perf:\n    + ## contrib/libgit-sys/src/lib.rs ##\n    +@@\n    + #[cfg(has_std__ffi__c_char)]\n    +-use std::ffi::c_char;\n    ++use std::ffi::{c_char, c_int};\n    + \n    + #[cfg(not(has_std__ffi__c_char))]\n    + #[allow(non_camel_case_types)]\n    + pub type c_char = i8;\n    + \n    ++#[cfg(not(has_std__ffi__c_char))]\n    ++#[allow(non_camel_case_types)]\n    ++pub type c_int = i32;\n    ++\n    + extern crate libz_sys;\n    + \n    ++#[allow(non_camel_case_types)]\n    ++#[repr(C)]\n    ++pub struct libgit_config_set {\n    ++    _data: [u8; 0],\n    ++    _marker: core::marker::PhantomData<(*mut u8, core::marker::PhantomPinned)>,\n    ++}\n    ++\n    + extern \"C\" {\n    +     pub fn libgit_user_agent() -> *const c_char;\n    +     pub fn libgit_user_agent_sanitized() -> *const c_char;\n    ++\n    ++    pub fn libgit_configset_alloc() -> *mut libgit_config_set;\n    ++    pub fn libgit_configset_free(cs: *mut libgit_config_set);\n    ++\n    ++    pub fn libgit_configset_add_file(cs: *mut libgit_config_set, filename: *const c_char) -> c_int;\n    ++\n    ++    pub fn libgit_configset_get_int(\n    ++        cs: *mut libgit_config_set,\n    ++        key: *const c_char,\n    ++        int: *mut c_int,\n    ++    ) -> c_int;\n    ++\n    ++    pub fn libgit_configset_get_string(\n    ++        cs: *mut libgit_config_set,\n    ++        key: *const c_char,\n    ++        dest: *mut *mut c_char,\n    ++    ) -> c_int;\n    ++\n    + }\n      \n    - .PHONY: pre-clean $(T) aggregate-results clean valgrind perf \\\n    - \tcheck-chainlint clean-chainlint test-chainlint $(UNIT_TESTS)\n    -+\n    -+.PHONY: libgit-sys-test\n    -+libgit-sys-test:\n    -+\t$(QUIET)(\\\n    -+\t\tcd ../contrib/libgit-sys && \\\n    -+\t\tcargo test \\\n    -+\t)\n    -+.PHONY: libgit-rs-test\n    -+libgit-rs-test:\n    -+\t$(QUIET)(\\\n    -+\t\tcd ../contrib/libgit-rs && \\\n    -+\t\tcargo test \\\n    -+\t)\n    -+ifdef INCLUDE_LIBGIT_RS\n    -+all:: libgit-sys-test libgit-rs-test\n    -+endif\n    + #[cfg(test)]\n4:  65166ea0c0 ! 4:  88425bb0b1 libgit: add higher-level libgit crate\n    @@ .gitignore: Release/\n      /contrib/libgit-sys/target\n     \n      ## Makefile ##\n    +@@ Makefile: include shared.mak\n    + # programs in oss-fuzz/.\n    + #\n    + # Define INCLUDE_LIBGIT_RS if you want `make all` and `make test` to build and\n    +-# test the Rust crate in contrib/libgit-sys.\n    ++# test the Rust crates in contrib/libgit-sys and contrib/libgit-rs.\n    + #\n    + # === Optional library: libintl ===\n    + #\n     @@ Makefile: clean: profile-clean coverage-clean cocciclean\n      \t$(RM) $(htmldocs).tar.gz $(manpages).tar.gz\n      \t$(MAKE) -C Documentation/ clean\n      \t$(RM) Documentation/GIT-EXCLUDED-PROGRAMS\n     -\t$(RM) -r contrib/libgit-sys/target\n    -+\t$(RM) -r contrib/libgit-rs/target contrib/libgit-sys/target\n    - \t$(RM) -r contrib/libgit-sys/partial_symbol_export.o\n    - \t$(RM) -r contrib/libgit-sys/hidden_symbol_export.o\n    - \t$(RM) -r contrib/libgit-sys/libgitpub.a\n    ++\t$(RM) -r contrib/libgit-sys/target contrib/libgit-rs/target\n    + \t$(RM) contrib/libgit-sys/partial_symbol_export.o\n    + \t$(RM) contrib/libgit-sys/hidden_symbol_export.o\n    + \t$(RM) contrib/libgit-sys/libgitpub.a\n    +@@ Makefile: build-unit-tests: $(UNIT_TEST_PROGS) $(CLAR_TEST_PROG)\n    + unit-tests: $(UNIT_TEST_PROGS) $(CLAR_TEST_PROG) t/helper/test-tool$X\n    + \t$(MAKE) -C t/ unit-tests\n    + \n    +-.PHONY: libgit-sys\n    ++.PHONY: libgit-sys libgit-rs\n    + libgit-sys:\n    + \t$(QUIET)(\\\n    + \t\tcd contrib/libgit-sys && \\\n    + \t\tcargo build \\\n    + \t)\n    ++libgit-rs:\n    ++\t$(QUIET)(\\\n    ++\t\tcd contrib/libgit-rs && \\\n    ++\t\tcargo build \\\n    ++\t)\n    + ifdef INCLUDE_LIBGIT_RS\n    +-all:: libgit-sys\n    ++all:: libgit-sys libgit-rs\n    + endif\n    + \n    + contrib/libgit-sys/partial_symbol_export.o: contrib/libgit-sys/public_symbol_export.o libgit.a reftable/libreftable.a xdiff/lib.a\n     \n      ## contrib/libgit-rs/Cargo.lock (new) ##\n     @@\n    @@ contrib/libgit-rs/Cargo.toml (new)\n     +version = \"0.1.0\"\n     +edition = \"2021\"\n     +build = \"build.rs\"\n    -+rust-version = \"1.63\"\n    ++rust-version = \"1.63\" # TODO: Once we hit 1.84 or newer, we may want to remove Cargo.lock from\n    ++                      # version control. See https://lore.kernel.org/git/Z47jgK-oMjFRSslr@tapette.crustytoothpaste.net/\n    ++\n     +\n     +[lib]\n     +path = \"src/lib.rs\"\n    @@ contrib/libgit-rs/build.rs (new)\n     +    ac.emit_has_path(\"std::ffi::c_char\");\n     +}\n     \n    - ## contrib/libgit-rs/src/lib.rs (new) ##\n    + ## contrib/libgit-rs/src/config.rs (new) ##\n     @@\n     +use std::ffi::{c_void, CStr, CString};\n     +use std::path::Path;\n    @@ contrib/libgit-rs/src/lib.rs (new)\n     +\n     +#[cfg(not(has_std__ffi__c_char))]\n     +#[allow(non_camel_case_types)]\n    -+pub type c_char = i8;\n    ++type c_char = i8;\n     +\n     +#[cfg(not(has_std__ffi__c_char))]\n     +#[allow(non_camel_case_types)]\n    -+pub type c_int = i32;\n    ++type c_int = i32;\n     +\n     +use libgit_sys::*;\n     +\n    ++/// A ConfigSet is an in-memory cache for config-like files such as `.gitmodules` or `.gitconfig`.\n    ++/// It does not support all config directives; notably, it will not process `include` or\n    ++/// `includeIf` directives (but it will store them so that callers can choose whether and how to\n    ++/// handle them).\n     +pub struct ConfigSet(*mut libgit_config_set);\n     +impl ConfigSet {\n    ++    /// Allocate a new ConfigSet\n     +    pub fn new() -> Self {\n     +        unsafe { ConfigSet(libgit_configset_alloc()) }\n     +    }\n     +\n    ++    /// Load the given files into the ConfigSet; conflicting directives in later files will\n    ++    /// override those given in earlier files.\n     +    pub fn add_files(&mut self, files: &[&Path]) {\n     +        for file in files {\n     +            let pstr = file.to_str().expect(\"Invalid UTF-8\");\n    @@ contrib/libgit-rs/src/lib.rs (new)\n     +        }\n     +    }\n     +\n    ++    /// Load the value for the given key and attempt to parse it as an i32. Dies with a fatal error\n    ++    /// if the value cannot be parsed. Returns None if the key is not present.\n     +    pub fn get_int(&mut self, key: &str) -> Option<i32> {\n     +        let key = CString::new(key).expect(\"Couldn't convert to CString\");\n     +        let mut val: c_int = 0;\n    @@ contrib/libgit-rs/src/lib.rs (new)\n     +        Some(val.into())\n     +    }\n     +\n    ++    /// Clones the value for the given key. Dies with a fatal error if the value cannot be\n    ++    /// converted to a String. Returns None if the key is not present.\n     +    pub fn get_string(&mut self, key: &str) -> Option<String> {\n     +        let key = CString::new(key).expect(\"Couldn't convert key to CString\");\n     +        let mut val: *mut c_char = std::ptr::null_mut();\n    @@ contrib/libgit-rs/src/lib.rs (new)\n     +    }\n     +}\n     \n    + ## contrib/libgit-rs/src/lib.rs (new) ##\n    +@@\n    ++pub mod config;\n    +\n      ## contrib/libgit-rs/testdata/config1 (new) ##\n     @@\n     +[trace2]\n    @@ contrib/libgit-sys/src/lib.rs: pub struct libgit_config_set {\n          pub fn libgit_user_agent() -> *const c_char;\n          pub fn libgit_user_agent_sanitized() -> *const c_char;\n      \n    +\n    + ## t/Makefile ##\n    +@@ t/Makefile: perf:\n    + .PHONY: pre-clean $(T) aggregate-results clean valgrind perf \\\n    + \tcheck-chainlint clean-chainlint test-chainlint $(UNIT_TESTS)\n    + \n    +-.PHONY: libgit-sys-test\n    ++.PHONY: libgit-sys-test libgit-rs-test\n    + libgit-sys-test:\n    + \t$(QUIET)(\\\n    + \t\tcd ../contrib/libgit-sys && \\\n    + \t\tcargo test \\\n    + \t)\n    ++libgit-rs-test:\n    ++\t$(QUIET)(\\\n    ++\t\tcd ../contrib/libgit-rs && \\\n    ++\t\tcargo test \\\n    ++\t)\n    + ifdef INCLUDE_LIBGIT_RS\n    +-all:: libgit-sys-test\n    ++all:: libgit-sys-test libgit-rs-test\n    + endif\n\nbase-commit: 757161efcca150a9a96b312d9e780a071e601a03\n-- \n2.48.1.262.g85cc9f2d1e-goog\n\n"},{"id":"511308","messageId":"cd0cb9aa046b99d2cd3f95b0a8cc7aaf29c677d5.1738023208.git.steadmon@google.com","threadId":"61917","inReplyTo":"cover.1738023208.git.steadmon@google.com","subject":"[PATCH v7 1/4] common-main: split init and exit code into new files","fromName":"Josh Steadmon","fromEmail":"steadmon@google.com","sentAt":"2025-01-28T00:19:02Z","receivedAt":"2025-01-28T00:19:10Z","isPatch":true,"sender":{"key":"steadmon@google.com","avatar":"https://avatars.githubusercontent.com/u/2654920?v=4"},"body":"Currently, object files in libgit.a reference common_exit(), which is\ncontained in common-main.o. However, common-main.o also includes main(),\nwhich references cmd_main() in git.o, which in turn depends on all the\nbuiltin/*.o objects.\n\nWe would like to allow external users to link libgit.a without needing\nto include so many extra objects. Enable this by splitting common_exit()\nand check_bug_if_BUG() into a new file common-exit.c, and add\ncommon-exit.o to LIB_OBJS so that these are included in libgit.a.\n\nThis split has previously been proposed ([1], [2]) to support fuzz tests\nand unit tests by avoiding conflicting definitions for main(). However,\nboth of those issues were resolved by other methods of avoiding symbol\nconflicts. Now we are trying to make libgit.a more self-contained, so\nhopefully we can revisit this approach.\n\nAdditionally, move the initialization code out of main() into a new\ninit_git() function in its own file. Include this in libgit.a as well,\nso that external users can share our setup code without calling our\nmain().\n\n[1] https://lore.kernel.org/git/Yp+wjCPhqieTku3X@google.com/\n[2] https://lore.kernel.org/git/20230517-unit-tests-v2-v2-1-21b5b60f4b32@google.com/\n\nHelped-by: Junio C Hamano <gitster@pobox.com>\nSigned-off-by: Josh Steadmon <steadmon@google.com>\n---\n Makefile      |  2 ++\n common-exit.c | 26 ++++++++++++++++\n common-init.c | 63 ++++++++++++++++++++++++++++++++++++++\n common-init.h |  6 ++++\n common-main.c | 83 ++-------------------------------------------------\n meson.build   |  2 ++\n 6 files changed, 101 insertions(+), 81 deletions(-)\n create mode 100644 common-exit.c\n create mode 100644 common-init.c\n create mode 100644 common-init.h\n\ndiff --git a/Makefile b/Makefile\nindex 97e8385b66..27e68ac039 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -981,6 +981,8 @@ LIB_OBJS += combine-diff.o\n LIB_OBJS += commit-graph.o\n LIB_OBJS += commit-reach.o\n LIB_OBJS += commit.o\n+LIB_OBJS += common-exit.o\n+LIB_OBJS += common-init.o\n LIB_OBJS += compat/nonblock.o\n LIB_OBJS += compat/obstack.o\n LIB_OBJS += compat/terminal.o\ndiff --git a/common-exit.c b/common-exit.c\nnew file mode 100644\nindex 0000000000..1aaa538be3\n--- /dev/null\n+++ b/common-exit.c\n@@ -0,0 +1,26 @@\n+#include \"git-compat-util.h\"\n+#include \"trace2.h\"\n+\n+static void check_bug_if_BUG(void)\n+{\n+\tif (!bug_called_must_BUG)\n+\t\treturn;\n+\tBUG(\"on exit(): had bug() call(s) in this process without explicit BUG_if_bug()\");\n+}\n+\n+/* We wrap exit() to call common_exit() in git-compat-util.h */\n+int common_exit(const char *file, int line, int code)\n+{\n+\t/*\n+\t * For non-POSIX systems: Take the lowest 8 bits of the \"code\"\n+\t * to e.g. turn -1 into 255. On a POSIX system this is\n+\t * redundant, see exit(3) and wait(2), but as it doesn't harm\n+\t * anything there we don't need to guard this with an \"ifdef\".\n+\t */\n+\tcode &= 0xff;\n+\n+\tcheck_bug_if_BUG();\n+\ttrace2_cmd_exit_fl(file, line, code);\n+\n+\treturn code;\n+}\ndiff --git a/common-init.c b/common-init.c\nnew file mode 100644\nindex 0000000000..5cc73f058c\n--- /dev/null\n+++ b/common-init.c\n@@ -0,0 +1,63 @@\n+#define USE_THE_REPOSITORY_VARIABLE\n+\n+#include \"git-compat-util.h\"\n+#include \"common-init.h\"\n+#include \"exec-cmd.h\"\n+#include \"gettext.h\"\n+#include \"attr.h\"\n+#include \"repository.h\"\n+#include \"setup.h\"\n+#include \"strbuf.h\"\n+#include \"trace2.h\"\n+\n+/*\n+ * Many parts of Git have subprograms communicate via pipe, expect the\n+ * upstream of a pipe to die with SIGPIPE when the downstream of a\n+ * pipe does not need to read all that is written.  Some third-party\n+ * programs that ignore or block SIGPIPE for their own reason forget\n+ * to restore SIGPIPE handling to the default before spawning Git and\n+ * break this carefully orchestrated machinery.\n+ *\n+ * Restore the way SIGPIPE is handled to default, which is what we\n+ * expect.\n+ */\n+static void restore_sigpipe_to_default(void)\n+{\n+\tsigset_t unblock;\n+\n+\tsigemptyset(&unblock);\n+\tsigaddset(&unblock, SIGPIPE);\n+\tsigprocmask(SIG_UNBLOCK, &unblock, NULL);\n+\tsignal(SIGPIPE, SIG_DFL);\n+}\n+\n+void init_git(const char **argv)\n+{\n+\tstruct strbuf tmp = STRBUF_INIT;\n+\n+\ttrace2_initialize_clock();\n+\n+\t/*\n+\t * Always open file descriptors 0/1/2 to avoid clobbering files\n+\t * in die().  It also avoids messing up when the pipes are dup'ed\n+\t * onto stdin/stdout/stderr in the child processes we spawn.\n+\t */\n+\tsanitize_stdfds();\n+\trestore_sigpipe_to_default();\n+\n+\tgit_resolve_executable_dir(argv[0]);\n+\n+\tsetlocale(LC_CTYPE, \"\");\n+\tgit_setup_gettext();\n+\n+\tinitialize_repository(the_repository);\n+\n+\tattr_start();\n+\n+\ttrace2_initialize();\n+\ttrace2_cmd_start(argv);\n+\ttrace2_collect_process_info(TRACE2_PROCESS_INFO_STARTUP);\n+\n+\tif (!strbuf_getcwd(&tmp))\n+\t\ttmp_original_cwd = strbuf_detach(&tmp, NULL);\n+}\ndiff --git a/common-init.h b/common-init.h\nnew file mode 100644\nindex 0000000000..3e6db20cae\n--- /dev/null\n+++ b/common-init.h\n@@ -0,0 +1,6 @@\n+#ifndef COMMON_INIT_H\n+#define COMMON_INIT_H\n+\n+void init_git(const char **argv);\n+\n+#endif /* COMMON_INIT_H */\ndiff --git a/common-main.c b/common-main.c\nindex 8e68ac9e42..6b7ab077b0 100644\n--- a/common-main.c\n+++ b/common-main.c\n@@ -1,92 +1,13 @@\n-#define USE_THE_REPOSITORY_VARIABLE\n-\n #include \"git-compat-util.h\"\n-#include \"exec-cmd.h\"\n-#include \"gettext.h\"\n-#include \"attr.h\"\n-#include \"repository.h\"\n-#include \"setup.h\"\n-#include \"strbuf.h\"\n-#include \"trace2.h\"\n-\n-/*\n- * Many parts of Git have subprograms communicate via pipe, expect the\n- * upstream of a pipe to die with SIGPIPE when the downstream of a\n- * pipe does not need to read all that is written.  Some third-party\n- * programs that ignore or block SIGPIPE for their own reason forget\n- * to restore SIGPIPE handling to the default before spawning Git and\n- * break this carefully orchestrated machinery.\n- *\n- * Restore the way SIGPIPE is handled to default, which is what we\n- * expect.\n- */\n-static void restore_sigpipe_to_default(void)\n-{\n-\tsigset_t unblock;\n-\n-\tsigemptyset(&unblock);\n-\tsigaddset(&unblock, SIGPIPE);\n-\tsigprocmask(SIG_UNBLOCK, &unblock, NULL);\n-\tsignal(SIGPIPE, SIG_DFL);\n-}\n+#include \"common-init.h\"\n \n int main(int argc, const char **argv)\n {\n \tint result;\n-\tstruct strbuf tmp = STRBUF_INIT;\n-\n-\ttrace2_initialize_clock();\n-\n-\t/*\n-\t * Always open file descriptors 0/1/2 to avoid clobbering files\n-\t * in die().  It also avoids messing up when the pipes are dup'ed\n-\t * onto stdin/stdout/stderr in the child processes we spawn.\n-\t */\n-\tsanitize_stdfds();\n-\trestore_sigpipe_to_default();\n-\n-\tgit_resolve_executable_dir(argv[0]);\n-\n-\tsetlocale(LC_CTYPE, \"\");\n-\tgit_setup_gettext();\n-\n-\tinitialize_repository(the_repository);\n-\n-\tattr_start();\n-\n-\ttrace2_initialize();\n-\ttrace2_cmd_start(argv);\n-\ttrace2_collect_process_info(TRACE2_PROCESS_INFO_STARTUP);\n-\n-\tif (!strbuf_getcwd(&tmp))\n-\t\ttmp_original_cwd = strbuf_detach(&tmp, NULL);\n \n+\tinit_git(argv);\n \tresult = cmd_main(argc, argv);\n \n \t/* Not exit(3), but a wrapper calling our common_exit() */\n \texit(result);\n }\n-\n-static void check_bug_if_BUG(void)\n-{\n-\tif (!bug_called_must_BUG)\n-\t\treturn;\n-\tBUG(\"on exit(): had bug() call(s) in this process without explicit BUG_if_bug()\");\n-}\n-\n-/* We wrap exit() to call common_exit() in git-compat-util.h */\n-int common_exit(const char *file, int line, int code)\n-{\n-\t/*\n-\t * For non-POSIX systems: Take the lowest 8 bits of the \"code\"\n-\t * to e.g. turn -1 into 255. On a POSIX system this is\n-\t * redundant, see exit(3) and wait(2), but as it doesn't harm\n-\t * anything there we don't need to guard this with an \"ifdef\".\n-\t */\n-\tcode &= 0xff;\n-\n-\tcheck_bug_if_BUG();\n-\ttrace2_cmd_exit_fl(file, line, code);\n-\n-\treturn code;\n-}\ndiff --git a/meson.build b/meson.build\nindex 0064eb64f5..e5ba28b47f 100644\n--- a/meson.build\n+++ b/meson.build\n@@ -245,6 +245,8 @@ libgit_sources = [\n   'commit-graph.c',\n   'commit-reach.c',\n   'commit.c',\n+  'common-exit.c',\n+  'common-init.c',\n   'compat/nonblock.c',\n   'compat/obstack.c',\n   'compat/terminal.c',\n-- \n2.48.1.262.g85cc9f2d1e-goog\n\n"},{"id":"511309","messageId":"f1502b85901d81889d86597f31eca40e1965d56f.1738023208.git.steadmon@google.com","threadId":"61917","inReplyTo":"cover.1738023208.git.steadmon@google.com","subject":"[PATCH v7 2/4] libgit-sys: introduce Rust wrapper for libgit.a","fromName":"Josh Steadmon","fromEmail":"steadmon@google.com","sentAt":"2025-01-28T00:19:03Z","receivedAt":"2025-01-28T00:19:12Z","isPatch":true,"sender":{"key":"steadmon@google.com","avatar":"https://avatars.githubusercontent.com/u/2654920?v=4"},"body":"Introduce libgit-sys, a Rust wrapper crate that allows Rust code to call\nfunctions in libgit.a. This initial patch defines build rules and an\ninterface that exposes user agent string getter functions as a proof of\nconcept. This library can be tested with `cargo test`. In later commits,\na higher-level library containing a more Rust-friendly interface will be\nadded at `contrib/libgit-rs`.\n\nSymbols in libgit can collide with symbols from other libraries such as\nlibgit2. We avoid this by first exposing library symbols in\npublic_symbol_export.[ch]. These symbols are prepended with \"libgit_\" to\navoid collisions and set to visible using a visibility pragma. In\nbuild.rs, Rust builds contrib/libgit-rs/libgit-sys/libgitpub.a, which also\ncontains libgit.a and other dependent libraries, with\n-fvisibility=hidden to hide all symbols within those libraries that\nhaven't been exposed with a visibility pragma.\n\nCo-authored-by: Kyle Lippincott <spectral@google.com>\nCo-authored-by: Calvin Wan <calvinwan@google.com>\nSigned-off-by: Calvin Wan <calvinwan@google.com>\nSigned-off-by: Kyle Lippincott <spectral@google.com>\nSigned-off-by: Josh Steadmon <steadmon@google.com>\n---\n .gitignore                                |  1 +\n Makefile                                  | 38 +++++++++++++\n contrib/libgit-sys/Cargo.lock             | 69 +++++++++++++++++++++++\n contrib/libgit-sys/Cargo.toml             | 19 +++++++\n contrib/libgit-sys/README.md              |  4 ++\n contrib/libgit-sys/build.rs               | 35 ++++++++++++\n contrib/libgit-sys/public_symbol_export.c | 22 ++++++++\n contrib/libgit-sys/public_symbol_export.h |  8 +++\n contrib/libgit-sys/src/lib.rs             | 46 +++++++++++++++\n t/Makefile                                | 10 ++++\n 10 files changed, 252 insertions(+)\n create mode 100644 contrib/libgit-sys/Cargo.lock\n create mode 100644 contrib/libgit-sys/Cargo.toml\n create mode 100644 contrib/libgit-sys/README.md\n create mode 100644 contrib/libgit-sys/build.rs\n create mode 100644 contrib/libgit-sys/public_symbol_export.c\n create mode 100644 contrib/libgit-sys/public_symbol_export.h\n create mode 100644 contrib/libgit-sys/src/lib.rs\n\ndiff --git a/.gitignore b/.gitignore\nindex e82aa19df0..31d7e64287 100644\n--- a/.gitignore\n+++ b/.gitignore\n@@ -250,3 +250,4 @@ Release/\n /git.VC.db\n *.dSYM\n /contrib/buildsystems/out\n+/contrib/libgit-sys/target\ndiff --git a/Makefile b/Makefile\nindex 27e68ac039..92989d3311 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -416,6 +416,9 @@ include shared.mak\n # Define LINK_FUZZ_PROGRAMS if you want `make all` to also build the fuzz test\n # programs in oss-fuzz/.\n #\n+# Define INCLUDE_LIBGIT_RS if you want `make all` and `make test` to build and\n+# test the Rust crate in contrib/libgit-sys.\n+#\n # === Optional library: libintl ===\n #\n # Define NO_GETTEXT if you don't want Git output to be translated.\n@@ -657,6 +660,8 @@ CURL_CONFIG = curl-config\n GCOV = gcov\n STRIP = strip\n SPATCH = spatch\n+LD = ld\n+OBJCOPY = objcopy\n \n export TCL_PATH TCLTK_PATH\n \n@@ -2236,6 +2241,12 @@ ifdef FSMONITOR_OS_SETTINGS\n \tCOMPAT_OBJS += compat/fsmonitor/fsm-path-utils-$(FSMONITOR_OS_SETTINGS).o\n endif\n \n+ifdef INCLUDE_LIBGIT_RS\n+\t# Enable symbol hiding in contrib/libgit-sys/libgitpub.a without making\n+\t# us rebuild the whole tree every time we run a Rust build.\n+\tBASIC_CFLAGS += -fvisibility=hidden\n+endif\n+\n ifeq ($(TCLTK_PATH),)\n NO_TCLTK = NoThanks\n endif\n@@ -2732,6 +2743,10 @@ OBJECTS += $(UNIT_TEST_OBJS)\n OBJECTS += $(CLAR_TEST_OBJS)\n OBJECTS += $(patsubst %,$(UNIT_TEST_DIR)/%.o,$(UNIT_TEST_PROGRAMS))\n \n+ifdef INCLUDE_LIBGIT_RS\n+\tOBJECTS += contrib/libgit-sys/public_symbol_export.o\n+endif\n+\n ifndef NO_CURL\n \tOBJECTS += http.o http-walker.o remote-curl.o\n endif\n@@ -3726,6 +3741,10 @@ clean: profile-clean coverage-clean cocciclean\n \t$(RM) $(htmldocs).tar.gz $(manpages).tar.gz\n \t$(MAKE) -C Documentation/ clean\n \t$(RM) Documentation/GIT-EXCLUDED-PROGRAMS\n+\t$(RM) -r contrib/libgit-sys/target\n+\t$(RM) contrib/libgit-sys/partial_symbol_export.o\n+\t$(RM) contrib/libgit-sys/hidden_symbol_export.o\n+\t$(RM) contrib/libgit-sys/libgitpub.a\n ifndef NO_PERL\n \t$(RM) -r perl/build/\n endif\n@@ -3887,3 +3906,22 @@ $(CLAR_TEST_PROG): $(UNIT_TEST_DIR)/clar.suite $(CLAR_TEST_OBJS) $(GITLIBS) GIT-\n build-unit-tests: $(UNIT_TEST_PROGS) $(CLAR_TEST_PROG)\n unit-tests: $(UNIT_TEST_PROGS) $(CLAR_TEST_PROG) t/helper/test-tool$X\n \t$(MAKE) -C t/ unit-tests\n+\n+.PHONY: libgit-sys\n+libgit-sys:\n+\t$(QUIET)(\\\n+\t\tcd contrib/libgit-sys && \\\n+\t\tcargo build \\\n+\t)\n+ifdef INCLUDE_LIBGIT_RS\n+all:: libgit-sys\n+endif\n+\n+contrib/libgit-sys/partial_symbol_export.o: contrib/libgit-sys/public_symbol_export.o libgit.a reftable/libreftable.a xdiff/lib.a\n+\t$(LD) -r $^ -o $@\n+\n+contrib/libgit-sys/hidden_symbol_export.o: contrib/libgit-sys/partial_symbol_export.o\n+\t$(OBJCOPY) --localize-hidden $^ $@\n+\n+contrib/libgit-sys/libgitpub.a: contrib/libgit-sys/hidden_symbol_export.o\n+\t$(AR) $(ARFLAGS) $@ $^\ndiff --git a/contrib/libgit-sys/Cargo.lock b/contrib/libgit-sys/Cargo.lock\nnew file mode 100644\nindex 0000000000..427a4c66b7\n--- /dev/null\n+++ b/contrib/libgit-sys/Cargo.lock\n@@ -0,0 +1,69 @@\n+# This file is automatically @generated by Cargo.\n+# It is not intended for manual editing.\n+version = 3\n+\n+[[package]]\n+name = \"autocfg\"\n+version = \"1.4.0\"\n+source = \"registry+https://github.com/rust-lang/crates.io-index\"\n+checksum = \"ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26\"\n+\n+[[package]]\n+name = \"cc\"\n+version = \"1.1.15\"\n+source = \"registry+https://github.com/rust-lang/crates.io-index\"\n+checksum = \"57b6a275aa2903740dc87da01c62040406b8812552e97129a63ea8850a17c6e6\"\n+dependencies = [\n+ \"shlex\",\n+]\n+\n+[[package]]\n+name = \"libc\"\n+version = \"0.2.158\"\n+source = \"registry+https://github.com/rust-lang/crates.io-index\"\n+checksum = \"d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439\"\n+\n+[[package]]\n+name = \"libgit-sys\"\n+version = \"0.1.0\"\n+dependencies = [\n+ \"autocfg\",\n+ \"libz-sys\",\n+ \"make-cmd\",\n+]\n+\n+[[package]]\n+name = \"libz-sys\"\n+version = \"1.1.20\"\n+source = \"registry+https://github.com/rust-lang/crates.io-index\"\n+checksum = \"d2d16453e800a8cf6dd2fc3eb4bc99b786a9b90c663b8559a5b1a041bf89e472\"\n+dependencies = [\n+ \"cc\",\n+ \"libc\",\n+ \"pkg-config\",\n+ \"vcpkg\",\n+]\n+\n+[[package]]\n+name = \"make-cmd\"\n+version = \"0.1.0\"\n+source = \"registry+https://github.com/rust-lang/crates.io-index\"\n+checksum = \"a8ca8afbe8af1785e09636acb5a41e08a765f5f0340568716c18a8700ba3c0d3\"\n+\n+[[package]]\n+name = \"pkg-config\"\n+version = \"0.3.30\"\n+source = \"registry+https://github.com/rust-lang/crates.io-index\"\n+checksum = \"d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec\"\n+\n+[[package]]\n+name = \"shlex\"\n+version = \"1.3.0\"\n+source = \"registry+https://github.com/rust-lang/crates.io-index\"\n+checksum = \"0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64\"\n+\n+[[package]]\n+name = \"vcpkg\"\n+version = \"0.2.15\"\n+source = \"registry+https://github.com/rust-lang/crates.io-index\"\n+checksum = \"accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426\"\ndiff --git a/contrib/libgit-sys/Cargo.toml b/contrib/libgit-sys/Cargo.toml\nnew file mode 100644\nindex 0000000000..e0623022c3\n--- /dev/null\n+++ b/contrib/libgit-sys/Cargo.toml\n@@ -0,0 +1,19 @@\n+[package]\n+name = \"libgit-sys\"\n+version = \"0.1.0\"\n+edition = \"2021\"\n+build = \"build.rs\"\n+links = \"gitpub\"\n+rust-version = \"1.63\" # TODO: Once we hit 1.84 or newer, we may want to remove Cargo.lock from\n+                      # version control. See https://lore.kernel.org/git/Z47jgK-oMjFRSslr@tapette.crustytoothpaste.net/\n+description = \"Native bindings to a portion of libgit\"\n+\n+[lib]\n+path = \"src/lib.rs\"\n+\n+[dependencies]\n+libz-sys = \"1.1.19\"\n+\n+[build-dependencies]\n+autocfg = \"1.4.0\"\n+make-cmd = \"0.1.0\"\ndiff --git a/contrib/libgit-sys/README.md b/contrib/libgit-sys/README.md\nnew file mode 100644\nindex 0000000000..c061cfcaf5\n--- /dev/null\n+++ b/contrib/libgit-sys/README.md\n@@ -0,0 +1,4 @@\n+# libgit-sys\n+\n+A small proof-of-concept crate showing how to provide a Rust FFI to Git\n+internals.\ndiff --git a/contrib/libgit-sys/build.rs b/contrib/libgit-sys/build.rs\nnew file mode 100644\nindex 0000000000..3ffd80ad91\n--- /dev/null\n+++ b/contrib/libgit-sys/build.rs\n@@ -0,0 +1,35 @@\n+use std::env;\n+use std::path::PathBuf;\n+\n+pub fn main() -> std::io::Result<()> {\n+    let ac = autocfg::new();\n+    ac.emit_has_path(\"std::ffi::c_char\");\n+\n+    let crate_root = PathBuf::from(env::var_os(\"CARGO_MANIFEST_DIR\").unwrap());\n+    let git_root = crate_root.join(\"../..\");\n+    let dst = PathBuf::from(env::var_os(\"OUT_DIR\").unwrap());\n+\n+    let make_output = make_cmd::gnu_make()\n+        .env(\"DEVELOPER\", \"1\")\n+        .env_remove(\"PROFILE\")\n+        .current_dir(git_root.clone())\n+        .args([\n+            \"INCLUDE_LIBGIT_RS=YesPlease\",\n+            \"contrib/libgit-sys/libgitpub.a\",\n+        ])\n+        .output()\n+        .expect(\"Make failed to run\");\n+    if !make_output.status.success() {\n+        panic!(\n+            \"Make failed:\\n  stdout = {}\\n  stderr = {}\\n\",\n+            String::from_utf8(make_output.stdout).unwrap(),\n+            String::from_utf8(make_output.stderr).unwrap()\n+        );\n+    }\n+    std::fs::copy(crate_root.join(\"libgitpub.a\"), dst.join(\"libgitpub.a\"))?;\n+    println!(\"cargo:rustc-link-search=native={}\", dst.display());\n+    println!(\"cargo:rustc-link-lib=gitpub\");\n+    println!(\"cargo:rerun-if-changed={}\", git_root.display());\n+\n+    Ok(())\n+}\ndiff --git a/contrib/libgit-sys/public_symbol_export.c b/contrib/libgit-sys/public_symbol_export.c\nnew file mode 100644\nindex 0000000000..cd1602206e\n--- /dev/null\n+++ b/contrib/libgit-sys/public_symbol_export.c\n@@ -0,0 +1,22 @@\n+/* Shim to publicly export Git symbols. These must be renamed so that the\n+ * original symbols can be hidden. Renaming these with a \"libgit_\" prefix also\n+ * avoids conflicts with other libraries such as libgit2.\n+ */\n+\n+#include \"git-compat-util.h\"\n+#include \"contrib/libgit-sys/public_symbol_export.h\"\n+#include \"version.h\"\n+\n+#pragma GCC visibility push(default)\n+\n+const char *libgit_user_agent(void)\n+{\n+\treturn git_user_agent();\n+}\n+\n+const char *libgit_user_agent_sanitized(void)\n+{\n+\treturn git_user_agent_sanitized();\n+}\n+\n+#pragma GCC visibility pop\ndiff --git a/contrib/libgit-sys/public_symbol_export.h b/contrib/libgit-sys/public_symbol_export.h\nnew file mode 100644\nindex 0000000000..a3372f93fa\n--- /dev/null\n+++ b/contrib/libgit-sys/public_symbol_export.h\n@@ -0,0 +1,8 @@\n+#ifndef PUBLIC_SYMBOL_EXPORT_H\n+#define PUBLIC_SYMBOL_EXPORT_H\n+\n+const char *libgit_user_agent(void);\n+\n+const char *libgit_user_agent_sanitized(void);\n+\n+#endif /* PUBLIC_SYMBOL_EXPORT_H */\ndiff --git a/contrib/libgit-sys/src/lib.rs b/contrib/libgit-sys/src/lib.rs\nnew file mode 100644\nindex 0000000000..d4853f3074\n--- /dev/null\n+++ b/contrib/libgit-sys/src/lib.rs\n@@ -0,0 +1,46 @@\n+#[cfg(has_std__ffi__c_char)]\n+use std::ffi::c_char;\n+\n+#[cfg(not(has_std__ffi__c_char))]\n+#[allow(non_camel_case_types)]\n+pub type c_char = i8;\n+\n+extern crate libz_sys;\n+\n+extern \"C\" {\n+    pub fn libgit_user_agent() -> *const c_char;\n+    pub fn libgit_user_agent_sanitized() -> *const c_char;\n+}\n+\n+#[cfg(test)]\n+mod tests {\n+    use std::ffi::CStr;\n+\n+    use super::*;\n+\n+    #[test]\n+    fn user_agent_starts_with_git() {\n+        let c_str = unsafe { CStr::from_ptr(libgit_user_agent()) };\n+        let agent = c_str\n+            .to_str()\n+            .expect(\"User agent contains invalid UTF-8 data\");\n+        assert!(\n+            agent.starts_with(\"git/\"),\n+            r#\"Expected user agent to start with \"git/\", got: {}\"#,\n+            agent\n+        );\n+    }\n+\n+    #[test]\n+    fn sanitized_user_agent_starts_with_git() {\n+        let c_str = unsafe { CStr::from_ptr(libgit_user_agent_sanitized()) };\n+        let agent = c_str\n+            .to_str()\n+            .expect(\"Sanitized user agent contains invalid UTF-8 data\");\n+        assert!(\n+            agent.starts_with(\"git/\"),\n+            r#\"Expected user agent to start with \"git/\", got: {}\"#,\n+            agent\n+        );\n+    }\n+}\ndiff --git a/t/Makefile b/t/Makefile\nindex daa5fcae86..53ba01c21b 100644\n--- a/t/Makefile\n+++ b/t/Makefile\n@@ -177,3 +177,13 @@ perf:\n \n .PHONY: pre-clean $(T) aggregate-results clean valgrind perf \\\n \tcheck-chainlint clean-chainlint test-chainlint $(UNIT_TESTS)\n+\n+.PHONY: libgit-sys-test\n+libgit-sys-test:\n+\t$(QUIET)(\\\n+\t\tcd ../contrib/libgit-sys && \\\n+\t\tcargo test \\\n+\t)\n+ifdef INCLUDE_LIBGIT_RS\n+all:: libgit-sys-test\n+endif\n-- \n2.48.1.262.g85cc9f2d1e-goog\n\n"},{"id":"511310","messageId":"d67d3648d1bdb7dde5e475f3a8eba834cc0ea891.1738023208.git.steadmon@google.com","threadId":"61917","inReplyTo":"cover.1738023208.git.steadmon@google.com","subject":"[PATCH v7 3/4] libgit-sys: also export some config_set functions","fromName":"Josh Steadmon","fromEmail":"steadmon@google.com","sentAt":"2025-01-28T00:19:04Z","receivedAt":"2025-01-28T00:19:14Z","isPatch":true,"sender":{"key":"steadmon@google.com","avatar":"https://avatars.githubusercontent.com/u/2654920?v=4"},"body":"In preparation for implementing a higher-level Rust API for accessing\nGit configs, export some of the upstream configset API via libgitpub and\nlibgit-sys. Since this will be exercised as part of the higher-level API\nin the next commit, no tests have been added for libgit-sys.\n\nWhile we're at it, add git_configset_alloc() and git_configset_free()\nfunctions in libgitpub so that callers can manage config_set structs on\nthe heap. This also allows non-C external consumers to treat config_sets\nas opaque structs.\n\nCo-authored-by: Calvin Wan <calvinwan@google.com>\nSigned-off-by: Calvin Wan <calvinwan@google.com>\nSigned-off-by: Josh Steadmon <steadmon@google.com>\n---\n contrib/libgit-sys/public_symbol_export.c | 29 +++++++++++++++++++++\n contrib/libgit-sys/public_symbol_export.h | 10 ++++++++\n contrib/libgit-sys/src/lib.rs             | 31 ++++++++++++++++++++++-\n 3 files changed, 69 insertions(+), 1 deletion(-)\n\ndiff --git a/contrib/libgit-sys/public_symbol_export.c b/contrib/libgit-sys/public_symbol_export.c\nindex cd1602206e..a0297cb1a5 100644\n--- a/contrib/libgit-sys/public_symbol_export.c\n+++ b/contrib/libgit-sys/public_symbol_export.c\n@@ -4,11 +4,40 @@\n  */\n \n #include \"git-compat-util.h\"\n+#include \"config.h\"\n #include \"contrib/libgit-sys/public_symbol_export.h\"\n #include \"version.h\"\n \n #pragma GCC visibility push(default)\n \n+struct libgit_config_set *libgit_configset_alloc(void)\n+{\n+\tstruct config_set *cs = xmalloc(sizeof(struct config_set));\n+\tgit_configset_init(cs);\n+\treturn (struct libgit_config_set *) cs;\n+}\n+\n+void libgit_configset_free(struct libgit_config_set *cs)\n+{\n+\tgit_configset_clear((struct config_set *) cs);\n+\tfree((struct config_set *) cs);\n+}\n+\n+int libgit_configset_add_file(struct libgit_config_set *cs, const char *filename)\n+{\n+\treturn git_configset_add_file((struct config_set *) cs, filename);\n+}\n+\n+int libgit_configset_get_int(struct libgit_config_set *cs, const char *key, int *dest)\n+{\n+\treturn git_configset_get_int((struct config_set *) cs, key, dest);\n+}\n+\n+int libgit_configset_get_string(struct libgit_config_set *cs, const char *key, char **dest)\n+{\n+\treturn git_configset_get_string((struct config_set *) cs, key, dest);\n+}\n+\n const char *libgit_user_agent(void)\n {\n \treturn git_user_agent();\ndiff --git a/contrib/libgit-sys/public_symbol_export.h b/contrib/libgit-sys/public_symbol_export.h\nindex a3372f93fa..701db92d53 100644\n--- a/contrib/libgit-sys/public_symbol_export.h\n+++ b/contrib/libgit-sys/public_symbol_export.h\n@@ -1,6 +1,16 @@\n #ifndef PUBLIC_SYMBOL_EXPORT_H\n #define PUBLIC_SYMBOL_EXPORT_H\n \n+struct libgit_config_set *libgit_configset_alloc(void);\n+\n+void libgit_configset_free(struct libgit_config_set *cs);\n+\n+int libgit_configset_add_file(struct libgit_config_set *cs, const char *filename);\n+\n+int libgit_configset_get_int(struct libgit_config_set *cs, const char *key, int *dest);\n+\n+int libgit_configset_get_string(struct libgit_config_set *cs, const char *key, char **dest);\n+\n const char *libgit_user_agent(void);\n \n const char *libgit_user_agent_sanitized(void);\ndiff --git a/contrib/libgit-sys/src/lib.rs b/contrib/libgit-sys/src/lib.rs\nindex d4853f3074..dadb4e5f40 100644\n--- a/contrib/libgit-sys/src/lib.rs\n+++ b/contrib/libgit-sys/src/lib.rs\n@@ -1,15 +1,44 @@\n #[cfg(has_std__ffi__c_char)]\n-use std::ffi::c_char;\n+use std::ffi::{c_char, c_int};\n \n #[cfg(not(has_std__ffi__c_char))]\n #[allow(non_camel_case_types)]\n pub type c_char = i8;\n \n+#[cfg(not(has_std__ffi__c_char))]\n+#[allow(non_camel_case_types)]\n+pub type c_int = i32;\n+\n extern crate libz_sys;\n \n+#[allow(non_camel_case_types)]\n+#[repr(C)]\n+pub struct libgit_config_set {\n+    _data: [u8; 0],\n+    _marker: core::marker::PhantomData<(*mut u8, core::marker::PhantomPinned)>,\n+}\n+\n extern \"C\" {\n     pub fn libgit_user_agent() -> *const c_char;\n     pub fn libgit_user_agent_sanitized() -> *const c_char;\n+\n+    pub fn libgit_configset_alloc() -> *mut libgit_config_set;\n+    pub fn libgit_configset_free(cs: *mut libgit_config_set);\n+\n+    pub fn libgit_configset_add_file(cs: *mut libgit_config_set, filename: *const c_char) -> c_int;\n+\n+    pub fn libgit_configset_get_int(\n+        cs: *mut libgit_config_set,\n+        key: *const c_char,\n+        int: *mut c_int,\n+    ) -> c_int;\n+\n+    pub fn libgit_configset_get_string(\n+        cs: *mut libgit_config_set,\n+        key: *const c_char,\n+        dest: *mut *mut c_char,\n+    ) -> c_int;\n+\n }\n \n #[cfg(test)]\n-- \n2.48.1.262.g85cc9f2d1e-goog\n\n"},{"id":"511311","messageId":"88425bb0b1b90cf6b3d2d5a1af57e46c7a03ad39.1738023208.git.steadmon@google.com","threadId":"61917","inReplyTo":"cover.1738023208.git.steadmon@google.com","subject":"[PATCH v7 4/4] libgit: add higher-level libgit crate","fromName":"Josh Steadmon","fromEmail":"steadmon@google.com","sentAt":"2025-01-28T00:19:05Z","receivedAt":"2025-01-28T00:19:16Z","isPatch":true,"sender":{"key":"steadmon@google.com","avatar":"https://avatars.githubusercontent.com/u/2654920?v=4"},"body":"From: Calvin Wan <calvinwan@google.com>\n\nThe C functions exported by libgit-sys do not provide an idiomatic Rust\ninterface. To make it easier to use these functions via Rust, add a\nhigher-level \"libgit\" crate, that wraps the lower-level configset API\nwith an interface that is more Rust-y.\n\nThis combination of $X and $X-sys crates is a common pattern for FFI in\nRust, as documented in \"The Cargo Book\" [1].\n\n[1] https://doc.rust-lang.org/cargo/reference/build-scripts.html#-sys-packages\n\nCo-authored-by: Josh Steadmon <steadmon@google.com>\nSigned-off-by: Josh Steadmon <steadmon@google.com>\nSigned-off-by: Calvin Wan <calvinwan@google.com>\n---\n .gitignore                         |   1 +\n Makefile                           |  13 ++--\n contrib/libgit-rs/Cargo.lock       |  77 +++++++++++++++++++++\n contrib/libgit-rs/Cargo.toml       |  17 +++++\n contrib/libgit-rs/README.md        |  13 ++++\n contrib/libgit-rs/build.rs         |   4 ++\n contrib/libgit-rs/src/config.rs    | 106 +++++++++++++++++++++++++++++\n contrib/libgit-rs/src/lib.rs       |   1 +\n contrib/libgit-rs/testdata/config1 |   2 +\n contrib/libgit-rs/testdata/config2 |   2 +\n contrib/libgit-rs/testdata/config3 |   2 +\n contrib/libgit-sys/src/lib.rs      |   4 ++\n t/Makefile                         |   9 ++-\n 13 files changed, 245 insertions(+), 6 deletions(-)\n create mode 100644 contrib/libgit-rs/Cargo.lock\n create mode 100644 contrib/libgit-rs/Cargo.toml\n create mode 100644 contrib/libgit-rs/README.md\n create mode 100644 contrib/libgit-rs/build.rs\n create mode 100644 contrib/libgit-rs/src/config.rs\n create mode 100644 contrib/libgit-rs/src/lib.rs\n create mode 100644 contrib/libgit-rs/testdata/config1\n create mode 100644 contrib/libgit-rs/testdata/config2\n create mode 100644 contrib/libgit-rs/testdata/config3\n\ndiff --git a/.gitignore b/.gitignore\nindex 31d7e64287..acdd8ce7c7 100644\n--- a/.gitignore\n+++ b/.gitignore\n@@ -250,4 +250,5 @@ Release/\n /git.VC.db\n *.dSYM\n /contrib/buildsystems/out\n+/contrib/libgit-rs/target\n /contrib/libgit-sys/target\ndiff --git a/Makefile b/Makefile\nindex 92989d3311..37b63de079 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -417,7 +417,7 @@ include shared.mak\n # programs in oss-fuzz/.\n #\n # Define INCLUDE_LIBGIT_RS if you want `make all` and `make test` to build and\n-# test the Rust crate in contrib/libgit-sys.\n+# test the Rust crates in contrib/libgit-sys and contrib/libgit-rs.\n #\n # === Optional library: libintl ===\n #\n@@ -3741,7 +3741,7 @@ clean: profile-clean coverage-clean cocciclean\n \t$(RM) $(htmldocs).tar.gz $(manpages).tar.gz\n \t$(MAKE) -C Documentation/ clean\n \t$(RM) Documentation/GIT-EXCLUDED-PROGRAMS\n-\t$(RM) -r contrib/libgit-sys/target\n+\t$(RM) -r contrib/libgit-sys/target contrib/libgit-rs/target\n \t$(RM) contrib/libgit-sys/partial_symbol_export.o\n \t$(RM) contrib/libgit-sys/hidden_symbol_export.o\n \t$(RM) contrib/libgit-sys/libgitpub.a\n@@ -3907,14 +3907,19 @@ build-unit-tests: $(UNIT_TEST_PROGS) $(CLAR_TEST_PROG)\n unit-tests: $(UNIT_TEST_PROGS) $(CLAR_TEST_PROG) t/helper/test-tool$X\n \t$(MAKE) -C t/ unit-tests\n \n-.PHONY: libgit-sys\n+.PHONY: libgit-sys libgit-rs\n libgit-sys:\n \t$(QUIET)(\\\n \t\tcd contrib/libgit-sys && \\\n \t\tcargo build \\\n \t)\n+libgit-rs:\n+\t$(QUIET)(\\\n+\t\tcd contrib/libgit-rs && \\\n+\t\tcargo build \\\n+\t)\n ifdef INCLUDE_LIBGIT_RS\n-all:: libgit-sys\n+all:: libgit-sys libgit-rs\n endif\n \n contrib/libgit-sys/partial_symbol_export.o: contrib/libgit-sys/public_symbol_export.o libgit.a reftable/libreftable.a xdiff/lib.a\ndiff --git a/contrib/libgit-rs/Cargo.lock b/contrib/libgit-rs/Cargo.lock\nnew file mode 100644\nindex 0000000000..a30c7c8d33\n--- /dev/null\n+++ b/contrib/libgit-rs/Cargo.lock\n@@ -0,0 +1,77 @@\n+# This file is automatically @generated by Cargo.\n+# It is not intended for manual editing.\n+version = 3\n+\n+[[package]]\n+name = \"autocfg\"\n+version = \"1.4.0\"\n+source = \"registry+https://github.com/rust-lang/crates.io-index\"\n+checksum = \"ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26\"\n+\n+[[package]]\n+name = \"cc\"\n+version = \"1.1.15\"\n+source = \"registry+https://github.com/rust-lang/crates.io-index\"\n+checksum = \"57b6a275aa2903740dc87da01c62040406b8812552e97129a63ea8850a17c6e6\"\n+dependencies = [\n+ \"shlex\",\n+]\n+\n+[[package]]\n+name = \"libc\"\n+version = \"0.2.158\"\n+source = \"registry+https://github.com/rust-lang/crates.io-index\"\n+checksum = \"d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439\"\n+\n+[[package]]\n+name = \"libgit\"\n+version = \"0.1.0\"\n+dependencies = [\n+ \"autocfg\",\n+ \"libgit-sys\",\n+]\n+\n+[[package]]\n+name = \"libgit-sys\"\n+version = \"0.1.0\"\n+dependencies = [\n+ \"autocfg\",\n+ \"libz-sys\",\n+ \"make-cmd\",\n+]\n+\n+[[package]]\n+name = \"libz-sys\"\n+version = \"1.1.20\"\n+source = \"registry+https://github.com/rust-lang/crates.io-index\"\n+checksum = \"d2d16453e800a8cf6dd2fc3eb4bc99b786a9b90c663b8559a5b1a041bf89e472\"\n+dependencies = [\n+ \"cc\",\n+ \"libc\",\n+ \"pkg-config\",\n+ \"vcpkg\",\n+]\n+\n+[[package]]\n+name = \"make-cmd\"\n+version = \"0.1.0\"\n+source = \"registry+https://github.com/rust-lang/crates.io-index\"\n+checksum = \"a8ca8afbe8af1785e09636acb5a41e08a765f5f0340568716c18a8700ba3c0d3\"\n+\n+[[package]]\n+name = \"pkg-config\"\n+version = \"0.3.30\"\n+source = \"registry+https://github.com/rust-lang/crates.io-index\"\n+checksum = \"d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec\"\n+\n+[[package]]\n+name = \"shlex\"\n+version = \"1.3.0\"\n+source = \"registry+https://github.com/rust-lang/crates.io-index\"\n+checksum = \"0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64\"\n+\n+[[package]]\n+name = \"vcpkg\"\n+version = \"0.2.15\"\n+source = \"registry+https://github.com/rust-lang/crates.io-index\"\n+checksum = \"accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426\"\ndiff --git a/contrib/libgit-rs/Cargo.toml b/contrib/libgit-rs/Cargo.toml\nnew file mode 100644\nindex 0000000000..c3289e69db\n--- /dev/null\n+++ b/contrib/libgit-rs/Cargo.toml\n@@ -0,0 +1,17 @@\n+[package]\n+name = \"libgit\"\n+version = \"0.1.0\"\n+edition = \"2021\"\n+build = \"build.rs\"\n+rust-version = \"1.63\" # TODO: Once we hit 1.84 or newer, we may want to remove Cargo.lock from\n+                      # version control. See https://lore.kernel.org/git/Z47jgK-oMjFRSslr@tapette.crustytoothpaste.net/\n+\n+\n+[lib]\n+path = \"src/lib.rs\"\n+\n+[dependencies]\n+libgit-sys = { version = \"0.1.0\", path = \"../libgit-sys\" }\n+\n+[build-dependencies]\n+autocfg = \"1.4.0\"\ndiff --git a/contrib/libgit-rs/README.md b/contrib/libgit-rs/README.md\nnew file mode 100644\nindex 0000000000..ff945e1ce2\n--- /dev/null\n+++ b/contrib/libgit-rs/README.md\n@@ -0,0 +1,13 @@\n+# libgit-rs\n+\n+Proof-of-concept Git bindings for Rust.\n+\n+```toml\n+[dependencies]\n+libgit = \"0.1.0\"\n+```\n+\n+## Rust version requirements\n+\n+libgit-rs should support Rust versions at least as old as the version included\n+in Debian stable (currently 1.63).\ndiff --git a/contrib/libgit-rs/build.rs b/contrib/libgit-rs/build.rs\nnew file mode 100644\nindex 0000000000..f8bd01a690\n--- /dev/null\n+++ b/contrib/libgit-rs/build.rs\n@@ -0,0 +1,4 @@\n+pub fn main() {\n+    let ac = autocfg::new();\n+    ac.emit_has_path(\"std::ffi::c_char\");\n+}\ndiff --git a/contrib/libgit-rs/src/config.rs b/contrib/libgit-rs/src/config.rs\nnew file mode 100644\nindex 0000000000..6bf04845c8\n--- /dev/null\n+++ b/contrib/libgit-rs/src/config.rs\n@@ -0,0 +1,106 @@\n+use std::ffi::{c_void, CStr, CString};\n+use std::path::Path;\n+\n+#[cfg(has_std__ffi__c_char)]\n+use std::ffi::{c_char, c_int};\n+\n+#[cfg(not(has_std__ffi__c_char))]\n+#[allow(non_camel_case_types)]\n+type c_char = i8;\n+\n+#[cfg(not(has_std__ffi__c_char))]\n+#[allow(non_camel_case_types)]\n+type c_int = i32;\n+\n+use libgit_sys::*;\n+\n+/// A ConfigSet is an in-memory cache for config-like files such as `.gitmodules` or `.gitconfig`.\n+/// It does not support all config directives; notably, it will not process `include` or\n+/// `includeIf` directives (but it will store them so that callers can choose whether and how to\n+/// handle them).\n+pub struct ConfigSet(*mut libgit_config_set);\n+impl ConfigSet {\n+    /// Allocate a new ConfigSet\n+    pub fn new() -> Self {\n+        unsafe { ConfigSet(libgit_configset_alloc()) }\n+    }\n+\n+    /// Load the given files into the ConfigSet; conflicting directives in later files will\n+    /// override those given in earlier files.\n+    pub fn add_files(&mut self, files: &[&Path]) {\n+        for file in files {\n+            let pstr = file.to_str().expect(\"Invalid UTF-8\");\n+            let rs = CString::new(pstr).expect(\"Couldn't convert to CString\");\n+            unsafe {\n+                libgit_configset_add_file(self.0, rs.as_ptr());\n+            }\n+        }\n+    }\n+\n+    /// Load the value for the given key and attempt to parse it as an i32. Dies with a fatal error\n+    /// if the value cannot be parsed. Returns None if the key is not present.\n+    pub fn get_int(&mut self, key: &str) -> Option<i32> {\n+        let key = CString::new(key).expect(\"Couldn't convert to CString\");\n+        let mut val: c_int = 0;\n+        unsafe {\n+            if libgit_configset_get_int(self.0, key.as_ptr(), &mut val as *mut c_int) != 0 {\n+                return None;\n+            }\n+        }\n+\n+        Some(val.into())\n+    }\n+\n+    /// Clones the value for the given key. Dies with a fatal error if the value cannot be\n+    /// converted to a String. Returns None if the key is not present.\n+    pub fn get_string(&mut self, key: &str) -> Option<String> {\n+        let key = CString::new(key).expect(\"Couldn't convert key to CString\");\n+        let mut val: *mut c_char = std::ptr::null_mut();\n+        unsafe {\n+            if libgit_configset_get_string(self.0, key.as_ptr(), &mut val as *mut *mut c_char) != 0\n+            {\n+                return None;\n+            }\n+            let borrowed_str = CStr::from_ptr(val);\n+            let owned_str =\n+                String::from(borrowed_str.to_str().expect(\"Couldn't convert val to str\"));\n+            free(val as *mut c_void); // Free the xstrdup()ed pointer from the C side\n+            Some(owned_str)\n+        }\n+    }\n+}\n+\n+impl Default for ConfigSet {\n+    fn default() -> Self {\n+        Self::new()\n+    }\n+}\n+\n+impl Drop for ConfigSet {\n+    fn drop(&mut self) {\n+        unsafe {\n+            libgit_configset_free(self.0);\n+        }\n+    }\n+}\n+\n+#[cfg(test)]\n+mod tests {\n+    use super::*;\n+\n+    #[test]\n+    fn load_configs_via_configset() {\n+        let mut cs = ConfigSet::new();\n+        cs.add_files(&[\n+            Path::new(\"testdata/config1\"),\n+            Path::new(\"testdata/config2\"),\n+            Path::new(\"testdata/config3\"),\n+        ]);\n+        // ConfigSet retrieves correct value\n+        assert_eq!(cs.get_int(\"trace2.eventTarget\"), Some(1));\n+        // ConfigSet respects last config value set\n+        assert_eq!(cs.get_int(\"trace2.eventNesting\"), Some(3));\n+        // ConfigSet returns None for missing key\n+        assert_eq!(cs.get_string(\"foo.bar\"), None);\n+    }\n+}\ndiff --git a/contrib/libgit-rs/src/lib.rs b/contrib/libgit-rs/src/lib.rs\nnew file mode 100644\nindex 0000000000..ef68c36943\n--- /dev/null\n+++ b/contrib/libgit-rs/src/lib.rs\n@@ -0,0 +1 @@\n+pub mod config;\ndiff --git a/contrib/libgit-rs/testdata/config1 b/contrib/libgit-rs/testdata/config1\nnew file mode 100644\nindex 0000000000..4e9a9d25d1\n--- /dev/null\n+++ b/contrib/libgit-rs/testdata/config1\n@@ -0,0 +1,2 @@\n+[trace2]\n+\teventNesting = 1\ndiff --git a/contrib/libgit-rs/testdata/config2 b/contrib/libgit-rs/testdata/config2\nnew file mode 100644\nindex 0000000000..b8d1eca423\n--- /dev/null\n+++ b/contrib/libgit-rs/testdata/config2\n@@ -0,0 +1,2 @@\n+[trace2]\n+\teventTarget = 1\ndiff --git a/contrib/libgit-rs/testdata/config3 b/contrib/libgit-rs/testdata/config3\nnew file mode 100644\nindex 0000000000..ca7b9a7c38\n--- /dev/null\n+++ b/contrib/libgit-rs/testdata/config3\n@@ -0,0 +1,2 @@\n+[trace2]\n+\teventNesting = 3\ndiff --git a/contrib/libgit-sys/src/lib.rs b/contrib/libgit-sys/src/lib.rs\nindex dadb4e5f40..4bfc650450 100644\n--- a/contrib/libgit-sys/src/lib.rs\n+++ b/contrib/libgit-sys/src/lib.rs\n@@ -1,3 +1,5 @@\n+use std::ffi::c_void;\n+\n #[cfg(has_std__ffi__c_char)]\n use std::ffi::{c_char, c_int};\n \n@@ -19,6 +21,8 @@ pub struct libgit_config_set {\n }\n \n extern \"C\" {\n+    pub fn free(ptr: *mut c_void);\n+\n     pub fn libgit_user_agent() -> *const c_char;\n     pub fn libgit_user_agent_sanitized() -> *const c_char;\n \ndiff --git a/t/Makefile b/t/Makefile\nindex 53ba01c21b..2994eb5fa9 100644\n--- a/t/Makefile\n+++ b/t/Makefile\n@@ -178,12 +178,17 @@ perf:\n .PHONY: pre-clean $(T) aggregate-results clean valgrind perf \\\n \tcheck-chainlint clean-chainlint test-chainlint $(UNIT_TESTS)\n \n-.PHONY: libgit-sys-test\n+.PHONY: libgit-sys-test libgit-rs-test\n libgit-sys-test:\n \t$(QUIET)(\\\n \t\tcd ../contrib/libgit-sys && \\\n \t\tcargo test \\\n \t)\n+libgit-rs-test:\n+\t$(QUIET)(\\\n+\t\tcd ../contrib/libgit-rs && \\\n+\t\tcargo test \\\n+\t)\n ifdef INCLUDE_LIBGIT_RS\n-all:: libgit-sys-test\n+all:: libgit-sys-test libgit-rs-test\n endif\n-- \n2.48.1.262.g85cc9f2d1e-goog\n\n"},{"id":"511312","messageId":"xmqqzfjbo7sb.fsf@gitster.g","threadId":"61917","inReplyTo":"88425bb0b1b90cf6b3d2d5a1af57e46c7a03ad39.1738023208.git.steadmon@google.com","subject":"Re: [PATCH v7 4/4] libgit: add higher-level libgit crate","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-01-28T04:56:52Z","receivedAt":"2025-01-28T04:56:55Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Josh Steadmon <steadmon@google.com> writes:\n\n> -.PHONY: libgit-sys\n> +.PHONY: libgit-sys libgit-rs\n>  libgit-sys:\n>  \t$(QUIET)(\\\n>  \t\tcd contrib/libgit-sys && \\\n>  \t\tcargo build \\\n>  \t)\n> +libgit-rs:\n> +\t$(QUIET)(\\\n> +\t\tcd contrib/libgit-rs && \\\n> +\t\tcargo build \\\n> +\t)\n>  ifdef INCLUDE_LIBGIT_RS\n> -all:: libgit-sys\n> +all:: libgit-sys libgit-rs\n>  endif\n\nI somehow would have expected this part of the patch to do\n\n\tlibgit-sys libgit-rs:\n\t\t$(QUIET)( \\\n\t\t\tcd contrib/$@ && cargo build \\\n\t\t)\n\nbut the above longhand is fine.\n\n> +    /// Load the value for the given key and attempt to parse it as an i32. Dies with a fatal error\n> +    /// if the value cannot be parsed. Returns None if the key is not present.\n> +    pub fn get_int(&mut self, key: &str) -> Option<i32> {\n> +        let key = CString::new(key).expect(\"Couldn't convert to CString\");\n> +        let mut val: c_int = 0;\n> +        unsafe {\n> +            if libgit_configset_get_int(self.0, key.as_ptr(), &mut val as *mut c_int) != 0 {\n> +                return None;\n> +            }\n> +        }\n> +\n> +        Some(val.into())\n> +    }\n\nNice.\n\nI was wondering why libgit_configset_get_int() in [3/4] does not do\nbetter than just wrapping the raw C interface, which returns the\nerror/success status separately from the value we parsed.  With this\nhigher-layer wrapper around it, the interface becomes a bit more\nlike a higher-level language.\n\nThanks, will queue.\n"},{"id":"511353","messageId":"8ed463cb-1191-4106-bf30-87e9e8bcc95a@gmail.com","threadId":"61917","inReplyTo":"d67d3648d1bdb7dde5e475f3a8eba834cc0ea891.1738023208.git.steadmon@google.com","subject":"Re: [PATCH v7 3/4] libgit-sys: also export some config_set functions","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2025-01-28T15:08:29Z","receivedAt":"2025-01-28T15:08:32Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Josh\n\nOn 28/01/2025 00:19, Josh Steadmon wrote:\n> In preparation for implementing a higher-level Rust API for accessing\n> Git configs, export some of the upstream configset API via libgitpub and\n> libgit-sys. Since this will be exercised as part of the higher-level API\n> in the next commit, no tests have been added for libgit-sys.\n> \n> While we're at it, add git_configset_alloc() and git_configset_free()\n> functions in libgitpub so that callers can manage config_set structs on\n> the heap. This also allows non-C external consumers to treat config_sets\n> as opaque structs.\n\nThis interface is looks nice, I've left a couple of comments below\n\n> diff --git a/contrib/libgit-sys/public_symbol_export.c b/contrib/libgit-sys/public_symbol_export.c\n> index cd1602206e..a0297cb1a5 100644\n> --- a/contrib/libgit-sys/public_symbol_export.c\n> +++ b/contrib/libgit-sys/public_symbol_export.c\n> @@ -4,11 +4,40 @@\n>    */\n>   \n>   #include \"git-compat-util.h\"\n> +#include \"config.h\"\n>   #include \"contrib/libgit-sys/public_symbol_export.h\"\n>   #include \"version.h\"\n>   \n>   #pragma GCC visibility push(default)\n\nPersonally I'd prefer it if we actually defined struct libgit_config_set \nhere\n\nstruct libgit_config_set {\n\tstruct config_set cs;\n}\n\nThen we could avoid all the casts below. For example\n\nstruct libgit_config_set *libgit_configset_alloc(void)\n{\n\tstruct libget_config_set *cs =\n\t\txmalloc(sizeof(struct libgit_config_set));\n\tgit_configset_init(&cs->cs);\n\treturn cs;\n}\n\n> +struct libgit_config_set *libgit_configset_alloc(void)\n> +{\n> +\tstruct config_set *cs = xmalloc(sizeof(struct config_set));\n> +\tgit_configset_init(cs);\n> +\treturn (struct libgit_config_set *) cs;\n> +}\n> +\n> +void libgit_configset_free(struct libgit_config_set *cs)\n> +{\n> +\tgit_configset_clear((struct config_set *) cs);\n> +\tfree((struct config_set *) cs);\n> +}\n> +\n> +int libgit_configset_add_file(struct libgit_config_set *cs, const char *filename)\n> +{\n> +\treturn git_configset_add_file((struct config_set *) cs, filename);\n> +}\n> +\n> +int libgit_configset_get_int(struct libgit_config_set *cs, const char *key, int *dest)\n\nStyle: this and the one below could do with being wrapped at 80 characters\n\nThis whole series looks pretty good to me\n\nBest Wishes\n\nPhillip\n\n> +{\n> +\treturn git_configset_get_int((struct config_set *) cs, key, dest);\n> +}\n> +\n> +int libgit_configset_get_string(struct libgit_config_set *cs, const char *key, char **dest)\n> +{\n> +\treturn git_configset_get_string((struct config_set *) cs, key, dest);\n> +}\n> +\n>   const char *libgit_user_agent(void)\n>   {\n>   \treturn git_user_agent();\n> diff --git a/contrib/libgit-sys/public_symbol_export.h b/contrib/libgit-sys/public_symbol_export.h\n> index a3372f93fa..701db92d53 100644\n> --- a/contrib/libgit-sys/public_symbol_export.h\n> +++ b/contrib/libgit-sys/public_symbol_export.h\n> @@ -1,6 +1,16 @@\n>   #ifndef PUBLIC_SYMBOL_EXPORT_H\n>   #define PUBLIC_SYMBOL_EXPORT_H\n>   \n> +struct libgit_config_set *libgit_configset_alloc(void);\n> +\n> +void libgit_configset_free(struct libgit_config_set *cs);\n> +\n> +int libgit_configset_add_file(struct libgit_config_set *cs, const char *filename);\n> +\n> +int libgit_configset_get_int(struct libgit_config_set *cs, const char *key, int *dest);\n> +\n> +int libgit_configset_get_string(struct libgit_config_set *cs, const char *key, char **dest);\n> +\n>   const char *libgit_user_agent(void);\n>   \n>   const char *libgit_user_agent_sanitized(void);\n> diff --git a/contrib/libgit-sys/src/lib.rs b/contrib/libgit-sys/src/lib.rs\n> index d4853f3074..dadb4e5f40 100644\n> --- a/contrib/libgit-sys/src/lib.rs\n> +++ b/contrib/libgit-sys/src/lib.rs\n> @@ -1,15 +1,44 @@\n>   #[cfg(has_std__ffi__c_char)]\n> -use std::ffi::c_char;\n> +use std::ffi::{c_char, c_int};\n>   \n>   #[cfg(not(has_std__ffi__c_char))]\n>   #[allow(non_camel_case_types)]\n>   pub type c_char = i8;\n>   \n> +#[cfg(not(has_std__ffi__c_char))]\n> +#[allow(non_camel_case_types)]\n> +pub type c_int = i32;\n> +\n>   extern crate libz_sys;\n>   \n> +#[allow(non_camel_case_types)]\n> +#[repr(C)]\n> +pub struct libgit_config_set {\n> +    _data: [u8; 0],\n> +    _marker: core::marker::PhantomData<(*mut u8, core::marker::PhantomPinned)>,\n> +}\n> +\n>   extern \"C\" {\n>       pub fn libgit_user_agent() -> *const c_char;\n>       pub fn libgit_user_agent_sanitized() -> *const c_char;\n> +\n> +    pub fn libgit_configset_alloc() -> *mut libgit_config_set;\n> +    pub fn libgit_configset_free(cs: *mut libgit_config_set);\n> +\n> +    pub fn libgit_configset_add_file(cs: *mut libgit_config_set, filename: *const c_char) -> c_int;\n> +\n> +    pub fn libgit_configset_get_int(\n> +        cs: *mut libgit_config_set,\n> +        key: *const c_char,\n> +        int: *mut c_int,\n> +    ) -> c_int;\n> +\n> +    pub fn libgit_configset_get_string(\n> +        cs: *mut libgit_config_set,\n> +        key: *const c_char,\n> +        dest: *mut *mut c_char,\n> +    ) -> c_int;\n> +\n>   }\n>   \n>   #[cfg(test)]\n\n"},{"id":"511354","messageId":"3559a70c-7ed3-41cd-a45f-390134218c52@gmail.com","threadId":"61917","inReplyTo":"f1502b85901d81889d86597f31eca40e1965d56f.1738023208.git.steadmon@google.com","subject":"Re: [PATCH v7 2/4] libgit-sys: introduce Rust wrapper for libgit.a","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2025-01-28T15:08:48Z","receivedAt":"2025-01-28T15:08:51Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"\n\nOn 28/01/2025 00:19, Josh Steadmon wrote:\n> Introduce libgit-sys, a Rust wrapper crate that allows Rust code to call\n> functions in libgit.a. This initial patch defines build rules and an\n> interface that exposes user agent string getter functions as a proof of\n> concept. This library can be tested with `cargo test`. \n\nIt's great to see some tests. This is looking good, I've left a couple \nof small comments below.\n\n> +contrib/libgit-sys/partial_symbol_export.o: contrib/libgit-sys/public_symbol_export.o libgit.a reftable/libreftable.a xdiff/lib.a\n> +\t$(LD) -r $^ -o $@\n\nThis is a very long line and the ones below are pretty long - perhaps we \ncould put the list of sources in a variable?\n\n> +contrib/libgit-sys/hidden_symbol_export.o: contrib/libgit-sys/partial_symbol_export.o\n> +\t$(OBJCOPY) --localize-hidden $^ $@\n> +\n> +contrib/libgit-sys/libgitpub.a: contrib/libgit-sys/hidden_symbol_export.o\n> +\t$(AR) $(ARFLAGS) $@ $^\n > [...]\n> diff --git a/contrib/libgit-sys/public_symbol_export.c b/contrib/libgit-sys/public_symbol_export.c\n> new file mode 100644\n> index 0000000000..cd1602206e\n> --- /dev/null\n> +++ b/contrib/libgit-sys/public_symbol_export.c\n> @@ -0,0 +1,22 @@\n> +/* Shim to publicly export Git symbols. These must be renamed so that the\n\nStyle: Multiline comments start with an empty \"/*\" so this should be\n\n/*\n  * Shim ...\n\n> + * original symbols can be hidden. Renaming these with a \"libgit_\" prefix also\n> + * avoids conflicts with other libraries such as libgit2.\n> + */\n\nBest Wishes\n\nPhillip\n\n> +#include \"git-compat-util.h\"\n> +#include \"contrib/libgit-sys/public_symbol_export.h\"\n> +#include \"version.h\"\n> +\n> +#pragma GCC visibility push(default)\n> +\n> +const char *libgit_user_agent(void)\n> +{\n> +\treturn git_user_agent();\n> +}\n> +\n> +const char *libgit_user_agent_sanitized(void)\n> +{\n> +\treturn git_user_agent_sanitized();\n> +}\n> +\n> +#pragma GCC visibility pop\n> diff --git a/contrib/libgit-sys/public_symbol_export.h b/contrib/libgit-sys/public_symbol_export.h\n> new file mode 100644\n> index 0000000000..a3372f93fa\n> --- /dev/null\n> +++ b/contrib/libgit-sys/public_symbol_export.h\n> @@ -0,0 +1,8 @@\n> +#ifndef PUBLIC_SYMBOL_EXPORT_H\n> +#define PUBLIC_SYMBOL_EXPORT_H\n> +\n> +const char *libgit_user_agent(void);\n> +\n> +const char *libgit_user_agent_sanitized(void);\n> +\n> +#endif /* PUBLIC_SYMBOL_EXPORT_H */\n> diff --git a/contrib/libgit-sys/src/lib.rs b/contrib/libgit-sys/src/lib.rs\n> new file mode 100644\n> index 0000000000..d4853f3074\n> --- /dev/null\n> +++ b/contrib/libgit-sys/src/lib.rs\n> @@ -0,0 +1,46 @@\n> +#[cfg(has_std__ffi__c_char)]\n> +use std::ffi::c_char;\n> +\n> +#[cfg(not(has_std__ffi__c_char))]\n> +#[allow(non_camel_case_types)]\n> +pub type c_char = i8;\n> +\n> +extern crate libz_sys;\n> +\n> +extern \"C\" {\n> +    pub fn libgit_user_agent() -> *const c_char;\n> +    pub fn libgit_user_agent_sanitized() -> *const c_char;\n> +}\n> +\n> +#[cfg(test)]\n> +mod tests {\n> +    use std::ffi::CStr;\n> +\n> +    use super::*;\n> +\n> +    #[test]\n> +    fn user_agent_starts_with_git() {\n> +        let c_str = unsafe { CStr::from_ptr(libgit_user_agent()) };\n> +        let agent = c_str\n> +            .to_str()\n> +            .expect(\"User agent contains invalid UTF-8 data\");\n> +        assert!(\n> +            agent.starts_with(\"git/\"),\n> +            r#\"Expected user agent to start with \"git/\", got: {}\"#,\n> +            agent\n> +        );\n> +    }\n> +\n> +    #[test]\n> +    fn sanitized_user_agent_starts_with_git() {\n> +        let c_str = unsafe { CStr::from_ptr(libgit_user_agent_sanitized()) };\n> +        let agent = c_str\n> +            .to_str()\n> +            .expect(\"Sanitized user agent contains invalid UTF-8 data\");\n> +        assert!(\n> +            agent.starts_with(\"git/\"),\n> +            r#\"Expected user agent to start with \"git/\", got: {}\"#,\n> +            agent\n> +        );\n> +    }\n> +}\n> diff --git a/t/Makefile b/t/Makefile\n> index daa5fcae86..53ba01c21b 100644\n> --- a/t/Makefile\n> +++ b/t/Makefile\n> @@ -177,3 +177,13 @@ perf:\n>   \n>   .PHONY: pre-clean $(T) aggregate-results clean valgrind perf \\\n>   \tcheck-chainlint clean-chainlint test-chainlint $(UNIT_TESTS)\n> +\n> +.PHONY: libgit-sys-test\n> +libgit-sys-test:\n> +\t$(QUIET)(\\\n> +\t\tcd ../contrib/libgit-sys && \\\n> +\t\tcargo test \\\n> +\t)\n> +ifdef INCLUDE_LIBGIT_RS\n> +all:: libgit-sys-test\n> +endif\n\n"},{"id":"511365","messageId":"ghkzloozvlb2hx5jxiqjydifppwu5m46rotjqirtedmyqowngv@w3rehiaqhskr","threadId":"61917","inReplyTo":"3559a70c-7ed3-41cd-a45f-390134218c52@gmail.com","subject":"Re: [PATCH v7 2/4] libgit-sys: introduce Rust wrapper for libgit.a","fromName":"Josh Steadmon","fromEmail":"steadmon@google.com","sentAt":"2025-01-28T20:26:58Z","receivedAt":"2025-01-28T20:27:04Z","isPatch":true,"sender":{"key":"steadmon@google.com","avatar":"https://avatars.githubusercontent.com/u/2654920?v=4"},"body":"On 2025.01.28 15:08, Phillip Wood wrote:\n> \n> \n> On 28/01/2025 00:19, Josh Steadmon wrote:\n> > Introduce libgit-sys, a Rust wrapper crate that allows Rust code to call\n> > functions in libgit.a. This initial patch defines build rules and an\n> > interface that exposes user agent string getter functions as a proof of\n> > concept. This library can be tested with `cargo test`.\n> \n> It's great to see some tests. This is looking good, I've left a couple of\n> small comments below.\n> \n> > +contrib/libgit-sys/partial_symbol_export.o: contrib/libgit-sys/public_symbol_export.o libgit.a reftable/libreftable.a xdiff/lib.a\n> > +\t$(LD) -r $^ -o $@\n> \n> This is a very long line and the ones below are pretty long - perhaps we\n> could put the list of sources in a variable?\n\nDone for V8.\n\n\n> > +contrib/libgit-sys/hidden_symbol_export.o: contrib/libgit-sys/partial_symbol_export.o\n> > +\t$(OBJCOPY) --localize-hidden $^ $@\n> > +\n> > +contrib/libgit-sys/libgitpub.a: contrib/libgit-sys/hidden_symbol_export.o\n> > +\t$(AR) $(ARFLAGS) $@ $^\n> > [...]\n> > diff --git a/contrib/libgit-sys/public_symbol_export.c b/contrib/libgit-sys/public_symbol_export.c\n> > new file mode 100644\n> > index 0000000000..cd1602206e\n> > --- /dev/null\n> > +++ b/contrib/libgit-sys/public_symbol_export.c\n> > @@ -0,0 +1,22 @@\n> > +/* Shim to publicly export Git symbols. These must be renamed so that the\n> \n> Style: Multiline comments start with an empty \"/*\" so this should be\n> \n> /*\n>  * Shim ...\n\nWhoops, fixed, thanks.\n"},{"id":"511366","messageId":"xmqqzfjamzwv.fsf@gitster.g","threadId":"61917","inReplyTo":"ghkzloozvlb2hx5jxiqjydifppwu5m46rotjqirtedmyqowngv@w3rehiaqhskr","subject":"Re: [PATCH v7 2/4] libgit-sys: introduce Rust wrapper for libgit.a","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-01-28T20:44:32Z","receivedAt":"2025-01-28T20:44:35Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Josh Steadmon <steadmon@google.com> writes:\n\n> On 2025.01.28 15:08, Phillip Wood wrote:\n>> \n>> \n>> On 28/01/2025 00:19, Josh Steadmon wrote:\n>> > Introduce libgit-sys, a Rust wrapper crate that allows Rust code to call\n>> > functions in libgit.a. This initial patch defines build rules and an\n>> > interface that exposes user agent string getter functions as a proof of\n>> > concept. This library can be tested with `cargo test`.\n>> \n>> It's great to see some tests. This is looking good, I've left a couple of\n>> small comments below.\n>> \n>> > +contrib/libgit-sys/partial_symbol_export.o: contrib/libgit-sys/public_symbol_export.o libgit.a reftable/libreftable.a xdiff/lib.a\n>> > +\t$(LD) -r $^ -o $@\n>> \n>> This is a very long line and the ones below are pretty long - perhaps we\n>> could put the list of sources in a variable?\n>\n> Done for V8.\n>\n>\n>> > +contrib/libgit-sys/hidden_symbol_export.o: contrib/libgit-sys/partial_symbol_export.o\n>> > +\t$(OBJCOPY) --localize-hidden $^ $@\n>> > +\n>> > +contrib/libgit-sys/libgitpub.a: contrib/libgit-sys/hidden_symbol_export.o\n>> > +\t$(AR) $(ARFLAGS) $@ $^\n>> > [...]\n>> > diff --git a/contrib/libgit-sys/public_symbol_export.c b/contrib/libgit-sys/public_symbol_export.c\n>> > new file mode 100644\n>> > index 0000000000..cd1602206e\n>> > --- /dev/null\n>> > +++ b/contrib/libgit-sys/public_symbol_export.c\n>> > @@ -0,0 +1,22 @@\n>> > +/* Shim to publicly export Git symbols. These must be renamed so that the\n>> \n>> Style: Multiline comments start with an empty \"/*\" so this should be\n>> \n>> /*\n>>  * Shim ...\n>\n> Whoops, fixed, thanks.\n\nThe series is looking good and it seems that we'll be expecting a\nhopefully small and final reroll?  I'll mark the topic in the\n\"What's cooking\" draft as such.\n\nThanks, all.\n"},{"id":"511367","messageId":"hlfseilciwto4qajr2c4qvoxrfycef3jcvikpwj5aosopf3r3e@ohdkdqllyjxo","threadId":"61917","inReplyTo":"8ed463cb-1191-4106-bf30-87e9e8bcc95a@gmail.com","subject":"Re: [PATCH v7 3/4] libgit-sys: also export some config_set functions","fromName":"Josh Steadmon","fromEmail":"steadmon@google.com","sentAt":"2025-01-28T20:47:28Z","receivedAt":"2025-01-28T20:47:33Z","isPatch":true,"sender":{"key":"steadmon@google.com","avatar":"https://avatars.githubusercontent.com/u/2654920?v=4"},"body":"On 2025.01.28 15:08, Phillip Wood wrote:\n> Hi Josh\n> \n> On 28/01/2025 00:19, Josh Steadmon wrote:\n> > In preparation for implementing a higher-level Rust API for accessing\n> > Git configs, export some of the upstream configset API via libgitpub and\n> > libgit-sys. Since this will be exercised as part of the higher-level API\n> > in the next commit, no tests have been added for libgit-sys.\n> > \n> > While we're at it, add git_configset_alloc() and git_configset_free()\n> > functions in libgitpub so that callers can manage config_set structs on\n> > the heap. This also allows non-C external consumers to treat config_sets\n> > as opaque structs.\n> \n> This interface is looks nice, I've left a couple of comments below\n> \n> > diff --git a/contrib/libgit-sys/public_symbol_export.c b/contrib/libgit-sys/public_symbol_export.c\n> > index cd1602206e..a0297cb1a5 100644\n> > --- a/contrib/libgit-sys/public_symbol_export.c\n> > +++ b/contrib/libgit-sys/public_symbol_export.c\n> > @@ -4,11 +4,40 @@\n> >    */\n> >   #include \"git-compat-util.h\"\n> > +#include \"config.h\"\n> >   #include \"contrib/libgit-sys/public_symbol_export.h\"\n> >   #include \"version.h\"\n> >   #pragma GCC visibility push(default)\n> \n> Personally I'd prefer it if we actually defined struct libgit_config_set\n> here\n> \n> struct libgit_config_set {\n> \tstruct config_set cs;\n> }\n> \n> Then we could avoid all the casts below. For example\n> \n> struct libgit_config_set *libgit_configset_alloc(void)\n> {\n> \tstruct libget_config_set *cs =\n> \t\txmalloc(sizeof(struct libgit_config_set));\n> \tgit_configset_init(&cs->cs);\n> \treturn cs;\n> }\n\nHmm yeah I remember this feedback from (checks Lore) back in V2. I think\nyou're right, we should have gone this way from the beginning. Done in\nV8.\n\n> > +struct libgit_config_set *libgit_configset_alloc(void)\n> > +{\n> > +\tstruct config_set *cs = xmalloc(sizeof(struct config_set));\n> > +\tgit_configset_init(cs);\n> > +\treturn (struct libgit_config_set *) cs;\n> > +}\n> > +\n> > +void libgit_configset_free(struct libgit_config_set *cs)\n> > +{\n> > +\tgit_configset_clear((struct config_set *) cs);\n> > +\tfree((struct config_set *) cs);\n> > +}\n> > +\n> > +int libgit_configset_add_file(struct libgit_config_set *cs, const char *filename)\n> > +{\n> > +\treturn git_configset_add_file((struct config_set *) cs, filename);\n> > +}\n> > +\n> > +int libgit_configset_get_int(struct libgit_config_set *cs, const char *key, int *dest)\n> \n> Style: this and the one below could do with being wrapped at 80 characters\n\nFixed.\n\n> This whole series looks pretty good to me\n> \n> Best Wishes\n> \n> Phillip\n"},{"id":"511368","messageId":"iexpjuyxdg3yzk2eeibtzfgyzzrnpo2fg2ic6eeajky5lm7elw@tby4wimoqjrg","threadId":"61917","inReplyTo":"xmqqzfjbo7sb.fsf@gitster.g","subject":"Re: [PATCH v7 4/4] libgit: add higher-level libgit crate","fromName":"Josh Steadmon","fromEmail":"steadmon@google.com","sentAt":"2025-01-28T20:50:32Z","receivedAt":"2025-01-28T20:50:38Z","isPatch":true,"sender":{"key":"steadmon@google.com","avatar":"https://avatars.githubusercontent.com/u/2654920?v=4"},"body":"On 2025.01.27 20:56, Junio C Hamano wrote:\n> Josh Steadmon <steadmon@google.com> writes:\n> \n> > -.PHONY: libgit-sys\n> > +.PHONY: libgit-sys libgit-rs\n> >  libgit-sys:\n> >  \t$(QUIET)(\\\n> >  \t\tcd contrib/libgit-sys && \\\n> >  \t\tcargo build \\\n> >  \t)\n> > +libgit-rs:\n> > +\t$(QUIET)(\\\n> > +\t\tcd contrib/libgit-rs && \\\n> > +\t\tcargo build \\\n> > +\t)\n> >  ifdef INCLUDE_LIBGIT_RS\n> > -all:: libgit-sys\n> > +all:: libgit-sys libgit-rs\n> >  endif\n> \n> I somehow would have expected this part of the patch to do\n> \n> \tlibgit-sys libgit-rs:\n> \t\t$(QUIET)( \\\n> \t\t\tcd contrib/$@ && cargo build \\\n> \t\t)\n> \n> but the above longhand is fine.\n\nFixed since I'm sending out a V8 for Phillip's feedback anyway. Thanks.\n\n> > +    /// Load the value for the given key and attempt to parse it as an i32. Dies with a fatal error\n> > +    /// if the value cannot be parsed. Returns None if the key is not present.\n> > +    pub fn get_int(&mut self, key: &str) -> Option<i32> {\n> > +        let key = CString::new(key).expect(\"Couldn't convert to CString\");\n> > +        let mut val: c_int = 0;\n> > +        unsafe {\n> > +            if libgit_configset_get_int(self.0, key.as_ptr(), &mut val as *mut c_int) != 0 {\n> > +                return None;\n> > +            }\n> > +        }\n> > +\n> > +        Some(val.into())\n> > +    }\n> \n> Nice.\n> \n> I was wondering why libgit_configset_get_int() in [3/4] does not do\n> better than just wrapping the raw C interface, which returns the\n> error/success status separately from the value we parsed.  With this\n> higher-layer wrapper around it, the interface becomes a bit more\n> like a higher-level language.\n> \n> Thanks, will queue.\n"},{"id":"511370","messageId":"xmqqikpymydi.fsf@gitster.g","threadId":"61917","inReplyTo":"iexpjuyxdg3yzk2eeibtzfgyzzrnpo2fg2ic6eeajky5lm7elw@tby4wimoqjrg","subject":"Re: [PATCH v7 4/4] libgit: add higher-level libgit crate","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-01-28T21:17:45Z","receivedAt":"2025-01-28T21:17:48Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Josh Steadmon <steadmon@google.com> writes:\n\n>> I somehow would have expected this part of the patch to do\n>> \n>> \tlibgit-sys libgit-rs:\n>> \t\t$(QUIET)( \\\n>> \t\t\tcd contrib/$@ && cargo build \\\n>> \t\t)\n>> \n>> but the above longhand is fine.\n>\n> Fixed since I'm sending out a V8 for Phillip's feedback anyway. Thanks.\n\nIt would be a \"fix\" only if we anticipate that we will add in the\nfuture more Rust things to contrib/ and all of them would follow the\nsame pattern.  Otherwise, it does not really matter, and as I said,\nwhat you posted is just fine.\n\nThanks.\n"},{"id":"511372","messageId":"cover.1738101256.git.steadmon@google.com","threadId":"61917","inReplyTo":"cover.1723054623.git.steadmon@google.com","subject":"[PATCH v8 0/4] Introduce libgit-rs, a Rust wrapper around libgit.a","fromName":"Josh Steadmon","fromEmail":"steadmon@google.com","sentAt":"2025-01-28T22:01:36Z","receivedAt":"2025-01-28T22:01:42Z","isPatch":true,"sender":{"key":"steadmon@google.com","avatar":"https://avatars.githubusercontent.com/u/2654920?v=4"},"body":"This series provides two small Rust wrapper libraries around parts of\nGit: \"libgit-sys\", which exposes a few functions from libgit.a, and\n\"libgit\", which provides a more Rust-friendly interface to some of those\nfunctions. In addition to included unit tests, at $DAYJOB we have tested\nbuilding JJ[1] with our library and used it to replace some of the\nlibgit2-rs uses.\n\n[1] https://github.com/jj-vcs/jj\n\nThere is known NEEDSWORK, but I feel that they can be addressed in\nfollow-up changes, rather than in this series. If you feel otherwise,\nplease let me know:\n\n* Investigate alternative methods of managing symbol visibility &\n  renaming.\n\n* Figure out symbol versioning\n\nChanges in V8:\n* Define a private libgit_config_set struct to avoid excessive casting\n  in public_symbol_export.c.\n\n* Style fixes: merge some Makefile rules, limit rule line length by\n  defining intermediate variables, add initial empty comment line, add\n  linebreaks in function definitions.\n\nChanges in V7:\n* Moved the ConfigSet implementation in libgit-rs to a `config` module.\n\n* Added doc comments for ConfigSet and its methods.\n\n* Fix meson builds by adding new object files to `libgit_sources`\n\n* Moved INCLUDE_LIBGIT_RS Makefile changes earlier in the series, so\n  that we can make it optional to compile some of the libgitpub sources.\n  Squashed V6 patch 5/5 into this series' patch 4/4.\n\n* Don't publicly export FFI types in libgit-rs.\n\n* Removed extraneous `-r` argument to $(RM) in the clean rules.\n\n* Added TODO reminder in Cargo.toml about removing Cargo.lock once we\n  hit a certain minimum supported Rust version.\n\n* Style cleanup in public_symbol_export.c\n\nChanges in V6:\n* Rebased onto current master, since V5 was several months old.\n\n* Move libgit-sys out of libgit-rs; while this sort of nesting is common\n  in Rust crates with standalone repositories, it doesn't make as much\n  sense when they're contained in the larger Git project's repo.\n\n* Standardize the naming of some of the Makefile targets to always\n  include a dash in the \"-rs\" or \"-sys\" suffixes.\n\n* Clean up READMEs and crate descriptions in preparation for\n  uploading to crates.io.\n\nChanges in V5:\n* When building with INCLUDE_LIBGIT_RS defined, add\n  \"-fvisibility=hidden\" to CFLAGS. This allows us to manage symbol\n  visibility in libgitpub.a without causing `make all` to rebuild from\n  scratch due to changing CFLAGS.\n\n* Avoid using c_int in the higher-level Rust API.\n\n* Remove libgitpub.a and intermediate files with `make clean`.\n\nChanges in V4:\n* Drop V3 patch #3, which added wrappers around repository\n  initialization and config access. These are not well-libified, and\n  they are not necessary for JJ's proof-of-concept use case, so let's\n  avoid exporting them for now.\n\n* Set a minimum supported Rust version of 1.63. Autodetect whether our\n  Rust version has c_int and c_char types; if not, define them\n  ourselves.\n\n* When building libgitpub.a via build.rs, set DEVELOPER=1 to catch\n  additional errors at build time.\n\n* In build.rs, use the make_cmd crate to portable select the correct\n  invocation of GNU Make.\n\n* Follow naming standards for _alloc() and _free() functions.\n\n* Use String instead of CString in higher-level API.\n\n* Move libgit_configset_alloc() and libgit_configset_free() out of\n  upstream Git, to the libgitpub shim library.\n\n* In libgitpub, initialize libgit_config_set structs in the _alloc()\n  function rather than with a separate _init() function.\n\n* Remove unnecessary comments in libgit-sys showing where the wrapped\n  functions were originally defined.\n\n* Fix clippy lint: don't reborrow configfile path references.\n\n* Various typo fixes and `cargo fmt` fixes.\n\nChanges in V3:\n* Renamed cgit-rs to libgit-rs and cgit-sys to libgit-sys\n\n* Makefile cleanup, particularly adding config.mak options that\n  developers can set to run Rust builds and tests by default (Patch 6)\n\n* Provide testdata configs for unit tests\n\n* ConfigSet API now uses &Path instead of &str -- more ergonomic for\n  Rust users to pass in and errors out if the path string isn't UTF-8\n\n* Fixed unresolved dependency on libz in Cargo.toml\n\n\nCalvin Wan (1):\n  libgit: add higher-level libgit crate\n\nJosh Steadmon (3):\n  common-main: split init and exit code into new files\n  libgit-sys: introduce Rust wrapper for libgit.a\n  libgit-sys: also export some config_set functions\n\n .gitignore                                |   2 +\n Makefile                                  |  49 ++++++++++\n common-exit.c                             |  26 ++++++\n common-init.c                             |  63 +++++++++++++\n common-init.h                             |   6 ++\n common-main.c                             |  83 +----------------\n contrib/libgit-rs/Cargo.lock              |  77 ++++++++++++++++\n contrib/libgit-rs/Cargo.toml              |  17 ++++\n contrib/libgit-rs/README.md               |  13 +++\n contrib/libgit-rs/build.rs                |   4 +\n contrib/libgit-rs/src/config.rs           | 106 ++++++++++++++++++++++\n contrib/libgit-rs/src/lib.rs              |   1 +\n contrib/libgit-rs/testdata/config1        |   2 +\n contrib/libgit-rs/testdata/config2        |   2 +\n contrib/libgit-rs/testdata/config3        |   2 +\n contrib/libgit-sys/Cargo.lock             |  69 ++++++++++++++\n contrib/libgit-sys/Cargo.toml             |  19 ++++\n contrib/libgit-sys/README.md              |   4 +\n contrib/libgit-sys/build.rs               |  35 +++++++\n contrib/libgit-sys/public_symbol_export.c |  59 ++++++++++++\n contrib/libgit-sys/public_symbol_export.h |  18 ++++\n contrib/libgit-sys/src/lib.rs             |  79 ++++++++++++++++\n meson.build                               |   2 +\n t/Makefile                                |  15 +++\n 24 files changed, 672 insertions(+), 81 deletions(-)\n create mode 100644 common-exit.c\n create mode 100644 common-init.c\n create mode 100644 common-init.h\n create mode 100644 contrib/libgit-rs/Cargo.lock\n create mode 100644 contrib/libgit-rs/Cargo.toml\n create mode 100644 contrib/libgit-rs/README.md\n create mode 100644 contrib/libgit-rs/build.rs\n create mode 100644 contrib/libgit-rs/src/config.rs\n create mode 100644 contrib/libgit-rs/src/lib.rs\n create mode 100644 contrib/libgit-rs/testdata/config1\n create mode 100644 contrib/libgit-rs/testdata/config2\n create mode 100644 contrib/libgit-rs/testdata/config3\n create mode 100644 contrib/libgit-sys/Cargo.lock\n create mode 100644 contrib/libgit-sys/Cargo.toml\n create mode 100644 contrib/libgit-sys/README.md\n create mode 100644 contrib/libgit-sys/build.rs\n create mode 100644 contrib/libgit-sys/public_symbol_export.c\n create mode 100644 contrib/libgit-sys/public_symbol_export.h\n create mode 100644 contrib/libgit-sys/src/lib.rs\n\nRange-diff against v7:\n1:  cd0cb9aa04 = 1:  cd0cb9aa04 common-main: split init and exit code into new files\n2:  f1502b8590 ! 2:  3588a3c3fc libgit-sys: introduce Rust wrapper for libgit.a\n    @@ Makefile: $(CLAR_TEST_PROG): $(UNIT_TEST_DIR)/clar.suite $(CLAR_TEST_OBJS) $(GIT\n     +all:: libgit-sys\n     +endif\n     +\n    -+contrib/libgit-sys/partial_symbol_export.o: contrib/libgit-sys/public_symbol_export.o libgit.a reftable/libreftable.a xdiff/lib.a\n    ++LIBGIT_PUB_OBJS = contrib/libgit-sys/public_symbol_export.o\n    ++LIBGIT_PUB_OBJS += libgit.a\n    ++LIBGIT_PUB_OBJS += reftable/libreftable.a\n    ++LIBGIT_PUB_OBJS += xdiff/lib.a\n    ++\n    ++LIBGIT_PARTIAL_EXPORT = contrib/libgit-sys/partial_symbol_export.o\n    ++\n    ++LIBGIT_HIDDEN_EXPORT = contrib/libgit-sys/hidden_symbol_export.o\n    ++\n    ++$(LIBGIT_PARTIAL_EXPORT): $(LIBGIT_PUB_OBJS)\n     +\t$(LD) -r $^ -o $@\n     +\n    -+contrib/libgit-sys/hidden_symbol_export.o: contrib/libgit-sys/partial_symbol_export.o\n    ++$(LIBGIT_HIDDEN_EXPORT): $(LIBGIT_PARTIAL_EXPORT)\n     +\t$(OBJCOPY) --localize-hidden $^ $@\n     +\n    -+contrib/libgit-sys/libgitpub.a: contrib/libgit-sys/hidden_symbol_export.o\n    ++contrib/libgit-sys/libgitpub.a: $(LIBGIT_HIDDEN_EXPORT)\n     +\t$(AR) $(ARFLAGS) $@ $^\n     \n      ## contrib/libgit-sys/Cargo.lock (new) ##\n    @@ contrib/libgit-sys/build.rs (new)\n     \n      ## contrib/libgit-sys/public_symbol_export.c (new) ##\n     @@\n    -+/* Shim to publicly export Git symbols. These must be renamed so that the\n    ++/*\n    ++ * Shim to publicly export Git symbols. These must be renamed so that the\n     + * original symbols can be hidden. Renaming these with a \"libgit_\" prefix also\n     + * avoids conflicts with other libraries such as libgit2.\n     + */\n3:  d67d3648d1 ! 3:  f4452fffe6 libgit-sys: also export some config_set functions\n    @@ contrib/libgit-sys/public_symbol_export.c\n      \n      #pragma GCC visibility push(default)\n      \n    ++struct libgit_config_set {\n    ++\tstruct config_set cs;\n    ++};\n    ++\n     +struct libgit_config_set *libgit_configset_alloc(void)\n     +{\n    -+\tstruct config_set *cs = xmalloc(sizeof(struct config_set));\n    -+\tgit_configset_init(cs);\n    -+\treturn (struct libgit_config_set *) cs;\n    ++\tstruct libgit_config_set *cs =\n    ++\t\t\txmalloc(sizeof(struct libgit_config_set));\n    ++\tgit_configset_init(&cs->cs);\n    ++\treturn cs;\n     +}\n     +\n     +void libgit_configset_free(struct libgit_config_set *cs)\n     +{\n    -+\tgit_configset_clear((struct config_set *) cs);\n    -+\tfree((struct config_set *) cs);\n    ++\tgit_configset_clear(&cs->cs);\n    ++\tfree(&cs->cs);\n     +}\n     +\n     +int libgit_configset_add_file(struct libgit_config_set *cs, const char *filename)\n     +{\n    -+\treturn git_configset_add_file((struct config_set *) cs, filename);\n    ++\treturn git_configset_add_file(&cs->cs, filename);\n     +}\n     +\n    -+int libgit_configset_get_int(struct libgit_config_set *cs, const char *key, int *dest)\n    ++int libgit_configset_get_int(struct libgit_config_set *cs, const char *key,\n    ++\t\t\t     int *dest)\n     +{\n    -+\treturn git_configset_get_int((struct config_set *) cs, key, dest);\n    ++\treturn git_configset_get_int(&cs->cs, key, dest);\n     +}\n     +\n    -+int libgit_configset_get_string(struct libgit_config_set *cs, const char *key, char **dest)\n    ++int libgit_configset_get_string(struct libgit_config_set *cs, const char *key,\n    ++\t\t\t\tchar **dest)\n     +{\n    -+\treturn git_configset_get_string((struct config_set *) cs, key, dest);\n    ++\treturn git_configset_get_string(&cs->cs, key, dest);\n     +}\n     +\n      const char *libgit_user_agent(void)\n4:  88425bb0b1 ! 4:  ada9fc0a13 libgit: add higher-level libgit crate\n    @@ Makefile: build-unit-tests: $(UNIT_TEST_PROGS) $(CLAR_TEST_PROG)\n      \t$(MAKE) -C t/ unit-tests\n      \n     -.PHONY: libgit-sys\n    +-libgit-sys:\n     +.PHONY: libgit-sys libgit-rs\n    - libgit-sys:\n    ++libgit-sys libgit-rs:\n      \t$(QUIET)(\\\n    - \t\tcd contrib/libgit-sys && \\\n    +-\t\tcd contrib/libgit-sys && \\\n    ++\t\tcd contrib/$@ && \\\n      \t\tcargo build \\\n      \t)\n    -+libgit-rs:\n    -+\t$(QUIET)(\\\n    -+\t\tcd contrib/libgit-rs && \\\n    -+\t\tcargo build \\\n    -+\t)\n      ifdef INCLUDE_LIBGIT_RS\n     -all:: libgit-sys\n     +all:: libgit-sys libgit-rs\n      endif\n      \n    - contrib/libgit-sys/partial_symbol_export.o: contrib/libgit-sys/public_symbol_export.o libgit.a reftable/libreftable.a xdiff/lib.a\n    + LIBGIT_PUB_OBJS = contrib/libgit-sys/public_symbol_export.o\n     \n      ## contrib/libgit-rs/Cargo.lock (new) ##\n     @@\n\nbase-commit: 757161efcca150a9a96b312d9e780a071e601a03\n-- \n2.48.1.262.g85cc9f2d1e-goog\n\n"},{"id":"511373","messageId":"cd0cb9aa046b99d2cd3f95b0a8cc7aaf29c677d5.1738101256.git.steadmon@google.com","threadId":"61917","inReplyTo":"cover.1738101256.git.steadmon@google.com","subject":"[PATCH v8 1/4] common-main: split init and exit code into new files","fromName":"Josh Steadmon","fromEmail":"steadmon@google.com","sentAt":"2025-01-28T22:01:37Z","receivedAt":"2025-01-28T22:01:44Z","isPatch":true,"sender":{"key":"steadmon@google.com","avatar":"https://avatars.githubusercontent.com/u/2654920?v=4"},"body":"Currently, object files in libgit.a reference common_exit(), which is\ncontained in common-main.o. However, common-main.o also includes main(),\nwhich references cmd_main() in git.o, which in turn depends on all the\nbuiltin/*.o objects.\n\nWe would like to allow external users to link libgit.a without needing\nto include so many extra objects. Enable this by splitting common_exit()\nand check_bug_if_BUG() into a new file common-exit.c, and add\ncommon-exit.o to LIB_OBJS so that these are included in libgit.a.\n\nThis split has previously been proposed ([1], [2]) to support fuzz tests\nand unit tests by avoiding conflicting definitions for main(). However,\nboth of those issues were resolved by other methods of avoiding symbol\nconflicts. Now we are trying to make libgit.a more self-contained, so\nhopefully we can revisit this approach.\n\nAdditionally, move the initialization code out of main() into a new\ninit_git() function in its own file. Include this in libgit.a as well,\nso that external users can share our setup code without calling our\nmain().\n\n[1] https://lore.kernel.org/git/Yp+wjCPhqieTku3X@google.com/\n[2] https://lore.kernel.org/git/20230517-unit-tests-v2-v2-1-21b5b60f4b32@google.com/\n\nSigned-off-by: Josh Steadmon <steadmon@google.com>\n---\n Makefile      |  2 ++\n common-exit.c | 26 ++++++++++++++++\n common-init.c | 63 ++++++++++++++++++++++++++++++++++++++\n common-init.h |  6 ++++\n common-main.c | 83 ++-------------------------------------------------\n meson.build   |  2 ++\n 6 files changed, 101 insertions(+), 81 deletions(-)\n create mode 100644 common-exit.c\n create mode 100644 common-init.c\n create mode 100644 common-init.h\n\ndiff --git a/Makefile b/Makefile\nindex 97e8385b66..27e68ac039 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -981,6 +981,8 @@ LIB_OBJS += combine-diff.o\n LIB_OBJS += commit-graph.o\n LIB_OBJS += commit-reach.o\n LIB_OBJS += commit.o\n+LIB_OBJS += common-exit.o\n+LIB_OBJS += common-init.o\n LIB_OBJS += compat/nonblock.o\n LIB_OBJS += compat/obstack.o\n LIB_OBJS += compat/terminal.o\ndiff --git a/common-exit.c b/common-exit.c\nnew file mode 100644\nindex 0000000000..1aaa538be3\n--- /dev/null\n+++ b/common-exit.c\n@@ -0,0 +1,26 @@\n+#include \"git-compat-util.h\"\n+#include \"trace2.h\"\n+\n+static void check_bug_if_BUG(void)\n+{\n+\tif (!bug_called_must_BUG)\n+\t\treturn;\n+\tBUG(\"on exit(): had bug() call(s) in this process without explicit BUG_if_bug()\");\n+}\n+\n+/* We wrap exit() to call common_exit() in git-compat-util.h */\n+int common_exit(const char *file, int line, int code)\n+{\n+\t/*\n+\t * For non-POSIX systems: Take the lowest 8 bits of the \"code\"\n+\t * to e.g. turn -1 into 255. On a POSIX system this is\n+\t * redundant, see exit(3) and wait(2), but as it doesn't harm\n+\t * anything there we don't need to guard this with an \"ifdef\".\n+\t */\n+\tcode &= 0xff;\n+\n+\tcheck_bug_if_BUG();\n+\ttrace2_cmd_exit_fl(file, line, code);\n+\n+\treturn code;\n+}\ndiff --git a/common-init.c b/common-init.c\nnew file mode 100644\nindex 0000000000..5cc73f058c\n--- /dev/null\n+++ b/common-init.c\n@@ -0,0 +1,63 @@\n+#define USE_THE_REPOSITORY_VARIABLE\n+\n+#include \"git-compat-util.h\"\n+#include \"common-init.h\"\n+#include \"exec-cmd.h\"\n+#include \"gettext.h\"\n+#include \"attr.h\"\n+#include \"repository.h\"\n+#include \"setup.h\"\n+#include \"strbuf.h\"\n+#include \"trace2.h\"\n+\n+/*\n+ * Many parts of Git have subprograms communicate via pipe, expect the\n+ * upstream of a pipe to die with SIGPIPE when the downstream of a\n+ * pipe does not need to read all that is written.  Some third-party\n+ * programs that ignore or block SIGPIPE for their own reason forget\n+ * to restore SIGPIPE handling to the default before spawning Git and\n+ * break this carefully orchestrated machinery.\n+ *\n+ * Restore the way SIGPIPE is handled to default, which is what we\n+ * expect.\n+ */\n+static void restore_sigpipe_to_default(void)\n+{\n+\tsigset_t unblock;\n+\n+\tsigemptyset(&unblock);\n+\tsigaddset(&unblock, SIGPIPE);\n+\tsigprocmask(SIG_UNBLOCK, &unblock, NULL);\n+\tsignal(SIGPIPE, SIG_DFL);\n+}\n+\n+void init_git(const char **argv)\n+{\n+\tstruct strbuf tmp = STRBUF_INIT;\n+\n+\ttrace2_initialize_clock();\n+\n+\t/*\n+\t * Always open file descriptors 0/1/2 to avoid clobbering files\n+\t * in die().  It also avoids messing up when the pipes are dup'ed\n+\t * onto stdin/stdout/stderr in the child processes we spawn.\n+\t */\n+\tsanitize_stdfds();\n+\trestore_sigpipe_to_default();\n+\n+\tgit_resolve_executable_dir(argv[0]);\n+\n+\tsetlocale(LC_CTYPE, \"\");\n+\tgit_setup_gettext();\n+\n+\tinitialize_repository(the_repository);\n+\n+\tattr_start();\n+\n+\ttrace2_initialize();\n+\ttrace2_cmd_start(argv);\n+\ttrace2_collect_process_info(TRACE2_PROCESS_INFO_STARTUP);\n+\n+\tif (!strbuf_getcwd(&tmp))\n+\t\ttmp_original_cwd = strbuf_detach(&tmp, NULL);\n+}\ndiff --git a/common-init.h b/common-init.h\nnew file mode 100644\nindex 0000000000..3e6db20cae\n--- /dev/null\n+++ b/common-init.h\n@@ -0,0 +1,6 @@\n+#ifndef COMMON_INIT_H\n+#define COMMON_INIT_H\n+\n+void init_git(const char **argv);\n+\n+#endif /* COMMON_INIT_H */\ndiff --git a/common-main.c b/common-main.c\nindex 8e68ac9e42..6b7ab077b0 100644\n--- a/common-main.c\n+++ b/common-main.c\n@@ -1,92 +1,13 @@\n-#define USE_THE_REPOSITORY_VARIABLE\n-\n #include \"git-compat-util.h\"\n-#include \"exec-cmd.h\"\n-#include \"gettext.h\"\n-#include \"attr.h\"\n-#include \"repository.h\"\n-#include \"setup.h\"\n-#include \"strbuf.h\"\n-#include \"trace2.h\"\n-\n-/*\n- * Many parts of Git have subprograms communicate via pipe, expect the\n- * upstream of a pipe to die with SIGPIPE when the downstream of a\n- * pipe does not need to read all that is written.  Some third-party\n- * programs that ignore or block SIGPIPE for their own reason forget\n- * to restore SIGPIPE handling to the default before spawning Git and\n- * break this carefully orchestrated machinery.\n- *\n- * Restore the way SIGPIPE is handled to default, which is what we\n- * expect.\n- */\n-static void restore_sigpipe_to_default(void)\n-{\n-\tsigset_t unblock;\n-\n-\tsigemptyset(&unblock);\n-\tsigaddset(&unblock, SIGPIPE);\n-\tsigprocmask(SIG_UNBLOCK, &unblock, NULL);\n-\tsignal(SIGPIPE, SIG_DFL);\n-}\n+#include \"common-init.h\"\n \n int main(int argc, const char **argv)\n {\n \tint result;\n-\tstruct strbuf tmp = STRBUF_INIT;\n-\n-\ttrace2_initialize_clock();\n-\n-\t/*\n-\t * Always open file descriptors 0/1/2 to avoid clobbering files\n-\t * in die().  It also avoids messing up when the pipes are dup'ed\n-\t * onto stdin/stdout/stderr in the child processes we spawn.\n-\t */\n-\tsanitize_stdfds();\n-\trestore_sigpipe_to_default();\n-\n-\tgit_resolve_executable_dir(argv[0]);\n-\n-\tsetlocale(LC_CTYPE, \"\");\n-\tgit_setup_gettext();\n-\n-\tinitialize_repository(the_repository);\n-\n-\tattr_start();\n-\n-\ttrace2_initialize();\n-\ttrace2_cmd_start(argv);\n-\ttrace2_collect_process_info(TRACE2_PROCESS_INFO_STARTUP);\n-\n-\tif (!strbuf_getcwd(&tmp))\n-\t\ttmp_original_cwd = strbuf_detach(&tmp, NULL);\n \n+\tinit_git(argv);\n \tresult = cmd_main(argc, argv);\n \n \t/* Not exit(3), but a wrapper calling our common_exit() */\n \texit(result);\n }\n-\n-static void check_bug_if_BUG(void)\n-{\n-\tif (!bug_called_must_BUG)\n-\t\treturn;\n-\tBUG(\"on exit(): had bug() call(s) in this process without explicit BUG_if_bug()\");\n-}\n-\n-/* We wrap exit() to call common_exit() in git-compat-util.h */\n-int common_exit(const char *file, int line, int code)\n-{\n-\t/*\n-\t * For non-POSIX systems: Take the lowest 8 bits of the \"code\"\n-\t * to e.g. turn -1 into 255. On a POSIX system this is\n-\t * redundant, see exit(3) and wait(2), but as it doesn't harm\n-\t * anything there we don't need to guard this with an \"ifdef\".\n-\t */\n-\tcode &= 0xff;\n-\n-\tcheck_bug_if_BUG();\n-\ttrace2_cmd_exit_fl(file, line, code);\n-\n-\treturn code;\n-}\ndiff --git a/meson.build b/meson.build\nindex 0064eb64f5..e5ba28b47f 100644\n--- a/meson.build\n+++ b/meson.build\n@@ -245,6 +245,8 @@ libgit_sources = [\n   'commit-graph.c',\n   'commit-reach.c',\n   'commit.c',\n+  'common-exit.c',\n+  'common-init.c',\n   'compat/nonblock.c',\n   'compat/obstack.c',\n   'compat/terminal.c',\n-- \n2.48.1.262.g85cc9f2d1e-goog\n\n"},{"id":"511374","messageId":"3588a3c3fc76fd2a98b89e837dae07fd97fb88af.1738101256.git.steadmon@google.com","threadId":"61917","inReplyTo":"cover.1738101256.git.steadmon@google.com","subject":"[PATCH v8 2/4] libgit-sys: introduce Rust wrapper for libgit.a","fromName":"Josh Steadmon","fromEmail":"steadmon@google.com","sentAt":"2025-01-28T22:01:38Z","receivedAt":"2025-01-28T22:01:46Z","isPatch":true,"sender":{"key":"steadmon@google.com","avatar":"https://avatars.githubusercontent.com/u/2654920?v=4"},"body":"Introduce libgit-sys, a Rust wrapper crate that allows Rust code to call\nfunctions in libgit.a. This initial patch defines build rules and an\ninterface that exposes user agent string getter functions as a proof of\nconcept. This library can be tested with `cargo test`. In later commits,\na higher-level library containing a more Rust-friendly interface will be\nadded at `contrib/libgit-rs`.\n\nSymbols in libgit can collide with symbols from other libraries such as\nlibgit2. We avoid this by first exposing library symbols in\npublic_symbol_export.[ch]. These symbols are prepended with \"libgit_\" to\navoid collisions and set to visible using a visibility pragma. In\nbuild.rs, Rust builds contrib/libgit-rs/libgit-sys/libgitpub.a, which also\ncontains libgit.a and other dependent libraries, with\n-fvisibility=hidden to hide all symbols within those libraries that\nhaven't been exposed with a visibility pragma.\n\nCo-authored-by: Kyle Lippincott <spectral@google.com>\nCo-authored-by: Calvin Wan <calvinwan@google.com>\nSigned-off-by: Calvin Wan <calvinwan@google.com>\nSigned-off-by: Kyle Lippincott <spectral@google.com>\nSigned-off-by: Josh Steadmon <steadmon@google.com>\n---\n .gitignore                                |  1 +\n Makefile                                  | 47 +++++++++++++++\n contrib/libgit-sys/Cargo.lock             | 69 +++++++++++++++++++++++\n contrib/libgit-sys/Cargo.toml             | 19 +++++++\n contrib/libgit-sys/README.md              |  4 ++\n contrib/libgit-sys/build.rs               | 35 ++++++++++++\n contrib/libgit-sys/public_symbol_export.c | 23 ++++++++\n contrib/libgit-sys/public_symbol_export.h |  8 +++\n contrib/libgit-sys/src/lib.rs             | 46 +++++++++++++++\n t/Makefile                                | 10 ++++\n 10 files changed, 262 insertions(+)\n create mode 100644 contrib/libgit-sys/Cargo.lock\n create mode 100644 contrib/libgit-sys/Cargo.toml\n create mode 100644 contrib/libgit-sys/README.md\n create mode 100644 contrib/libgit-sys/build.rs\n create mode 100644 contrib/libgit-sys/public_symbol_export.c\n create mode 100644 contrib/libgit-sys/public_symbol_export.h\n create mode 100644 contrib/libgit-sys/src/lib.rs\n\ndiff --git a/.gitignore b/.gitignore\nindex e82aa19df0..31d7e64287 100644\n--- a/.gitignore\n+++ b/.gitignore\n@@ -250,3 +250,4 @@ Release/\n /git.VC.db\n *.dSYM\n /contrib/buildsystems/out\n+/contrib/libgit-sys/target\ndiff --git a/Makefile b/Makefile\nindex 27e68ac039..f2c08df4cb 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -416,6 +416,9 @@ include shared.mak\n # Define LINK_FUZZ_PROGRAMS if you want `make all` to also build the fuzz test\n # programs in oss-fuzz/.\n #\n+# Define INCLUDE_LIBGIT_RS if you want `make all` and `make test` to build and\n+# test the Rust crate in contrib/libgit-sys.\n+#\n # === Optional library: libintl ===\n #\n # Define NO_GETTEXT if you don't want Git output to be translated.\n@@ -657,6 +660,8 @@ CURL_CONFIG = curl-config\n GCOV = gcov\n STRIP = strip\n SPATCH = spatch\n+LD = ld\n+OBJCOPY = objcopy\n \n export TCL_PATH TCLTK_PATH\n \n@@ -2236,6 +2241,12 @@ ifdef FSMONITOR_OS_SETTINGS\n \tCOMPAT_OBJS += compat/fsmonitor/fsm-path-utils-$(FSMONITOR_OS_SETTINGS).o\n endif\n \n+ifdef INCLUDE_LIBGIT_RS\n+\t# Enable symbol hiding in contrib/libgit-sys/libgitpub.a without making\n+\t# us rebuild the whole tree every time we run a Rust build.\n+\tBASIC_CFLAGS += -fvisibility=hidden\n+endif\n+\n ifeq ($(TCLTK_PATH),)\n NO_TCLTK = NoThanks\n endif\n@@ -2732,6 +2743,10 @@ OBJECTS += $(UNIT_TEST_OBJS)\n OBJECTS += $(CLAR_TEST_OBJS)\n OBJECTS += $(patsubst %,$(UNIT_TEST_DIR)/%.o,$(UNIT_TEST_PROGRAMS))\n \n+ifdef INCLUDE_LIBGIT_RS\n+\tOBJECTS += contrib/libgit-sys/public_symbol_export.o\n+endif\n+\n ifndef NO_CURL\n \tOBJECTS += http.o http-walker.o remote-curl.o\n endif\n@@ -3726,6 +3741,10 @@ clean: profile-clean coverage-clean cocciclean\n \t$(RM) $(htmldocs).tar.gz $(manpages).tar.gz\n \t$(MAKE) -C Documentation/ clean\n \t$(RM) Documentation/GIT-EXCLUDED-PROGRAMS\n+\t$(RM) -r contrib/libgit-sys/target\n+\t$(RM) contrib/libgit-sys/partial_symbol_export.o\n+\t$(RM) contrib/libgit-sys/hidden_symbol_export.o\n+\t$(RM) contrib/libgit-sys/libgitpub.a\n ifndef NO_PERL\n \t$(RM) -r perl/build/\n endif\n@@ -3887,3 +3906,31 @@ $(CLAR_TEST_PROG): $(UNIT_TEST_DIR)/clar.suite $(CLAR_TEST_OBJS) $(GITLIBS) GIT-\n build-unit-tests: $(UNIT_TEST_PROGS) $(CLAR_TEST_PROG)\n unit-tests: $(UNIT_TEST_PROGS) $(CLAR_TEST_PROG) t/helper/test-tool$X\n \t$(MAKE) -C t/ unit-tests\n+\n+.PHONY: libgit-sys\n+libgit-sys:\n+\t$(QUIET)(\\\n+\t\tcd contrib/libgit-sys && \\\n+\t\tcargo build \\\n+\t)\n+ifdef INCLUDE_LIBGIT_RS\n+all:: libgit-sys\n+endif\n+\n+LIBGIT_PUB_OBJS = contrib/libgit-sys/public_symbol_export.o\n+LIBGIT_PUB_OBJS += libgit.a\n+LIBGIT_PUB_OBJS += reftable/libreftable.a\n+LIBGIT_PUB_OBJS += xdiff/lib.a\n+\n+LIBGIT_PARTIAL_EXPORT = contrib/libgit-sys/partial_symbol_export.o\n+\n+LIBGIT_HIDDEN_EXPORT = contrib/libgit-sys/hidden_symbol_export.o\n+\n+$(LIBGIT_PARTIAL_EXPORT): $(LIBGIT_PUB_OBJS)\n+\t$(LD) -r $^ -o $@\n+\n+$(LIBGIT_HIDDEN_EXPORT): $(LIBGIT_PARTIAL_EXPORT)\n+\t$(OBJCOPY) --localize-hidden $^ $@\n+\n+contrib/libgit-sys/libgitpub.a: $(LIBGIT_HIDDEN_EXPORT)\n+\t$(AR) $(ARFLAGS) $@ $^\ndiff --git a/contrib/libgit-sys/Cargo.lock b/contrib/libgit-sys/Cargo.lock\nnew file mode 100644\nindex 0000000000..427a4c66b7\n--- /dev/null\n+++ b/contrib/libgit-sys/Cargo.lock\n@@ -0,0 +1,69 @@\n+# This file is automatically @generated by Cargo.\n+# It is not intended for manual editing.\n+version = 3\n+\n+[[package]]\n+name = \"autocfg\"\n+version = \"1.4.0\"\n+source = \"registry+https://github.com/rust-lang/crates.io-index\"\n+checksum = \"ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26\"\n+\n+[[package]]\n+name = \"cc\"\n+version = \"1.1.15\"\n+source = \"registry+https://github.com/rust-lang/crates.io-index\"\n+checksum = \"57b6a275aa2903740dc87da01c62040406b8812552e97129a63ea8850a17c6e6\"\n+dependencies = [\n+ \"shlex\",\n+]\n+\n+[[package]]\n+name = \"libc\"\n+version = \"0.2.158\"\n+source = \"registry+https://github.com/rust-lang/crates.io-index\"\n+checksum = \"d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439\"\n+\n+[[package]]\n+name = \"libgit-sys\"\n+version = \"0.1.0\"\n+dependencies = [\n+ \"autocfg\",\n+ \"libz-sys\",\n+ \"make-cmd\",\n+]\n+\n+[[package]]\n+name = \"libz-sys\"\n+version = \"1.1.20\"\n+source = \"registry+https://github.com/rust-lang/crates.io-index\"\n+checksum = \"d2d16453e800a8cf6dd2fc3eb4bc99b786a9b90c663b8559a5b1a041bf89e472\"\n+dependencies = [\n+ \"cc\",\n+ \"libc\",\n+ \"pkg-config\",\n+ \"vcpkg\",\n+]\n+\n+[[package]]\n+name = \"make-cmd\"\n+version = \"0.1.0\"\n+source = \"registry+https://github.com/rust-lang/crates.io-index\"\n+checksum = \"a8ca8afbe8af1785e09636acb5a41e08a765f5f0340568716c18a8700ba3c0d3\"\n+\n+[[package]]\n+name = \"pkg-config\"\n+version = \"0.3.30\"\n+source = \"registry+https://github.com/rust-lang/crates.io-index\"\n+checksum = \"d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec\"\n+\n+[[package]]\n+name = \"shlex\"\n+version = \"1.3.0\"\n+source = \"registry+https://github.com/rust-lang/crates.io-index\"\n+checksum = \"0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64\"\n+\n+[[package]]\n+name = \"vcpkg\"\n+version = \"0.2.15\"\n+source = \"registry+https://github.com/rust-lang/crates.io-index\"\n+checksum = \"accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426\"\ndiff --git a/contrib/libgit-sys/Cargo.toml b/contrib/libgit-sys/Cargo.toml\nnew file mode 100644\nindex 0000000000..e0623022c3\n--- /dev/null\n+++ b/contrib/libgit-sys/Cargo.toml\n@@ -0,0 +1,19 @@\n+[package]\n+name = \"libgit-sys\"\n+version = \"0.1.0\"\n+edition = \"2021\"\n+build = \"build.rs\"\n+links = \"gitpub\"\n+rust-version = \"1.63\" # TODO: Once we hit 1.84 or newer, we may want to remove Cargo.lock from\n+                      # version control. See https://lore.kernel.org/git/Z47jgK-oMjFRSslr@tapette.crustytoothpaste.net/\n+description = \"Native bindings to a portion of libgit\"\n+\n+[lib]\n+path = \"src/lib.rs\"\n+\n+[dependencies]\n+libz-sys = \"1.1.19\"\n+\n+[build-dependencies]\n+autocfg = \"1.4.0\"\n+make-cmd = \"0.1.0\"\ndiff --git a/contrib/libgit-sys/README.md b/contrib/libgit-sys/README.md\nnew file mode 100644\nindex 0000000000..c061cfcaf5\n--- /dev/null\n+++ b/contrib/libgit-sys/README.md\n@@ -0,0 +1,4 @@\n+# libgit-sys\n+\n+A small proof-of-concept crate showing how to provide a Rust FFI to Git\n+internals.\ndiff --git a/contrib/libgit-sys/build.rs b/contrib/libgit-sys/build.rs\nnew file mode 100644\nindex 0000000000..3ffd80ad91\n--- /dev/null\n+++ b/contrib/libgit-sys/build.rs\n@@ -0,0 +1,35 @@\n+use std::env;\n+use std::path::PathBuf;\n+\n+pub fn main() -> std::io::Result<()> {\n+    let ac = autocfg::new();\n+    ac.emit_has_path(\"std::ffi::c_char\");\n+\n+    let crate_root = PathBuf::from(env::var_os(\"CARGO_MANIFEST_DIR\").unwrap());\n+    let git_root = crate_root.join(\"../..\");\n+    let dst = PathBuf::from(env::var_os(\"OUT_DIR\").unwrap());\n+\n+    let make_output = make_cmd::gnu_make()\n+        .env(\"DEVELOPER\", \"1\")\n+        .env_remove(\"PROFILE\")\n+        .current_dir(git_root.clone())\n+        .args([\n+            \"INCLUDE_LIBGIT_RS=YesPlease\",\n+            \"contrib/libgit-sys/libgitpub.a\",\n+        ])\n+        .output()\n+        .expect(\"Make failed to run\");\n+    if !make_output.status.success() {\n+        panic!(\n+            \"Make failed:\\n  stdout = {}\\n  stderr = {}\\n\",\n+            String::from_utf8(make_output.stdout).unwrap(),\n+            String::from_utf8(make_output.stderr).unwrap()\n+        );\n+    }\n+    std::fs::copy(crate_root.join(\"libgitpub.a\"), dst.join(\"libgitpub.a\"))?;\n+    println!(\"cargo:rustc-link-search=native={}\", dst.display());\n+    println!(\"cargo:rustc-link-lib=gitpub\");\n+    println!(\"cargo:rerun-if-changed={}\", git_root.display());\n+\n+    Ok(())\n+}\ndiff --git a/contrib/libgit-sys/public_symbol_export.c b/contrib/libgit-sys/public_symbol_export.c\nnew file mode 100644\nindex 0000000000..4c153c6f0d\n--- /dev/null\n+++ b/contrib/libgit-sys/public_symbol_export.c\n@@ -0,0 +1,23 @@\n+/*\n+ * Shim to publicly export Git symbols. These must be renamed so that the\n+ * original symbols can be hidden. Renaming these with a \"libgit_\" prefix also\n+ * avoids conflicts with other libraries such as libgit2.\n+ */\n+\n+#include \"git-compat-util.h\"\n+#include \"contrib/libgit-sys/public_symbol_export.h\"\n+#include \"version.h\"\n+\n+#pragma GCC visibility push(default)\n+\n+const char *libgit_user_agent(void)\n+{\n+\treturn git_user_agent();\n+}\n+\n+const char *libgit_user_agent_sanitized(void)\n+{\n+\treturn git_user_agent_sanitized();\n+}\n+\n+#pragma GCC visibility pop\ndiff --git a/contrib/libgit-sys/public_symbol_export.h b/contrib/libgit-sys/public_symbol_export.h\nnew file mode 100644\nindex 0000000000..a3372f93fa\n--- /dev/null\n+++ b/contrib/libgit-sys/public_symbol_export.h\n@@ -0,0 +1,8 @@\n+#ifndef PUBLIC_SYMBOL_EXPORT_H\n+#define PUBLIC_SYMBOL_EXPORT_H\n+\n+const char *libgit_user_agent(void);\n+\n+const char *libgit_user_agent_sanitized(void);\n+\n+#endif /* PUBLIC_SYMBOL_EXPORT_H */\ndiff --git a/contrib/libgit-sys/src/lib.rs b/contrib/libgit-sys/src/lib.rs\nnew file mode 100644\nindex 0000000000..d4853f3074\n--- /dev/null\n+++ b/contrib/libgit-sys/src/lib.rs\n@@ -0,0 +1,46 @@\n+#[cfg(has_std__ffi__c_char)]\n+use std::ffi::c_char;\n+\n+#[cfg(not(has_std__ffi__c_char))]\n+#[allow(non_camel_case_types)]\n+pub type c_char = i8;\n+\n+extern crate libz_sys;\n+\n+extern \"C\" {\n+    pub fn libgit_user_agent() -> *const c_char;\n+    pub fn libgit_user_agent_sanitized() -> *const c_char;\n+}\n+\n+#[cfg(test)]\n+mod tests {\n+    use std::ffi::CStr;\n+\n+    use super::*;\n+\n+    #[test]\n+    fn user_agent_starts_with_git() {\n+        let c_str = unsafe { CStr::from_ptr(libgit_user_agent()) };\n+        let agent = c_str\n+            .to_str()\n+            .expect(\"User agent contains invalid UTF-8 data\");\n+        assert!(\n+            agent.starts_with(\"git/\"),\n+            r#\"Expected user agent to start with \"git/\", got: {}\"#,\n+            agent\n+        );\n+    }\n+\n+    #[test]\n+    fn sanitized_user_agent_starts_with_git() {\n+        let c_str = unsafe { CStr::from_ptr(libgit_user_agent_sanitized()) };\n+        let agent = c_str\n+            .to_str()\n+            .expect(\"Sanitized user agent contains invalid UTF-8 data\");\n+        assert!(\n+            agent.starts_with(\"git/\"),\n+            r#\"Expected user agent to start with \"git/\", got: {}\"#,\n+            agent\n+        );\n+    }\n+}\ndiff --git a/t/Makefile b/t/Makefile\nindex daa5fcae86..53ba01c21b 100644\n--- a/t/Makefile\n+++ b/t/Makefile\n@@ -177,3 +177,13 @@ perf:\n \n .PHONY: pre-clean $(T) aggregate-results clean valgrind perf \\\n \tcheck-chainlint clean-chainlint test-chainlint $(UNIT_TESTS)\n+\n+.PHONY: libgit-sys-test\n+libgit-sys-test:\n+\t$(QUIET)(\\\n+\t\tcd ../contrib/libgit-sys && \\\n+\t\tcargo test \\\n+\t)\n+ifdef INCLUDE_LIBGIT_RS\n+all:: libgit-sys-test\n+endif\n-- \n2.48.1.262.g85cc9f2d1e-goog\n\n"},{"id":"511375","messageId":"f4452fffe66168dcf410c428c9ab8c52e087603f.1738101256.git.steadmon@google.com","threadId":"61917","inReplyTo":"cover.1738101256.git.steadmon@google.com","subject":"[PATCH v8 3/4] libgit-sys: also export some config_set functions","fromName":"Josh Steadmon","fromEmail":"steadmon@google.com","sentAt":"2025-01-28T22:01:39Z","receivedAt":"2025-01-28T22:01:48Z","isPatch":true,"sender":{"key":"steadmon@google.com","avatar":"https://avatars.githubusercontent.com/u/2654920?v=4"},"body":"In preparation for implementing a higher-level Rust API for accessing\nGit configs, export some of the upstream configset API via libgitpub and\nlibgit-sys. Since this will be exercised as part of the higher-level API\nin the next commit, no tests have been added for libgit-sys.\n\nWhile we're at it, add git_configset_alloc() and git_configset_free()\nfunctions in libgitpub so that callers can manage config_set structs on\nthe heap. This also allows non-C external consumers to treat config_sets\nas opaque structs.\n\nCo-authored-by: Calvin Wan <calvinwan@google.com>\nSigned-off-by: Calvin Wan <calvinwan@google.com>\nSigned-off-by: Josh Steadmon <steadmon@google.com>\n---\n contrib/libgit-sys/public_symbol_export.c | 36 +++++++++++++++++++++++\n contrib/libgit-sys/public_symbol_export.h | 10 +++++++\n contrib/libgit-sys/src/lib.rs             | 31 ++++++++++++++++++-\n 3 files changed, 76 insertions(+), 1 deletion(-)\n\ndiff --git a/contrib/libgit-sys/public_symbol_export.c b/contrib/libgit-sys/public_symbol_export.c\nindex 4c153c6f0d..dbb7948104 100644\n--- a/contrib/libgit-sys/public_symbol_export.c\n+++ b/contrib/libgit-sys/public_symbol_export.c\n@@ -5,11 +5,47 @@\n  */\n \n #include \"git-compat-util.h\"\n+#include \"config.h\"\n #include \"contrib/libgit-sys/public_symbol_export.h\"\n #include \"version.h\"\n \n #pragma GCC visibility push(default)\n \n+struct libgit_config_set {\n+\tstruct config_set cs;\n+};\n+\n+struct libgit_config_set *libgit_configset_alloc(void)\n+{\n+\tstruct libgit_config_set *cs =\n+\t\t\txmalloc(sizeof(struct libgit_config_set));\n+\tgit_configset_init(&cs->cs);\n+\treturn cs;\n+}\n+\n+void libgit_configset_free(struct libgit_config_set *cs)\n+{\n+\tgit_configset_clear(&cs->cs);\n+\tfree(&cs->cs);\n+}\n+\n+int libgit_configset_add_file(struct libgit_config_set *cs, const char *filename)\n+{\n+\treturn git_configset_add_file(&cs->cs, filename);\n+}\n+\n+int libgit_configset_get_int(struct libgit_config_set *cs, const char *key,\n+\t\t\t     int *dest)\n+{\n+\treturn git_configset_get_int(&cs->cs, key, dest);\n+}\n+\n+int libgit_configset_get_string(struct libgit_config_set *cs, const char *key,\n+\t\t\t\tchar **dest)\n+{\n+\treturn git_configset_get_string(&cs->cs, key, dest);\n+}\n+\n const char *libgit_user_agent(void)\n {\n \treturn git_user_agent();\ndiff --git a/contrib/libgit-sys/public_symbol_export.h b/contrib/libgit-sys/public_symbol_export.h\nindex a3372f93fa..701db92d53 100644\n--- a/contrib/libgit-sys/public_symbol_export.h\n+++ b/contrib/libgit-sys/public_symbol_export.h\n@@ -1,6 +1,16 @@\n #ifndef PUBLIC_SYMBOL_EXPORT_H\n #define PUBLIC_SYMBOL_EXPORT_H\n \n+struct libgit_config_set *libgit_configset_alloc(void);\n+\n+void libgit_configset_free(struct libgit_config_set *cs);\n+\n+int libgit_configset_add_file(struct libgit_config_set *cs, const char *filename);\n+\n+int libgit_configset_get_int(struct libgit_config_set *cs, const char *key, int *dest);\n+\n+int libgit_configset_get_string(struct libgit_config_set *cs, const char *key, char **dest);\n+\n const char *libgit_user_agent(void);\n \n const char *libgit_user_agent_sanitized(void);\ndiff --git a/contrib/libgit-sys/src/lib.rs b/contrib/libgit-sys/src/lib.rs\nindex d4853f3074..dadb4e5f40 100644\n--- a/contrib/libgit-sys/src/lib.rs\n+++ b/contrib/libgit-sys/src/lib.rs\n@@ -1,15 +1,44 @@\n #[cfg(has_std__ffi__c_char)]\n-use std::ffi::c_char;\n+use std::ffi::{c_char, c_int};\n \n #[cfg(not(has_std__ffi__c_char))]\n #[allow(non_camel_case_types)]\n pub type c_char = i8;\n \n+#[cfg(not(has_std__ffi__c_char))]\n+#[allow(non_camel_case_types)]\n+pub type c_int = i32;\n+\n extern crate libz_sys;\n \n+#[allow(non_camel_case_types)]\n+#[repr(C)]\n+pub struct libgit_config_set {\n+    _data: [u8; 0],\n+    _marker: core::marker::PhantomData<(*mut u8, core::marker::PhantomPinned)>,\n+}\n+\n extern \"C\" {\n     pub fn libgit_user_agent() -> *const c_char;\n     pub fn libgit_user_agent_sanitized() -> *const c_char;\n+\n+    pub fn libgit_configset_alloc() -> *mut libgit_config_set;\n+    pub fn libgit_configset_free(cs: *mut libgit_config_set);\n+\n+    pub fn libgit_configset_add_file(cs: *mut libgit_config_set, filename: *const c_char) -> c_int;\n+\n+    pub fn libgit_configset_get_int(\n+        cs: *mut libgit_config_set,\n+        key: *const c_char,\n+        int: *mut c_int,\n+    ) -> c_int;\n+\n+    pub fn libgit_configset_get_string(\n+        cs: *mut libgit_config_set,\n+        key: *const c_char,\n+        dest: *mut *mut c_char,\n+    ) -> c_int;\n+\n }\n \n #[cfg(test)]\n-- \n2.48.1.262.g85cc9f2d1e-goog\n\n"},{"id":"511376","messageId":"ada9fc0a138287fbb92a7f7da0f5be37e3f56006.1738101256.git.steadmon@google.com","threadId":"61917","inReplyTo":"cover.1738101256.git.steadmon@google.com","subject":"[PATCH v8 4/4] libgit: add higher-level libgit crate","fromName":"Josh Steadmon","fromEmail":"steadmon@google.com","sentAt":"2025-01-28T22:01:40Z","receivedAt":"2025-01-28T22:01:50Z","isPatch":true,"sender":{"key":"steadmon@google.com","avatar":"https://avatars.githubusercontent.com/u/2654920?v=4"},"body":"From: Calvin Wan <calvinwan@google.com>\n\nThe C functions exported by libgit-sys do not provide an idiomatic Rust\ninterface. To make it easier to use these functions via Rust, add a\nhigher-level \"libgit\" crate, that wraps the lower-level configset API\nwith an interface that is more Rust-y.\n\nThis combination of $X and $X-sys crates is a common pattern for FFI in\nRust, as documented in \"The Cargo Book\" [1].\n\n[1] https://doc.rust-lang.org/cargo/reference/build-scripts.html#-sys-packages\n\nCo-authored-by: Josh Steadmon <steadmon@google.com>\nSigned-off-by: Josh Steadmon <steadmon@google.com>\nSigned-off-by: Calvin Wan <calvinwan@google.com>\n---\n .gitignore                         |   1 +\n Makefile                           |  12 ++--\n contrib/libgit-rs/Cargo.lock       |  77 +++++++++++++++++++++\n contrib/libgit-rs/Cargo.toml       |  17 +++++\n contrib/libgit-rs/README.md        |  13 ++++\n contrib/libgit-rs/build.rs         |   4 ++\n contrib/libgit-rs/src/config.rs    | 106 +++++++++++++++++++++++++++++\n contrib/libgit-rs/src/lib.rs       |   1 +\n contrib/libgit-rs/testdata/config1 |   2 +\n contrib/libgit-rs/testdata/config2 |   2 +\n contrib/libgit-rs/testdata/config3 |   2 +\n contrib/libgit-sys/src/lib.rs      |   4 ++\n t/Makefile                         |   9 ++-\n 13 files changed, 242 insertions(+), 8 deletions(-)\n create mode 100644 contrib/libgit-rs/Cargo.lock\n create mode 100644 contrib/libgit-rs/Cargo.toml\n create mode 100644 contrib/libgit-rs/README.md\n create mode 100644 contrib/libgit-rs/build.rs\n create mode 100644 contrib/libgit-rs/src/config.rs\n create mode 100644 contrib/libgit-rs/src/lib.rs\n create mode 100644 contrib/libgit-rs/testdata/config1\n create mode 100644 contrib/libgit-rs/testdata/config2\n create mode 100644 contrib/libgit-rs/testdata/config3\n\ndiff --git a/.gitignore b/.gitignore\nindex 31d7e64287..acdd8ce7c7 100644\n--- a/.gitignore\n+++ b/.gitignore\n@@ -250,4 +250,5 @@ Release/\n /git.VC.db\n *.dSYM\n /contrib/buildsystems/out\n+/contrib/libgit-rs/target\n /contrib/libgit-sys/target\ndiff --git a/Makefile b/Makefile\nindex f2c08df4cb..f883d310aa 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -417,7 +417,7 @@ include shared.mak\n # programs in oss-fuzz/.\n #\n # Define INCLUDE_LIBGIT_RS if you want `make all` and `make test` to build and\n-# test the Rust crate in contrib/libgit-sys.\n+# test the Rust crates in contrib/libgit-sys and contrib/libgit-rs.\n #\n # === Optional library: libintl ===\n #\n@@ -3741,7 +3741,7 @@ clean: profile-clean coverage-clean cocciclean\n \t$(RM) $(htmldocs).tar.gz $(manpages).tar.gz\n \t$(MAKE) -C Documentation/ clean\n \t$(RM) Documentation/GIT-EXCLUDED-PROGRAMS\n-\t$(RM) -r contrib/libgit-sys/target\n+\t$(RM) -r contrib/libgit-sys/target contrib/libgit-rs/target\n \t$(RM) contrib/libgit-sys/partial_symbol_export.o\n \t$(RM) contrib/libgit-sys/hidden_symbol_export.o\n \t$(RM) contrib/libgit-sys/libgitpub.a\n@@ -3907,14 +3907,14 @@ build-unit-tests: $(UNIT_TEST_PROGS) $(CLAR_TEST_PROG)\n unit-tests: $(UNIT_TEST_PROGS) $(CLAR_TEST_PROG) t/helper/test-tool$X\n \t$(MAKE) -C t/ unit-tests\n \n-.PHONY: libgit-sys\n-libgit-sys:\n+.PHONY: libgit-sys libgit-rs\n+libgit-sys libgit-rs:\n \t$(QUIET)(\\\n-\t\tcd contrib/libgit-sys && \\\n+\t\tcd contrib/$@ && \\\n \t\tcargo build \\\n \t)\n ifdef INCLUDE_LIBGIT_RS\n-all:: libgit-sys\n+all:: libgit-sys libgit-rs\n endif\n \n LIBGIT_PUB_OBJS = contrib/libgit-sys/public_symbol_export.o\ndiff --git a/contrib/libgit-rs/Cargo.lock b/contrib/libgit-rs/Cargo.lock\nnew file mode 100644\nindex 0000000000..a30c7c8d33\n--- /dev/null\n+++ b/contrib/libgit-rs/Cargo.lock\n@@ -0,0 +1,77 @@\n+# This file is automatically @generated by Cargo.\n+# It is not intended for manual editing.\n+version = 3\n+\n+[[package]]\n+name = \"autocfg\"\n+version = \"1.4.0\"\n+source = \"registry+https://github.com/rust-lang/crates.io-index\"\n+checksum = \"ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26\"\n+\n+[[package]]\n+name = \"cc\"\n+version = \"1.1.15\"\n+source = \"registry+https://github.com/rust-lang/crates.io-index\"\n+checksum = \"57b6a275aa2903740dc87da01c62040406b8812552e97129a63ea8850a17c6e6\"\n+dependencies = [\n+ \"shlex\",\n+]\n+\n+[[package]]\n+name = \"libc\"\n+version = \"0.2.158\"\n+source = \"registry+https://github.com/rust-lang/crates.io-index\"\n+checksum = \"d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439\"\n+\n+[[package]]\n+name = \"libgit\"\n+version = \"0.1.0\"\n+dependencies = [\n+ \"autocfg\",\n+ \"libgit-sys\",\n+]\n+\n+[[package]]\n+name = \"libgit-sys\"\n+version = \"0.1.0\"\n+dependencies = [\n+ \"autocfg\",\n+ \"libz-sys\",\n+ \"make-cmd\",\n+]\n+\n+[[package]]\n+name = \"libz-sys\"\n+version = \"1.1.20\"\n+source = \"registry+https://github.com/rust-lang/crates.io-index\"\n+checksum = \"d2d16453e800a8cf6dd2fc3eb4bc99b786a9b90c663b8559a5b1a041bf89e472\"\n+dependencies = [\n+ \"cc\",\n+ \"libc\",\n+ \"pkg-config\",\n+ \"vcpkg\",\n+]\n+\n+[[package]]\n+name = \"make-cmd\"\n+version = \"0.1.0\"\n+source = \"registry+https://github.com/rust-lang/crates.io-index\"\n+checksum = \"a8ca8afbe8af1785e09636acb5a41e08a765f5f0340568716c18a8700ba3c0d3\"\n+\n+[[package]]\n+name = \"pkg-config\"\n+version = \"0.3.30\"\n+source = \"registry+https://github.com/rust-lang/crates.io-index\"\n+checksum = \"d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec\"\n+\n+[[package]]\n+name = \"shlex\"\n+version = \"1.3.0\"\n+source = \"registry+https://github.com/rust-lang/crates.io-index\"\n+checksum = \"0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64\"\n+\n+[[package]]\n+name = \"vcpkg\"\n+version = \"0.2.15\"\n+source = \"registry+https://github.com/rust-lang/crates.io-index\"\n+checksum = \"accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426\"\ndiff --git a/contrib/libgit-rs/Cargo.toml b/contrib/libgit-rs/Cargo.toml\nnew file mode 100644\nindex 0000000000..c3289e69db\n--- /dev/null\n+++ b/contrib/libgit-rs/Cargo.toml\n@@ -0,0 +1,17 @@\n+[package]\n+name = \"libgit\"\n+version = \"0.1.0\"\n+edition = \"2021\"\n+build = \"build.rs\"\n+rust-version = \"1.63\" # TODO: Once we hit 1.84 or newer, we may want to remove Cargo.lock from\n+                      # version control. See https://lore.kernel.org/git/Z47jgK-oMjFRSslr@tapette.crustytoothpaste.net/\n+\n+\n+[lib]\n+path = \"src/lib.rs\"\n+\n+[dependencies]\n+libgit-sys = { version = \"0.1.0\", path = \"../libgit-sys\" }\n+\n+[build-dependencies]\n+autocfg = \"1.4.0\"\ndiff --git a/contrib/libgit-rs/README.md b/contrib/libgit-rs/README.md\nnew file mode 100644\nindex 0000000000..ff945e1ce2\n--- /dev/null\n+++ b/contrib/libgit-rs/README.md\n@@ -0,0 +1,13 @@\n+# libgit-rs\n+\n+Proof-of-concept Git bindings for Rust.\n+\n+```toml\n+[dependencies]\n+libgit = \"0.1.0\"\n+```\n+\n+## Rust version requirements\n+\n+libgit-rs should support Rust versions at least as old as the version included\n+in Debian stable (currently 1.63).\ndiff --git a/contrib/libgit-rs/build.rs b/contrib/libgit-rs/build.rs\nnew file mode 100644\nindex 0000000000..f8bd01a690\n--- /dev/null\n+++ b/contrib/libgit-rs/build.rs\n@@ -0,0 +1,4 @@\n+pub fn main() {\n+    let ac = autocfg::new();\n+    ac.emit_has_path(\"std::ffi::c_char\");\n+}\ndiff --git a/contrib/libgit-rs/src/config.rs b/contrib/libgit-rs/src/config.rs\nnew file mode 100644\nindex 0000000000..6bf04845c8\n--- /dev/null\n+++ b/contrib/libgit-rs/src/config.rs\n@@ -0,0 +1,106 @@\n+use std::ffi::{c_void, CStr, CString};\n+use std::path::Path;\n+\n+#[cfg(has_std__ffi__c_char)]\n+use std::ffi::{c_char, c_int};\n+\n+#[cfg(not(has_std__ffi__c_char))]\n+#[allow(non_camel_case_types)]\n+type c_char = i8;\n+\n+#[cfg(not(has_std__ffi__c_char))]\n+#[allow(non_camel_case_types)]\n+type c_int = i32;\n+\n+use libgit_sys::*;\n+\n+/// A ConfigSet is an in-memory cache for config-like files such as `.gitmodules` or `.gitconfig`.\n+/// It does not support all config directives; notably, it will not process `include` or\n+/// `includeIf` directives (but it will store them so that callers can choose whether and how to\n+/// handle them).\n+pub struct ConfigSet(*mut libgit_config_set);\n+impl ConfigSet {\n+    /// Allocate a new ConfigSet\n+    pub fn new() -> Self {\n+        unsafe { ConfigSet(libgit_configset_alloc()) }\n+    }\n+\n+    /// Load the given files into the ConfigSet; conflicting directives in later files will\n+    /// override those given in earlier files.\n+    pub fn add_files(&mut self, files: &[&Path]) {\n+        for file in files {\n+            let pstr = file.to_str().expect(\"Invalid UTF-8\");\n+            let rs = CString::new(pstr).expect(\"Couldn't convert to CString\");\n+            unsafe {\n+                libgit_configset_add_file(self.0, rs.as_ptr());\n+            }\n+        }\n+    }\n+\n+    /// Load the value for the given key and attempt to parse it as an i32. Dies with a fatal error\n+    /// if the value cannot be parsed. Returns None if the key is not present.\n+    pub fn get_int(&mut self, key: &str) -> Option<i32> {\n+        let key = CString::new(key).expect(\"Couldn't convert to CString\");\n+        let mut val: c_int = 0;\n+        unsafe {\n+            if libgit_configset_get_int(self.0, key.as_ptr(), &mut val as *mut c_int) != 0 {\n+                return None;\n+            }\n+        }\n+\n+        Some(val.into())\n+    }\n+\n+    /// Clones the value for the given key. Dies with a fatal error if the value cannot be\n+    /// converted to a String. Returns None if the key is not present.\n+    pub fn get_string(&mut self, key: &str) -> Option<String> {\n+        let key = CString::new(key).expect(\"Couldn't convert key to CString\");\n+        let mut val: *mut c_char = std::ptr::null_mut();\n+        unsafe {\n+            if libgit_configset_get_string(self.0, key.as_ptr(), &mut val as *mut *mut c_char) != 0\n+            {\n+                return None;\n+            }\n+            let borrowed_str = CStr::from_ptr(val);\n+            let owned_str =\n+                String::from(borrowed_str.to_str().expect(\"Couldn't convert val to str\"));\n+            free(val as *mut c_void); // Free the xstrdup()ed pointer from the C side\n+            Some(owned_str)\n+        }\n+    }\n+}\n+\n+impl Default for ConfigSet {\n+    fn default() -> Self {\n+        Self::new()\n+    }\n+}\n+\n+impl Drop for ConfigSet {\n+    fn drop(&mut self) {\n+        unsafe {\n+            libgit_configset_free(self.0);\n+        }\n+    }\n+}\n+\n+#[cfg(test)]\n+mod tests {\n+    use super::*;\n+\n+    #[test]\n+    fn load_configs_via_configset() {\n+        let mut cs = ConfigSet::new();\n+        cs.add_files(&[\n+            Path::new(\"testdata/config1\"),\n+            Path::new(\"testdata/config2\"),\n+            Path::new(\"testdata/config3\"),\n+        ]);\n+        // ConfigSet retrieves correct value\n+        assert_eq!(cs.get_int(\"trace2.eventTarget\"), Some(1));\n+        // ConfigSet respects last config value set\n+        assert_eq!(cs.get_int(\"trace2.eventNesting\"), Some(3));\n+        // ConfigSet returns None for missing key\n+        assert_eq!(cs.get_string(\"foo.bar\"), None);\n+    }\n+}\ndiff --git a/contrib/libgit-rs/src/lib.rs b/contrib/libgit-rs/src/lib.rs\nnew file mode 100644\nindex 0000000000..ef68c36943\n--- /dev/null\n+++ b/contrib/libgit-rs/src/lib.rs\n@@ -0,0 +1 @@\n+pub mod config;\ndiff --git a/contrib/libgit-rs/testdata/config1 b/contrib/libgit-rs/testdata/config1\nnew file mode 100644\nindex 0000000000..4e9a9d25d1\n--- /dev/null\n+++ b/contrib/libgit-rs/testdata/config1\n@@ -0,0 +1,2 @@\n+[trace2]\n+\teventNesting = 1\ndiff --git a/contrib/libgit-rs/testdata/config2 b/contrib/libgit-rs/testdata/config2\nnew file mode 100644\nindex 0000000000..b8d1eca423\n--- /dev/null\n+++ b/contrib/libgit-rs/testdata/config2\n@@ -0,0 +1,2 @@\n+[trace2]\n+\teventTarget = 1\ndiff --git a/contrib/libgit-rs/testdata/config3 b/contrib/libgit-rs/testdata/config3\nnew file mode 100644\nindex 0000000000..ca7b9a7c38\n--- /dev/null\n+++ b/contrib/libgit-rs/testdata/config3\n@@ -0,0 +1,2 @@\n+[trace2]\n+\teventNesting = 3\ndiff --git a/contrib/libgit-sys/src/lib.rs b/contrib/libgit-sys/src/lib.rs\nindex dadb4e5f40..4bfc650450 100644\n--- a/contrib/libgit-sys/src/lib.rs\n+++ b/contrib/libgit-sys/src/lib.rs\n@@ -1,3 +1,5 @@\n+use std::ffi::c_void;\n+\n #[cfg(has_std__ffi__c_char)]\n use std::ffi::{c_char, c_int};\n \n@@ -19,6 +21,8 @@ pub struct libgit_config_set {\n }\n \n extern \"C\" {\n+    pub fn free(ptr: *mut c_void);\n+\n     pub fn libgit_user_agent() -> *const c_char;\n     pub fn libgit_user_agent_sanitized() -> *const c_char;\n \ndiff --git a/t/Makefile b/t/Makefile\nindex 53ba01c21b..2994eb5fa9 100644\n--- a/t/Makefile\n+++ b/t/Makefile\n@@ -178,12 +178,17 @@ perf:\n .PHONY: pre-clean $(T) aggregate-results clean valgrind perf \\\n \tcheck-chainlint clean-chainlint test-chainlint $(UNIT_TESTS)\n \n-.PHONY: libgit-sys-test\n+.PHONY: libgit-sys-test libgit-rs-test\n libgit-sys-test:\n \t$(QUIET)(\\\n \t\tcd ../contrib/libgit-sys && \\\n \t\tcargo test \\\n \t)\n+libgit-rs-test:\n+\t$(QUIET)(\\\n+\t\tcd ../contrib/libgit-rs && \\\n+\t\tcargo test \\\n+\t)\n ifdef INCLUDE_LIBGIT_RS\n-all:: libgit-sys-test\n+all:: libgit-sys-test libgit-rs-test\n endif\n-- \n2.48.1.262.g85cc9f2d1e-goog\n\n"},{"id":"511377","messageId":"xmqqcyg6mudv.fsf@gitster.g","threadId":"61917","inReplyTo":"3588a3c3fc76fd2a98b89e837dae07fd97fb88af.1738101256.git.steadmon@google.com","subject":"Re: [PATCH v8 2/4] libgit-sys: introduce Rust wrapper for libgit.a","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-01-28T22:43:56Z","receivedAt":"2025-01-28T22:43:59Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Josh Steadmon <steadmon@google.com> writes:\n\n> diff --git a/Makefile b/Makefile\n> index 27e68ac039..f2c08df4cb 100644\n> --- a/Makefile\n> +++ b/Makefile\n> @@ -416,6 +416,9 @@ include shared.mak\n>  # Define LINK_FUZZ_PROGRAMS if you want `make all` to also build the fuzz test\n>  # programs in oss-fuzz/.\n>  #\n> +# Define INCLUDE_LIBGIT_RS if you want `make all` and `make test` to build and\n> +# test the Rust crate in contrib/libgit-sys.\n> +#\n>  # === Optional library: libintl ===\n>  #\n>  # Define NO_GETTEXT if you don't want Git output to be translated.\n> @@ -657,6 +660,8 @@ CURL_CONFIG = curl-config\n>  GCOV = gcov\n>  STRIP = strip\n>  SPATCH = spatch\n> +LD = ld\n> +OBJCOPY = objcopy\n>  \n>  export TCL_PATH TCLTK_PATH\n\nGood.\n\n> +LIBGIT_PUB_OBJS = contrib/libgit-sys/public_symbol_export.o\n> +LIBGIT_PUB_OBJS += libgit.a\n> +LIBGIT_PUB_OBJS += reftable/libreftable.a\n> +LIBGIT_PUB_OBJS += xdiff/lib.a\n\nThis is a fairly minor point, but if we look at the places where\nOBJECTS, PROGRAMS, and friends are prepared in the Makefile, they\nall start with empty, i.e.\n\n\tLIBGIT_PUB_OBJS =\n\nat a fairly early place in the file, and then appends real contents\nto the symbol with += assignment.  It probably makes sense to follow\nsuit, although the result would be the same.\n\nOther than that, nothing jumped at me as fishy.  Nicely done.\n\nThanks.  Will replace.\n\n"},{"id":"511413","messageId":"6ed6cf83-070c-4be2-b58d-398006cbb072@gmail.com","threadId":"61917","inReplyTo":"cover.1738101256.git.steadmon@google.com","subject":"Re: [PATCH v8 0/4] Introduce libgit-rs, a Rust wrapper around libgit.a","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2025-01-29T15:24:05Z","receivedAt":"2025-01-29T15:24:16Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Josh\n\nOn 28/01/2025 22:01, Josh Steadmon wrote:\n\nThanks for re-rolling, the range-diff looks good to me apart from\n\n>       +void libgit_configset_free(struct libgit_config_set *cs)\n>       +{\n>      -+\tgit_configset_clear((struct config_set *) cs);\n>      -+\tfree((struct config_set *) cs);\n>      ++\tgit_configset_clear(&cs->cs);\n>      ++\tfree(&cs->cs);\n\nWhich I think should be \"free(cs)\". In practice it does not matter \nbecause we pass the same value to free() but it seems a bit odd to pass \nthe address of the first member of the struct rather than the address of \nthe struct itself.\n\nI'm looking forward to seeing this merged soon\n\nBest Wishes\n\nPhillip\n\n"},{"id":"511435","messageId":"xie3u2fhhkuhwezhi23ivs2wuzkkxt2drnq6jvcjb7432hqiol@du4wavlj3imv","threadId":"61917","inReplyTo":"xmqqcyg6mudv.fsf@gitster.g","subject":"Re: [PATCH v8 2/4] libgit-sys: introduce Rust wrapper for libgit.a","fromName":"Josh Steadmon","fromEmail":"steadmon@google.com","sentAt":"2025-01-29T21:40:46Z","receivedAt":"2025-01-29T21:40:52Z","isPatch":true,"sender":{"key":"steadmon@google.com","avatar":"https://avatars.githubusercontent.com/u/2654920?v=4"},"body":"On 2025.01.28 14:43, Junio C Hamano wrote:\n> Josh Steadmon <steadmon@google.com> writes:\n> \n> > diff --git a/Makefile b/Makefile\n> > index 27e68ac039..f2c08df4cb 100644\n> > --- a/Makefile\n> > +++ b/Makefile\n> > @@ -416,6 +416,9 @@ include shared.mak\n> >  # Define LINK_FUZZ_PROGRAMS if you want `make all` to also build the fuzz test\n> >  # programs in oss-fuzz/.\n> >  #\n> > +# Define INCLUDE_LIBGIT_RS if you want `make all` and `make test` to build and\n> > +# test the Rust crate in contrib/libgit-sys.\n> > +#\n> >  # === Optional library: libintl ===\n> >  #\n> >  # Define NO_GETTEXT if you don't want Git output to be translated.\n> > @@ -657,6 +660,8 @@ CURL_CONFIG = curl-config\n> >  GCOV = gcov\n> >  STRIP = strip\n> >  SPATCH = spatch\n> > +LD = ld\n> > +OBJCOPY = objcopy\n> >  \n> >  export TCL_PATH TCLTK_PATH\n> \n> Good.\n> \n> > +LIBGIT_PUB_OBJS = contrib/libgit-sys/public_symbol_export.o\n> > +LIBGIT_PUB_OBJS += libgit.a\n> > +LIBGIT_PUB_OBJS += reftable/libreftable.a\n> > +LIBGIT_PUB_OBJS += xdiff/lib.a\n> \n> This is a fairly minor point, but if we look at the places where\n> OBJECTS, PROGRAMS, and friends are prepared in the Makefile, they\n> all start with empty, i.e.\n> \n> \tLIBGIT_PUB_OBJS =\n> \n> at a fairly early place in the file, and then appends real contents\n> to the symbol with += assignment.  It probably makes sense to follow\n> suit, although the result would be the same.\n> \n> Other than that, nothing jumped at me as fishy.  Nicely done.\n> \n> Thanks.  Will replace.\n\nAck, will send a V9 soon with this & a fix for Phillip's feedback.\nThanks.\n\n"},{"id":"511436","messageId":"5g5wvfoyi7avilbex54m77ds3llwymgawk35o67szrzeffgctm@dvg6vl4a25my","threadId":"61917","inReplyTo":"6ed6cf83-070c-4be2-b58d-398006cbb072@gmail.com","subject":"Re: [PATCH v8 0/4] Introduce libgit-rs, a Rust wrapper around libgit.a","fromName":"Josh Steadmon","fromEmail":"steadmon@google.com","sentAt":"2025-01-29T21:42:44Z","receivedAt":"2025-01-29T21:42:50Z","isPatch":true,"sender":{"key":"steadmon@google.com","avatar":"https://avatars.githubusercontent.com/u/2654920?v=4"},"body":"On 2025.01.29 15:24, Phillip Wood wrote:\n> Hi Josh\n> \n> On 28/01/2025 22:01, Josh Steadmon wrote:\n> \n> Thanks for re-rolling, the range-diff looks good to me apart from\n> \n> >       +void libgit_configset_free(struct libgit_config_set *cs)\n> >       +{\n> >      -+\tgit_configset_clear((struct config_set *) cs);\n> >      -+\tfree((struct config_set *) cs);\n> >      ++\tgit_configset_clear(&cs->cs);\n> >      ++\tfree(&cs->cs);\n> \n> Which I think should be \"free(cs)\". In practice it does not matter because\n> we pass the same value to free() but it seems a bit odd to pass the address\n> of the first member of the struct rather than the address of the struct\n> itself.\n\nYep sorry, got a bit careless with search-and-replace. Thanks for the\ncatch!\n\n> I'm looking forward to seeing this merged soon\n> \n> Best Wishes\n> \n> Phillip\n> \n"},{"id":"511437","messageId":"cover.1738187176.git.steadmon@google.com","threadId":"61917","inReplyTo":"cover.1723054623.git.steadmon@google.com","subject":"[PATCH v9 0/4] Introduce libgit-rs, a Rust wrapper around libgit.a","fromName":"Josh Steadmon","fromEmail":"steadmon@google.com","sentAt":"2025-01-29T21:50:40Z","receivedAt":"2025-01-29T21:50:47Z","isPatch":true,"sender":{"key":"steadmon@google.com","avatar":"https://avatars.githubusercontent.com/u/2654920?v=4"},"body":"This series provides two small Rust wrapper libraries around parts of\nGit: \"libgit-sys\", which exposes a few functions from libgit.a, and\n\"libgit\", which provides a more Rust-friendly interface to some of those\nfunctions. In addition to included unit tests, at $DAYJOB we have tested\nbuilding JJ[1] with our library and used it to replace some of the\nlibgit2-rs uses.\n\n[1] https://github.com/jj-vcs/jj\n\nThere is known NEEDSWORK, but I feel that they can be addressed in\nfollow-up changes, rather than in this series. If you feel otherwise,\nplease let me know:\n\n* Investigate alternative methods of managing symbol visibility &\n  renaming.\n\n* Figure out symbol versioning\n\nChanges in V9:\n* Properly initialize Makefile var LIBGIT_PUB_OBJS.\n\n* Pass the correct pointer to free in libgit_configset_free().\n\nChanges in V8:\n* Define a private libgit_config_set struct to avoid excessive casting\n  in public_symbol_export.c.\n\n* Style fixes: merge some Makefile rules, limit rule line length by\n  defining intermediate variables, add initial empty comment line, add\n  linebreaks in function definitions.\n\nChanges in V7:\n* Moved the ConfigSet implementation in libgit-rs to a `config` module.\n\n* Added doc comments for ConfigSet and its methods.\n\n* Fix meson builds by adding new object files to `libgit_sources`\n\n* Moved INCLUDE_LIBGIT_RS Makefile changes earlier in the series, so\n  that we can make it optional to compile some of the libgitpub sources.\n  Squashed V6 patch 5/5 into this series' patch 4/4.\n\n* Don't publicly export FFI types in libgit-rs.\n\n* Removed extraneous `-r` argument to $(RM) in the clean rules.\n\n* Added TODO reminder in Cargo.toml about removing Cargo.lock once we\n  hit a certain minimum supported Rust version.\n\n* Style cleanup in public_symbol_export.c\n\nChanges in V6:\n* Rebased onto current master, since V5 was several months old.\n\n* Move libgit-sys out of libgit-rs; while this sort of nesting is common\n  in Rust crates with standalone repositories, it doesn't make as much\n  sense when they're contained in the larger Git project's repo.\n\n* Standardize the naming of some of the Makefile targets to always\n  include a dash in the \"-rs\" or \"-sys\" suffixes.\n\n* Clean up READMEs and crate descriptions in preparation for\n  uploading to crates.io.\n\nChanges in V5:\n* When building with INCLUDE_LIBGIT_RS defined, add\n  \"-fvisibility=hidden\" to CFLAGS. This allows us to manage symbol\n  visibility in libgitpub.a without causing `make all` to rebuild from\n  scratch due to changing CFLAGS.\n\n* Avoid using c_int in the higher-level Rust API.\n\n* Remove libgitpub.a and intermediate files with `make clean`.\n\nChanges in V4:\n* Drop V3 patch #3, which added wrappers around repository\n  initialization and config access. These are not well-libified, and\n  they are not necessary for JJ's proof-of-concept use case, so let's\n  avoid exporting them for now.\n\n* Set a minimum supported Rust version of 1.63. Autodetect whether our\n  Rust version has c_int and c_char types; if not, define them\n  ourselves.\n\n* When building libgitpub.a via build.rs, set DEVELOPER=1 to catch\n  additional errors at build time.\n\n* In build.rs, use the make_cmd crate to portable select the correct\n  invocation of GNU Make.\n\n* Follow naming standards for _alloc() and _free() functions.\n\n* Use String instead of CString in higher-level API.\n\n* Move libgit_configset_alloc() and libgit_configset_free() out of\n  upstream Git, to the libgitpub shim library.\n\n* In libgitpub, initialize libgit_config_set structs in the _alloc()\n  function rather than with a separate _init() function.\n\n* Remove unnecessary comments in libgit-sys showing where the wrapped\n  functions were originally defined.\n\n* Fix clippy lint: don't reborrow configfile path references.\n\n* Various typo fixes and `cargo fmt` fixes.\n\nChanges in V3:\n* Renamed cgit-rs to libgit-rs and cgit-sys to libgit-sys\n\n* Makefile cleanup, particularly adding config.mak options that\n  developers can set to run Rust builds and tests by default (Patch 6)\n\n* Provide testdata configs for unit tests\n\n* ConfigSet API now uses &Path instead of &str -- more ergonomic for\n  Rust users to pass in and errors out if the path string isn't UTF-8\n\n* Fixed unresolved dependency on libz in Cargo.toml\n\n\nCalvin Wan (1):\n  libgit: add higher-level libgit crate\n\nJosh Steadmon (3):\n  common-main: split init and exit code into new files\n  libgit-sys: introduce Rust wrapper for libgit.a\n  libgit-sys: also export some config_set functions\n\n .gitignore                                |   2 +\n Makefile                                  |  50 ++++++++++\n common-exit.c                             |  26 ++++++\n common-init.c                             |  63 +++++++++++++\n common-init.h                             |   6 ++\n common-main.c                             |  83 +----------------\n contrib/libgit-rs/Cargo.lock              |  77 ++++++++++++++++\n contrib/libgit-rs/Cargo.toml              |  17 ++++\n contrib/libgit-rs/README.md               |  13 +++\n contrib/libgit-rs/build.rs                |   4 +\n contrib/libgit-rs/src/config.rs           | 106 ++++++++++++++++++++++\n contrib/libgit-rs/src/lib.rs              |   1 +\n contrib/libgit-rs/testdata/config1        |   2 +\n contrib/libgit-rs/testdata/config2        |   2 +\n contrib/libgit-rs/testdata/config3        |   2 +\n contrib/libgit-sys/Cargo.lock             |  69 ++++++++++++++\n contrib/libgit-sys/Cargo.toml             |  19 ++++\n contrib/libgit-sys/README.md              |   4 +\n contrib/libgit-sys/build.rs               |  35 +++++++\n contrib/libgit-sys/public_symbol_export.c |  59 ++++++++++++\n contrib/libgit-sys/public_symbol_export.h |  18 ++++\n contrib/libgit-sys/src/lib.rs             |  79 ++++++++++++++++\n meson.build                               |   2 +\n t/Makefile                                |  15 +++\n 24 files changed, 673 insertions(+), 81 deletions(-)\n create mode 100644 common-exit.c\n create mode 100644 common-init.c\n create mode 100644 common-init.h\n create mode 100644 contrib/libgit-rs/Cargo.lock\n create mode 100644 contrib/libgit-rs/Cargo.toml\n create mode 100644 contrib/libgit-rs/README.md\n create mode 100644 contrib/libgit-rs/build.rs\n create mode 100644 contrib/libgit-rs/src/config.rs\n create mode 100644 contrib/libgit-rs/src/lib.rs\n create mode 100644 contrib/libgit-rs/testdata/config1\n create mode 100644 contrib/libgit-rs/testdata/config2\n create mode 100644 contrib/libgit-rs/testdata/config3\n create mode 100644 contrib/libgit-sys/Cargo.lock\n create mode 100644 contrib/libgit-sys/Cargo.toml\n create mode 100644 contrib/libgit-sys/README.md\n create mode 100644 contrib/libgit-sys/build.rs\n create mode 100644 contrib/libgit-sys/public_symbol_export.c\n create mode 100644 contrib/libgit-sys/public_symbol_export.h\n create mode 100644 contrib/libgit-sys/src/lib.rs\n\nRange-diff against v8:\n1:  cd0cb9aa04 = 1:  cd0cb9aa04 common-main: split init and exit code into new files\n2:  3588a3c3fc ! 2:  8793ff64a7 libgit-sys: introduce Rust wrapper for libgit.a\n    @@ Makefile: $(CLAR_TEST_PROG): $(UNIT_TEST_DIR)/clar.suite $(CLAR_TEST_OBJS) $(GIT\n     +all:: libgit-sys\n     +endif\n     +\n    -+LIBGIT_PUB_OBJS = contrib/libgit-sys/public_symbol_export.o\n    ++LIBGIT_PUB_OBJS =\n    ++LIBGIT_PUB_OBJS += contrib/libgit-sys/public_symbol_export.o\n     +LIBGIT_PUB_OBJS += libgit.a\n     +LIBGIT_PUB_OBJS += reftable/libreftable.a\n     +LIBGIT_PUB_OBJS += xdiff/lib.a\n3:  f4452fffe6 ! 3:  ab32bd1d07 libgit-sys: also export some config_set functions\n    @@ contrib/libgit-sys/public_symbol_export.c\n     +void libgit_configset_free(struct libgit_config_set *cs)\n     +{\n     +\tgit_configset_clear(&cs->cs);\n    -+\tfree(&cs->cs);\n    ++\tfree(cs);\n     +}\n     +\n     +int libgit_configset_add_file(struct libgit_config_set *cs, const char *filename)\n4:  ada9fc0a13 ! 4:  1bf8c5392c libgit: add higher-level libgit crate\n    @@ Makefile: build-unit-tests: $(UNIT_TEST_PROGS) $(CLAR_TEST_PROG)\n     +all:: libgit-sys libgit-rs\n      endif\n      \n    - LIBGIT_PUB_OBJS = contrib/libgit-sys/public_symbol_export.o\n    + LIBGIT_PUB_OBJS =\n     \n      ## contrib/libgit-rs/Cargo.lock (new) ##\n     @@\n\nbase-commit: 757161efcca150a9a96b312d9e780a071e601a03\n-- \n2.48.1.362.g079036d154-goog\n\n"},{"id":"511438","messageId":"cd0cb9aa046b99d2cd3f95b0a8cc7aaf29c677d5.1738187176.git.steadmon@google.com","threadId":"61917","inReplyTo":"cover.1738187176.git.steadmon@google.com","subject":"[PATCH v9 1/4] common-main: split init and exit code into new files","fromName":"Josh Steadmon","fromEmail":"steadmon@google.com","sentAt":"2025-01-29T21:50:41Z","receivedAt":"2025-01-29T21:50:49Z","isPatch":true,"sender":{"key":"steadmon@google.com","avatar":"https://avatars.githubusercontent.com/u/2654920?v=4"},"body":"Currently, object files in libgit.a reference common_exit(), which is\ncontained in common-main.o. However, common-main.o also includes main(),\nwhich references cmd_main() in git.o, which in turn depends on all the\nbuiltin/*.o objects.\n\nWe would like to allow external users to link libgit.a without needing\nto include so many extra objects. Enable this by splitting common_exit()\nand check_bug_if_BUG() into a new file common-exit.c, and add\ncommon-exit.o to LIB_OBJS so that these are included in libgit.a.\n\nThis split has previously been proposed ([1], [2]) to support fuzz tests\nand unit tests by avoiding conflicting definitions for main(). However,\nboth of those issues were resolved by other methods of avoiding symbol\nconflicts. Now we are trying to make libgit.a more self-contained, so\nhopefully we can revisit this approach.\n\nAdditionally, move the initialization code out of main() into a new\ninit_git() function in its own file. Include this in libgit.a as well,\nso that external users can share our setup code without calling our\nmain().\n\n[1] https://lore.kernel.org/git/Yp+wjCPhqieTku3X@google.com/\n[2] https://lore.kernel.org/git/20230517-unit-tests-v2-v2-1-21b5b60f4b32@google.com/\n\nSigned-off-by: Josh Steadmon <steadmon@google.com>\n---\n Makefile      |  2 ++\n common-exit.c | 26 ++++++++++++++++\n common-init.c | 63 ++++++++++++++++++++++++++++++++++++++\n common-init.h |  6 ++++\n common-main.c | 83 ++-------------------------------------------------\n meson.build   |  2 ++\n 6 files changed, 101 insertions(+), 81 deletions(-)\n create mode 100644 common-exit.c\n create mode 100644 common-init.c\n create mode 100644 common-init.h\n\ndiff --git a/Makefile b/Makefile\nindex 97e8385b66..27e68ac039 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -981,6 +981,8 @@ LIB_OBJS += combine-diff.o\n LIB_OBJS += commit-graph.o\n LIB_OBJS += commit-reach.o\n LIB_OBJS += commit.o\n+LIB_OBJS += common-exit.o\n+LIB_OBJS += common-init.o\n LIB_OBJS += compat/nonblock.o\n LIB_OBJS += compat/obstack.o\n LIB_OBJS += compat/terminal.o\ndiff --git a/common-exit.c b/common-exit.c\nnew file mode 100644\nindex 0000000000..1aaa538be3\n--- /dev/null\n+++ b/common-exit.c\n@@ -0,0 +1,26 @@\n+#include \"git-compat-util.h\"\n+#include \"trace2.h\"\n+\n+static void check_bug_if_BUG(void)\n+{\n+\tif (!bug_called_must_BUG)\n+\t\treturn;\n+\tBUG(\"on exit(): had bug() call(s) in this process without explicit BUG_if_bug()\");\n+}\n+\n+/* We wrap exit() to call common_exit() in git-compat-util.h */\n+int common_exit(const char *file, int line, int code)\n+{\n+\t/*\n+\t * For non-POSIX systems: Take the lowest 8 bits of the \"code\"\n+\t * to e.g. turn -1 into 255. On a POSIX system this is\n+\t * redundant, see exit(3) and wait(2), but as it doesn't harm\n+\t * anything there we don't need to guard this with an \"ifdef\".\n+\t */\n+\tcode &= 0xff;\n+\n+\tcheck_bug_if_BUG();\n+\ttrace2_cmd_exit_fl(file, line, code);\n+\n+\treturn code;\n+}\ndiff --git a/common-init.c b/common-init.c\nnew file mode 100644\nindex 0000000000..5cc73f058c\n--- /dev/null\n+++ b/common-init.c\n@@ -0,0 +1,63 @@\n+#define USE_THE_REPOSITORY_VARIABLE\n+\n+#include \"git-compat-util.h\"\n+#include \"common-init.h\"\n+#include \"exec-cmd.h\"\n+#include \"gettext.h\"\n+#include \"attr.h\"\n+#include \"repository.h\"\n+#include \"setup.h\"\n+#include \"strbuf.h\"\n+#include \"trace2.h\"\n+\n+/*\n+ * Many parts of Git have subprograms communicate via pipe, expect the\n+ * upstream of a pipe to die with SIGPIPE when the downstream of a\n+ * pipe does not need to read all that is written.  Some third-party\n+ * programs that ignore or block SIGPIPE for their own reason forget\n+ * to restore SIGPIPE handling to the default before spawning Git and\n+ * break this carefully orchestrated machinery.\n+ *\n+ * Restore the way SIGPIPE is handled to default, which is what we\n+ * expect.\n+ */\n+static void restore_sigpipe_to_default(void)\n+{\n+\tsigset_t unblock;\n+\n+\tsigemptyset(&unblock);\n+\tsigaddset(&unblock, SIGPIPE);\n+\tsigprocmask(SIG_UNBLOCK, &unblock, NULL);\n+\tsignal(SIGPIPE, SIG_DFL);\n+}\n+\n+void init_git(const char **argv)\n+{\n+\tstruct strbuf tmp = STRBUF_INIT;\n+\n+\ttrace2_initialize_clock();\n+\n+\t/*\n+\t * Always open file descriptors 0/1/2 to avoid clobbering files\n+\t * in die().  It also avoids messing up when the pipes are dup'ed\n+\t * onto stdin/stdout/stderr in the child processes we spawn.\n+\t */\n+\tsanitize_stdfds();\n+\trestore_sigpipe_to_default();\n+\n+\tgit_resolve_executable_dir(argv[0]);\n+\n+\tsetlocale(LC_CTYPE, \"\");\n+\tgit_setup_gettext();\n+\n+\tinitialize_repository(the_repository);\n+\n+\tattr_start();\n+\n+\ttrace2_initialize();\n+\ttrace2_cmd_start(argv);\n+\ttrace2_collect_process_info(TRACE2_PROCESS_INFO_STARTUP);\n+\n+\tif (!strbuf_getcwd(&tmp))\n+\t\ttmp_original_cwd = strbuf_detach(&tmp, NULL);\n+}\ndiff --git a/common-init.h b/common-init.h\nnew file mode 100644\nindex 0000000000..3e6db20cae\n--- /dev/null\n+++ b/common-init.h\n@@ -0,0 +1,6 @@\n+#ifndef COMMON_INIT_H\n+#define COMMON_INIT_H\n+\n+void init_git(const char **argv);\n+\n+#endif /* COMMON_INIT_H */\ndiff --git a/common-main.c b/common-main.c\nindex 8e68ac9e42..6b7ab077b0 100644\n--- a/common-main.c\n+++ b/common-main.c\n@@ -1,92 +1,13 @@\n-#define USE_THE_REPOSITORY_VARIABLE\n-\n #include \"git-compat-util.h\"\n-#include \"exec-cmd.h\"\n-#include \"gettext.h\"\n-#include \"attr.h\"\n-#include \"repository.h\"\n-#include \"setup.h\"\n-#include \"strbuf.h\"\n-#include \"trace2.h\"\n-\n-/*\n- * Many parts of Git have subprograms communicate via pipe, expect the\n- * upstream of a pipe to die with SIGPIPE when the downstream of a\n- * pipe does not need to read all that is written.  Some third-party\n- * programs that ignore or block SIGPIPE for their own reason forget\n- * to restore SIGPIPE handling to the default before spawning Git and\n- * break this carefully orchestrated machinery.\n- *\n- * Restore the way SIGPIPE is handled to default, which is what we\n- * expect.\n- */\n-static void restore_sigpipe_to_default(void)\n-{\n-\tsigset_t unblock;\n-\n-\tsigemptyset(&unblock);\n-\tsigaddset(&unblock, SIGPIPE);\n-\tsigprocmask(SIG_UNBLOCK, &unblock, NULL);\n-\tsignal(SIGPIPE, SIG_DFL);\n-}\n+#include \"common-init.h\"\n \n int main(int argc, const char **argv)\n {\n \tint result;\n-\tstruct strbuf tmp = STRBUF_INIT;\n-\n-\ttrace2_initialize_clock();\n-\n-\t/*\n-\t * Always open file descriptors 0/1/2 to avoid clobbering files\n-\t * in die().  It also avoids messing up when the pipes are dup'ed\n-\t * onto stdin/stdout/stderr in the child processes we spawn.\n-\t */\n-\tsanitize_stdfds();\n-\trestore_sigpipe_to_default();\n-\n-\tgit_resolve_executable_dir(argv[0]);\n-\n-\tsetlocale(LC_CTYPE, \"\");\n-\tgit_setup_gettext();\n-\n-\tinitialize_repository(the_repository);\n-\n-\tattr_start();\n-\n-\ttrace2_initialize();\n-\ttrace2_cmd_start(argv);\n-\ttrace2_collect_process_info(TRACE2_PROCESS_INFO_STARTUP);\n-\n-\tif (!strbuf_getcwd(&tmp))\n-\t\ttmp_original_cwd = strbuf_detach(&tmp, NULL);\n \n+\tinit_git(argv);\n \tresult = cmd_main(argc, argv);\n \n \t/* Not exit(3), but a wrapper calling our common_exit() */\n \texit(result);\n }\n-\n-static void check_bug_if_BUG(void)\n-{\n-\tif (!bug_called_must_BUG)\n-\t\treturn;\n-\tBUG(\"on exit(): had bug() call(s) in this process without explicit BUG_if_bug()\");\n-}\n-\n-/* We wrap exit() to call common_exit() in git-compat-util.h */\n-int common_exit(const char *file, int line, int code)\n-{\n-\t/*\n-\t * For non-POSIX systems: Take the lowest 8 bits of the \"code\"\n-\t * to e.g. turn -1 into 255. On a POSIX system this is\n-\t * redundant, see exit(3) and wait(2), but as it doesn't harm\n-\t * anything there we don't need to guard this with an \"ifdef\".\n-\t */\n-\tcode &= 0xff;\n-\n-\tcheck_bug_if_BUG();\n-\ttrace2_cmd_exit_fl(file, line, code);\n-\n-\treturn code;\n-}\ndiff --git a/meson.build b/meson.build\nindex 0064eb64f5..e5ba28b47f 100644\n--- a/meson.build\n+++ b/meson.build\n@@ -245,6 +245,8 @@ libgit_sources = [\n   'commit-graph.c',\n   'commit-reach.c',\n   'commit.c',\n+  'common-exit.c',\n+  'common-init.c',\n   'compat/nonblock.c',\n   'compat/obstack.c',\n   'compat/terminal.c',\n-- \n2.48.1.362.g079036d154-goog\n\n"},{"id":"511439","messageId":"8793ff64a7f6c4c04dd03b71162a85849feda944.1738187176.git.steadmon@google.com","threadId":"61917","inReplyTo":"cover.1738187176.git.steadmon@google.com","subject":"[PATCH v9 2/4] libgit-sys: introduce Rust wrapper for libgit.a","fromName":"Josh Steadmon","fromEmail":"steadmon@google.com","sentAt":"2025-01-29T21:50:42Z","receivedAt":"2025-01-29T21:50:50Z","isPatch":true,"sender":{"key":"steadmon@google.com","avatar":"https://avatars.githubusercontent.com/u/2654920?v=4"},"body":"Introduce libgit-sys, a Rust wrapper crate that allows Rust code to call\nfunctions in libgit.a. This initial patch defines build rules and an\ninterface that exposes user agent string getter functions as a proof of\nconcept. This library can be tested with `cargo test`. In later commits,\na higher-level library containing a more Rust-friendly interface will be\nadded at `contrib/libgit-rs`.\n\nSymbols in libgit can collide with symbols from other libraries such as\nlibgit2. We avoid this by first exposing library symbols in\npublic_symbol_export.[ch]. These symbols are prepended with \"libgit_\" to\navoid collisions and set to visible using a visibility pragma. In\nbuild.rs, Rust builds contrib/libgit-rs/libgit-sys/libgitpub.a, which also\ncontains libgit.a and other dependent libraries, with\n-fvisibility=hidden to hide all symbols within those libraries that\nhaven't been exposed with a visibility pragma.\n\nCo-authored-by: Kyle Lippincott <spectral@google.com>\nCo-authored-by: Calvin Wan <calvinwan@google.com>\nSigned-off-by: Calvin Wan <calvinwan@google.com>\nSigned-off-by: Kyle Lippincott <spectral@google.com>\nSigned-off-by: Josh Steadmon <steadmon@google.com>\n---\n .gitignore                                |  1 +\n Makefile                                  | 48 ++++++++++++++++\n contrib/libgit-sys/Cargo.lock             | 69 +++++++++++++++++++++++\n contrib/libgit-sys/Cargo.toml             | 19 +++++++\n contrib/libgit-sys/README.md              |  4 ++\n contrib/libgit-sys/build.rs               | 35 ++++++++++++\n contrib/libgit-sys/public_symbol_export.c | 23 ++++++++\n contrib/libgit-sys/public_symbol_export.h |  8 +++\n contrib/libgit-sys/src/lib.rs             | 46 +++++++++++++++\n t/Makefile                                | 10 ++++\n 10 files changed, 263 insertions(+)\n create mode 100644 contrib/libgit-sys/Cargo.lock\n create mode 100644 contrib/libgit-sys/Cargo.toml\n create mode 100644 contrib/libgit-sys/README.md\n create mode 100644 contrib/libgit-sys/build.rs\n create mode 100644 contrib/libgit-sys/public_symbol_export.c\n create mode 100644 contrib/libgit-sys/public_symbol_export.h\n create mode 100644 contrib/libgit-sys/src/lib.rs\n\ndiff --git a/.gitignore b/.gitignore\nindex e82aa19df0..31d7e64287 100644\n--- a/.gitignore\n+++ b/.gitignore\n@@ -250,3 +250,4 @@ Release/\n /git.VC.db\n *.dSYM\n /contrib/buildsystems/out\n+/contrib/libgit-sys/target\ndiff --git a/Makefile b/Makefile\nindex 27e68ac039..a772bff186 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -416,6 +416,9 @@ include shared.mak\n # Define LINK_FUZZ_PROGRAMS if you want `make all` to also build the fuzz test\n # programs in oss-fuzz/.\n #\n+# Define INCLUDE_LIBGIT_RS if you want `make all` and `make test` to build and\n+# test the Rust crate in contrib/libgit-sys.\n+#\n # === Optional library: libintl ===\n #\n # Define NO_GETTEXT if you don't want Git output to be translated.\n@@ -657,6 +660,8 @@ CURL_CONFIG = curl-config\n GCOV = gcov\n STRIP = strip\n SPATCH = spatch\n+LD = ld\n+OBJCOPY = objcopy\n \n export TCL_PATH TCLTK_PATH\n \n@@ -2236,6 +2241,12 @@ ifdef FSMONITOR_OS_SETTINGS\n \tCOMPAT_OBJS += compat/fsmonitor/fsm-path-utils-$(FSMONITOR_OS_SETTINGS).o\n endif\n \n+ifdef INCLUDE_LIBGIT_RS\n+\t# Enable symbol hiding in contrib/libgit-sys/libgitpub.a without making\n+\t# us rebuild the whole tree every time we run a Rust build.\n+\tBASIC_CFLAGS += -fvisibility=hidden\n+endif\n+\n ifeq ($(TCLTK_PATH),)\n NO_TCLTK = NoThanks\n endif\n@@ -2732,6 +2743,10 @@ OBJECTS += $(UNIT_TEST_OBJS)\n OBJECTS += $(CLAR_TEST_OBJS)\n OBJECTS += $(patsubst %,$(UNIT_TEST_DIR)/%.o,$(UNIT_TEST_PROGRAMS))\n \n+ifdef INCLUDE_LIBGIT_RS\n+\tOBJECTS += contrib/libgit-sys/public_symbol_export.o\n+endif\n+\n ifndef NO_CURL\n \tOBJECTS += http.o http-walker.o remote-curl.o\n endif\n@@ -3726,6 +3741,10 @@ clean: profile-clean coverage-clean cocciclean\n \t$(RM) $(htmldocs).tar.gz $(manpages).tar.gz\n \t$(MAKE) -C Documentation/ clean\n \t$(RM) Documentation/GIT-EXCLUDED-PROGRAMS\n+\t$(RM) -r contrib/libgit-sys/target\n+\t$(RM) contrib/libgit-sys/partial_symbol_export.o\n+\t$(RM) contrib/libgit-sys/hidden_symbol_export.o\n+\t$(RM) contrib/libgit-sys/libgitpub.a\n ifndef NO_PERL\n \t$(RM) -r perl/build/\n endif\n@@ -3887,3 +3906,32 @@ $(CLAR_TEST_PROG): $(UNIT_TEST_DIR)/clar.suite $(CLAR_TEST_OBJS) $(GITLIBS) GIT-\n build-unit-tests: $(UNIT_TEST_PROGS) $(CLAR_TEST_PROG)\n unit-tests: $(UNIT_TEST_PROGS) $(CLAR_TEST_PROG) t/helper/test-tool$X\n \t$(MAKE) -C t/ unit-tests\n+\n+.PHONY: libgit-sys\n+libgit-sys:\n+\t$(QUIET)(\\\n+\t\tcd contrib/libgit-sys && \\\n+\t\tcargo build \\\n+\t)\n+ifdef INCLUDE_LIBGIT_RS\n+all:: libgit-sys\n+endif\n+\n+LIBGIT_PUB_OBJS =\n+LIBGIT_PUB_OBJS += contrib/libgit-sys/public_symbol_export.o\n+LIBGIT_PUB_OBJS += libgit.a\n+LIBGIT_PUB_OBJS += reftable/libreftable.a\n+LIBGIT_PUB_OBJS += xdiff/lib.a\n+\n+LIBGIT_PARTIAL_EXPORT = contrib/libgit-sys/partial_symbol_export.o\n+\n+LIBGIT_HIDDEN_EXPORT = contrib/libgit-sys/hidden_symbol_export.o\n+\n+$(LIBGIT_PARTIAL_EXPORT): $(LIBGIT_PUB_OBJS)\n+\t$(LD) -r $^ -o $@\n+\n+$(LIBGIT_HIDDEN_EXPORT): $(LIBGIT_PARTIAL_EXPORT)\n+\t$(OBJCOPY) --localize-hidden $^ $@\n+\n+contrib/libgit-sys/libgitpub.a: $(LIBGIT_HIDDEN_EXPORT)\n+\t$(AR) $(ARFLAGS) $@ $^\ndiff --git a/contrib/libgit-sys/Cargo.lock b/contrib/libgit-sys/Cargo.lock\nnew file mode 100644\nindex 0000000000..427a4c66b7\n--- /dev/null\n+++ b/contrib/libgit-sys/Cargo.lock\n@@ -0,0 +1,69 @@\n+# This file is automatically @generated by Cargo.\n+# It is not intended for manual editing.\n+version = 3\n+\n+[[package]]\n+name = \"autocfg\"\n+version = \"1.4.0\"\n+source = \"registry+https://github.com/rust-lang/crates.io-index\"\n+checksum = \"ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26\"\n+\n+[[package]]\n+name = \"cc\"\n+version = \"1.1.15\"\n+source = \"registry+https://github.com/rust-lang/crates.io-index\"\n+checksum = \"57b6a275aa2903740dc87da01c62040406b8812552e97129a63ea8850a17c6e6\"\n+dependencies = [\n+ \"shlex\",\n+]\n+\n+[[package]]\n+name = \"libc\"\n+version = \"0.2.158\"\n+source = \"registry+https://github.com/rust-lang/crates.io-index\"\n+checksum = \"d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439\"\n+\n+[[package]]\n+name = \"libgit-sys\"\n+version = \"0.1.0\"\n+dependencies = [\n+ \"autocfg\",\n+ \"libz-sys\",\n+ \"make-cmd\",\n+]\n+\n+[[package]]\n+name = \"libz-sys\"\n+version = \"1.1.20\"\n+source = \"registry+https://github.com/rust-lang/crates.io-index\"\n+checksum = \"d2d16453e800a8cf6dd2fc3eb4bc99b786a9b90c663b8559a5b1a041bf89e472\"\n+dependencies = [\n+ \"cc\",\n+ \"libc\",\n+ \"pkg-config\",\n+ \"vcpkg\",\n+]\n+\n+[[package]]\n+name = \"make-cmd\"\n+version = \"0.1.0\"\n+source = \"registry+https://github.com/rust-lang/crates.io-index\"\n+checksum = \"a8ca8afbe8af1785e09636acb5a41e08a765f5f0340568716c18a8700ba3c0d3\"\n+\n+[[package]]\n+name = \"pkg-config\"\n+version = \"0.3.30\"\n+source = \"registry+https://github.com/rust-lang/crates.io-index\"\n+checksum = \"d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec\"\n+\n+[[package]]\n+name = \"shlex\"\n+version = \"1.3.0\"\n+source = \"registry+https://github.com/rust-lang/crates.io-index\"\n+checksum = \"0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64\"\n+\n+[[package]]\n+name = \"vcpkg\"\n+version = \"0.2.15\"\n+source = \"registry+https://github.com/rust-lang/crates.io-index\"\n+checksum = \"accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426\"\ndiff --git a/contrib/libgit-sys/Cargo.toml b/contrib/libgit-sys/Cargo.toml\nnew file mode 100644\nindex 0000000000..e0623022c3\n--- /dev/null\n+++ b/contrib/libgit-sys/Cargo.toml\n@@ -0,0 +1,19 @@\n+[package]\n+name = \"libgit-sys\"\n+version = \"0.1.0\"\n+edition = \"2021\"\n+build = \"build.rs\"\n+links = \"gitpub\"\n+rust-version = \"1.63\" # TODO: Once we hit 1.84 or newer, we may want to remove Cargo.lock from\n+                      # version control. See https://lore.kernel.org/git/Z47jgK-oMjFRSslr@tapette.crustytoothpaste.net/\n+description = \"Native bindings to a portion of libgit\"\n+\n+[lib]\n+path = \"src/lib.rs\"\n+\n+[dependencies]\n+libz-sys = \"1.1.19\"\n+\n+[build-dependencies]\n+autocfg = \"1.4.0\"\n+make-cmd = \"0.1.0\"\ndiff --git a/contrib/libgit-sys/README.md b/contrib/libgit-sys/README.md\nnew file mode 100644\nindex 0000000000..c061cfcaf5\n--- /dev/null\n+++ b/contrib/libgit-sys/README.md\n@@ -0,0 +1,4 @@\n+# libgit-sys\n+\n+A small proof-of-concept crate showing how to provide a Rust FFI to Git\n+internals.\ndiff --git a/contrib/libgit-sys/build.rs b/contrib/libgit-sys/build.rs\nnew file mode 100644\nindex 0000000000..3ffd80ad91\n--- /dev/null\n+++ b/contrib/libgit-sys/build.rs\n@@ -0,0 +1,35 @@\n+use std::env;\n+use std::path::PathBuf;\n+\n+pub fn main() -> std::io::Result<()> {\n+    let ac = autocfg::new();\n+    ac.emit_has_path(\"std::ffi::c_char\");\n+\n+    let crate_root = PathBuf::from(env::var_os(\"CARGO_MANIFEST_DIR\").unwrap());\n+    let git_root = crate_root.join(\"../..\");\n+    let dst = PathBuf::from(env::var_os(\"OUT_DIR\").unwrap());\n+\n+    let make_output = make_cmd::gnu_make()\n+        .env(\"DEVELOPER\", \"1\")\n+        .env_remove(\"PROFILE\")\n+        .current_dir(git_root.clone())\n+        .args([\n+            \"INCLUDE_LIBGIT_RS=YesPlease\",\n+            \"contrib/libgit-sys/libgitpub.a\",\n+        ])\n+        .output()\n+        .expect(\"Make failed to run\");\n+    if !make_output.status.success() {\n+        panic!(\n+            \"Make failed:\\n  stdout = {}\\n  stderr = {}\\n\",\n+            String::from_utf8(make_output.stdout).unwrap(),\n+            String::from_utf8(make_output.stderr).unwrap()\n+        );\n+    }\n+    std::fs::copy(crate_root.join(\"libgitpub.a\"), dst.join(\"libgitpub.a\"))?;\n+    println!(\"cargo:rustc-link-search=native={}\", dst.display());\n+    println!(\"cargo:rustc-link-lib=gitpub\");\n+    println!(\"cargo:rerun-if-changed={}\", git_root.display());\n+\n+    Ok(())\n+}\ndiff --git a/contrib/libgit-sys/public_symbol_export.c b/contrib/libgit-sys/public_symbol_export.c\nnew file mode 100644\nindex 0000000000..4c153c6f0d\n--- /dev/null\n+++ b/contrib/libgit-sys/public_symbol_export.c\n@@ -0,0 +1,23 @@\n+/*\n+ * Shim to publicly export Git symbols. These must be renamed so that the\n+ * original symbols can be hidden. Renaming these with a \"libgit_\" prefix also\n+ * avoids conflicts with other libraries such as libgit2.\n+ */\n+\n+#include \"git-compat-util.h\"\n+#include \"contrib/libgit-sys/public_symbol_export.h\"\n+#include \"version.h\"\n+\n+#pragma GCC visibility push(default)\n+\n+const char *libgit_user_agent(void)\n+{\n+\treturn git_user_agent();\n+}\n+\n+const char *libgit_user_agent_sanitized(void)\n+{\n+\treturn git_user_agent_sanitized();\n+}\n+\n+#pragma GCC visibility pop\ndiff --git a/contrib/libgit-sys/public_symbol_export.h b/contrib/libgit-sys/public_symbol_export.h\nnew file mode 100644\nindex 0000000000..a3372f93fa\n--- /dev/null\n+++ b/contrib/libgit-sys/public_symbol_export.h\n@@ -0,0 +1,8 @@\n+#ifndef PUBLIC_SYMBOL_EXPORT_H\n+#define PUBLIC_SYMBOL_EXPORT_H\n+\n+const char *libgit_user_agent(void);\n+\n+const char *libgit_user_agent_sanitized(void);\n+\n+#endif /* PUBLIC_SYMBOL_EXPORT_H */\ndiff --git a/contrib/libgit-sys/src/lib.rs b/contrib/libgit-sys/src/lib.rs\nnew file mode 100644\nindex 0000000000..d4853f3074\n--- /dev/null\n+++ b/contrib/libgit-sys/src/lib.rs\n@@ -0,0 +1,46 @@\n+#[cfg(has_std__ffi__c_char)]\n+use std::ffi::c_char;\n+\n+#[cfg(not(has_std__ffi__c_char))]\n+#[allow(non_camel_case_types)]\n+pub type c_char = i8;\n+\n+extern crate libz_sys;\n+\n+extern \"C\" {\n+    pub fn libgit_user_agent() -> *const c_char;\n+    pub fn libgit_user_agent_sanitized() -> *const c_char;\n+}\n+\n+#[cfg(test)]\n+mod tests {\n+    use std::ffi::CStr;\n+\n+    use super::*;\n+\n+    #[test]\n+    fn user_agent_starts_with_git() {\n+        let c_str = unsafe { CStr::from_ptr(libgit_user_agent()) };\n+        let agent = c_str\n+            .to_str()\n+            .expect(\"User agent contains invalid UTF-8 data\");\n+        assert!(\n+            agent.starts_with(\"git/\"),\n+            r#\"Expected user agent to start with \"git/\", got: {}\"#,\n+            agent\n+        );\n+    }\n+\n+    #[test]\n+    fn sanitized_user_agent_starts_with_git() {\n+        let c_str = unsafe { CStr::from_ptr(libgit_user_agent_sanitized()) };\n+        let agent = c_str\n+            .to_str()\n+            .expect(\"Sanitized user agent contains invalid UTF-8 data\");\n+        assert!(\n+            agent.starts_with(\"git/\"),\n+            r#\"Expected user agent to start with \"git/\", got: {}\"#,\n+            agent\n+        );\n+    }\n+}\ndiff --git a/t/Makefile b/t/Makefile\nindex daa5fcae86..53ba01c21b 100644\n--- a/t/Makefile\n+++ b/t/Makefile\n@@ -177,3 +177,13 @@ perf:\n \n .PHONY: pre-clean $(T) aggregate-results clean valgrind perf \\\n \tcheck-chainlint clean-chainlint test-chainlint $(UNIT_TESTS)\n+\n+.PHONY: libgit-sys-test\n+libgit-sys-test:\n+\t$(QUIET)(\\\n+\t\tcd ../contrib/libgit-sys && \\\n+\t\tcargo test \\\n+\t)\n+ifdef INCLUDE_LIBGIT_RS\n+all:: libgit-sys-test\n+endif\n-- \n2.48.1.362.g079036d154-goog\n\n"},{"id":"511440","messageId":"ab32bd1d07fe60a3b8bc94e0867f8ab3252aa6d1.1738187176.git.steadmon@google.com","threadId":"61917","inReplyTo":"cover.1738187176.git.steadmon@google.com","subject":"[PATCH v9 3/4] libgit-sys: also export some config_set functions","fromName":"Josh Steadmon","fromEmail":"steadmon@google.com","sentAt":"2025-01-29T21:50:43Z","receivedAt":"2025-01-29T21:50:52Z","isPatch":true,"sender":{"key":"steadmon@google.com","avatar":"https://avatars.githubusercontent.com/u/2654920?v=4"},"body":"In preparation for implementing a higher-level Rust API for accessing\nGit configs, export some of the upstream configset API via libgitpub and\nlibgit-sys. Since this will be exercised as part of the higher-level API\nin the next commit, no tests have been added for libgit-sys.\n\nWhile we're at it, add git_configset_alloc() and git_configset_free()\nfunctions in libgitpub so that callers can manage config_set structs on\nthe heap. This also allows non-C external consumers to treat config_sets\nas opaque structs.\n\nCo-authored-by: Calvin Wan <calvinwan@google.com>\nSigned-off-by: Calvin Wan <calvinwan@google.com>\nSigned-off-by: Josh Steadmon <steadmon@google.com>\n---\n contrib/libgit-sys/public_symbol_export.c | 36 +++++++++++++++++++++++\n contrib/libgit-sys/public_symbol_export.h | 10 +++++++\n contrib/libgit-sys/src/lib.rs             | 31 ++++++++++++++++++-\n 3 files changed, 76 insertions(+), 1 deletion(-)\n\ndiff --git a/contrib/libgit-sys/public_symbol_export.c b/contrib/libgit-sys/public_symbol_export.c\nindex 4c153c6f0d..dfbb257115 100644\n--- a/contrib/libgit-sys/public_symbol_export.c\n+++ b/contrib/libgit-sys/public_symbol_export.c\n@@ -5,11 +5,47 @@\n  */\n \n #include \"git-compat-util.h\"\n+#include \"config.h\"\n #include \"contrib/libgit-sys/public_symbol_export.h\"\n #include \"version.h\"\n \n #pragma GCC visibility push(default)\n \n+struct libgit_config_set {\n+\tstruct config_set cs;\n+};\n+\n+struct libgit_config_set *libgit_configset_alloc(void)\n+{\n+\tstruct libgit_config_set *cs =\n+\t\t\txmalloc(sizeof(struct libgit_config_set));\n+\tgit_configset_init(&cs->cs);\n+\treturn cs;\n+}\n+\n+void libgit_configset_free(struct libgit_config_set *cs)\n+{\n+\tgit_configset_clear(&cs->cs);\n+\tfree(cs);\n+}\n+\n+int libgit_configset_add_file(struct libgit_config_set *cs, const char *filename)\n+{\n+\treturn git_configset_add_file(&cs->cs, filename);\n+}\n+\n+int libgit_configset_get_int(struct libgit_config_set *cs, const char *key,\n+\t\t\t     int *dest)\n+{\n+\treturn git_configset_get_int(&cs->cs, key, dest);\n+}\n+\n+int libgit_configset_get_string(struct libgit_config_set *cs, const char *key,\n+\t\t\t\tchar **dest)\n+{\n+\treturn git_configset_get_string(&cs->cs, key, dest);\n+}\n+\n const char *libgit_user_agent(void)\n {\n \treturn git_user_agent();\ndiff --git a/contrib/libgit-sys/public_symbol_export.h b/contrib/libgit-sys/public_symbol_export.h\nindex a3372f93fa..701db92d53 100644\n--- a/contrib/libgit-sys/public_symbol_export.h\n+++ b/contrib/libgit-sys/public_symbol_export.h\n@@ -1,6 +1,16 @@\n #ifndef PUBLIC_SYMBOL_EXPORT_H\n #define PUBLIC_SYMBOL_EXPORT_H\n \n+struct libgit_config_set *libgit_configset_alloc(void);\n+\n+void libgit_configset_free(struct libgit_config_set *cs);\n+\n+int libgit_configset_add_file(struct libgit_config_set *cs, const char *filename);\n+\n+int libgit_configset_get_int(struct libgit_config_set *cs, const char *key, int *dest);\n+\n+int libgit_configset_get_string(struct libgit_config_set *cs, const char *key, char **dest);\n+\n const char *libgit_user_agent(void);\n \n const char *libgit_user_agent_sanitized(void);\ndiff --git a/contrib/libgit-sys/src/lib.rs b/contrib/libgit-sys/src/lib.rs\nindex d4853f3074..dadb4e5f40 100644\n--- a/contrib/libgit-sys/src/lib.rs\n+++ b/contrib/libgit-sys/src/lib.rs\n@@ -1,15 +1,44 @@\n #[cfg(has_std__ffi__c_char)]\n-use std::ffi::c_char;\n+use std::ffi::{c_char, c_int};\n \n #[cfg(not(has_std__ffi__c_char))]\n #[allow(non_camel_case_types)]\n pub type c_char = i8;\n \n+#[cfg(not(has_std__ffi__c_char))]\n+#[allow(non_camel_case_types)]\n+pub type c_int = i32;\n+\n extern crate libz_sys;\n \n+#[allow(non_camel_case_types)]\n+#[repr(C)]\n+pub struct libgit_config_set {\n+    _data: [u8; 0],\n+    _marker: core::marker::PhantomData<(*mut u8, core::marker::PhantomPinned)>,\n+}\n+\n extern \"C\" {\n     pub fn libgit_user_agent() -> *const c_char;\n     pub fn libgit_user_agent_sanitized() -> *const c_char;\n+\n+    pub fn libgit_configset_alloc() -> *mut libgit_config_set;\n+    pub fn libgit_configset_free(cs: *mut libgit_config_set);\n+\n+    pub fn libgit_configset_add_file(cs: *mut libgit_config_set, filename: *const c_char) -> c_int;\n+\n+    pub fn libgit_configset_get_int(\n+        cs: *mut libgit_config_set,\n+        key: *const c_char,\n+        int: *mut c_int,\n+    ) -> c_int;\n+\n+    pub fn libgit_configset_get_string(\n+        cs: *mut libgit_config_set,\n+        key: *const c_char,\n+        dest: *mut *mut c_char,\n+    ) -> c_int;\n+\n }\n \n #[cfg(test)]\n-- \n2.48.1.362.g079036d154-goog\n\n"},{"id":"511441","messageId":"1bf8c5392c2045b43419aeed21a8859ee97ba6e6.1738187176.git.steadmon@google.com","threadId":"61917","inReplyTo":"cover.1738187176.git.steadmon@google.com","subject":"[PATCH v9 4/4] libgit: add higher-level libgit crate","fromName":"Josh Steadmon","fromEmail":"steadmon@google.com","sentAt":"2025-01-29T21:50:44Z","receivedAt":"2025-01-29T21:50:54Z","isPatch":true,"sender":{"key":"steadmon@google.com","avatar":"https://avatars.githubusercontent.com/u/2654920?v=4"},"body":"From: Calvin Wan <calvinwan@google.com>\n\nThe C functions exported by libgit-sys do not provide an idiomatic Rust\ninterface. To make it easier to use these functions via Rust, add a\nhigher-level \"libgit\" crate, that wraps the lower-level configset API\nwith an interface that is more Rust-y.\n\nThis combination of $X and $X-sys crates is a common pattern for FFI in\nRust, as documented in \"The Cargo Book\" [1].\n\n[1] https://doc.rust-lang.org/cargo/reference/build-scripts.html#-sys-packages\n\nCo-authored-by: Josh Steadmon <steadmon@google.com>\nSigned-off-by: Josh Steadmon <steadmon@google.com>\nSigned-off-by: Calvin Wan <calvinwan@google.com>\n---\n .gitignore                         |   1 +\n Makefile                           |  12 ++--\n contrib/libgit-rs/Cargo.lock       |  77 +++++++++++++++++++++\n contrib/libgit-rs/Cargo.toml       |  17 +++++\n contrib/libgit-rs/README.md        |  13 ++++\n contrib/libgit-rs/build.rs         |   4 ++\n contrib/libgit-rs/src/config.rs    | 106 +++++++++++++++++++++++++++++\n contrib/libgit-rs/src/lib.rs       |   1 +\n contrib/libgit-rs/testdata/config1 |   2 +\n contrib/libgit-rs/testdata/config2 |   2 +\n contrib/libgit-rs/testdata/config3 |   2 +\n contrib/libgit-sys/src/lib.rs      |   4 ++\n t/Makefile                         |   9 ++-\n 13 files changed, 242 insertions(+), 8 deletions(-)\n create mode 100644 contrib/libgit-rs/Cargo.lock\n create mode 100644 contrib/libgit-rs/Cargo.toml\n create mode 100644 contrib/libgit-rs/README.md\n create mode 100644 contrib/libgit-rs/build.rs\n create mode 100644 contrib/libgit-rs/src/config.rs\n create mode 100644 contrib/libgit-rs/src/lib.rs\n create mode 100644 contrib/libgit-rs/testdata/config1\n create mode 100644 contrib/libgit-rs/testdata/config2\n create mode 100644 contrib/libgit-rs/testdata/config3\n\ndiff --git a/.gitignore b/.gitignore\nindex 31d7e64287..acdd8ce7c7 100644\n--- a/.gitignore\n+++ b/.gitignore\n@@ -250,4 +250,5 @@ Release/\n /git.VC.db\n *.dSYM\n /contrib/buildsystems/out\n+/contrib/libgit-rs/target\n /contrib/libgit-sys/target\ndiff --git a/Makefile b/Makefile\nindex a772bff186..7681ec9a3d 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -417,7 +417,7 @@ include shared.mak\n # programs in oss-fuzz/.\n #\n # Define INCLUDE_LIBGIT_RS if you want `make all` and `make test` to build and\n-# test the Rust crate in contrib/libgit-sys.\n+# test the Rust crates in contrib/libgit-sys and contrib/libgit-rs.\n #\n # === Optional library: libintl ===\n #\n@@ -3741,7 +3741,7 @@ clean: profile-clean coverage-clean cocciclean\n \t$(RM) $(htmldocs).tar.gz $(manpages).tar.gz\n \t$(MAKE) -C Documentation/ clean\n \t$(RM) Documentation/GIT-EXCLUDED-PROGRAMS\n-\t$(RM) -r contrib/libgit-sys/target\n+\t$(RM) -r contrib/libgit-sys/target contrib/libgit-rs/target\n \t$(RM) contrib/libgit-sys/partial_symbol_export.o\n \t$(RM) contrib/libgit-sys/hidden_symbol_export.o\n \t$(RM) contrib/libgit-sys/libgitpub.a\n@@ -3907,14 +3907,14 @@ build-unit-tests: $(UNIT_TEST_PROGS) $(CLAR_TEST_PROG)\n unit-tests: $(UNIT_TEST_PROGS) $(CLAR_TEST_PROG) t/helper/test-tool$X\n \t$(MAKE) -C t/ unit-tests\n \n-.PHONY: libgit-sys\n-libgit-sys:\n+.PHONY: libgit-sys libgit-rs\n+libgit-sys libgit-rs:\n \t$(QUIET)(\\\n-\t\tcd contrib/libgit-sys && \\\n+\t\tcd contrib/$@ && \\\n \t\tcargo build \\\n \t)\n ifdef INCLUDE_LIBGIT_RS\n-all:: libgit-sys\n+all:: libgit-sys libgit-rs\n endif\n \n LIBGIT_PUB_OBJS =\ndiff --git a/contrib/libgit-rs/Cargo.lock b/contrib/libgit-rs/Cargo.lock\nnew file mode 100644\nindex 0000000000..a30c7c8d33\n--- /dev/null\n+++ b/contrib/libgit-rs/Cargo.lock\n@@ -0,0 +1,77 @@\n+# This file is automatically @generated by Cargo.\n+# It is not intended for manual editing.\n+version = 3\n+\n+[[package]]\n+name = \"autocfg\"\n+version = \"1.4.0\"\n+source = \"registry+https://github.com/rust-lang/crates.io-index\"\n+checksum = \"ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26\"\n+\n+[[package]]\n+name = \"cc\"\n+version = \"1.1.15\"\n+source = \"registry+https://github.com/rust-lang/crates.io-index\"\n+checksum = \"57b6a275aa2903740dc87da01c62040406b8812552e97129a63ea8850a17c6e6\"\n+dependencies = [\n+ \"shlex\",\n+]\n+\n+[[package]]\n+name = \"libc\"\n+version = \"0.2.158\"\n+source = \"registry+https://github.com/rust-lang/crates.io-index\"\n+checksum = \"d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439\"\n+\n+[[package]]\n+name = \"libgit\"\n+version = \"0.1.0\"\n+dependencies = [\n+ \"autocfg\",\n+ \"libgit-sys\",\n+]\n+\n+[[package]]\n+name = \"libgit-sys\"\n+version = \"0.1.0\"\n+dependencies = [\n+ \"autocfg\",\n+ \"libz-sys\",\n+ \"make-cmd\",\n+]\n+\n+[[package]]\n+name = \"libz-sys\"\n+version = \"1.1.20\"\n+source = \"registry+https://github.com/rust-lang/crates.io-index\"\n+checksum = \"d2d16453e800a8cf6dd2fc3eb4bc99b786a9b90c663b8559a5b1a041bf89e472\"\n+dependencies = [\n+ \"cc\",\n+ \"libc\",\n+ \"pkg-config\",\n+ \"vcpkg\",\n+]\n+\n+[[package]]\n+name = \"make-cmd\"\n+version = \"0.1.0\"\n+source = \"registry+https://github.com/rust-lang/crates.io-index\"\n+checksum = \"a8ca8afbe8af1785e09636acb5a41e08a765f5f0340568716c18a8700ba3c0d3\"\n+\n+[[package]]\n+name = \"pkg-config\"\n+version = \"0.3.30\"\n+source = \"registry+https://github.com/rust-lang/crates.io-index\"\n+checksum = \"d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec\"\n+\n+[[package]]\n+name = \"shlex\"\n+version = \"1.3.0\"\n+source = \"registry+https://github.com/rust-lang/crates.io-index\"\n+checksum = \"0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64\"\n+\n+[[package]]\n+name = \"vcpkg\"\n+version = \"0.2.15\"\n+source = \"registry+https://github.com/rust-lang/crates.io-index\"\n+checksum = \"accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426\"\ndiff --git a/contrib/libgit-rs/Cargo.toml b/contrib/libgit-rs/Cargo.toml\nnew file mode 100644\nindex 0000000000..c3289e69db\n--- /dev/null\n+++ b/contrib/libgit-rs/Cargo.toml\n@@ -0,0 +1,17 @@\n+[package]\n+name = \"libgit\"\n+version = \"0.1.0\"\n+edition = \"2021\"\n+build = \"build.rs\"\n+rust-version = \"1.63\" # TODO: Once we hit 1.84 or newer, we may want to remove Cargo.lock from\n+                      # version control. See https://lore.kernel.org/git/Z47jgK-oMjFRSslr@tapette.crustytoothpaste.net/\n+\n+\n+[lib]\n+path = \"src/lib.rs\"\n+\n+[dependencies]\n+libgit-sys = { version = \"0.1.0\", path = \"../libgit-sys\" }\n+\n+[build-dependencies]\n+autocfg = \"1.4.0\"\ndiff --git a/contrib/libgit-rs/README.md b/contrib/libgit-rs/README.md\nnew file mode 100644\nindex 0000000000..ff945e1ce2\n--- /dev/null\n+++ b/contrib/libgit-rs/README.md\n@@ -0,0 +1,13 @@\n+# libgit-rs\n+\n+Proof-of-concept Git bindings for Rust.\n+\n+```toml\n+[dependencies]\n+libgit = \"0.1.0\"\n+```\n+\n+## Rust version requirements\n+\n+libgit-rs should support Rust versions at least as old as the version included\n+in Debian stable (currently 1.63).\ndiff --git a/contrib/libgit-rs/build.rs b/contrib/libgit-rs/build.rs\nnew file mode 100644\nindex 0000000000..f8bd01a690\n--- /dev/null\n+++ b/contrib/libgit-rs/build.rs\n@@ -0,0 +1,4 @@\n+pub fn main() {\n+    let ac = autocfg::new();\n+    ac.emit_has_path(\"std::ffi::c_char\");\n+}\ndiff --git a/contrib/libgit-rs/src/config.rs b/contrib/libgit-rs/src/config.rs\nnew file mode 100644\nindex 0000000000..6bf04845c8\n--- /dev/null\n+++ b/contrib/libgit-rs/src/config.rs\n@@ -0,0 +1,106 @@\n+use std::ffi::{c_void, CStr, CString};\n+use std::path::Path;\n+\n+#[cfg(has_std__ffi__c_char)]\n+use std::ffi::{c_char, c_int};\n+\n+#[cfg(not(has_std__ffi__c_char))]\n+#[allow(non_camel_case_types)]\n+type c_char = i8;\n+\n+#[cfg(not(has_std__ffi__c_char))]\n+#[allow(non_camel_case_types)]\n+type c_int = i32;\n+\n+use libgit_sys::*;\n+\n+/// A ConfigSet is an in-memory cache for config-like files such as `.gitmodules` or `.gitconfig`.\n+/// It does not support all config directives; notably, it will not process `include` or\n+/// `includeIf` directives (but it will store them so that callers can choose whether and how to\n+/// handle them).\n+pub struct ConfigSet(*mut libgit_config_set);\n+impl ConfigSet {\n+    /// Allocate a new ConfigSet\n+    pub fn new() -> Self {\n+        unsafe { ConfigSet(libgit_configset_alloc()) }\n+    }\n+\n+    /// Load the given files into the ConfigSet; conflicting directives in later files will\n+    /// override those given in earlier files.\n+    pub fn add_files(&mut self, files: &[&Path]) {\n+        for file in files {\n+            let pstr = file.to_str().expect(\"Invalid UTF-8\");\n+            let rs = CString::new(pstr).expect(\"Couldn't convert to CString\");\n+            unsafe {\n+                libgit_configset_add_file(self.0, rs.as_ptr());\n+            }\n+        }\n+    }\n+\n+    /// Load the value for the given key and attempt to parse it as an i32. Dies with a fatal error\n+    /// if the value cannot be parsed. Returns None if the key is not present.\n+    pub fn get_int(&mut self, key: &str) -> Option<i32> {\n+        let key = CString::new(key).expect(\"Couldn't convert to CString\");\n+        let mut val: c_int = 0;\n+        unsafe {\n+            if libgit_configset_get_int(self.0, key.as_ptr(), &mut val as *mut c_int) != 0 {\n+                return None;\n+            }\n+        }\n+\n+        Some(val.into())\n+    }\n+\n+    /// Clones the value for the given key. Dies with a fatal error if the value cannot be\n+    /// converted to a String. Returns None if the key is not present.\n+    pub fn get_string(&mut self, key: &str) -> Option<String> {\n+        let key = CString::new(key).expect(\"Couldn't convert key to CString\");\n+        let mut val: *mut c_char = std::ptr::null_mut();\n+        unsafe {\n+            if libgit_configset_get_string(self.0, key.as_ptr(), &mut val as *mut *mut c_char) != 0\n+            {\n+                return None;\n+            }\n+            let borrowed_str = CStr::from_ptr(val);\n+            let owned_str =\n+                String::from(borrowed_str.to_str().expect(\"Couldn't convert val to str\"));\n+            free(val as *mut c_void); // Free the xstrdup()ed pointer from the C side\n+            Some(owned_str)\n+        }\n+    }\n+}\n+\n+impl Default for ConfigSet {\n+    fn default() -> Self {\n+        Self::new()\n+    }\n+}\n+\n+impl Drop for ConfigSet {\n+    fn drop(&mut self) {\n+        unsafe {\n+            libgit_configset_free(self.0);\n+        }\n+    }\n+}\n+\n+#[cfg(test)]\n+mod tests {\n+    use super::*;\n+\n+    #[test]\n+    fn load_configs_via_configset() {\n+        let mut cs = ConfigSet::new();\n+        cs.add_files(&[\n+            Path::new(\"testdata/config1\"),\n+            Path::new(\"testdata/config2\"),\n+            Path::new(\"testdata/config3\"),\n+        ]);\n+        // ConfigSet retrieves correct value\n+        assert_eq!(cs.get_int(\"trace2.eventTarget\"), Some(1));\n+        // ConfigSet respects last config value set\n+        assert_eq!(cs.get_int(\"trace2.eventNesting\"), Some(3));\n+        // ConfigSet returns None for missing key\n+        assert_eq!(cs.get_string(\"foo.bar\"), None);\n+    }\n+}\ndiff --git a/contrib/libgit-rs/src/lib.rs b/contrib/libgit-rs/src/lib.rs\nnew file mode 100644\nindex 0000000000..ef68c36943\n--- /dev/null\n+++ b/contrib/libgit-rs/src/lib.rs\n@@ -0,0 +1 @@\n+pub mod config;\ndiff --git a/contrib/libgit-rs/testdata/config1 b/contrib/libgit-rs/testdata/config1\nnew file mode 100644\nindex 0000000000..4e9a9d25d1\n--- /dev/null\n+++ b/contrib/libgit-rs/testdata/config1\n@@ -0,0 +1,2 @@\n+[trace2]\n+\teventNesting = 1\ndiff --git a/contrib/libgit-rs/testdata/config2 b/contrib/libgit-rs/testdata/config2\nnew file mode 100644\nindex 0000000000..b8d1eca423\n--- /dev/null\n+++ b/contrib/libgit-rs/testdata/config2\n@@ -0,0 +1,2 @@\n+[trace2]\n+\teventTarget = 1\ndiff --git a/contrib/libgit-rs/testdata/config3 b/contrib/libgit-rs/testdata/config3\nnew file mode 100644\nindex 0000000000..ca7b9a7c38\n--- /dev/null\n+++ b/contrib/libgit-rs/testdata/config3\n@@ -0,0 +1,2 @@\n+[trace2]\n+\teventNesting = 3\ndiff --git a/contrib/libgit-sys/src/lib.rs b/contrib/libgit-sys/src/lib.rs\nindex dadb4e5f40..4bfc650450 100644\n--- a/contrib/libgit-sys/src/lib.rs\n+++ b/contrib/libgit-sys/src/lib.rs\n@@ -1,3 +1,5 @@\n+use std::ffi::c_void;\n+\n #[cfg(has_std__ffi__c_char)]\n use std::ffi::{c_char, c_int};\n \n@@ -19,6 +21,8 @@ pub struct libgit_config_set {\n }\n \n extern \"C\" {\n+    pub fn free(ptr: *mut c_void);\n+\n     pub fn libgit_user_agent() -> *const c_char;\n     pub fn libgit_user_agent_sanitized() -> *const c_char;\n \ndiff --git a/t/Makefile b/t/Makefile\nindex 53ba01c21b..2994eb5fa9 100644\n--- a/t/Makefile\n+++ b/t/Makefile\n@@ -178,12 +178,17 @@ perf:\n .PHONY: pre-clean $(T) aggregate-results clean valgrind perf \\\n \tcheck-chainlint clean-chainlint test-chainlint $(UNIT_TESTS)\n \n-.PHONY: libgit-sys-test\n+.PHONY: libgit-sys-test libgit-rs-test\n libgit-sys-test:\n \t$(QUIET)(\\\n \t\tcd ../contrib/libgit-sys && \\\n \t\tcargo test \\\n \t)\n+libgit-rs-test:\n+\t$(QUIET)(\\\n+\t\tcd ../contrib/libgit-rs && \\\n+\t\tcargo test \\\n+\t)\n ifdef INCLUDE_LIBGIT_RS\n-all:: libgit-sys-test\n+all:: libgit-sys-test libgit-rs-test\n endif\n-- \n2.48.1.362.g079036d154-goog\n\n"},{"id":"511482","messageId":"2217da71-d18a-446f-a7a1-6db4d358bbe5@gmail.com","threadId":"61917","inReplyTo":"cover.1738187176.git.steadmon@google.com","subject":"Re: [PATCH v9 0/4] Introduce libgit-rs, a Rust wrapper around libgit.a","fromName":"","fromEmail":"phillip.wood123@gmail.com","sentAt":"2025-01-30T10:49:43Z","receivedAt":"2025-01-30T10:49:49Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Josh\n\nThe range-diff looks good. It's really nice to see this being added to git.\n\nBest Wishes\n\nPhillip\n\nOn 29/01/2025 21:50, Josh Steadmon wrote:\n> This series provides two small Rust wrapper libraries around parts of\n> Git: \"libgit-sys\", which exposes a few functions from libgit.a, and\n> \"libgit\", which provides a more Rust-friendly interface to some of those\n> functions. In addition to included unit tests, at $DAYJOB we have tested\n> building JJ[1] with our library and used it to replace some of the\n> libgit2-rs uses.\n> \n> [1] https://github.com/jj-vcs/jj\n> \n> There is known NEEDSWORK, but I feel that they can be addressed in\n> follow-up changes, rather than in this series. If you feel otherwise,\n> please let me know:\n> \n> * Investigate alternative methods of managing symbol visibility &\n>    renaming.\n> \n> * Figure out symbol versioning\n> \n> Changes in V9:\n> * Properly initialize Makefile var LIBGIT_PUB_OBJS.\n> \n> * Pass the correct pointer to free in libgit_configset_free().\n> \n> Changes in V8:\n> * Define a private libgit_config_set struct to avoid excessive casting\n>    in public_symbol_export.c.\n> \n> * Style fixes: merge some Makefile rules, limit rule line length by\n>    defining intermediate variables, add initial empty comment line, add\n>    linebreaks in function definitions.\n> \n> Changes in V7:\n> * Moved the ConfigSet implementation in libgit-rs to a `config` module.\n> \n> * Added doc comments for ConfigSet and its methods.\n> \n> * Fix meson builds by adding new object files to `libgit_sources`\n> \n> * Moved INCLUDE_LIBGIT_RS Makefile changes earlier in the series, so\n>    that we can make it optional to compile some of the libgitpub sources.\n>    Squashed V6 patch 5/5 into this series' patch 4/4.\n> \n> * Don't publicly export FFI types in libgit-rs.\n> \n> * Removed extraneous `-r` argument to $(RM) in the clean rules.\n> \n> * Added TODO reminder in Cargo.toml about removing Cargo.lock once we\n>    hit a certain minimum supported Rust version.\n> \n> * Style cleanup in public_symbol_export.c\n> \n> Changes in V6:\n> * Rebased onto current master, since V5 was several months old.\n> \n> * Move libgit-sys out of libgit-rs; while this sort of nesting is common\n>    in Rust crates with standalone repositories, it doesn't make as much\n>    sense when they're contained in the larger Git project's repo.\n> \n> * Standardize the naming of some of the Makefile targets to always\n>    include a dash in the \"-rs\" or \"-sys\" suffixes.\n> \n> * Clean up READMEs and crate descriptions in preparation for\n>    uploading to crates.io.\n> \n> Changes in V5:\n> * When building with INCLUDE_LIBGIT_RS defined, add\n>    \"-fvisibility=hidden\" to CFLAGS. This allows us to manage symbol\n>    visibility in libgitpub.a without causing `make all` to rebuild from\n>    scratch due to changing CFLAGS.\n> \n> * Avoid using c_int in the higher-level Rust API.\n> \n> * Remove libgitpub.a and intermediate files with `make clean`.\n> \n> Changes in V4:\n> * Drop V3 patch #3, which added wrappers around repository\n>    initialization and config access. These are not well-libified, and\n>    they are not necessary for JJ's proof-of-concept use case, so let's\n>    avoid exporting them for now.\n> \n> * Set a minimum supported Rust version of 1.63. Autodetect whether our\n>    Rust version has c_int and c_char types; if not, define them\n>    ourselves.\n> \n> * When building libgitpub.a via build.rs, set DEVELOPER=1 to catch\n>    additional errors at build time.\n> \n> * In build.rs, use the make_cmd crate to portable select the correct\n>    invocation of GNU Make.\n> \n> * Follow naming standards for _alloc() and _free() functions.\n> \n> * Use String instead of CString in higher-level API.\n> \n> * Move libgit_configset_alloc() and libgit_configset_free() out of\n>    upstream Git, to the libgitpub shim library.\n> \n> * In libgitpub, initialize libgit_config_set structs in the _alloc()\n>    function rather than with a separate _init() function.\n> \n> * Remove unnecessary comments in libgit-sys showing where the wrapped\n>    functions were originally defined.\n> \n> * Fix clippy lint: don't reborrow configfile path references.\n> \n> * Various typo fixes and `cargo fmt` fixes.\n> \n> Changes in V3:\n> * Renamed cgit-rs to libgit-rs and cgit-sys to libgit-sys\n> \n> * Makefile cleanup, particularly adding config.mak options that\n>    developers can set to run Rust builds and tests by default (Patch 6)\n> \n> * Provide testdata configs for unit tests\n> \n> * ConfigSet API now uses &Path instead of &str -- more ergonomic for\n>    Rust users to pass in and errors out if the path string isn't UTF-8\n> \n> * Fixed unresolved dependency on libz in Cargo.toml\n> \n> \n> Calvin Wan (1):\n>    libgit: add higher-level libgit crate\n> \n> Josh Steadmon (3):\n>    common-main: split init and exit code into new files\n>    libgit-sys: introduce Rust wrapper for libgit.a\n>    libgit-sys: also export some config_set functions\n> \n>   .gitignore                                |   2 +\n>   Makefile                                  |  50 ++++++++++\n>   common-exit.c                             |  26 ++++++\n>   common-init.c                             |  63 +++++++++++++\n>   common-init.h                             |   6 ++\n>   common-main.c                             |  83 +----------------\n>   contrib/libgit-rs/Cargo.lock              |  77 ++++++++++++++++\n>   contrib/libgit-rs/Cargo.toml              |  17 ++++\n>   contrib/libgit-rs/README.md               |  13 +++\n>   contrib/libgit-rs/build.rs                |   4 +\n>   contrib/libgit-rs/src/config.rs           | 106 ++++++++++++++++++++++\n>   contrib/libgit-rs/src/lib.rs              |   1 +\n>   contrib/libgit-rs/testdata/config1        |   2 +\n>   contrib/libgit-rs/testdata/config2        |   2 +\n>   contrib/libgit-rs/testdata/config3        |   2 +\n>   contrib/libgit-sys/Cargo.lock             |  69 ++++++++++++++\n>   contrib/libgit-sys/Cargo.toml             |  19 ++++\n>   contrib/libgit-sys/README.md              |   4 +\n>   contrib/libgit-sys/build.rs               |  35 +++++++\n>   contrib/libgit-sys/public_symbol_export.c |  59 ++++++++++++\n>   contrib/libgit-sys/public_symbol_export.h |  18 ++++\n>   contrib/libgit-sys/src/lib.rs             |  79 ++++++++++++++++\n>   meson.build                               |   2 +\n>   t/Makefile                                |  15 +++\n>   24 files changed, 673 insertions(+), 81 deletions(-)\n>   create mode 100644 common-exit.c\n>   create mode 100644 common-init.c\n>   create mode 100644 common-init.h\n>   create mode 100644 contrib/libgit-rs/Cargo.lock\n>   create mode 100644 contrib/libgit-rs/Cargo.toml\n>   create mode 100644 contrib/libgit-rs/README.md\n>   create mode 100644 contrib/libgit-rs/build.rs\n>   create mode 100644 contrib/libgit-rs/src/config.rs\n>   create mode 100644 contrib/libgit-rs/src/lib.rs\n>   create mode 100644 contrib/libgit-rs/testdata/config1\n>   create mode 100644 contrib/libgit-rs/testdata/config2\n>   create mode 100644 contrib/libgit-rs/testdata/config3\n>   create mode 100644 contrib/libgit-sys/Cargo.lock\n>   create mode 100644 contrib/libgit-sys/Cargo.toml\n>   create mode 100644 contrib/libgit-sys/README.md\n>   create mode 100644 contrib/libgit-sys/build.rs\n>   create mode 100644 contrib/libgit-sys/public_symbol_export.c\n>   create mode 100644 contrib/libgit-sys/public_symbol_export.h\n>   create mode 100644 contrib/libgit-sys/src/lib.rs\n> \n> Range-diff against v8:\n> 1:  cd0cb9aa04 = 1:  cd0cb9aa04 common-main: split init and exit code into new files\n> 2:  3588a3c3fc ! 2:  8793ff64a7 libgit-sys: introduce Rust wrapper for libgit.a\n>      @@ Makefile: $(CLAR_TEST_PROG): $(UNIT_TEST_DIR)/clar.suite $(CLAR_TEST_OBJS) $(GIT\n>       +all:: libgit-sys\n>       +endif\n>       +\n>      -+LIBGIT_PUB_OBJS = contrib/libgit-sys/public_symbol_export.o\n>      ++LIBGIT_PUB_OBJS =\n>      ++LIBGIT_PUB_OBJS += contrib/libgit-sys/public_symbol_export.o\n>       +LIBGIT_PUB_OBJS += libgit.a\n>       +LIBGIT_PUB_OBJS += reftable/libreftable.a\n>       +LIBGIT_PUB_OBJS += xdiff/lib.a\n> 3:  f4452fffe6 ! 3:  ab32bd1d07 libgit-sys: also export some config_set functions\n>      @@ contrib/libgit-sys/public_symbol_export.c\n>       +void libgit_configset_free(struct libgit_config_set *cs)\n>       +{\n>       +\tgit_configset_clear(&cs->cs);\n>      -+\tfree(&cs->cs);\n>      ++\tfree(cs);\n>       +}\n>       +\n>       +int libgit_configset_add_file(struct libgit_config_set *cs, const char *filename)\n> 4:  ada9fc0a13 ! 4:  1bf8c5392c libgit: add higher-level libgit crate\n>      @@ Makefile: build-unit-tests: $(UNIT_TEST_PROGS) $(CLAR_TEST_PROG)\n>       +all:: libgit-sys libgit-rs\n>        endif\n>        \n>      - LIBGIT_PUB_OBJS = contrib/libgit-sys/public_symbol_export.o\n>      + LIBGIT_PUB_OBJS =\n>       \n>        ## contrib/libgit-rs/Cargo.lock (new) ##\n>       @@\n> \n> base-commit: 757161efcca150a9a96b312d9e780a071e601a03\n\n"},{"id":"511522","messageId":"xmqq7c6ccd2y.fsf@gitster.g","threadId":"61917","inReplyTo":"2217da71-d18a-446f-a7a1-6db4d358bbe5@gmail.com","subject":"Re: [PATCH v9 0/4] Introduce libgit-rs, a Rust wrapper around libgit.a","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-01-30T19:32:21Z","receivedAt":"2025-01-30T19:32:24Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"phillip.wood123@gmail.com writes:\n\n> Hi Josh\n>\n> The range-diff looks good. It's really nice to see this being added to git.\n>\n> Best Wishes\n>\n> Phillip\n\nThanks.  Let's plan to mark the topic for 'next' unless there are\nother comments.\n"}]}