git.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 00/15] Introduce rust: In xdiff
@ 2025-08-29 19:42 Ezekiel Newren via GitGitGadget
  2025-08-29 19:42 ` [PATCH 01/15] doc: add a policy for using Rust brian m. carlson via GitGitGadget
                   ` (15 more replies)
  0 siblings, 16 replies; 100+ messages in thread
From: Ezekiel Newren via GitGitGadget @ 2025-08-29 19:42 UTC (permalink / raw)
  To: git; +Cc: Ezekiel Newren

This is a continuation of
https://lore.kernel.org/git/pull.1980.v3.git.git.1755921356.gitgitgadget@gmail.com/,
but I am removing the RFC label.

Changes since the last RFC patch series (range-diff added below):

 * Addressed feedback:
   * Use self instead of this
   * Update ivec to use Git code style
   * Remove environment variable CI_IS_DOCKER from github workflows
   * Remove PHONY target interop-objs
   * Removed left over asserts in Rust and fix near by code
 * Other changes:
   * Avoid name mangling of ivec if c++ compiler used
   * Fixed a typo in patch 11 commit message

High level overview:

 * patch 1: add a policy for using Rust (brian's patch, with a small tweak)
 * patch 2: introduce Rust to the codebase
 * patches 3-5: adapt CI (github workflows) to build Git with Rust
 * patch 6: introduce the ivec type
 * patches 7-14: xdiff code cleanup in preparation for translating to Rust
 * patch 15: translate a C function into Rust and call it from C

Build results for these changes:
https://github.com/git/git/actions/runs/17330190596

Range-Diff against last RFC version:

 1:  6d065f550f =  1:  6d065f550f doc: add a policy for using Rust
 2:  0393995125 =  2:  cc9cf87775 xdiff: introduce rust
 3:  a98d9e4d21 !  3:  6c47401ba0 github workflows: install rust
    @@ .github/workflows/main.yml: jobs:
                image: fedora:latest
              # A RHEL 8 compatible distro.  Supported until 2029-05-31.
     @@ .github/workflows/main.yml: jobs:
    -       jobname: ${{matrix.vector.jobname}}
            CC: ${{matrix.vector.cc}}
            CI_JOB_IMAGE: ${{matrix.vector.image}}
    -+      CI_IS_DOCKER: "true"
            CUSTOM_PATH: /custom
     +      CARGO_HOME: /home/builder/.cargo
          runs-on: ubuntu-latest
 4:  0d2b39c3e0 =  4:  8e350700bc win+Meson: do allow linking with the Rust-built xdiff
 5:  e65488ab99 =  5:  a00cbf7bcb github workflows: upload Cargo.lock
 6:  db5d22b188 !  6:  922d506ed6 ivec: create a vector type that is interoperable between C and Rust
    @@ Makefile: reconfigure config.mak.autogen: config.status
      endif
      
     +INTEROP_OBJS += interop/ivec.o
    -+.PHONY: interop-objs
    -+interop-objs: $(INTEROP_OBJS)
     +
      XDIFF_OBJS += xdiff/xdiffi.o
      XDIFF_OBJS += xdiff/xemit.o
    @@ interop/ivec.c (new)
     @@
     +#include "ivec.h"
     +
    -+static void ivec_set_capacity(void* self, usize new_capacity) {
    -+	struct rawivec *this = self;
    ++#ifdef __cplusplus
    ++extern "C" {
    ++#endif
    ++
    ++static void ivec_set_capacity(void *self_, usize new_capacity)
    ++{
    ++	struct rawivec *self = self_;
    ++
     +	if (new_capacity == 0)
    -+		FREE_AND_NULL(this->ptr);
    ++		FREE_AND_NULL(self->ptr);
     +	else
    -+		this->ptr = xrealloc(this->ptr, new_capacity * this->element_size);
    -+	this->capacity = new_capacity;
    ++		self->ptr = xrealloc(self->ptr, new_capacity * self->element_size);
    ++	self->capacity = new_capacity;
     +}
     +
    -+void ivec_init(void* self, usize element_size) {
    -+	struct rawivec *this = self;
    -+	this->ptr = NULL;
    -+	this->length = 0;
    -+	this->capacity = 0;
    -+	this->element_size = element_size;
    ++void ivec_init(void *self_, usize element_size)
    ++{
    ++	struct rawivec *self = self_;
    ++
    ++	self->ptr = NULL;
    ++	self->length = 0;
    ++	self->capacity = 0;
    ++	self->element_size = element_size;
     +}
     +
     +/*
    @@ interop/ivec.c (new)
     + * to the specified capacity, and then calloc self.capacity number of
     + * elements.
     + */
    -+void ivec_zero(void* self, usize capacity) {
    -+	struct rawivec *this = self;
    -+	if (this->ptr)
    -+		FREE_AND_NULL(this->ptr);
    -+	this->capacity = this->length = capacity;
    -+	this->ptr = xcalloc(this->capacity, this->element_size);
    ++void ivec_zero(void *self_, usize capacity)
    ++{
    ++	struct rawivec *self = self_;
    ++
    ++	if (self->ptr)
    ++		FREE_AND_NULL(self->ptr);
    ++	self->capacity = self->length = capacity;
    ++	self->ptr = xcalloc(self->capacity, self->element_size);
     +}
     +
    -+void ivec_clear(void* self) {
    -+	struct rawivec *this = self;
    -+	this->length = 0;
    ++void ivec_clear(void *self_)
    ++{
    ++	struct rawivec *self = self_;
    ++
    ++	self->length = 0;
     +}
     +
    -+void ivec_reserve_exact(void* self, usize additional) {
    -+	struct rawivec *this = self;
    -+	usize new_capacity = this->capacity + additional;
    ++void ivec_reserve_exact(void *self_, usize additional)
    ++{
    ++	struct rawivec *self = self_;
    ++	usize new_capacity = self->capacity + additional;
    ++
     +	ivec_set_capacity(self, new_capacity);
     +}
     +
    -+void ivec_reserve(void* self, usize additional) {
    -+	struct rawivec *this = self;
    ++void ivec_reserve(void *self_, usize additional)
    ++{
    ++	struct rawivec *self = self_;
     +	usize growby = 128;
    -+	if (this->capacity > growby) {
    -+		growby = this->capacity;
    ++
    ++	if (self->capacity > growby) {
    ++		growby = self->capacity;
     +	}
     +	if (additional > growby) {
     +		growby = additional;
    @@ interop/ivec.c (new)
     +	ivec_reserve_exact(self, growby);
     +}
     +
    -+void ivec_shrink_to_fit(void* self) {
    -+	struct rawivec *this = self;
    -+	ivec_set_capacity(self, this->length);
    ++void ivec_shrink_to_fit(void *self_)
    ++{
    ++	struct rawivec *self = self_;
    ++
    ++	ivec_set_capacity(self_, self->length);
     +}
     +
    -+void ivec_resize(void* self, usize new_length, void* default_value) {
    -+	struct rawivec *this = self;
    -+	isize additional = (isize) (new_length - this->capacity);
    ++void ivec_resize(void *self_, usize new_length, void *default_value)
    ++{
    ++	struct rawivec *self = self_;
    ++	isize additional = (isize) (new_length - self->capacity);
    ++
     +	if (additional > 0) {
    -+		ivec_reserve(self, additional);
    ++		ivec_reserve(self_, additional);
     +	}
     +
    -+	for (usize i = this->length; i < new_length; i++) {
    -+		void* dst = (u8*) this->ptr + (this->length + i) * this->element_size;
    -+		memcpy(dst, default_value, this->element_size);
    ++	for (usize i = self->length; i < new_length; i++) {
    ++		void *dst = (u8 *)self->ptr + (self->length + i) * self->element_size;
    ++		memcpy(dst, default_value, self->element_size);
     +	}
    -+	this->length = new_length;
    ++	self->length = new_length;
     +}
     +
    -+void ivec_push(void* self, void* value) {
    -+	struct rawivec *this = self;
    -+	u8* dst;
    ++void ivec_push(void *self_, void *value)
    ++{
    ++	struct rawivec *self = self_;
    ++	u8 *dst;
     +
    -+	if (this->length == this->capacity) {
    -+		ivec_reserve(self, 1);
    ++	if (self->length == self->capacity) {
    ++		ivec_reserve(self_, 1);
     +	}
    -+	dst = (u8*) this->ptr + this->length * this->element_size;
    -+	memcpy(dst, value, this->element_size);
    -+	this->length++;
    ++	dst = (u8 *)self->ptr + self->length * self->element_size;
    ++	memcpy(dst, value, self->element_size);
    ++	self->length++;
     +}
     +
    -+void ivec_extend_from_slice(void* self, void const* ptr, usize size) {
    -+	struct rawivec *this = self;
    -+	u8* dst;
    ++void ivec_extend_from_slice(void *self_, void const *ptr, usize size)
    ++{
    ++	struct rawivec *self = self_;
    ++	u8 *dst;
     +
     +	if (size == 0)
     +		return;
     +
    -+	if (this->length + size > this->capacity) {
    -+		ivec_reserve(self, this->capacity - this->length + size);
    ++	if (self->length + size > self->capacity) {
    ++		ivec_reserve(self_, self->capacity - self->length + size);
     +	}
    -+	dst = (u8*) this->ptr + this->length * this->element_size;
    -+	memcpy(dst, ptr, size * this->element_size);
    -+	this->length += size;
    ++	dst = (u8 *)self->ptr + self->length * self->element_size;
    ++	memcpy(dst, ptr, size * self->element_size);
    ++	self->length += size;
     +}
     +
    -+bool ivec_equal(void* self, void* other) {
    -+	struct rawivec *lhs = self;
    ++bool ivec_equal(void *self_, void *other)
    ++{
    ++	struct rawivec *lhs = self_;
     +	struct rawivec *rhs = other;
     +
     +	if (lhs->element_size != rhs->element_size) {
    @@ interop/ivec.c (new)
     +		return false;
     +	}
     +
    -+
     +	for (usize i = 0; i < lhs->length; i++) {
    -+		void* left = (u8 *) lhs->ptr + i * lhs->element_size;
    -+		void* right = (u8 *) rhs->ptr + i * rhs->element_size;
    ++		void *left = (u8 *)lhs->ptr + i * lhs->element_size;
    ++		void *right = (u8 *)rhs->ptr + i * rhs->element_size;
     +		if (memcmp(left, right, lhs->element_size) != 0) {
     +			return false;
     +		}
    @@ interop/ivec.c (new)
     +}
     +
     +
    -+void ivec_free(void* self) {
    -+	struct rawivec *this = self;
    -+	FREE_AND_NULL(this->ptr);
    -+	this->length = 0;
    -+	this->capacity = 0;
    ++void ivec_free(void *self_)
    ++{
    ++	struct rawivec *self = self_;
    ++
    ++	FREE_AND_NULL(self->ptr);
    ++	self->length = 0;
    ++	self->capacity = 0;
     +	/* don't modify self->element_size */
     +}
     +
    -+void ivec_move(void* source, void* destination) {
    -+	struct rawivec *this = source;
    -+	struct rawivec *that = destination;
    ++void ivec_move(void *source, void *destination)
    ++{
    ++	struct rawivec *src = source;
    ++	struct rawivec *dst = destination;
     +
    -+	if (this->element_size != that->element_size)
    ++	if (src->element_size != dst->element_size)
     +		BUG("mismatched element_size");
     +
     +	ivec_free(destination);
    -+	that->ptr = this->ptr;
    -+	that->length = this->length;
    -+	that->capacity = this->capacity;
    ++	dst->ptr = src->ptr;
    ++	dst->length = src->length;
    ++	dst->capacity = src->capacity;
    ++
    ++	src->ptr = NULL;
    ++	src->length = 0;
    ++	src->capacity = 0;
    ++}
     +
    -+	this->ptr = NULL;
    -+	this->length = 0;
    -+	this->capacity = 0;
    ++#ifdef __cplusplus
     +}
    ++#endif
     
      ## interop/ivec.h (new) ##
     @@
     +#ifndef IVEC_H
     +#define IVEC_H
     +
    -+#include "../git-compat-util.h"
    ++#include <git-compat-util.h>
    ++
    ++#ifdef __cplusplus
    ++extern "C" {
    ++#endif
     +
     +struct rawivec {
    -+	void* ptr;
    ++	void *ptr;
     +	usize length;
     +	usize capacity;
     +	usize element_size;
    @@ interop/ivec.h (new)
     +
     +#define DEFINE_IVEC_TYPE(type, suffix) \
     +struct ivec_##suffix { \
    -+	type* ptr; \
    ++	type *ptr; \
     +	size_t length; \
     +	size_t capacity; \
     +	size_t element_size; \
    @@ interop/ivec.h (new)
     +DEFINE_IVEC_TYPE(usize, usize);
     +DEFINE_IVEC_TYPE(isize, isize);
     +
    -+void ivec_init(void* self, usize element_size);
    -+void ivec_zero(void* self, usize capacity);
    -+void ivec_clear(void* self);
    -+void ivec_reserve_exact(void* self, usize additional);
    -+void ivec_reserve(void* self, usize additional);
    -+void ivec_shrink_to_fit(void* self);
    -+void ivec_resize(void* self, usize new_length, void* default_value);
    -+void ivec_push(void* self, void* value);
    -+void ivec_extend_from_slice(void* self, void const* ptr, usize size);
    -+bool ivec_equal(void* self, void* other);
    -+void ivec_free(void* self);
    -+void ivec_move(void* source, void* destination);
    ++void ivec_init(void *self_, usize element_size);
    ++void ivec_zero(void *self_, usize capacity);
    ++void ivec_clear(void *self_);
    ++void ivec_reserve_exact(void *self_, usize additional);
    ++void ivec_reserve(void *self_, usize additional);
    ++void ivec_shrink_to_fit(void *self_);
    ++void ivec_resize(void *self_, usize new_length, void *default_value);
    ++void ivec_push(void *self_, void *value);
    ++void ivec_extend_from_slice(void *self_, void const *ptr, usize size);
    ++bool ivec_equal(void *self_, void *other);
    ++void ivec_free(void *self_);
    ++void ivec_move(void *source, void *destination);
    ++
    ++#ifdef __cplusplus
    ++}
    ++#endif
     +
     +#endif //IVEC_H
     
    @@ rust/interop/src/ivec.rs (new)
     +
     +impl<T> Drop for IVec<T> {
     +    fn drop(&mut self) {
    -+        unsafe {
    -+            self._free();
    -+        }
    ++        self._set_capacity(0);
     +    }
     +}
     +
    @@ rust/interop/src/ivec.rs (new)
     +        }
     +    }
     +
    -+    fn _zero(&mut self) {
    -+        self.ptr = std::ptr::null_mut();
    -+        self.length = 0;
    -+        self.capacity = 0;
    -+        // DO NOT MODIFY element_size!!!
    -+    }
    -+
    -+    unsafe fn _free(&mut self) {
    -+        free(self.ptr as *mut std::ffi::c_void);
    -+        self._zero();
    -+    }
    -+
     +    fn _set_capacity(&mut self, new_capacity: usize) {
     +        unsafe {
     +            if new_capacity == self.capacity {
     +                return;
     +            }
    ++            if new_capacity < self.length {
    ++                self.truncate(new_capacity);
    ++            }
     +            if new_capacity == 0 {
    -+                self._free();
    ++                free(self.ptr as *mut c_void);
    ++                self.ptr = std::ptr::null_mut();
    ++                self.length = 0;
    ++                self.capacity = 0;
    ++                // DO NOT MODIFY element_size!!!
     +            } else {
    -+                let t = realloc(
    -+                    self.ptr as *mut std::ffi::c_void,
    -+                    new_capacity * size_of::<T>(),
    -+                );
    ++                let t = realloc(self.ptr as *mut c_void, new_capacity * size_of::<T>());
     +                if t.is_null() {
     +                    panic!("out of memory");
     +                }
    @@ rust/interop/src/ivec.rs (new)
     +            /* capacity does not need to be changed */
     +        }
     +
    ++        /* IVec grows */
     +        if new_length > self.length {
     +            let range = self.length..new_length;
     +            self._buffer_mut()[range].fill(default_value);
    ++            self.length = new_length;
     +        }
     +
    -+        self.length = new_length;
    ++        /* IVec shrinks */
    ++        if new_length < self.length {
    ++            self.truncate(new_length);
    ++        }
     +    }
     +
     +    fn _buffer_mut(&mut self) -> &mut [T] {
    @@ rust/interop/src/ivec.rs (new)
     +        self._resize(new_length, default_value, true);
     +    }
     +
    ++    pub fn truncate(&mut self, new_length: usize) {
    ++        if new_length >= self.length {
    ++            return;
    ++        }
    ++
    ++        if std::mem::needs_drop::<T>() {
    ++            let range = new_length..self.length;
    ++            for v in &mut self.as_mut_slice()[range] {
    ++                unsafe {
    ++                    std::ptr::drop_in_place(v);
    ++                }
    ++            }
    ++        }
    ++
    ++        self.length = new_length;
    ++    }
    ++
     +    pub fn insert(&mut self, index: usize, value: T) {
     +        if self.length == self.capacity {
     +            self.reserve(1);
    @@ rust/interop/src/ivec.rs (new)
     +#[cfg(test)]
     +mod tests {
     +    use crate::ivec::IVec;
    ++    use std::cell::RefCell;
    ++    use std::mem::size_of;
    ++    use std::ops::{Deref, DerefMut};
     +    use std::panic;
    ++    use std::rc::Rc;
    ++
    ++    struct DropTest {
    ++        value: Rc<RefCell<u64>>,
    ++    }
    ++
    ++    impl DropTest {
    ++        fn new(value: Rc<RefCell<u64>>) -> Self {
    ++            Self { value }
    ++        }
    ++    }
    ++
    ++    impl Drop for DropTest {
    ++        fn drop(&mut self) {
    ++            *self.value.borrow_mut() -= 1;
    ++        }
    ++    }
    ++
    ++    #[test]
    ++    fn test_drop_elements() {
    ++        let counter = Rc::new(RefCell::new(0u64));
    ++        let size = 5;
    ++
    ++        /* drop whole IVec */
    ++        let mut vec = IVec::new();
    ++        for _ in 0..size {
    ++            let tmp = DropTest::new(counter.clone());
    ++            *tmp.value.borrow_mut() += 1;
    ++            vec.push(tmp);
    ++        }
    ++        let cur = *counter.borrow();
    ++        assert_eq!(size, cur);
    ++        drop(vec);
    ++
    ++        let cur = *counter.borrow();
    ++        assert_eq!(0u64, cur);
    ++
    ++        /* drop some elements */
    ++        let mut vec = IVec::new();
    ++        for i in 0..size {
    ++            let tmp = DropTest::new(counter.clone());
    ++            *tmp.value.borrow_mut() += 1;
    ++            vec.push(tmp);
    ++        }
    ++        let cur = *counter.borrow().deref();
    ++        assert_eq!(size, cur);
    ++
    ++        let expected = 2u64;
    ++        vec.truncate(expected as usize);
    ++
    ++        let cur = *counter.borrow();
    ++        assert_eq!(expected, cur);
    ++        drop(vec);
    ++    }
     +
     +    #[test]
     +    fn test_panic_on_out_of_bounds() {
    @@ rust/interop/src/ivec.rs (new)
     +        let expected = vec.capacity + 10;
     +        let default_value = 19;
     +        vec.resize(expected, default_value);
    -+        // assert_eq!(vec.capacity, vec.slice.len());
     +        assert_eq!(expected, vec.length);
     +        assert!(vec.capacity >= expected);
     +        for i in 0..vec.length {
    @@ rust/interop/src/ivec.rs (new)
     +        }
     +
     +        vec.reserve(10);
    -+        // assert_eq!(vec.capacity, vec.slice.len());
     +        assert!(vec.capacity > vec.length);
     +        let length_before = vec.length;
     +        vec.shrink_to_fit();
     +        assert_eq!(length_before, vec.length);
     +        assert_eq!(vec.length, vec.capacity);
    -+        // assert_eq!(vec.capacity, vec.slice.len());
     +    }
     +
     +    #[test]
    @@ rust/interop/src/ivec.rs (new)
     +        assert_eq!(16, vec.element_size);
     +        assert_eq!(size_of::<usize>() * 4, size_of::<IVec<u128>>());
     +    }
    -+
    -+    #[test]
    -+    fn test_manual_free() {
    -+        type TestType = i16;
    -+        let mut vec = IVec::<TestType>::new();
    -+
    -+        unsafe { vec._free() };
    -+        assert!(vec.ptr.is_null());
    -+        assert_eq!(0, vec.length);
    -+        assert_eq!(0, vec.capacity);
    -+        assert_eq!(size_of::<TestType>(), vec.element_size);
    -+    }
     +}
     
      ## rust/interop/src/lib.rs ##
    @@ rust/interop/src/lib.rs
     +    pub fn realloc(ptr: *mut c_void, size: usize) -> *mut c_void;
     +    pub fn free(ptr: *mut c_void);
     +}
    -
    - ## rust/xdiff/src/lib.rs ##
    -@@
    -+
 7:  d4bed95463 =  7:  3a6f822e2a xdiff/xprepare: remove superfluous forward declarations
 8:  7c68ce5349 =  8:  c7cd71dae0 xdiff: delete unnecessary fields from xrecord_t and xdfile_t
 9:  e516ccc8c0 =  9:  1096c8f0a4 xdiff: make fields of xrecord_t Rust friendly
10:  21bfb9f088 = 10:  0cd2168512 xdiff: use one definition for freeing xdfile_t
11:  6ce0e252b3 ! 11:  7ea2dccd71 xdiff: replace chastore with an ivec in xdfile_t
    @@ Commit message
         xdiff: replace chastore with an ivec in xdfile_t
     
         xdfile_t currently uses a chastore which functions as a memory pool and
    -    a vector which maps to the alocations created by the chastore. It seems
    +    a vector which maps to the allocations created by the chastore. It seems
         like xrecord_t used to be a linked list until the recs and nrec fields
         were added. I think that xrecord_t.next was meant to be removed, but
         was overlooked. This dual data structure setup make the code somewhat
12:  0cfc6cf26b = 12:  d8f561e173 xdiff: delete nrec field from xdfile_t
13:  cf0387d851 = 13:  15c2854a32 xdiff: delete recs field from xdfile_t
14:  ea699135f9 = 14:  3dacb2c09d xdiff: make xdfile_t more rust friendly
15:  b18544b74f ! 15:  9fdd23acf6 xdiff: implement xdl_trim_ends() in Rust
    @@ rust/xdiff/src/lib.rs
     @@
     +pub mod xprepare;
     +pub mod xtypes;
    - 
    ++
     +use crate::xprepare::trim_ends;
     +use crate::xtypes::xdfile;
     +


Ezekiel Newren (13):
  xdiff: introduce rust
  github workflows: install rust
  github workflows: upload Cargo.lock
  ivec: create a vector type that is interoperable between C and Rust
  xdiff/xprepare: remove superfluous forward declarations
  xdiff: delete unnecessary fields from xrecord_t and xdfile_t
  xdiff: make fields of xrecord_t Rust friendly
  xdiff: use one definition for freeing xdfile_t
  xdiff: replace chastore with an ivec in xdfile_t
  xdiff: delete nrec field from xdfile_t
  xdiff: delete recs field from xdfile_t
  xdiff: make xdfile_t more rust friendly
  xdiff: implement xdl_trim_ends() in Rust

Johannes Schindelin (1):
  win+Meson: do allow linking with the Rust-built xdiff

brian m. carlson (1):
  doc: add a policy for using Rust

 .github/workflows/main.yml                    |  88 ++-
 .gitignore                                    |   3 +
 Documentation/Makefile                        |   1 +
 Documentation/technical/platform-support.adoc |   2 +
 Documentation/technical/rust-support.adoc     | 142 +++++
 Makefile                                      |  67 ++-
 build_rust.sh                                 |  57 ++
 ci/install-dependencies.sh                    |  14 +-
 ci/install-rust-toolchain.sh                  |  30 +
 ci/install-rustup.sh                          |  25 +
 ci/lib.sh                                     |   1 +
 ci/make-test-artifacts.sh                     |   9 +
 ci/run-build-and-tests.sh                     |  13 +
 config.mak.uname                              |   4 +
 git-compat-util.h                             |  17 +
 interop/ivec.c                                | 180 ++++++
 interop/ivec.h                                |  60 ++
 meson.build                                   |  54 +-
 rust/Cargo.toml                               |   6 +
 rust/interop/Cargo.toml                       |  14 +
 rust/interop/src/ivec.rs                      | 516 ++++++++++++++++++
 rust/interop/src/lib.rs                       |  10 +
 rust/xdiff/Cargo.toml                         |  15 +
 rust/xdiff/src/lib.rs                         |  15 +
 rust/xdiff/src/xprepare.rs                    |  27 +
 rust/xdiff/src/xtypes.rs                      |  19 +
 xdiff/xdiffi.c                                |  60 +-
 xdiff/xdiffi.h                                |   8 +-
 xdiff/xemit.c                                 |  24 +-
 xdiff/xhistogram.c                            |   2 +-
 xdiff/xmerge.c                                |  72 +--
 xdiff/xpatience.c                             |  16 +-
 xdiff/xprepare.c                              | 271 ++++-----
 xdiff/xtypes.h                                |  27 +-
 xdiff/xutils.c                                |  12 +-
 35 files changed, 1562 insertions(+), 319 deletions(-)
 create mode 100644 Documentation/technical/rust-support.adoc
 create mode 100755 build_rust.sh
 create mode 100755 ci/install-rust-toolchain.sh
 create mode 100755 ci/install-rustup.sh
 create mode 100644 interop/ivec.c
 create mode 100644 interop/ivec.h
 create mode 100644 rust/Cargo.toml
 create mode 100644 rust/interop/Cargo.toml
 create mode 100644 rust/interop/src/ivec.rs
 create mode 100644 rust/interop/src/lib.rs
 create mode 100644 rust/xdiff/Cargo.toml
 create mode 100644 rust/xdiff/src/lib.rs
 create mode 100644 rust/xdiff/src/xprepare.rs
 create mode 100644 rust/xdiff/src/xtypes.rs


base-commit: 16bd9f20a403117f2e0d9bcda6c6e621d3763e77
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-git-2043%2Fezekielnewren%2Fintroduce_rust-v1
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-git-2043/ezekielnewren/introduce_rust-v1
Pull-Request: https://github.com/git/git/pull/2043
-- 
gitgitgadget

^ permalink raw reply	[flat|nested] 100+ messages in thread

* [PATCH 01/15] doc: add a policy for using Rust
  2025-08-29 19:42 [PATCH 00/15] Introduce rust: In xdiff Ezekiel Newren via GitGitGadget
@ 2025-08-29 19:42 ` brian m. carlson via GitGitGadget
  2025-08-29 20:00   ` brian m. carlson
  2025-08-29 19:42 ` [PATCH 02/15] xdiff: introduce rust Ezekiel Newren via GitGitGadget
                   ` (14 subsequent siblings)
  15 siblings, 1 reply; 100+ messages in thread
From: brian m. carlson via GitGitGadget @ 2025-08-29 19:42 UTC (permalink / raw)
  To: git; +Cc: Ezekiel Newren, brian m. carlson

From: "brian m. carlson" <sandals@crustytoothpaste.net>

Git has historically been written primarily in C, with some shell and
Perl.  However, C is not memory safe, which makes it more likely that
security vulnerabilities or other bugs will be introduced, and it is
also more verbose and less ergonomic than other, more modern languages.

One of the most common modern compiled languages which is easily
interoperable with C is Rust.  It is popular (the most admired language
on the 2024 Stack Overflow Developer Survey), efficient, portable, and
robust.

Introduce a document laying out the incremental introduction of Rust to
Git and provide a detailed rationale for doing so, including the points
above.  Propose a design for this approach that addresses the needs of
downstreams and distributors, as well as contributors.

Since we don't want to carry both a C and Rust version of code and want
to be able to add new features only in Rust, mention that Rust is a
required part of our platform support policy.

It should be noted that a recent discussion at the Berlin Git Merge
Contributor Summit found widespread support for the addition of Rust to
Git.  While of course not all contributors were represented, the
proposal appeared to have the support of a majority of active
contributors.

Signed-off-by: brian m. carlson <sandals@crustytoothpaste.net>
[en: Added some comments about types, and changed the recommondations
     about cbindgen, bindgen, rustix, libc.]
Signed-off-by: Ezekiel Newren <ezekielnewren@gmail.com>
---
 Documentation/Makefile                        |   1 +
 Documentation/technical/platform-support.adoc |   2 +
 Documentation/technical/rust-support.adoc     | 142 ++++++++++++++++++
 3 files changed, 145 insertions(+)
 create mode 100644 Documentation/technical/rust-support.adoc

diff --git a/Documentation/Makefile b/Documentation/Makefile
index b109d25e9c..066b761c01 100644
--- a/Documentation/Makefile
+++ b/Documentation/Makefile
@@ -127,6 +127,7 @@ TECH_DOCS += technical/parallel-checkout
 TECH_DOCS += technical/partial-clone
 TECH_DOCS += technical/platform-support
 TECH_DOCS += technical/racy-git
+TECH_DOCS += technical/rust-support
 TECH_DOCS += technical/reftable
 TECH_DOCS += technical/scalar
 TECH_DOCS += technical/send-pack-pipeline
diff --git a/Documentation/technical/platform-support.adoc b/Documentation/technical/platform-support.adoc
index 0a2fb28d62..dc71672dcb 100644
--- a/Documentation/technical/platform-support.adoc
+++ b/Documentation/technical/platform-support.adoc
@@ -33,6 +33,8 @@ meet the following minimum requirements:
 
 * Has active security support (taking security releases of dependencies, etc)
 
+* Supports Rust and the toolchain version specified in link:rust-support.adoc[].
+
 These requirements are a starting point, and not sufficient on their own for the
 Git community to be enthusiastic about supporting your platform. Maintainers of
 platforms which do meet these requirements can follow the steps below to make it
diff --git a/Documentation/technical/rust-support.adoc b/Documentation/technical/rust-support.adoc
new file mode 100644
index 0000000000..57a001fa2d
--- /dev/null
+++ b/Documentation/technical/rust-support.adoc
@@ -0,0 +1,142 @@
+Usage of Rust in Git
+====================
+
+Objective
+---------
+Introduce Rust into Git incrementally to improve security and maintainability.
+
+Background
+----------
+Git has historically been written primarily in C, with some portions in shell,
+Perl, or other languages.  At the time it was originally written, this was
+important for portability and was a logical choice for software development.
+
+:0: link:https://security.googleblog.com/2024/09/eliminating-memory-safety-vulnerabilities-Android.html
+:1: link:https://www.cisa.gov/resources-tools/resources/product-security-bad-practices
+
+However, as time has progressed, we've seen an increased concern with memory
+safety vulnerabilities and the development of newer languages, such as Rust,
+that substantially limit or eliminate this class of vulnerabilities.
+Development in a variety of projects has found that memory safety
+vulnerabilities constitute about 70% of vulnerabilities of software in
+languages that are not memory safe.  For instance, {0}[one survey of Android]
+found that memory safety vulnerabilities decreased from 76% to 24% over six
+years due to an increase in memory safe code.  Similarly, the U.S. government
+is {1}[proposing to classify development in memory unsafe languages as a
+Product Security Bad Practice"].
+
+These risks are even more substantial when we consider the fact that Git is a
+network-facing service.  Many organizations run Git servers internally or use a
+cloud-based forge, and the risk of accidental exposure or compromise of user
+data is substantial.  It's important to ensure that Git, whether it's used
+locally or remotely, is robustly secure.
+
+In addition, C is a difficult language to write well and concisely.  While it
+is of course possible to do anything with C, it lacks built-in support for
+niceties found in modern languages, such as hash tables, generics, typed
+errors, and automatic destruction, and most modern language offer shorter, more
+ergonomic syntax for expressing code.  This is valuable functionality that can
+allow Git to be developed more rapidly, more easily, by more developers of a
+variety of levels, and with more confidence in the correctness of the code.
+
+For these reasons, adding Rust to Git is a sensible and prudent move that will
+allow us to improve the quality of the code and potentially attract new developers.
+
+Goals
+-----
+1. Git continues to build, run, and pass tests on a wide variety of operating
+   systems and architectures.
+2. Transition from C to Rust is incremental; that is, code can be ported as it
+   is convenient and Git does not need to transition all at once.
+3. Git continues to support older operating systems in conformance with the
+   platform support policy.
+
+Non-Goals
+---------
+1. Support for every possible operating system and architecture.  Git already
+   has a platform support policy which defines what is supported and we already
+   exclude some operating systems for various reasons (e.g., lacking enough POSIX
+   tools to pass the test suite).
+2. Implementing C-only versions of Rust code or compiling a C-only Git.  This
+   would be difficult to maintain and would not offer the ergonomic benefits we
+   desire.
+
+Design
+------
+Git will adopt Rust incrementally.  This transition will start with the
+creation of a static library that can be linked into the existing Git binaries.
+At some point, we may wish to expose a dynamic library and compile the Git
+binaries themselves using Rust.  Using an incremental approach allows us to
+determine as we go along how to structure our code in the best way for the
+project and avoids the need to make hard, potentially disruptive, transitions
+caused by porting a binary wholesale from one language to another that might
+introduce bugs.
+
+Crates like libc or rustix define types like c_long, but in ways that are not
+safe across platforms.
+From https://docs.rs/rustix/latest/rustix/ffi/type.c_long.html:
+
+    This type will always be i32 or i64.  Most notably, many Linux-based
+    systems assume an i64, but Windows assumes i32.  The C standard technically
+    only requires that this type be a signed integer that is at least 32 bits
+    and at least the size of an int, although in practice, no system would
+    have a long that is neither an i32 nor i64.
+
+Also, note that other locations, such as
+https://docs.rs/libc/latest/libc/type.c_long.html, just hardcode c_long as i64
+even though C may mean i32 on some platforms.
+
+As such, using the c_long type would give us portability issues, and
+perpetuate some of the bugs git has faced across platforms.  Avoid using C's
+types (long, unsigned, char, etc.), and switch to unambiguous types (e.g. i32
+or i64) before trying to make C and Rust interoperate.
+
+Crates like libc and rustix may have also traditionally aided interoperability
+with older versions of Rust (e.g.  when worrying about stat[64] system calls),
+but the Rust standard library in newer versions of Rust handle these concerns
+in a platform agnostic way.  There may arise cases where we need to consider
+these crates, but for now we omit them.
+
+Tools like bindgen and cbindgen create C-styled unsafe Rust code rather than
+idiomatic Rust; where possible, we prefer to switch to idiomatic Rust.  Any
+standard C library functions that are needed can be manually wrapped on the
+Rust side.
+
+Rust upstream releases every six weeks and only supports the latest stable
+release.  While it is nice that upstream is active, we would like our software
+releases to have a lifespan exceeding six weeks.  To allow compiling our code
+on a variety of systems, we will support the version of Rust in Debian stable,
+plus, for a year after a new Debian stable is released, the version in Debian
+oldstable.
+
+This provides an approximately three-year lifespan of support for a Rust
+release and allows us to support a variety of operating systems and
+architectures, including those for which Rust upstream does not build binaries.
+Debian stable is the benchmark distribution used by many Rust projects when
+determining supported Rust versions, and it is an extremely portable and
+popular free software operating system that is available to the public at no
+charge, which makes it a sensible choice for us as well.
+
+We may change this policy if the Rust project issues long-term support releases
+or the Rust community and distributors agree on releases to target as if they
+were long-term support releases.
+
+This version support policy necessitates that we be very careful about the
+dependencies we include, since many Rust projects support only the latest
+stable version.  However, we typically have been careful about dependencies in
+the first place, so this should not be a major departure from existing policy,
+although it may be a change for some existing Rust developers.
+
+We will avoid including the `Cargo.lock` file in the repository and instead
+specify minimum dependency versions in the `Cargo.toml` file.  We want to allow
+people to use newer versions of dependencies if necessary to support newer
+platforms without needing to force upgrades of dependencies on all users, and
+it provides additional flexibility for distribution maintainers.
+
+We do not plan to support beta or nightly versions of the Rust compiler.  These
+versions may change rapidly and especially parts of the toolchain such as
+Clippy, the lint tool, can have false positives or add additional warnings with
+too great of a frequency to be supportable by the project.  However, we do plan
+to support alternate compilers, such as the rust_codegen_gcc backend and gccrs
+when they are stable and support our desired release versions.  This will
+provide greater support for more operating systems and architectures.
-- 
gitgitgadget


^ permalink raw reply related	[flat|nested] 100+ messages in thread

* [PATCH 02/15] xdiff: introduce rust
  2025-08-29 19:42 [PATCH 00/15] Introduce rust: In xdiff Ezekiel Newren via GitGitGadget
  2025-08-29 19:42 ` [PATCH 01/15] doc: add a policy for using Rust brian m. carlson via GitGitGadget
@ 2025-08-29 19:42 ` Ezekiel Newren via GitGitGadget
  2025-08-29 19:42 ` [PATCH 03/15] github workflows: install rust Ezekiel Newren via GitGitGadget
                   ` (13 subsequent siblings)
  15 siblings, 0 replies; 100+ messages in thread
From: Ezekiel Newren via GitGitGadget @ 2025-08-29 19:42 UTC (permalink / raw)
  To: git; +Cc: Ezekiel Newren, Ezekiel Newren

From: Ezekiel Newren <ezekielnewren@gmail.com>

Upcoming patches will simplify xdiff, while also porting parts of it to
Rust. In preparation, add some stubs and setup the Rust build. For now,
it is easier to let cargo build rust and have make or meson merely link
against the static library that cargo builds. In line with ongoing
libification efforts, use multiple crates to allow more modularity on
the Rust side. xdiff is the crate that this series will focus on, but
we also introduce the interop crate for future patch series.

In order to facilitate interoperability between C and Rust, introduce C
definitions for Rust primitive types in git-compat-util.h.

Signed-off-by: Ezekiel Newren <ezekielnewren@gmail.com>
---
 .gitignore              |  3 +++
 Makefile                | 53 ++++++++++++++++++++++++++++----------
 build_rust.sh           | 57 +++++++++++++++++++++++++++++++++++++++++
 git-compat-util.h       | 17 ++++++++++++
 meson.build             | 52 +++++++++++++++++++++++++++++++------
 rust/Cargo.toml         |  6 +++++
 rust/interop/Cargo.toml | 14 ++++++++++
 rust/interop/src/lib.rs |  0
 rust/xdiff/Cargo.toml   | 15 +++++++++++
 rust/xdiff/src/lib.rs   |  0
 10 files changed, 196 insertions(+), 21 deletions(-)
 create mode 100755 build_rust.sh
 create mode 100644 rust/Cargo.toml
 create mode 100644 rust/interop/Cargo.toml
 create mode 100644 rust/interop/src/lib.rs
 create mode 100644 rust/xdiff/Cargo.toml
 create mode 100644 rust/xdiff/src/lib.rs

diff --git a/.gitignore b/.gitignore
index 04c444404e..ff81e3580c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -254,3 +254,6 @@ Release/
 /contrib/buildsystems/out
 /contrib/libgit-rs/target
 /contrib/libgit-sys/target
+/.idea/
+/rust/target/
+/rust/Cargo.lock
diff --git a/Makefile b/Makefile
index 70d1543b6b..1ec0c1ee66 100644
--- a/Makefile
+++ b/Makefile
@@ -919,6 +919,29 @@ TEST_SHELL_PATH = $(SHELL_PATH)
 
 LIB_FILE = libgit.a
 XDIFF_LIB = xdiff/lib.a
+
+EXTLIBS =
+
+ifeq ($(DEBUG), 1)
+  RUST_BUILD_MODE = debug
+else
+  RUST_BUILD_MODE = release
+endif
+
+RUST_TARGET_DIR = rust/target/$(RUST_BUILD_MODE)
+RUST_FLAGS_FOR_C = -L$(RUST_TARGET_DIR)
+
+.PHONY: compile_rust
+compile_rust:
+	./build_rust.sh . $(RUST_BUILD_MODE) xdiff
+
+EXTLIBS += ./$(RUST_TARGET_DIR)/libxdiff.a
+
+UNAME_S := $(shell uname -s)
+ifeq ($(UNAME_S),Linux)
+  EXTLIBS += -ldl
+endif
+
 REFTABLE_LIB = reftable/libreftable.a
 
 GENERATED_H += command-list.h
@@ -1390,7 +1413,7 @@ UNIT_TEST_OBJS += $(UNIT_TEST_DIR)/lib-reftable.o
 
 # xdiff and reftable libs may in turn depend on what is in libgit.a
 GITLIBS = common-main.o $(LIB_FILE) $(XDIFF_LIB) $(REFTABLE_LIB) $(LIB_FILE)
-EXTLIBS =
+
 
 GIT_USER_AGENT = git/$(GIT_VERSION)
 
@@ -2541,7 +2564,7 @@ git.sp git.s git.o: EXTRA_CPPFLAGS = \
 	'-DGIT_MAN_PATH="$(mandir_relative_SQ)"' \
 	'-DGIT_INFO_PATH="$(infodir_relative_SQ)"'
 
-git$X: git.o GIT-LDFLAGS $(BUILTIN_OBJS) $(GITLIBS)
+git$X: git.o GIT-LDFLAGS $(BUILTIN_OBJS) $(GITLIBS) compile_rust
 	$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) \
 		$(filter %.o,$^) $(LIBS)
 
@@ -2891,17 +2914,17 @@ headless-git.o: compat/win32/headless.c GIT-CFLAGS
 headless-git$X: headless-git.o git.res GIT-LDFLAGS
 	$(QUIET_LINK)$(CC) $(ALL_CFLAGS) $(ALL_LDFLAGS) -mwindows -o $@ $< git.res
 
-git-%$X: %.o GIT-LDFLAGS $(GITLIBS)
+git-%$X: %.o GIT-LDFLAGS $(GITLIBS) compile_rust
 	$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(LIBS)
 
-git-imap-send$X: imap-send.o $(IMAP_SEND_BUILDDEPS) GIT-LDFLAGS $(GITLIBS)
+git-imap-send$X: imap-send.o $(IMAP_SEND_BUILDDEPS) GIT-LDFLAGS $(GITLIBS) compile_rust
 	$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
 		$(IMAP_SEND_LDFLAGS) $(LIBS)
 
-git-http-fetch$X: http.o http-walker.o http-fetch.o GIT-LDFLAGS $(GITLIBS)
+git-http-fetch$X: http.o http-walker.o http-fetch.o GIT-LDFLAGS $(GITLIBS) compile_rust
 	$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
 		$(CURL_LIBCURL) $(LIBS)
-git-http-push$X: http.o http-push.o GIT-LDFLAGS $(GITLIBS)
+git-http-push$X: http.o http-push.o GIT-LDFLAGS $(GITLIBS) compile_rust
 	$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
 		$(CURL_LIBCURL) $(EXPAT_LIBEXPAT) $(LIBS)
 
@@ -2911,11 +2934,11 @@ $(REMOTE_CURL_ALIASES): $(REMOTE_CURL_PRIMARY)
 	ln -s $< $@ 2>/dev/null || \
 	cp $< $@
 
-$(REMOTE_CURL_PRIMARY): remote-curl.o http.o http-walker.o GIT-LDFLAGS $(GITLIBS)
+$(REMOTE_CURL_PRIMARY): remote-curl.o http.o http-walker.o GIT-LDFLAGS $(GITLIBS) compile_rust
 	$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
 		$(CURL_LIBCURL) $(EXPAT_LIBEXPAT) $(LIBS)
 
-scalar$X: scalar.o GIT-LDFLAGS $(GITLIBS)
+scalar$X: scalar.o GIT-LDFLAGS $(GITLIBS) compile_rust
 	$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) \
 		$(filter %.o,$^) $(LIBS)
 
@@ -2925,6 +2948,7 @@ $(LIB_FILE): $(LIB_OBJS)
 $(XDIFF_LIB): $(XDIFF_OBJS)
 	$(QUIET_AR)$(RM) $@ && $(AR) $(ARFLAGS) $@ $^
 
+
 $(REFTABLE_LIB): $(REFTABLE_OBJS)
 	$(QUIET_AR)$(RM) $@ && $(AR) $(ARFLAGS) $@ $^
 
@@ -3294,7 +3318,7 @@ perf: all
 
 t/helper/test-tool$X: $(patsubst %,t/helper/%,$(TEST_BUILTINS_OBJS)) $(UNIT_TEST_DIR)/test-lib.o
 
-t/helper/test-%$X: t/helper/test-%.o GIT-LDFLAGS $(GITLIBS)
+t/helper/test-%$X: t/helper/test-%.o GIT-LDFLAGS $(GITLIBS) compile_rust
 	$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(filter %.a,$^) $(LIBS)
 
 check-sha1:: t/helper/test-tool$X
@@ -3756,7 +3780,10 @@ cocciclean:
 	$(RM) -r .build/contrib/coccinelle
 	$(RM) contrib/coccinelle/*.cocci.patch
 
-clean: profile-clean coverage-clean cocciclean
+rustclean:
+	cd rust && cargo clean
+
+clean: profile-clean coverage-clean cocciclean rustclean
 	$(RM) -r .build $(UNIT_TEST_BIN)
 	$(RM) GIT-TEST-SUITES
 	$(RM) po/git.pot po/git-core.pot
@@ -3911,13 +3938,13 @@ FUZZ_CXXFLAGS ?= $(ALL_CFLAGS)
 .PHONY: fuzz-all
 fuzz-all: $(FUZZ_PROGRAMS)
 
-$(FUZZ_PROGRAMS): %: %.o oss-fuzz/dummy-cmd-main.o $(GITLIBS) GIT-LDFLAGS
+$(FUZZ_PROGRAMS): %: %.o oss-fuzz/dummy-cmd-main.o $(GITLIBS) GIT-LDFLAGS compile_rust
 	$(QUIET_LINK)$(FUZZ_CXX) $(FUZZ_CXXFLAGS) -o $@ $(ALL_LDFLAGS) \
 		-Wl,--allow-multiple-definition \
 		$(filter %.o,$^) $(filter %.a,$^) $(LIBS) $(LIB_FUZZING_ENGINE)
 
 $(UNIT_TEST_PROGS): $(UNIT_TEST_BIN)/%$X: $(UNIT_TEST_DIR)/%.o $(UNIT_TEST_OBJS) \
-	$(GITLIBS) GIT-LDFLAGS
+	$(GITLIBS) GIT-LDFLAGS compile_rust
 	$(call mkdir_p_parent_template)
 	$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) \
 		$(filter %.o,$^) $(filter %.a,$^) $(LIBS)
@@ -3936,7 +3963,7 @@ $(UNIT_TEST_DIR)/clar.suite: $(UNIT_TEST_DIR)/clar-decls.h $(UNIT_TEST_DIR)/gene
 $(UNIT_TEST_DIR)/clar/clar.o: $(UNIT_TEST_DIR)/clar.suite
 $(CLAR_TEST_OBJS): $(UNIT_TEST_DIR)/clar-decls.h
 $(CLAR_TEST_OBJS): EXTRA_CPPFLAGS = -I$(UNIT_TEST_DIR)
-$(CLAR_TEST_PROG): $(UNIT_TEST_DIR)/clar.suite $(CLAR_TEST_OBJS) $(GITLIBS) GIT-LDFLAGS
+$(CLAR_TEST_PROG): $(UNIT_TEST_DIR)/clar.suite $(CLAR_TEST_OBJS) $(GITLIBS) GIT-LDFLAGS compile_rust
 	$(call mkdir_p_parent_template)
 	$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(LIBS)
 
diff --git a/build_rust.sh b/build_rust.sh
new file mode 100755
index 0000000000..192385a1d9
--- /dev/null
+++ b/build_rust.sh
@@ -0,0 +1,57 @@
+#!/bin/sh
+
+
+rustc -vV || exit $?
+cargo --version || exit $?
+
+dir_git_root=${0%/*}
+dir_build=$1
+rust_build_profile=$2
+crate=$3
+
+dir_rust=$dir_git_root/rust
+
+if [ "$dir_git_root" = "" ]; then
+  echo "did not specify the directory for the root of git"
+  exit 1
+fi
+
+if [ "$dir_build" = "" ]; then
+  echo "did not specify the build directory"
+  exit 1
+fi
+
+if [ "$rust_build_profile" = "" ]; then
+  echo "did not specify the rust_build_profile"
+  exit 1
+fi
+
+if [ "$rust_build_profile" = "release" ]; then
+  rust_args="--release"
+  export RUSTFLAGS=''
+elif [ "$rust_build_profile" = "debug" ]; then
+  rust_args=""
+  export RUSTFLAGS='-C debuginfo=2 -C opt-level=1 -C force-frame-pointers=yes'
+else
+  echo "illegal rust_build_profile value $rust_build_profile"
+  exit 1
+fi
+
+cd $dir_rust && cargo clean && pwd && cargo build -p $crate $rust_args; cd $dir_git_root
+
+libfile="lib${crate}.a"
+if rustup show active-toolchain | grep windows-msvc; then
+  libfile="${crate}.lib"
+fi
+dst=$dir_build/$libfile
+
+if [ "$dir_git_root" != "$dir_build" ]; then
+  src=$dir_rust/target/$rust_build_profile/$libfile
+  if [ ! -f $src ]; then
+    echo >&2 "::error:: cannot find path of static library $src is not a file or does not exist"
+    exit 5
+  fi
+
+  rm $dst 2>/dev/null
+  mv $src $dst
+fi
diff --git a/git-compat-util.h b/git-compat-util.h
index 4678e21c4c..82dc99764a 100644
--- a/git-compat-util.h
+++ b/git-compat-util.h
@@ -196,6 +196,23 @@ static inline int is_xplatform_dir_sep(int c)
 #include "compat/msvc.h"
 #endif
 
+/* rust types */
+typedef uint8_t   u8;
+typedef uint16_t  u16;
+typedef uint32_t  u32;
+typedef uint64_t  u64;
+
+typedef int8_t    i8;
+typedef int16_t   i16;
+typedef int32_t   i32;
+typedef int64_t   i64;
+
+typedef float     f32;
+typedef double    f64;
+
+typedef size_t    usize;
+typedef ptrdiff_t isize;
+
 /* used on Mac OS X */
 #ifdef PRECOMPOSE_UNICODE
 #include "compat/precompose_utf8.h"
diff --git a/meson.build b/meson.build
index 596f5ac711..324f968338 100644
--- a/meson.build
+++ b/meson.build
@@ -267,6 +267,40 @@ version_gen_environment.set('GIT_DATE', get_option('build_date'))
 version_gen_environment.set('GIT_USER_AGENT', get_option('user_agent'))
 version_gen_environment.set('GIT_VERSION', get_option('version'))
 
+if get_option('optimization') in ['2', '3', 's', 'z']
+  rust_build_profile = 'release'
+else
+  rust_build_profile = 'debug'
+endif
+
+# Run `rustup show active-toolchain` and capture output
+rustup_out = run_command('rustup', 'show', 'active-toolchain',
+                         check: true).stdout().strip()
+
+rust_crates = ['xdiff']
+rust_builds = []
+
+foreach crate : rust_crates
+  if rustup_out.contains('windows-msvc')
+    libfile = crate + '.lib'
+  else
+    libfile = 'lib' + crate + '.a'
+  endif
+
+  rust_builds += custom_target(
+    'rust_build_'+crate,
+    output: libfile,
+    build_by_default: true,
+    build_always_stale: true,
+    command: [
+      meson.project_source_root() / 'build_rust.sh',
+      meson.current_build_dir(), rust_build_profile, crate,
+    ],
+    install: false,
+  )
+endforeach
+
+
 compiler = meson.get_compiler('c')
 
 libgit_sources = [
@@ -1678,14 +1712,16 @@ version_def_h = custom_target(
 libgit_sources += version_def_h
 
 libgit = declare_dependency(
-  link_with: static_library('git',
-    sources: libgit_sources,
-    c_args: libgit_c_args + [
-      '-DGIT_VERSION_H="' + version_def_h.full_path() + '"',
-    ],
-    dependencies: libgit_dependencies,
-    include_directories: libgit_include_directories,
-  ),
+  link_with: [
+    static_library('git',
+      sources: libgit_sources,
+      c_args: libgit_c_args + [
+        '-DGIT_VERSION_H="' + version_def_h.full_path() + '"',
+      ],
+      dependencies: libgit_dependencies,
+      include_directories: libgit_include_directories,
+    ),
+  ] + rust_builds,
   compile_args: libgit_c_args,
   dependencies: libgit_dependencies,
   include_directories: libgit_include_directories,
diff --git a/rust/Cargo.toml b/rust/Cargo.toml
new file mode 100644
index 0000000000..ed3d79d7f8
--- /dev/null
+++ b/rust/Cargo.toml
@@ -0,0 +1,6 @@
+[workspace]
+members = [
+    "xdiff",
+    "interop",
+]
+resolver = "2"
diff --git a/rust/interop/Cargo.toml b/rust/interop/Cargo.toml
new file mode 100644
index 0000000000..045e3b01cf
--- /dev/null
+++ b/rust/interop/Cargo.toml
@@ -0,0 +1,14 @@
+[package]
+name = "interop"
+version = "0.1.0"
+edition = "2021"
+
+[lib]
+name = "interop"
+path = "src/lib.rs"
+## staticlib to generate xdiff.a for use by gcc
+## cdylib (optional) to generate xdiff.so for use by gcc
+## rlib is required by the rust unit tests
+crate-type = ["staticlib", "rlib"]
+
+[dependencies]
diff --git a/rust/interop/src/lib.rs b/rust/interop/src/lib.rs
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/rust/xdiff/Cargo.toml b/rust/xdiff/Cargo.toml
new file mode 100644
index 0000000000..eb7966aada
--- /dev/null
+++ b/rust/xdiff/Cargo.toml
@@ -0,0 +1,15 @@
+[package]
+name = "xdiff"
+version = "0.1.0"
+edition = "2021"
+
+[lib]
+name = "xdiff"
+path = "src/lib.rs"
+## staticlib to generate xdiff.a for use by gcc
+## cdylib (optional) to generate xdiff.so for use by gcc
+## rlib is required by the rust unit tests
+crate-type = ["staticlib", "rlib"]
+
+[dependencies]
+interop = { path = "../interop" }
diff --git a/rust/xdiff/src/lib.rs b/rust/xdiff/src/lib.rs
new file mode 100644
index 0000000000..e69de29bb2
-- 
gitgitgadget


^ permalink raw reply related	[flat|nested] 100+ messages in thread

* [PATCH 03/15] github workflows: install rust
  2025-08-29 19:42 [PATCH 00/15] Introduce rust: In xdiff Ezekiel Newren via GitGitGadget
  2025-08-29 19:42 ` [PATCH 01/15] doc: add a policy for using Rust brian m. carlson via GitGitGadget
  2025-08-29 19:42 ` [PATCH 02/15] xdiff: introduce rust Ezekiel Newren via GitGitGadget
@ 2025-08-29 19:42 ` Ezekiel Newren via GitGitGadget
  2025-08-29 19:42 ` [PATCH 04/15] win+Meson: do allow linking with the Rust-built xdiff Johannes Schindelin via GitGitGadget
                   ` (12 subsequent siblings)
  15 siblings, 0 replies; 100+ messages in thread
From: Ezekiel Newren via GitGitGadget @ 2025-08-29 19:42 UTC (permalink / raw)
  To: git; +Cc: Ezekiel Newren, Ezekiel Newren

From: Ezekiel Newren <ezekielnewren@gmail.com>

Prefer using actions-rs/toolchain@v1 where possible to install rustup,
but for docker targets use a script to install rustup. Consolidate the
Rust toolchain definitions in main.yaml. Use install-rust-toolchain.sh
to ensure the correct toolchain is used. Five overrides are used in
main.yaml:

  * On Windows: Rust didn't resolve the bcrypt library on Windows
    correctly until version 1.78.0. Also since rustup mis-identifies
    the Rust toolchain, the Rust target triple must be set to
    x86_64-pc-windows-gnu for make (win build), and
    x86_64-pc-windows-msvc for meson (win+Meson build).
  * On musl: libc differences, such as ftruncate64 vs ftruncate, were
    not accounted for until Rust version 1.72.0. No older version of
    Rust will work on musl for our needs.
  * In a 32-bit docker container running on a 64-bit host, we need to
    override the Rust target triple. This is because rustup asks the
    kernel for the bitness of the system and it says 64, even though
    the container is 32-bit. This also allows us to remove the
    BITNESS environment variable in ci/lib.sh.

The logic for selecting library names was initially provided in a patch
from Johannes, but was reworked and squashed into this commit.

Helped-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>
Signed-off-by: Ezekiel Newren <ezekielnewren@gmail.com>
---
 .github/workflows/main.yml   | 60 +++++++++++++++++++++++++++++++++++-
 ci/install-dependencies.sh   | 14 ++++-----
 ci/install-rust-toolchain.sh | 30 ++++++++++++++++++
 ci/install-rustup.sh         | 25 +++++++++++++++
 ci/lib.sh                    |  1 +
 ci/make-test-artifacts.sh    |  9 ++++++
 ci/run-build-and-tests.sh    | 13 ++++++++
 7 files changed, 144 insertions(+), 8 deletions(-)
 create mode 100755 ci/install-rust-toolchain.sh
 create mode 100755 ci/install-rustup.sh

diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index 7dbf9f7f12..ac1d583ab2 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -26,6 +26,13 @@ jobs:
     outputs:
       enabled: ${{ steps.check-ref.outputs.enabled }}${{ steps.skip-if-redundant.outputs.enabled }}
       skip_concurrent: ${{ steps.check-ref.outputs.skip_concurrent }}
+      rust_version_minimum: 1.61.0
+      rust_version_windows: 1.78.0
+      rust_version_musl: 1.72.0
+      ## the rust target is inferred by rustup unless specified
+      rust_target_windows_make: x86_64-pc-windows-gnu
+      rust_target_windows_meson: x86_64-pc-windows-msvc
+      rust_target_32bit_linux: i686-unknown-linux-gnu
     steps:
       - name: try to clone ci-config branch
         run: |
@@ -108,12 +115,26 @@ jobs:
     needs: ci-config
     if: needs.ci-config.outputs.enabled == 'yes'
     runs-on: windows-latest
+    env:
+      CARGO_HOME: "/c/Users/runneradmin/.cargo"
     concurrency:
       group: windows-build-${{ github.ref }}
       cancel-in-progress: ${{ needs.ci-config.outputs.skip_concurrent == 'yes' }}
     steps:
     - uses: actions/checkout@v4
     - uses: git-for-windows/setup-git-for-windows-sdk@v1
+    - name: Install rustup via github actions
+      uses: actions-rs/toolchain@v1
+      with:
+        toolchain: stable
+        profile: minimal
+        override: false
+    - name: Install Rust toolchain
+      shell: bash
+      env:
+        RUST_VERSION: ${{ needs.ci-config.outputs.rust_version_windows }}
+        RUST_TARGET: ${{ needs.ci-config.outputs.rust_target_windows_make }}
+      run: ci/install-rust-toolchain.sh
     - name: build
       shell: bash
       env:
@@ -254,12 +275,26 @@ jobs:
     needs: ci-config
     if: needs.ci-config.outputs.enabled == 'yes'
     runs-on: windows-latest
+    env:
+      CARGO_HOME: "/c/Users/runneradmin/.cargo"
     concurrency:
       group: windows-meson-build-${{ github.ref }}
       cancel-in-progress: ${{ needs.ci-config.outputs.skip_concurrent == 'yes' }}
     steps:
     - uses: actions/checkout@v4
     - uses: actions/setup-python@v5
+    - name: Install rustup via github actions
+      uses: actions-rs/toolchain@v1
+      with:
+        toolchain: stable
+        profile: minimal
+        override: false
+    - name: Install Rust toolchain
+      shell: bash
+      env:
+        RUST_VERSION: ${{ needs.ci-config.outputs.rust_version_windows }}
+        RUST_TARGET: ${{ needs.ci-config.outputs.rust_target_windows_meson }}
+      run: ci/install-rust-toolchain.sh
     - name: Set up dependencies
       shell: pwsh
       run: pip install meson ninja
@@ -329,11 +364,24 @@ jobs:
       jobname: ${{matrix.vector.jobname}}
       CI_JOB_IMAGE: ${{matrix.vector.pool}}
       TEST_OUTPUT_DIRECTORY: ${{github.workspace}}/t
+      CARGO_HOME: "/Users/runner/.cargo"
     runs-on: ${{matrix.vector.pool}}
     steps:
     - uses: actions/checkout@v4
     - run: ci/install-dependencies.sh
-    - run: ci/run-build-and-tests.sh
+    - name: Install rustup via github actions
+      uses: actions-rs/toolchain@v1
+      with:
+        toolchain: stable
+        profile: minimal
+        override: false
+    - name: Install Rust toolchain
+      shell: bash
+      env:
+        RUST_VERSION: ${{ needs.ci-config.outputs.rust_version_minimum }}
+      run: ci/install-rust-toolchain.sh
+    - name: Run build and tests
+      run: ci/run-build-and-tests.sh
     - name: print test failures
       if: failure() && env.FAILED_TEST_ARTIFACTS != ''
       run: ci/print-test-failures.sh
@@ -393,9 +441,11 @@ jobs:
           cc: gcc
         - jobname: linux-musl-meson
           image: alpine:latest
+          rust_version_override: ${{ needs.ci-config.outputs.rust_version_musl }}
         # Supported until 2025-04-02.
         - jobname: linux32
           image: i386/ubuntu:focal
+          rust_target_override: ${{ needs.ci-config.outputs.rust_target_32bit_linux }}
         - jobname: pedantic
           image: fedora:latest
         # A RHEL 8 compatible distro.  Supported until 2029-05-31.
@@ -409,6 +459,7 @@ jobs:
       CC: ${{matrix.vector.cc}}
       CI_JOB_IMAGE: ${{matrix.vector.image}}
       CUSTOM_PATH: /custom
+      CARGO_HOME: /home/builder/.cargo
     runs-on: ubuntu-latest
     container: ${{matrix.vector.image}}
     steps:
@@ -433,6 +484,13 @@ jobs:
     - run: ci/install-dependencies.sh
     - run: useradd builder --create-home
     - run: chown -R builder .
+    - name: Install rustup via script
+      run: sudo --preserve-env --set-home --user=builder ci/install-rustup.sh
+    - name: Install Rust toolchain
+      env:
+        RUST_VERSION: ${{ matrix.vector.rust_version_override || needs.ci-config.outputs.rust_version_minimum }}
+        RUST_TARGET: ${{ matrix.vector.rust_target_override || '' }}
+      run: sudo --preserve-env --set-home --user=builder ci/install-rust-toolchain.sh
     - run: sudo --preserve-env --set-home --user=builder ci/run-build-and-tests.sh
     - name: print test failures
       if: failure() && env.FAILED_TEST_ARTIFACTS != ''
diff --git a/ci/install-dependencies.sh b/ci/install-dependencies.sh
index d061a47293..7801075821 100755
--- a/ci/install-dependencies.sh
+++ b/ci/install-dependencies.sh
@@ -24,14 +24,14 @@ fi
 
 case "$distro" in
 alpine-*)
-	apk add --update shadow sudo meson ninja-build gcc libc-dev curl-dev openssl-dev expat-dev gettext \
+	apk add --update shadow sudo meson ninja-build gcc libc-dev curl curl-dev openssl-dev expat-dev gettext \
 		zlib-ng-dev pcre2-dev python3 musl-libintl perl-utils ncurses \
 		apache2 apache2-http2 apache2-proxy apache2-ssl apache2-webdav apr-util-dbd_sqlite3 \
 		bash cvs gnupg perl-cgi perl-dbd-sqlite perl-io-tty >/dev/null
 	;;
 fedora-*|almalinux-*)
 	dnf -yq update >/dev/null &&
-	dnf -yq install shadow-utils sudo make gcc findutils diffutils perl python3 gawk gettext zlib-devel expat-devel openssl-devel curl-devel pcre2-devel >/dev/null
+	dnf -yq install shadow-utils sudo make gcc findutils diffutils perl python3 gawk gettext zlib-devel expat-devel openssl-devel curl curl-devel pcre2-devel >/dev/null
 	;;
 ubuntu-*|i386/ubuntu-*|debian-*)
 	# Required so that apt doesn't wait for user input on certain packages.
@@ -55,8 +55,8 @@ ubuntu-*|i386/ubuntu-*|debian-*)
 	sudo apt-get -q update
 	sudo apt-get -q -y install \
 		$LANGUAGES apache2 cvs cvsps git gnupg $SVN \
-		make libssl-dev libcurl4-openssl-dev libexpat-dev wget sudo default-jre \
-		tcl tk gettext zlib1g-dev perl-modules liberror-perl libauthen-sasl-perl \
+		make libssl-dev curl libcurl4-openssl-dev libexpat-dev wget sudo default-jre \
+		tcl tk gettext zlib1g zlib1g-dev perl-modules liberror-perl libauthen-sasl-perl \
 		libemail-valid-perl libio-pty-perl libio-socket-ssl-perl libnet-smtp-ssl-perl libdbd-sqlite3-perl libcgi-pm-perl \
 		libsecret-1-dev libpcre2-dev meson ninja-build pkg-config \
 		${CC_PACKAGE:-${CC:-gcc}} $PYTHON_PACKAGE
@@ -121,13 +121,13 @@ ClangFormat)
 	;;
 StaticAnalysis)
 	sudo apt-get -q update
-	sudo apt-get -q -y install coccinelle libcurl4-openssl-dev libssl-dev \
+	sudo apt-get -q -y install coccinelle curl libcurl4-openssl-dev libssl-dev \
 		libexpat-dev gettext make
 	;;
 sparse)
 	sudo apt-get -q update -q
-	sudo apt-get -q -y install libssl-dev libcurl4-openssl-dev \
-		libexpat-dev gettext zlib1g-dev sparse
+	sudo apt-get -q -y install libssl-dev curl libcurl4-openssl-dev \
+		libexpat-dev gettext zlib1g zlib1g-dev sparse
 	;;
 Documentation)
 	sudo apt-get -q update
diff --git a/ci/install-rust-toolchain.sh b/ci/install-rust-toolchain.sh
new file mode 100755
index 0000000000..06a29c4cfa
--- /dev/null
+++ b/ci/install-rust-toolchain.sh
@@ -0,0 +1,30 @@
+#!/bin/sh
+
+if [ "$CARGO_HOME" = "" ]; then
+  echo >&2 "::error:: CARGO_HOME is not set"
+  exit 2
+fi
+export PATH="$CARGO_HOME/bin:$PATH"
+rustup -vV || exit $?
+
+## Enforce the correct Rust toolchain
+rustup override unset || true
+
+## install a specific version of rust
+if [ "$RUST_TARGET" != "" ]; then
+  rustup default --force-non-host "$RUST_VERSION-$RUST_TARGET" || exit $?
+else
+  rustup default "$RUST_VERSION" || exit $?
+fi
+
+rustc -vV || exit $?
+
+RE_RUST_TARGET="$RUST_TARGET"
+if [ "$RUST_TARGET" = "" ]; then
+  RE_RUST_TARGET="[^ ]+"
+fi
+
+if ! rustup show active-toolchain | grep -E "^$RUST_VERSION-$RE_RUST_TARGET \(default\)$"; then
+  echo >&2 "::error:: wrong Rust toolchain, active-toolchain: $(rustup show active-toolchain)"
+  exit 3
+fi
diff --git a/ci/install-rustup.sh b/ci/install-rustup.sh
new file mode 100755
index 0000000000..0036231aee
--- /dev/null
+++ b/ci/install-rustup.sh
@@ -0,0 +1,25 @@
+#!/bin/sh
+
+## github workflows actions-rs/toolchain@v1 doesn't work for docker
+## targets. This script should only be used if the ci pipeline
+## doesn't support installing rust on a particular target.
+
+if [ "$(id -u)" -eq 0 ]; then
+  echo >&2 "::warning:: installing rust as root"
+fi
+
+if [ "$CARGO_HOME" = "" ]; then
+  echo >&2 "::error:: CARGO_HOME is not set"
+  exit 2
+fi
+
+export RUSTUP_HOME=$CARGO_HOME
+
+## install rustup
+curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- --default-toolchain none -y
+if [ ! -f $CARGO_HOME/env ]; then
+  echo "PATH=$CARGO_HOME/bin:\$PATH" > $CARGO_HOME/env
+fi
+. $CARGO_HOME/env
+
+rustup -vV
diff --git a/ci/lib.sh b/ci/lib.sh
index f561884d40..a7992b22fd 100755
--- a/ci/lib.sh
+++ b/ci/lib.sh
@@ -1,5 +1,6 @@
 # Library of functions shared by all CI scripts
 
+
 if test true = "$GITHUB_ACTIONS"
 then
 	begin_group () {
diff --git a/ci/make-test-artifacts.sh b/ci/make-test-artifacts.sh
index 74141af0cc..e37ed7030c 100755
--- a/ci/make-test-artifacts.sh
+++ b/ci/make-test-artifacts.sh
@@ -7,6 +7,15 @@ mkdir -p "$1" # in case ci/lib.sh decides to quit early
 
 . ${0%/*}/lib.sh
 
+## ensure rustup is in the PATH variable
+if [ "$CARGO_HOME" = "" ]; then
+  echo >&2 "::error:: CARGO_HOME is not set"
+  exit 2
+fi
+export PATH="$CARGO_HOME/bin:$PATH"
+
+rustc -vV
+
 group Build make artifacts-tar ARTIFACTS_DIRECTORY="$1"
 
 check_unignored_build_artifacts
diff --git a/ci/run-build-and-tests.sh b/ci/run-build-and-tests.sh
index 01823fd0f1..22b61e2812 100755
--- a/ci/run-build-and-tests.sh
+++ b/ci/run-build-and-tests.sh
@@ -5,6 +5,15 @@
 
 . ${0%/*}/lib.sh
 
+## ensure rustup is in the PATH variable
+if [ "$CARGO_HOME" = "" ]; then
+  echo >&2 "::error:: CARGO_HOME is not set"
+  exit 2
+fi
+. $CARGO_HOME/env
+
+rustc -vV || exit $?
+
 run_tests=t
 
 case "$jobname" in
@@ -72,5 +81,9 @@ case "$jobname" in
 	;;
 esac
 
+if [ -d "$CARGO_HOME" ]; then
+  rm -rf $CARGO_HOME
+fi
+
 check_unignored_build_artifacts
 save_good_tree
-- 
gitgitgadget


^ permalink raw reply related	[flat|nested] 100+ messages in thread

* [PATCH 04/15] win+Meson: do allow linking with the Rust-built xdiff
  2025-08-29 19:42 [PATCH 00/15] Introduce rust: In xdiff Ezekiel Newren via GitGitGadget
                   ` (2 preceding siblings ...)
  2025-08-29 19:42 ` [PATCH 03/15] github workflows: install rust Ezekiel Newren via GitGitGadget
@ 2025-08-29 19:42 ` Johannes Schindelin via GitGitGadget
  2025-08-29 19:42 ` [PATCH 05/15] github workflows: upload Cargo.lock Ezekiel Newren via GitGitGadget
                   ` (11 subsequent siblings)
  15 siblings, 0 replies; 100+ messages in thread
From: Johannes Schindelin via GitGitGadget @ 2025-08-29 19:42 UTC (permalink / raw)
  To: git; +Cc: Ezekiel Newren, Johannes Schindelin

From: Johannes Schindelin <johannes.schindelin@gmx.de>

When linking against the Rust-built `xdiff`, there is now a new required
dependency: Without _also_ linking to the system library `userenv`, the
compile would fail with this error message:

  xdiff.lib(std-c85e9beb7923f636.std.df32d1bc89881d89-cgu.0.rcgu.o) :
  error LNK2019: unresolved external symbol __imp_GetUserProfileDirectoryW
  referenced in function _ZN3std3env8home_dir17hfd1c3b6676cd78f6E

Therefore, just like we do in case of Makefile-based builds on Windows,
we now also link to that library when building with Meson.

Note that if we only have Rust depend upon libuserenv then at link time
GCC would complain about:

  undefined reference to `GetUserProfileDirectoryW'

Apparently there is _some_ closure that gets compiled in that requires
this function, and that in turn forces Git to link to libuserenv.

This is a new requirement, and therefore has not been made part of the
"minimal Git for Windows SDK".

In the near future, I intend to include it, but for now let's just
ensure that the file is added manually if it is missing.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
[en: Squashed a few of Johannes's patches, and moved lib userenv
 handling from an earlier patch]
Signed-off-by: Ezekiel Newren <ezekielnewren@gmail.com>
---
 .github/workflows/main.yml | 8 ++++++++
 config.mak.uname           | 4 ++++
 meson.build                | 1 +
 3 files changed, 13 insertions(+)

diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index ac1d583ab2..ff2f90d122 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -123,6 +123,14 @@ jobs:
     steps:
     - uses: actions/checkout@v4
     - uses: git-for-windows/setup-git-for-windows-sdk@v1
+    - name: ensure that libuserenv.a is present
+      shell: bash
+      run: |
+        cd /mingw64/lib && {
+          test -f libuserenv.a ||
+          /c/Program\ Files/Git/mingw64/bin/curl -Lo libuserenv.a \
+            https://github.com/git-for-windows/git-sdk-64/raw/HEAD/mingw64/lib/libuserenv.a
+        }
     - name: Install rustup via github actions
       uses: actions-rs/toolchain@v1
       with:
diff --git a/config.mak.uname b/config.mak.uname
index 3e26bb074a..6805e3778a 100644
--- a/config.mak.uname
+++ b/config.mak.uname
@@ -740,6 +740,10 @@ ifeq ($(uname_S),MINGW)
 		COMPAT_CFLAGS += -D_USE_32BIT_TIME_T
 		BASIC_LDFLAGS += -Wl,--large-address-aware
         endif
+
+	# Unfortunately now needed because of Rust
+	EXTLIBS += -luserenv
+
 	CC = gcc
 	COMPAT_CFLAGS += -D__USE_MINGW_ANSI_STDIO=0 -DDETECT_MSYS_TTY \
 		-fstack-protector-strong
diff --git a/meson.build b/meson.build
index 324f968338..5aa9901bfc 100644
--- a/meson.build
+++ b/meson.build
@@ -1267,6 +1267,7 @@ elif host_machine.system() == 'windows'
   ]
 
   libgit_dependencies += compiler.find_library('ntdll')
+  libgit_dependencies += compiler.find_library('userenv')
   libgit_include_directories += 'compat/win32'
   if compiler.get_id() == 'msvc'
     libgit_include_directories += 'compat/vcbuild/include'
-- 
gitgitgadget


^ permalink raw reply related	[flat|nested] 100+ messages in thread

* [PATCH 05/15] github workflows: upload Cargo.lock
  2025-08-29 19:42 [PATCH 00/15] Introduce rust: In xdiff Ezekiel Newren via GitGitGadget
                   ` (3 preceding siblings ...)
  2025-08-29 19:42 ` [PATCH 04/15] win+Meson: do allow linking with the Rust-built xdiff Johannes Schindelin via GitGitGadget
@ 2025-08-29 19:42 ` Ezekiel Newren via GitGitGadget
  2025-08-29 19:42 ` [PATCH 06/15] ivec: create a vector type that is interoperable between C and Rust Ezekiel Newren via GitGitGadget
                   ` (10 subsequent siblings)
  15 siblings, 0 replies; 100+ messages in thread
From: Ezekiel Newren via GitGitGadget @ 2025-08-29 19:42 UTC (permalink / raw)
  To: git; +Cc: Ezekiel Newren, Ezekiel Newren

From: Ezekiel Newren <ezekielnewren@gmail.com>

Make each ci workflow upload its Cargo.lock file as a build artifact so
that we can audit build dependencies.

Signed-off-by: Ezekiel Newren <ezekielnewren@gmail.com>
---
 .github/workflows/main.yml | 20 ++++++++++++++++++++
 1 file changed, 20 insertions(+)

diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index ff2f90d122..cdd57b4ee1 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -156,6 +156,11 @@ jobs:
       with:
         name: windows-artifacts
         path: artifacts
+    - name: upload Cargo.lock
+      uses: actions/upload-artifact@v4
+      with:
+        name: cargo-lock-windows
+        path: rust/Cargo.lock
   windows-test:
     name: win test
     runs-on: windows-latest
@@ -317,6 +322,11 @@ jobs:
       with:
         name: windows-meson-artifacts
         path: build
+    - name: Upload Cargo.lock
+      uses: actions/upload-artifact@v4
+      with:
+        name: cargo-lock-windows-meson
+        path: rust/Cargo.lock
   windows-meson-test:
     name: win+Meson test
     runs-on: windows-latest
@@ -399,6 +409,11 @@ jobs:
       with:
         name: failed-tests-${{matrix.vector.jobname}}
         path: ${{env.FAILED_TEST_ARTIFACTS}}
+    - name: Upload Cargo.lock
+      uses: actions/upload-artifact@v4
+      with:
+        name: cargo-lock-${{matrix.vector.jobname}}
+        path: rust/Cargo.lock
   fuzz-smoke-test:
     name: fuzz smoke test
     needs: ci-config
@@ -509,6 +524,11 @@ jobs:
       with:
         name: failed-tests-${{matrix.vector.jobname}}
         path: ${{env.FAILED_TEST_ARTIFACTS}}
+    - name: Upload Cargo.lock
+      uses: actions/upload-artifact@v4
+      with:
+        name: cargo-lock-${{matrix.vector.jobname}}
+        path: rust/Cargo.lock
   static-analysis:
     needs: ci-config
     if: needs.ci-config.outputs.enabled == 'yes'
-- 
gitgitgadget


^ permalink raw reply related	[flat|nested] 100+ messages in thread

* [PATCH 06/15] ivec: create a vector type that is interoperable between C and Rust
  2025-08-29 19:42 [PATCH 00/15] Introduce rust: In xdiff Ezekiel Newren via GitGitGadget
                   ` (4 preceding siblings ...)
  2025-08-29 19:42 ` [PATCH 05/15] github workflows: upload Cargo.lock Ezekiel Newren via GitGitGadget
@ 2025-08-29 19:42 ` Ezekiel Newren via GitGitGadget
  2025-08-29 19:42 ` [PATCH 07/15] xdiff/xprepare: remove superfluous forward declarations Ezekiel Newren via GitGitGadget
                   ` (9 subsequent siblings)
  15 siblings, 0 replies; 100+ messages in thread
From: Ezekiel Newren via GitGitGadget @ 2025-08-29 19:42 UTC (permalink / raw)
  To: git; +Cc: Ezekiel Newren, Ezekiel Newren

From: Ezekiel Newren <ezekielnewren@gmail.com>

Trying to use Rust's Vec in C, or git's ALLOC_GROW() macros (via
wrapper functions) in Rust is painful because:

  * C doing vector things the Rust way would require wrapper functions,
    and Rust doing vector things the C way would require wrapper
    functions, so ivec was created to ensure a consistent contract
    between the 2 languages for how to manipulate a vector.
  * Currently, Rust defines its own 'Vec' type that is generic, but its
    memory allocator and struct layout weren't designed for
    interoperability with C (or any language for that matter), meaning
    that the C side cannot push to or expand a 'Vec' without defining
    wrapper functions in Rust that C can call. Without special care,
    the two languages might use different allocators (malloc/free on
    the C side, and possibly something else in Rust), which would make
    it difficult for a function in one language to free elements
    allocated by a call from a function in the other language.
  * Similarly, git defines ALLOC_GROW() and related macros in
    git-compat-util.h. While we could add functions allowing Rust to
    invoke something similar to those macros, passing three variables
    (pointer, length, allocated_size) instead of a single variable
    (vector) across the language boundary requires more cognitive
    overhead for readers to keep track of and makes it easier to make
    mistakes. Further, for low-level components that we want to
    eventually convert to pure Rust, such triplets would feel very out
    of place.

To address these issue, introduce a new type, ivec -- short for
interoperable vector. (We refer to it as 'ivec' generally, though on
the Rust side the struct is called IVec to match Rust style.)  This new
type is specifically designed for FFI purposes, so that both languages
handle the vector in the same way, though it could be used on either
side independently. This type is designed such that it can easily be
replaced by a standard Rust 'Vec' once interoperability is no longer a
concern.

One particular item to note is that Git's macros to handle vec
operations infer the amount that a vec needs to grow from the size of
a pointer, but that makes it somewhat specific to the macros used in C.
To avoid defining every ivec function as a macro I opted to also
include an element_size field that allows concrete functions like
push() to know how much to grow the memory. This element_size also
helps in verifying that the ivec is correct when passing from C to
Rust.

Signed-off-by: Ezekiel Newren <ezekielnewren@gmail.com>
---
 Makefile                 |  14 +-
 interop/ivec.c           | 180 ++++++++++++++
 interop/ivec.h           |  60 +++++
 meson.build              |   1 +
 rust/interop/src/ivec.rs | 516 +++++++++++++++++++++++++++++++++++++++
 rust/interop/src/lib.rs  |  10 +
 6 files changed, 778 insertions(+), 3 deletions(-)
 create mode 100644 interop/ivec.c
 create mode 100644 interop/ivec.h
 create mode 100644 rust/interop/src/ivec.rs

diff --git a/Makefile b/Makefile
index 1ec0c1ee66..46e940c96d 100644
--- a/Makefile
+++ b/Makefile
@@ -672,6 +672,7 @@ BUILTIN_OBJS =
 BUILT_INS =
 COMPAT_CFLAGS =
 COMPAT_OBJS =
+INTEROP_OBJS =
 XDIFF_OBJS =
 GENERATED_H =
 EXTRA_CPPFLAGS =
@@ -918,6 +919,7 @@ export PYTHON_PATH
 TEST_SHELL_PATH = $(SHELL_PATH)
 
 LIB_FILE = libgit.a
+INTEROP_LIB = interop/lib.a
 XDIFF_LIB = xdiff/lib.a
 
 EXTLIBS =
@@ -1412,7 +1414,7 @@ UNIT_TEST_OBJS += $(UNIT_TEST_DIR)/test-lib.o
 UNIT_TEST_OBJS += $(UNIT_TEST_DIR)/lib-reftable.o
 
 # xdiff and reftable libs may in turn depend on what is in libgit.a
-GITLIBS = common-main.o $(LIB_FILE) $(XDIFF_LIB) $(REFTABLE_LIB) $(LIB_FILE)
+GITLIBS = common-main.o $(LIB_FILE) $(INTEROP_LIB) $(XDIFF_LIB) $(REFTABLE_LIB) $(LIB_FILE)
 
 
 GIT_USER_AGENT = git/$(GIT_VERSION)
@@ -2747,6 +2749,8 @@ reconfigure config.mak.autogen: config.status
 .PHONY: reconfigure # This is a convenience target.
 endif
 
+INTEROP_OBJS += interop/ivec.o
+
 XDIFF_OBJS += xdiff/xdiffi.o
 XDIFF_OBJS += xdiff/xemit.o
 XDIFF_OBJS += xdiff/xhistogram.o
@@ -2791,6 +2795,7 @@ OBJECTS += $(GIT_OBJS)
 OBJECTS += $(SCALAR_OBJS)
 OBJECTS += $(PROGRAM_OBJS)
 OBJECTS += $(TEST_OBJS)
+OBJECTS += $(INTEROP_OBJS)
 OBJECTS += $(XDIFF_OBJS)
 OBJECTS += $(FUZZ_OBJS)
 OBJECTS += $(REFTABLE_OBJS) $(REFTABLE_TEST_OBJS)
@@ -2945,7 +2950,10 @@ scalar$X: scalar.o GIT-LDFLAGS $(GITLIBS) compile_rust
 $(LIB_FILE): $(LIB_OBJS)
 	$(QUIET_AR)$(RM) $@ && $(AR) $(ARFLAGS) $@ $^
 
-$(XDIFF_LIB): $(XDIFF_OBJS)
+$(INTEROP_LIB): $(INTEROP_OBJS)
+	$(QUIET_AR)$(RM) $@ && $(AR) $(ARFLAGS) $@ $^
+
+$(XDIFF_LIB): $(INTEROP_OBJS) $(XDIFF_OBJS)
 	$(QUIET_AR)$(RM) $@ && $(AR) $(ARFLAGS) $@ $^
 
 
@@ -3790,7 +3798,7 @@ clean: profile-clean coverage-clean cocciclean rustclean
 	$(RM) git.rc git.res
 	$(RM) $(OBJECTS)
 	$(RM) headless-git.o
-	$(RM) $(LIB_FILE) $(XDIFF_LIB) $(REFTABLE_LIB)
+	$(RM) $(LIB_FILE) $(INTEROP_LIB) $(XDIFF_LIB) $(REFTABLE_LIB)
 	$(RM) $(ALL_PROGRAMS) $(SCRIPT_LIB) $(BUILT_INS) $(OTHER_PROGRAMS)
 	$(RM) $(TEST_PROGRAMS)
 	$(RM) $(FUZZ_PROGRAMS)
diff --git a/interop/ivec.c b/interop/ivec.c
new file mode 100644
index 0000000000..1c4bf20707
--- /dev/null
+++ b/interop/ivec.c
@@ -0,0 +1,180 @@
+#include "ivec.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+static void ivec_set_capacity(void *self_, usize new_capacity)
+{
+	struct rawivec *self = self_;
+
+	if (new_capacity == 0)
+		FREE_AND_NULL(self->ptr);
+	else
+		self->ptr = xrealloc(self->ptr, new_capacity * self->element_size);
+	self->capacity = new_capacity;
+}
+
+void ivec_init(void *self_, usize element_size)
+{
+	struct rawivec *self = self_;
+
+	self->ptr = NULL;
+	self->length = 0;
+	self->capacity = 0;
+	self->element_size = element_size;
+}
+
+/*
+ * MUST CALL IVEC_INIT() FIRST!!!
+ * This function will free the ivec, set self.capacity and self.length
+ * to the specified capacity, and then calloc self.capacity number of
+ * elements.
+ */
+void ivec_zero(void *self_, usize capacity)
+{
+	struct rawivec *self = self_;
+
+	if (self->ptr)
+		FREE_AND_NULL(self->ptr);
+	self->capacity = self->length = capacity;
+	self->ptr = xcalloc(self->capacity, self->element_size);
+}
+
+void ivec_clear(void *self_)
+{
+	struct rawivec *self = self_;
+
+	self->length = 0;
+}
+
+void ivec_reserve_exact(void *self_, usize additional)
+{
+	struct rawivec *self = self_;
+	usize new_capacity = self->capacity + additional;
+
+	ivec_set_capacity(self, new_capacity);
+}
+
+void ivec_reserve(void *self_, usize additional)
+{
+	struct rawivec *self = self_;
+	usize growby = 128;
+
+	if (self->capacity > growby) {
+		growby = self->capacity;
+	}
+	if (additional > growby) {
+		growby = additional;
+	}
+	ivec_reserve_exact(self, growby);
+}
+
+void ivec_shrink_to_fit(void *self_)
+{
+	struct rawivec *self = self_;
+
+	ivec_set_capacity(self_, self->length);
+}
+
+void ivec_resize(void *self_, usize new_length, void *default_value)
+{
+	struct rawivec *self = self_;
+	isize additional = (isize) (new_length - self->capacity);
+
+	if (additional > 0) {
+		ivec_reserve(self_, additional);
+	}
+
+	for (usize i = self->length; i < new_length; i++) {
+		void *dst = (u8 *)self->ptr + (self->length + i) * self->element_size;
+		memcpy(dst, default_value, self->element_size);
+	}
+	self->length = new_length;
+}
+
+void ivec_push(void *self_, void *value)
+{
+	struct rawivec *self = self_;
+	u8 *dst;
+
+	if (self->length == self->capacity) {
+		ivec_reserve(self_, 1);
+	}
+	dst = (u8 *)self->ptr + self->length * self->element_size;
+	memcpy(dst, value, self->element_size);
+	self->length++;
+}
+
+void ivec_extend_from_slice(void *self_, void const *ptr, usize size)
+{
+	struct rawivec *self = self_;
+	u8 *dst;
+
+	if (size == 0)
+		return;
+
+	if (self->length + size > self->capacity) {
+		ivec_reserve(self_, self->capacity - self->length + size);
+	}
+	dst = (u8 *)self->ptr + self->length * self->element_size;
+	memcpy(dst, ptr, size * self->element_size);
+	self->length += size;
+}
+
+bool ivec_equal(void *self_, void *other)
+{
+	struct rawivec *lhs = self_;
+	struct rawivec *rhs = other;
+
+	if (lhs->element_size != rhs->element_size) {
+		return false;
+	}
+
+	if (lhs->length != rhs->length) {
+		return false;
+	}
+
+	for (usize i = 0; i < lhs->length; i++) {
+		void *left = (u8 *)lhs->ptr + i * lhs->element_size;
+		void *right = (u8 *)rhs->ptr + i * rhs->element_size;
+		if (memcmp(left, right, lhs->element_size) != 0) {
+			return false;
+		}
+	}
+
+	return true;
+}
+
+
+void ivec_free(void *self_)
+{
+	struct rawivec *self = self_;
+
+	FREE_AND_NULL(self->ptr);
+	self->length = 0;
+	self->capacity = 0;
+	/* don't modify self->element_size */
+}
+
+void ivec_move(void *source, void *destination)
+{
+	struct rawivec *src = source;
+	struct rawivec *dst = destination;
+
+	if (src->element_size != dst->element_size)
+		BUG("mismatched element_size");
+
+	ivec_free(destination);
+	dst->ptr = src->ptr;
+	dst->length = src->length;
+	dst->capacity = src->capacity;
+
+	src->ptr = NULL;
+	src->length = 0;
+	src->capacity = 0;
+}
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/interop/ivec.h b/interop/ivec.h
new file mode 100644
index 0000000000..ed3afff944
--- /dev/null
+++ b/interop/ivec.h
@@ -0,0 +1,60 @@
+#ifndef IVEC_H
+#define IVEC_H
+
+#include <git-compat-util.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct rawivec {
+	void *ptr;
+	usize length;
+	usize capacity;
+	usize element_size;
+};
+
+#define DEFINE_IVEC_TYPE(type, suffix) \
+struct ivec_##suffix { \
+	type *ptr; \
+	size_t length; \
+	size_t capacity; \
+	size_t element_size; \
+}
+
+#define IVEC_INIT(variable) ivec_init(&(variable), sizeof(*(variable).ptr))
+
+DEFINE_IVEC_TYPE(u8, u8);
+DEFINE_IVEC_TYPE(u16, u16);
+DEFINE_IVEC_TYPE(u32, u32);
+DEFINE_IVEC_TYPE(u64, u64);
+
+DEFINE_IVEC_TYPE(i8, i8);
+DEFINE_IVEC_TYPE(i16, i16);
+DEFINE_IVEC_TYPE(i32, i32);
+DEFINE_IVEC_TYPE(i64, i64);
+
+DEFINE_IVEC_TYPE(f32, f32);
+DEFINE_IVEC_TYPE(f64, f64);
+
+DEFINE_IVEC_TYPE(usize, usize);
+DEFINE_IVEC_TYPE(isize, isize);
+
+void ivec_init(void *self_, usize element_size);
+void ivec_zero(void *self_, usize capacity);
+void ivec_clear(void *self_);
+void ivec_reserve_exact(void *self_, usize additional);
+void ivec_reserve(void *self_, usize additional);
+void ivec_shrink_to_fit(void *self_);
+void ivec_resize(void *self_, usize new_length, void *default_value);
+void ivec_push(void *self_, void *value);
+void ivec_extend_from_slice(void *self_, void const *ptr, usize size);
+bool ivec_equal(void *self_, void *other);
+void ivec_free(void *self_);
+void ivec_move(void *source, void *destination);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif //IVEC_H
diff --git a/meson.build b/meson.build
index 5aa9901bfc..fc7c133f79 100644
--- a/meson.build
+++ b/meson.build
@@ -395,6 +395,7 @@ libgit_sources = [
   'hex-ll.c',
   'hook.c',
   'ident.c',
+  'interop/ivec.c',
   'json-writer.c',
   'kwset.c',
   'levenshtein.c',
diff --git a/rust/interop/src/ivec.rs b/rust/interop/src/ivec.rs
new file mode 100644
index 0000000000..ba9393a82b
--- /dev/null
+++ b/rust/interop/src/ivec.rs
@@ -0,0 +1,516 @@
+use crate::*;
+use core::mem::{align_of, size_of};
+use std::fmt::{Debug, Formatter};
+use std::ops::{Index, IndexMut};
+
+#[repr(C)]
+pub struct IVec<T> {
+    ptr: *mut T,
+    length: usize,
+    capacity: usize,
+    element_size: usize,
+}
+
+impl<T> Default for IVec<T> {
+    fn default() -> Self {
+        Self::new()
+    }
+}
+
+impl<T> Drop for IVec<T> {
+    fn drop(&mut self) {
+        self._set_capacity(0);
+    }
+}
+
+impl<T: Clone> Clone for IVec<T> {
+    fn clone(&self) -> Self {
+        let mut copy = Self::new();
+        copy.reserve_exact(self.len());
+        for i in 0..self.len() {
+            copy.push(self[i].clone());
+        }
+
+        copy
+    }
+}
+
+impl<T: PartialEq> PartialEq for IVec<T> {
+    fn eq(&self, other: &Self) -> bool {
+        if self.len() != other.len() {
+            return false;
+        }
+
+        let lhs = self.as_slice();
+        let rhs = &other.as_slice()[..lhs.len()];
+        for i in 0..lhs.len() {
+            if lhs[i] != rhs[i] {
+                return false;
+            }
+        }
+
+        true
+    }
+}
+
+impl<T: PartialEq> Eq for IVec<T> {}
+
+/*
+ * constructors
+ */
+impl<T> IVec<T> {
+    pub fn new() -> Self {
+        Self {
+            ptr: std::ptr::null_mut(),
+            length: 0,
+            capacity: 0,
+            element_size: size_of::<T>(),
+        }
+    }
+
+    /// uses calloc to create the IVec, it's unsafe because
+    /// zeroed memory may not be a valid default value
+    pub unsafe fn zero(capacity: usize) -> Self {
+        Self {
+            ptr: calloc(capacity, size_of::<T>()) as *mut T,
+            length: capacity,
+            capacity,
+            element_size: size_of::<T>(),
+        }
+    }
+
+    pub fn with_capacity(capacity: usize) -> Self {
+        let mut vec = Self::new();
+        vec._set_capacity(capacity);
+        vec
+    }
+
+    pub fn with_capacity_and_default(capacity: usize, default_value: T) -> Self
+    where
+        T: Copy,
+    {
+        let mut vec = Self::new();
+        vec._set_capacity(capacity);
+        vec._buffer_mut().fill(default_value);
+        vec
+    }
+
+    pub unsafe fn from_raw_mut<'a>(raw: *mut Self) -> &'a mut Self {
+        let vec = raw.as_mut().expect("null pointer");
+        #[cfg(debug_assertions)]
+        vec.test_invariants();
+        vec
+    }
+
+    pub unsafe fn from_raw<'a>(raw: *const Self) -> &'a Self {
+        let vec = &*raw.as_ref().expect("null pointer");
+        #[cfg(debug_assertions)]
+        vec.test_invariants();
+        vec
+    }
+}
+
+/*
+ * private methods
+ */
+impl<T> IVec<T> {
+    pub fn test_invariants(&self) {
+        if !self.ptr.is_null() && (self.ptr as usize) % align_of::<T>() != 0 {
+            panic!(
+                "misaligned pointer: expected {:x}, got {:x}",
+                align_of::<T>(),
+                self.ptr as usize
+            );
+        }
+        if self.ptr.is_null() && (self.length > 0 || self.capacity > 0) {
+            panic!("ptr is null, but length or capacity is > 0");
+        }
+        if !self.ptr.is_null() && self.capacity == 0 {
+            panic!("ptr ISN'T null, but capacity == 0");
+        }
+        if self.element_size != size_of::<T>() {
+            panic!(
+                "incorrect element size, should be: {}, but was: {}",
+                size_of::<T>(),
+                self.element_size
+            );
+        }
+        if self.length > self.capacity {
+            panic!("length: {} > capacity: {}", self.length, self.capacity);
+        }
+        if self.capacity > usize::MAX / size_of::<T>() {
+            panic!(
+                "Capacity {} is too large, potential overflow detected",
+                self.capacity
+            );
+        }
+    }
+
+    fn _set_capacity(&mut self, new_capacity: usize) {
+        unsafe {
+            if new_capacity == self.capacity {
+                return;
+            }
+            if new_capacity < self.length {
+                self.truncate(new_capacity);
+            }
+            if new_capacity == 0 {
+                free(self.ptr as *mut c_void);
+                self.ptr = std::ptr::null_mut();
+                self.length = 0;
+                self.capacity = 0;
+                // DO NOT MODIFY element_size!!!
+            } else {
+                let t = realloc(self.ptr as *mut c_void, new_capacity * size_of::<T>());
+                if t.is_null() {
+                    panic!("out of memory");
+                }
+                self.ptr = t as *mut T;
+            }
+            self.capacity = new_capacity;
+        }
+    }
+
+    fn _resize(&mut self, new_length: usize, default_value: T, exact: bool)
+    where
+        T: Copy,
+    {
+        if exact {
+            self._set_capacity(new_length);
+        } else if new_length > self.capacity {
+            self.reserve(new_length - self.capacity);
+        } else {
+            /* capacity does not need to be changed */
+        }
+
+        /* IVec grows */
+        if new_length > self.length {
+            let range = self.length..new_length;
+            self._buffer_mut()[range].fill(default_value);
+            self.length = new_length;
+        }
+
+        /* IVec shrinks */
+        if new_length < self.length {
+            self.truncate(new_length);
+        }
+    }
+
+    fn _buffer_mut(&mut self) -> &mut [T] {
+        if self.ptr.is_null() {
+            &mut []
+        } else {
+            unsafe { std::slice::from_raw_parts_mut(self.ptr, self.capacity) }
+        }
+    }
+
+    fn _buffer(&self) -> &[T] {
+        if self.ptr.is_null() {
+            &[]
+        } else {
+            unsafe { std::slice::from_raw_parts(self.ptr, self.capacity) }
+        }
+    }
+}
+
+/*
+ * methods
+ */
+impl<T> IVec<T> {
+    pub fn len(&self) -> usize {
+        self.length
+    }
+
+    pub unsafe fn set_len(&mut self, new_length: usize) {
+        self.length = new_length;
+    }
+
+    pub fn capacity(&self) -> usize {
+        self.capacity
+    }
+
+    pub fn reserve_exact(&mut self, additional: usize) {
+        self._set_capacity(self.capacity + additional);
+    }
+
+    pub fn reserve(&mut self, additional: usize) {
+        let growby = std::cmp::max(128, self.capacity);
+        self.reserve_exact(std::cmp::max(additional, growby));
+    }
+
+    pub fn shrink_to_fit(&mut self) {
+        self._set_capacity(self.length);
+    }
+
+    pub fn resize(&mut self, new_length: usize, default_value: T)
+    where
+        T: Copy,
+    {
+        self._resize(new_length, default_value, false);
+    }
+
+    pub fn resize_exact(&mut self, new_length: usize, default_value: T)
+    where
+        T: Copy,
+    {
+        self._resize(new_length, default_value, true);
+    }
+
+    pub fn truncate(&mut self, new_length: usize) {
+        if new_length >= self.length {
+            return;
+        }
+
+        if std::mem::needs_drop::<T>() {
+            let range = new_length..self.length;
+            for v in &mut self.as_mut_slice()[range] {
+                unsafe {
+                    std::ptr::drop_in_place(v);
+                }
+            }
+        }
+
+        self.length = new_length;
+    }
+
+    pub fn insert(&mut self, index: usize, value: T) {
+        if self.length == self.capacity {
+            self.reserve(1);
+        }
+
+        unsafe {
+            let src = &self._buffer()[index] as *const T;
+            let dst = src.add(1) as *mut T;
+            let len = self.length - index;
+            std::ptr::copy(src, dst, len);
+            std::ptr::write(self.ptr.add(index), value);
+        }
+    }
+
+    pub fn push(&mut self, value: T) {
+        if self.length == self.capacity {
+            self.reserve(1);
+        }
+
+        let i = self.length;
+        unsafe {
+            std::ptr::write(self.ptr.add(i), value);
+        }
+        self.length += 1;
+    }
+
+    pub fn extend_from_slice(&mut self, slice: &[T])
+    where
+        T: Clone,
+    {
+        for v in slice {
+            self.push(v.clone());
+        }
+    }
+
+    pub fn clear(&mut self) {
+        self.length = 0;
+    }
+
+    pub fn as_ptr(&self) -> *const T {
+        self.ptr
+    }
+
+    pub fn as_mut_ptr(&self) -> *mut T {
+        self.ptr
+    }
+
+    pub fn as_slice(&self) -> &[T] {
+        &self._buffer()[0..self.length]
+    }
+
+    pub fn as_mut_slice(&mut self) -> &mut [T] {
+        let range = 0..self.length;
+        &mut self._buffer_mut()[range]
+    }
+}
+
+impl<T> Extend<T> for IVec<T> {
+    fn extend<IT: IntoIterator<Item = T>>(&mut self, iter: IT) {
+        for v in iter {
+            self.push(v);
+        }
+    }
+}
+
+impl<T> Index<usize> for IVec<T> {
+    type Output = T;
+
+    fn index(&self, index: usize) -> &Self::Output {
+        &self.as_slice()[index]
+    }
+}
+
+impl<T> IndexMut<usize> for IVec<T> {
+    fn index_mut(&mut self, index: usize) -> &mut Self::Output {
+        &mut self.as_mut_slice()[index]
+    }
+}
+
+impl<T: Debug> Debug for IVec<T> {
+    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
+        writeln!(
+            f,
+            "ptr: {}, capacity: {}, len: {}, element_size: {}, content: {:?}",
+            self.ptr as usize,
+            self.capacity,
+            self.length,
+            self.element_size,
+            self.as_slice()
+        )
+    }
+}
+
+impl std::fmt::Write for IVec<u8> {
+    fn write_str(&mut self, s: &str) -> std::fmt::Result {
+        Ok(self.extend_from_slice(s.as_bytes()))
+    }
+}
+
+impl std::io::Write for IVec<u8> {
+    fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
+        self.extend_from_slice(buf);
+        Ok(buf.len())
+    }
+
+    fn flush(&mut self) -> std::io::Result<()> {
+        Ok(())
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use crate::ivec::IVec;
+    use std::cell::RefCell;
+    use std::mem::size_of;
+    use std::ops::{Deref, DerefMut};
+    use std::panic;
+    use std::rc::Rc;
+
+    struct DropTest {
+        value: Rc<RefCell<u64>>,
+    }
+
+    impl DropTest {
+        fn new(value: Rc<RefCell<u64>>) -> Self {
+            Self { value }
+        }
+    }
+
+    impl Drop for DropTest {
+        fn drop(&mut self) {
+            *self.value.borrow_mut() -= 1;
+        }
+    }
+
+    #[test]
+    fn test_drop_elements() {
+        let counter = Rc::new(RefCell::new(0u64));
+        let size = 5;
+
+        /* drop whole IVec */
+        let mut vec = IVec::new();
+        for _ in 0..size {
+            let tmp = DropTest::new(counter.clone());
+            *tmp.value.borrow_mut() += 1;
+            vec.push(tmp);
+        }
+        let cur = *counter.borrow();
+        assert_eq!(size, cur);
+        drop(vec);
+
+        let cur = *counter.borrow();
+        assert_eq!(0u64, cur);
+
+        /* drop some elements */
+        let mut vec = IVec::new();
+        for i in 0..size {
+            let tmp = DropTest::new(counter.clone());
+            *tmp.value.borrow_mut() += 1;
+            vec.push(tmp);
+        }
+        let cur = *counter.borrow().deref();
+        assert_eq!(size, cur);
+
+        let expected = 2u64;
+        vec.truncate(expected as usize);
+
+        let cur = *counter.borrow();
+        assert_eq!(expected, cur);
+        drop(vec);
+    }
+
+    #[test]
+    fn test_panic_on_out_of_bounds() {
+        type TestType = i16;
+        let result = panic::catch_unwind(|| {
+            let mut v = IVec::<TestType>::with_capacity(1_000_000);
+            v[0] = 55;
+        });
+
+        match result {
+            Ok(_) => assert!(false, "index was out of bounds, but no panic was triggered"),
+            Err(_) => assert!(true),
+        }
+    }
+
+    #[test]
+    fn test_push_clear_resize_then_shrink_to_fit() {
+        let mut vec = IVec::<u64>::new();
+        let mut monotonic = 1;
+
+        vec.reserve_exact(1);
+        assert_eq!(1, vec.capacity);
+
+        // test push
+        for _ in 0..10 {
+            vec.push(monotonic);
+            assert_eq!(monotonic as usize, vec.length);
+            assert_eq!(monotonic, vec[(monotonic - 1) as usize]);
+            assert!(vec.capacity >= vec.length);
+            monotonic += 1;
+        }
+
+        // test clear
+        let expected = vec.capacity;
+        vec.clear();
+        assert_eq!(0, vec.length);
+        assert_eq!(expected, vec.capacity);
+
+        // test resize
+        let expected = vec.capacity + 10;
+        let default_value = 19;
+        vec.resize(expected, default_value);
+        assert_eq!(expected, vec.length);
+        assert!(vec.capacity >= expected);
+        for i in 0..vec.length {
+            assert_eq!(default_value, vec[i]);
+        }
+
+        vec.reserve(10);
+        assert!(vec.capacity > vec.length);
+        let length_before = vec.length;
+        vec.shrink_to_fit();
+        assert_eq!(length_before, vec.length);
+        assert_eq!(vec.length, vec.capacity);
+    }
+
+    #[test]
+    fn test_struct_size() {
+        let vec = IVec::<i16>::new();
+
+        assert_eq!(2, vec.element_size);
+        assert_eq!(size_of::<usize>() * 4, size_of::<IVec<i16>>());
+
+        drop(vec);
+
+        let vec = IVec::<u128>::new();
+        assert_eq!(16, vec.element_size);
+        assert_eq!(size_of::<usize>() * 4, size_of::<IVec<u128>>());
+    }
+}
diff --git a/rust/interop/src/lib.rs b/rust/interop/src/lib.rs
index e69de29bb2..4850f66e5b 100644
--- a/rust/interop/src/lib.rs
+++ b/rust/interop/src/lib.rs
@@ -0,0 +1,10 @@
+pub mod ivec;
+
+use std::ffi::c_void;
+
+extern "C" {
+    pub fn malloc(size: usize) -> *mut c_void;
+    pub fn calloc(nmemb: usize, size: usize) -> *mut c_void;
+    pub fn realloc(ptr: *mut c_void, size: usize) -> *mut c_void;
+    pub fn free(ptr: *mut c_void);
+}
-- 
gitgitgadget


^ permalink raw reply related	[flat|nested] 100+ messages in thread

* [PATCH 07/15] xdiff/xprepare: remove superfluous forward declarations
  2025-08-29 19:42 [PATCH 00/15] Introduce rust: In xdiff Ezekiel Newren via GitGitGadget
                   ` (5 preceding siblings ...)
  2025-08-29 19:42 ` [PATCH 06/15] ivec: create a vector type that is interoperable between C and Rust Ezekiel Newren via GitGitGadget
@ 2025-08-29 19:42 ` Ezekiel Newren via GitGitGadget
  2025-08-29 19:42 ` [PATCH 08/15] xdiff: delete unnecessary fields from xrecord_t and xdfile_t Ezekiel Newren via GitGitGadget
                   ` (8 subsequent siblings)
  15 siblings, 0 replies; 100+ messages in thread
From: Ezekiel Newren via GitGitGadget @ 2025-08-29 19:42 UTC (permalink / raw)
  To: git; +Cc: Ezekiel Newren, Ezekiel Newren

From: Ezekiel Newren <ezekielnewren@gmail.com>

Move xdl_prepare_env() later in the file to avoid the need
for forward declarations.

Signed-off-by: Ezekiel Newren <ezekielnewren@gmail.com>
---
 xdiff/xprepare.c | 116 ++++++++++++++++++++---------------------------
 1 file changed, 50 insertions(+), 66 deletions(-)

diff --git a/xdiff/xprepare.c b/xdiff/xprepare.c
index e1d4017b2d..a45c5ee208 100644
--- a/xdiff/xprepare.c
+++ b/xdiff/xprepare.c
@@ -53,21 +53,6 @@ typedef struct s_xdlclassifier {
 
 
 
-static int xdl_init_classifier(xdlclassifier_t *cf, long size, long flags);
-static void xdl_free_classifier(xdlclassifier_t *cf);
-static int xdl_classify_record(unsigned int pass, xdlclassifier_t *cf, xrecord_t **rhash,
-			       unsigned int hbits, xrecord_t *rec);
-static int xdl_prepare_ctx(unsigned int pass, mmfile_t *mf, long narec, xpparam_t const *xpp,
-			   xdlclassifier_t *cf, xdfile_t *xdf);
-static void xdl_free_ctx(xdfile_t *xdf);
-static int xdl_clean_mmatch(char const *dis, long i, long s, long e);
-static int xdl_cleanup_records(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xdf2);
-static int xdl_trim_ends(xdfile_t *xdf1, xdfile_t *xdf2);
-static int xdl_optimize_ctxs(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xdf2);
-
-
-
-
 static int xdl_init_classifier(xdlclassifier_t *cf, long size, long flags) {
 	cf->flags = flags;
 
@@ -242,57 +227,6 @@ static void xdl_free_ctx(xdfile_t *xdf) {
 }
 
 
-int xdl_prepare_env(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,
-		    xdfenv_t *xe) {
-	long enl1, enl2, sample;
-	xdlclassifier_t cf;
-
-	memset(&cf, 0, sizeof(cf));
-
-	/*
-	 * For histogram diff, we can afford a smaller sample size and
-	 * thus a poorer estimate of the number of lines, as the hash
-	 * table (rhash) won't be filled up/grown. The number of lines
-	 * (nrecs) will be updated correctly anyway by
-	 * xdl_prepare_ctx().
-	 */
-	sample = (XDF_DIFF_ALG(xpp->flags) == XDF_HISTOGRAM_DIFF
-		  ? XDL_GUESS_NLINES2 : XDL_GUESS_NLINES1);
-
-	enl1 = xdl_guess_lines(mf1, sample) + 1;
-	enl2 = xdl_guess_lines(mf2, sample) + 1;
-
-	if (xdl_init_classifier(&cf, enl1 + enl2 + 1, xpp->flags) < 0)
-		return -1;
-
-	if (xdl_prepare_ctx(1, mf1, enl1, xpp, &cf, &xe->xdf1) < 0) {
-
-		xdl_free_classifier(&cf);
-		return -1;
-	}
-	if (xdl_prepare_ctx(2, mf2, enl2, xpp, &cf, &xe->xdf2) < 0) {
-
-		xdl_free_ctx(&xe->xdf1);
-		xdl_free_classifier(&cf);
-		return -1;
-	}
-
-	if ((XDF_DIFF_ALG(xpp->flags) != XDF_PATIENCE_DIFF) &&
-	    (XDF_DIFF_ALG(xpp->flags) != XDF_HISTOGRAM_DIFF) &&
-	    xdl_optimize_ctxs(&cf, &xe->xdf1, &xe->xdf2) < 0) {
-
-		xdl_free_ctx(&xe->xdf2);
-		xdl_free_ctx(&xe->xdf1);
-		xdl_free_classifier(&cf);
-		return -1;
-	}
-
-	xdl_free_classifier(&cf);
-
-	return 0;
-}
-
-
 void xdl_free_env(xdfenv_t *xe) {
 
 	xdl_free_ctx(&xe->xdf2);
@@ -460,3 +394,53 @@ static int xdl_optimize_ctxs(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xdf2
 
 	return 0;
 }
+
+int xdl_prepare_env(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,
+		    xdfenv_t *xe) {
+	long enl1, enl2, sample;
+	xdlclassifier_t cf;
+
+	memset(&cf, 0, sizeof(cf));
+
+	/*
+	 * For histogram diff, we can afford a smaller sample size and
+	 * thus a poorer estimate of the number of lines, as the hash
+	 * table (rhash) won't be filled up/grown. The number of lines
+	 * (nrecs) will be updated correctly anyway by
+	 * xdl_prepare_ctx().
+	 */
+	sample = (XDF_DIFF_ALG(xpp->flags) == XDF_HISTOGRAM_DIFF
+		  ? XDL_GUESS_NLINES2 : XDL_GUESS_NLINES1);
+
+	enl1 = xdl_guess_lines(mf1, sample) + 1;
+	enl2 = xdl_guess_lines(mf2, sample) + 1;
+
+	if (xdl_init_classifier(&cf, enl1 + enl2 + 1, xpp->flags) < 0)
+		return -1;
+
+	if (xdl_prepare_ctx(1, mf1, enl1, xpp, &cf, &xe->xdf1) < 0) {
+
+		xdl_free_classifier(&cf);
+		return -1;
+	}
+	if (xdl_prepare_ctx(2, mf2, enl2, xpp, &cf, &xe->xdf2) < 0) {
+
+		xdl_free_ctx(&xe->xdf1);
+		xdl_free_classifier(&cf);
+		return -1;
+	}
+
+	if ((XDF_DIFF_ALG(xpp->flags) != XDF_PATIENCE_DIFF) &&
+	    (XDF_DIFF_ALG(xpp->flags) != XDF_HISTOGRAM_DIFF) &&
+	    xdl_optimize_ctxs(&cf, &xe->xdf1, &xe->xdf2) < 0) {
+
+		xdl_free_ctx(&xe->xdf2);
+		xdl_free_ctx(&xe->xdf1);
+		xdl_free_classifier(&cf);
+		return -1;
+	    }
+
+	xdl_free_classifier(&cf);
+
+	return 0;
+}
-- 
gitgitgadget


^ permalink raw reply related	[flat|nested] 100+ messages in thread

* [PATCH 08/15] xdiff: delete unnecessary fields from xrecord_t and xdfile_t
  2025-08-29 19:42 [PATCH 00/15] Introduce rust: In xdiff Ezekiel Newren via GitGitGadget
                   ` (6 preceding siblings ...)
  2025-08-29 19:42 ` [PATCH 07/15] xdiff/xprepare: remove superfluous forward declarations Ezekiel Newren via GitGitGadget
@ 2025-08-29 19:42 ` Ezekiel Newren via GitGitGadget
  2025-08-29 19:42 ` [PATCH 09/15] xdiff: make fields of xrecord_t Rust friendly Ezekiel Newren via GitGitGadget
                   ` (7 subsequent siblings)
  15 siblings, 0 replies; 100+ messages in thread
From: Ezekiel Newren via GitGitGadget @ 2025-08-29 19:42 UTC (permalink / raw)
  To: git; +Cc: Ezekiel Newren, Ezekiel Newren

From: Ezekiel Newren <ezekielnewren@gmail.com>

xrecord_t.next, xdfile_t.hbits, xdfile_t.rhash are initialized,
but never used for anything by the code. Remove them.

Signed-off-by: Ezekiel Newren <ezekielnewren@gmail.com>
---
 xdiff/xprepare.c | 24 +++---------------------
 xdiff/xtypes.h   |  3 ---
 2 files changed, 3 insertions(+), 24 deletions(-)

diff --git a/xdiff/xprepare.c b/xdiff/xprepare.c
index a45c5ee208..ad356281f9 100644
--- a/xdiff/xprepare.c
+++ b/xdiff/xprepare.c
@@ -91,8 +91,7 @@ static void xdl_free_classifier(xdlclassifier_t *cf) {
 }
 
 
-static int xdl_classify_record(unsigned int pass, xdlclassifier_t *cf, xrecord_t **rhash,
-			       unsigned int hbits, xrecord_t *rec) {
+static int xdl_classify_record(unsigned int pass, xdlclassifier_t *cf, xrecord_t *rec) {
 	long hi;
 	char const *line;
 	xdlclass_t *rcrec;
@@ -126,23 +125,17 @@ static int xdl_classify_record(unsigned int pass, xdlclassifier_t *cf, xrecord_t
 
 	rec->ha = (unsigned long) rcrec->idx;
 
-	hi = (long) XDL_HASHLONG(rec->ha, hbits);
-	rec->next = rhash[hi];
-	rhash[hi] = rec;
-
 	return 0;
 }
 
 
 static int xdl_prepare_ctx(unsigned int pass, mmfile_t *mf, long narec, xpparam_t const *xpp,
 			   xdlclassifier_t *cf, xdfile_t *xdf) {
-	unsigned int hbits;
-	long nrec, hsize, bsize;
+	long nrec, bsize;
 	unsigned long hav;
 	char const *blk, *cur, *top, *prev;
 	xrecord_t *crec;
 	xrecord_t **recs;
-	xrecord_t **rhash;
 	unsigned long *ha;
 	char *rchg;
 	long *rindex;
@@ -150,7 +143,6 @@ static int xdl_prepare_ctx(unsigned int pass, mmfile_t *mf, long narec, xpparam_
 	ha = NULL;
 	rindex = NULL;
 	rchg = NULL;
-	rhash = NULL;
 	recs = NULL;
 
 	if (xdl_cha_init(&xdf->rcha, sizeof(xrecord_t), narec / 4 + 1) < 0)
@@ -158,11 +150,6 @@ static int xdl_prepare_ctx(unsigned int pass, mmfile_t *mf, long narec, xpparam_
 	if (!XDL_ALLOC_ARRAY(recs, narec))
 		goto abort;
 
-	hbits = xdl_hashbits((unsigned int) narec);
-	hsize = 1 << hbits;
-	if (!XDL_CALLOC_ARRAY(rhash, hsize))
-		goto abort;
-
 	nrec = 0;
 	if ((cur = blk = xdl_mmfile_first(mf, &bsize))) {
 		for (top = blk + bsize; cur < top; ) {
@@ -176,7 +163,7 @@ static int xdl_prepare_ctx(unsigned int pass, mmfile_t *mf, long narec, xpparam_
 			crec->size = (long) (cur - prev);
 			crec->ha = hav;
 			recs[nrec++] = crec;
-			if (xdl_classify_record(pass, cf, rhash, hbits, crec) < 0)
+			if (xdl_classify_record(pass, cf, crec) < 0)
 				goto abort;
 		}
 	}
@@ -194,8 +181,6 @@ static int xdl_prepare_ctx(unsigned int pass, mmfile_t *mf, long narec, xpparam_
 
 	xdf->nrec = nrec;
 	xdf->recs = recs;
-	xdf->hbits = hbits;
-	xdf->rhash = rhash;
 	xdf->rchg = rchg + 1;
 	xdf->rindex = rindex;
 	xdf->nreff = 0;
@@ -209,7 +194,6 @@ abort:
 	xdl_free(ha);
 	xdl_free(rindex);
 	xdl_free(rchg);
-	xdl_free(rhash);
 	xdl_free(recs);
 	xdl_cha_free(&xdf->rcha);
 	return -1;
@@ -217,8 +201,6 @@ abort:
 
 
 static void xdl_free_ctx(xdfile_t *xdf) {
-
-	xdl_free(xdf->rhash);
 	xdl_free(xdf->rindex);
 	xdl_free(xdf->rchg - 1);
 	xdl_free(xdf->ha);
diff --git a/xdiff/xtypes.h b/xdiff/xtypes.h
index 8442bd436e..8b8467360e 100644
--- a/xdiff/xtypes.h
+++ b/xdiff/xtypes.h
@@ -39,7 +39,6 @@ typedef struct s_chastore {
 } chastore_t;
 
 typedef struct s_xrecord {
-	struct s_xrecord *next;
 	char const *ptr;
 	long size;
 	unsigned long ha;
@@ -48,8 +47,6 @@ typedef struct s_xrecord {
 typedef struct s_xdfile {
 	chastore_t rcha;
 	long nrec;
-	unsigned int hbits;
-	xrecord_t **rhash;
 	long dstart, dend;
 	xrecord_t **recs;
 	char *rchg;
-- 
gitgitgadget


^ permalink raw reply related	[flat|nested] 100+ messages in thread

* [PATCH 09/15] xdiff: make fields of xrecord_t Rust friendly
  2025-08-29 19:42 [PATCH 00/15] Introduce rust: In xdiff Ezekiel Newren via GitGitGadget
                   ` (7 preceding siblings ...)
  2025-08-29 19:42 ` [PATCH 08/15] xdiff: delete unnecessary fields from xrecord_t and xdfile_t Ezekiel Newren via GitGitGadget
@ 2025-08-29 19:42 ` Ezekiel Newren via GitGitGadget
  2025-08-29 19:42 ` [PATCH 10/15] xdiff: use one definition for freeing xdfile_t Ezekiel Newren via GitGitGadget
                   ` (6 subsequent siblings)
  15 siblings, 0 replies; 100+ messages in thread
From: Ezekiel Newren via GitGitGadget @ 2025-08-29 19:42 UTC (permalink / raw)
  To: git; +Cc: Ezekiel Newren, Ezekiel Newren

From: Ezekiel Newren <ezekielnewren@gmail.com>

A few commits ago, we added definitions for Rust primitive types,
to facilitate interoperability between C and Rust. Switch a
few variables to use these types. Which, for now, will
require adding some casts.

Also change xdlclass_t::ha to be u64 to match xrecord_t::ha, as
pointed out by Johannes.

Helped-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>
Signed-off-by: Ezekiel Newren <ezekielnewren@gmail.com>
---
 xdiff/xdiffi.c    |  8 ++++----
 xdiff/xemit.c     |  2 +-
 xdiff/xmerge.c    | 14 +++++++-------
 xdiff/xpatience.c |  2 +-
 xdiff/xprepare.c  |  8 ++++----
 xdiff/xtypes.h    |  6 +++---
 xdiff/xutils.c    |  4 ++--
 7 files changed, 22 insertions(+), 22 deletions(-)

diff --git a/xdiff/xdiffi.c b/xdiff/xdiffi.c
index 5a96e36dfb..3b364c61f6 100644
--- a/xdiff/xdiffi.c
+++ b/xdiff/xdiffi.c
@@ -418,7 +418,7 @@ static int get_indent(xrecord_t *rec)
 	long i;
 	int ret = 0;
 
-	for (i = 0; i < rec->size; i++) {
+	for (i = 0; i < (long) rec->size; i++) {
 		char c = rec->ptr[i];
 
 		if (!XDL_ISSPACE(c))
@@ -1005,11 +1005,11 @@ static void xdl_mark_ignorable_lines(xdchange_t *xscr, xdfenv_t *xe, long flags)
 
 		rec = &xe->xdf1.recs[xch->i1];
 		for (i = 0; i < xch->chg1 && ignore; i++)
-			ignore = xdl_blankline(rec[i]->ptr, rec[i]->size, flags);
+			ignore = xdl_blankline((const char*) rec[i]->ptr, rec[i]->size, flags);
 
 		rec = &xe->xdf2.recs[xch->i2];
 		for (i = 0; i < xch->chg2 && ignore; i++)
-			ignore = xdl_blankline(rec[i]->ptr, rec[i]->size, flags);
+			ignore = xdl_blankline((const char*)rec[i]->ptr, rec[i]->size, flags);
 
 		xch->ignore = ignore;
 	}
@@ -1020,7 +1020,7 @@ static int record_matches_regex(xrecord_t *rec, xpparam_t const *xpp) {
 	size_t i;
 
 	for (i = 0; i < xpp->ignore_regex_nr; i++)
-		if (!regexec_buf(xpp->ignore_regex[i], rec->ptr, rec->size, 1,
+		if (!regexec_buf(xpp->ignore_regex[i], (const char*) rec->ptr, rec->size, 1,
 				 &regmatch, 0))
 			return 1;
 
diff --git a/xdiff/xemit.c b/xdiff/xemit.c
index 1d40c9cb40..bbf7b7f8c8 100644
--- a/xdiff/xemit.c
+++ b/xdiff/xemit.c
@@ -24,7 +24,7 @@
 
 static long xdl_get_rec(xdfile_t *xdf, long ri, char const **rec) {
 
-	*rec = xdf->recs[ri]->ptr;
+	*rec = (char const*) xdf->recs[ri]->ptr;
 
 	return xdf->recs[ri]->size;
 }
diff --git a/xdiff/xmerge.c b/xdiff/xmerge.c
index af40c88a5b..6fa6ea61a2 100644
--- a/xdiff/xmerge.c
+++ b/xdiff/xmerge.c
@@ -101,8 +101,8 @@ static int xdl_merge_cmp_lines(xdfenv_t *xe1, int i1, xdfenv_t *xe2, int i2,
 	xrecord_t **rec2 = xe2->xdf2.recs + i2;
 
 	for (i = 0; i < line_count; i++) {
-		int result = xdl_recmatch(rec1[i]->ptr, rec1[i]->size,
-			rec2[i]->ptr, rec2[i]->size, flags);
+		int result = xdl_recmatch((const char*) rec1[i]->ptr, rec1[i]->size,
+			(const char*) rec2[i]->ptr, rec2[i]->size, flags);
 		if (!result)
 			return -1;
 	}
@@ -324,8 +324,8 @@ static int xdl_fill_merge_buffer(xdfenv_t *xe1, const char *name1,
 
 static int recmatch(xrecord_t *rec1, xrecord_t *rec2, unsigned long flags)
 {
-	return xdl_recmatch(rec1->ptr, rec1->size,
-			    rec2->ptr, rec2->size, flags);
+	return xdl_recmatch((char const*) rec1->ptr, rec1->size,
+			    (char const*) rec2->ptr, rec2->size, flags);
 }
 
 /*
@@ -383,10 +383,10 @@ static int xdl_refine_conflicts(xdfenv_t *xe1, xdfenv_t *xe2, xdmerge_t *m,
 		 */
 		t1.ptr = (char *)xe1->xdf2.recs[m->i1]->ptr;
 		t1.size = xe1->xdf2.recs[m->i1 + m->chg1 - 1]->ptr
-			+ xe1->xdf2.recs[m->i1 + m->chg1 - 1]->size - t1.ptr;
+			+ xe1->xdf2.recs[m->i1 + m->chg1 - 1]->size - (u8 const*) t1.ptr;
 		t2.ptr = (char *)xe2->xdf2.recs[m->i2]->ptr;
 		t2.size = xe2->xdf2.recs[m->i2 + m->chg2 - 1]->ptr
-			+ xe2->xdf2.recs[m->i2 + m->chg2 - 1]->size - t2.ptr;
+			+ xe2->xdf2.recs[m->i2 + m->chg2 - 1]->size - (u8 const*) t2.ptr;
 		if (xdl_do_diff(&t1, &t2, xpp, &xe) < 0)
 			return -1;
 		if (xdl_change_compact(&xe.xdf1, &xe.xdf2, xpp->flags) < 0 ||
@@ -440,7 +440,7 @@ static int line_contains_alnum(const char *ptr, long size)
 static int lines_contain_alnum(xdfenv_t *xe, int i, int chg)
 {
 	for (; chg; chg--, i++)
-		if (line_contains_alnum(xe->xdf2.recs[i]->ptr,
+		if (line_contains_alnum((char const*) xe->xdf2.recs[i]->ptr,
 				xe->xdf2.recs[i]->size))
 			return 1;
 	return 0;
diff --git a/xdiff/xpatience.c b/xdiff/xpatience.c
index 77dc411d19..986a3a3f74 100644
--- a/xdiff/xpatience.c
+++ b/xdiff/xpatience.c
@@ -121,7 +121,7 @@ static void insert_record(xpparam_t const *xpp, int line, struct hashmap *map,
 		return;
 	map->entries[index].line1 = line;
 	map->entries[index].hash = record->ha;
-	map->entries[index].anchor = is_anchor(xpp, map->env->xdf1.recs[line - 1]->ptr);
+	map->entries[index].anchor = is_anchor(xpp, (const char*) map->env->xdf1.recs[line - 1]->ptr);
 	if (!map->first)
 		map->first = map->entries + index;
 	if (map->last) {
diff --git a/xdiff/xprepare.c b/xdiff/xprepare.c
index ad356281f9..00cdf7d8a0 100644
--- a/xdiff/xprepare.c
+++ b/xdiff/xprepare.c
@@ -32,7 +32,7 @@
 
 typedef struct s_xdlclass {
 	struct s_xdlclass *next;
-	unsigned long ha;
+	u64 ha;
 	char const *line;
 	long size;
 	long idx;
@@ -96,12 +96,12 @@ static int xdl_classify_record(unsigned int pass, xdlclassifier_t *cf, xrecord_t
 	char const *line;
 	xdlclass_t *rcrec;
 
-	line = rec->ptr;
+	line = (char const*) rec->ptr;
 	hi = (long) XDL_HASHLONG(rec->ha, cf->hbits);
 	for (rcrec = cf->rchash[hi]; rcrec; rcrec = rcrec->next)
 		if (rcrec->ha == rec->ha &&
 				xdl_recmatch(rcrec->line, rcrec->size,
-					rec->ptr, rec->size, cf->flags))
+					(const char*) rec->ptr, rec->size, cf->flags))
 			break;
 
 	if (!rcrec) {
@@ -159,7 +159,7 @@ static int xdl_prepare_ctx(unsigned int pass, mmfile_t *mf, long narec, xpparam_
 				goto abort;
 			if (!(crec = xdl_cha_alloc(&xdf->rcha)))
 				goto abort;
-			crec->ptr = prev;
+			crec->ptr = (u8 const*) prev;
 			crec->size = (long) (cur - prev);
 			crec->ha = hav;
 			recs[nrec++] = crec;
diff --git a/xdiff/xtypes.h b/xdiff/xtypes.h
index 8b8467360e..6e5f67ebf3 100644
--- a/xdiff/xtypes.h
+++ b/xdiff/xtypes.h
@@ -39,9 +39,9 @@ typedef struct s_chastore {
 } chastore_t;
 
 typedef struct s_xrecord {
-	char const *ptr;
-	long size;
-	unsigned long ha;
+	u8 const* ptr;
+	usize size;
+	u64 ha;
 } xrecord_t;
 
 typedef struct s_xdfile {
diff --git a/xdiff/xutils.c b/xdiff/xutils.c
index 444a108f87..10e4f20b7c 100644
--- a/xdiff/xutils.c
+++ b/xdiff/xutils.c
@@ -418,10 +418,10 @@ int xdl_fall_back_diff(xdfenv_t *diff_env, xpparam_t const *xpp,
 
 	subfile1.ptr = (char *)diff_env->xdf1.recs[line1 - 1]->ptr;
 	subfile1.size = diff_env->xdf1.recs[line1 + count1 - 2]->ptr +
-		diff_env->xdf1.recs[line1 + count1 - 2]->size - subfile1.ptr;
+		diff_env->xdf1.recs[line1 + count1 - 2]->size - (u8 const*) subfile1.ptr;
 	subfile2.ptr = (char *)diff_env->xdf2.recs[line2 - 1]->ptr;
 	subfile2.size = diff_env->xdf2.recs[line2 + count2 - 2]->ptr +
-		diff_env->xdf2.recs[line2 + count2 - 2]->size - subfile2.ptr;
+		diff_env->xdf2.recs[line2 + count2 - 2]->size - (u8 const*) subfile2.ptr;
 	if (xdl_do_diff(&subfile1, &subfile2, xpp, &env) < 0)
 		return -1;
 
-- 
gitgitgadget


^ permalink raw reply related	[flat|nested] 100+ messages in thread

* [PATCH 10/15] xdiff: use one definition for freeing xdfile_t
  2025-08-29 19:42 [PATCH 00/15] Introduce rust: In xdiff Ezekiel Newren via GitGitGadget
                   ` (8 preceding siblings ...)
  2025-08-29 19:42 ` [PATCH 09/15] xdiff: make fields of xrecord_t Rust friendly Ezekiel Newren via GitGitGadget
@ 2025-08-29 19:42 ` Ezekiel Newren via GitGitGadget
  2025-08-29 19:42 ` [PATCH 11/15] xdiff: replace chastore with an ivec in xdfile_t Ezekiel Newren via GitGitGadget
                   ` (5 subsequent siblings)
  15 siblings, 0 replies; 100+ messages in thread
From: Ezekiel Newren via GitGitGadget @ 2025-08-29 19:42 UTC (permalink / raw)
  To: git; +Cc: Ezekiel Newren, Ezekiel Newren

From: Ezekiel Newren <ezekielnewren@gmail.com>

Simplify xdl_prepare_ctx() by using xdl_free_ctx() instead of using
local variables with hand rolled memory management.

Signed-off-by: Ezekiel Newren <ezekielnewren@gmail.com>
---
 xdiff/xprepare.c | 60 +++++++++++++++++++-----------------------------
 1 file changed, 24 insertions(+), 36 deletions(-)

diff --git a/xdiff/xprepare.c b/xdiff/xprepare.c
index 00cdf7d8a0..55e1cc3087 100644
--- a/xdiff/xprepare.c
+++ b/xdiff/xprepare.c
@@ -129,86 +129,74 @@ static int xdl_classify_record(unsigned int pass, xdlclassifier_t *cf, xrecord_t
 }
 
 
+static void xdl_free_ctx(xdfile_t *xdf) {
+	xdl_free(xdf->rindex);
+	xdl_free(xdf->rchg - 1);
+	xdl_free(xdf->ha);
+	xdl_free(xdf->recs);
+	xdl_cha_free(&xdf->rcha);
+}
+
+
 static int xdl_prepare_ctx(unsigned int pass, mmfile_t *mf, long narec, xpparam_t const *xpp,
 			   xdlclassifier_t *cf, xdfile_t *xdf) {
-	long nrec, bsize;
+	long bsize;
 	unsigned long hav;
 	char const *blk, *cur, *top, *prev;
 	xrecord_t *crec;
-	xrecord_t **recs;
-	unsigned long *ha;
-	char *rchg;
-	long *rindex;
 
-	ha = NULL;
-	rindex = NULL;
-	rchg = NULL;
-	recs = NULL;
+	xdf->ha = NULL;
+	xdf->rindex = NULL;
+	xdf->rchg = NULL;
+	xdf->recs = NULL;
+	xdf->nrec = 0;
 
 	if (xdl_cha_init(&xdf->rcha, sizeof(xrecord_t), narec / 4 + 1) < 0)
 		goto abort;
-	if (!XDL_ALLOC_ARRAY(recs, narec))
+	if (!XDL_ALLOC_ARRAY(xdf->recs, narec))
 		goto abort;
 
-	nrec = 0;
 	if ((cur = blk = xdl_mmfile_first(mf, &bsize))) {
 		for (top = blk + bsize; cur < top; ) {
 			prev = cur;
 			hav = xdl_hash_record(&cur, top, xpp->flags);
-			if (XDL_ALLOC_GROW(recs, nrec + 1, narec))
+			if (XDL_ALLOC_GROW(xdf->recs, xdf->nrec + 1, narec))
 				goto abort;
 			if (!(crec = xdl_cha_alloc(&xdf->rcha)))
 				goto abort;
 			crec->ptr = (u8 const*) prev;
 			crec->size = (long) (cur - prev);
 			crec->ha = hav;
-			recs[nrec++] = crec;
+			xdf->recs[xdf->nrec++] = crec;
 			if (xdl_classify_record(pass, cf, crec) < 0)
 				goto abort;
 		}
 	}
 
-	if (!XDL_CALLOC_ARRAY(rchg, nrec + 2))
+	if (!XDL_CALLOC_ARRAY(xdf->rchg, xdf->nrec + 2))
 		goto abort;
 
 	if ((XDF_DIFF_ALG(xpp->flags) != XDF_PATIENCE_DIFF) &&
 	    (XDF_DIFF_ALG(xpp->flags) != XDF_HISTOGRAM_DIFF)) {
-		if (!XDL_ALLOC_ARRAY(rindex, nrec + 1))
+		if (!XDL_ALLOC_ARRAY(xdf->rindex, xdf->nrec + 1))
 			goto abort;
-		if (!XDL_ALLOC_ARRAY(ha, nrec + 1))
+		if (!XDL_ALLOC_ARRAY(xdf->ha, xdf->nrec + 1))
 			goto abort;
 	}
 
-	xdf->nrec = nrec;
-	xdf->recs = recs;
-	xdf->rchg = rchg + 1;
-	xdf->rindex = rindex;
+	xdf->rchg += 1;
 	xdf->nreff = 0;
-	xdf->ha = ha;
 	xdf->dstart = 0;
-	xdf->dend = nrec - 1;
+	xdf->dend = xdf->nrec - 1;
 
 	return 0;
 
 abort:
-	xdl_free(ha);
-	xdl_free(rindex);
-	xdl_free(rchg);
-	xdl_free(recs);
-	xdl_cha_free(&xdf->rcha);
+	xdl_free_ctx(xdf);
 	return -1;
 }
 
 
-static void xdl_free_ctx(xdfile_t *xdf) {
-	xdl_free(xdf->rindex);
-	xdl_free(xdf->rchg - 1);
-	xdl_free(xdf->ha);
-	xdl_free(xdf->recs);
-	xdl_cha_free(&xdf->rcha);
-}
-
-
 void xdl_free_env(xdfenv_t *xe) {
 
 	xdl_free_ctx(&xe->xdf2);
-- 
gitgitgadget


^ permalink raw reply related	[flat|nested] 100+ messages in thread

* [PATCH 11/15] xdiff: replace chastore with an ivec in xdfile_t
  2025-08-29 19:42 [PATCH 00/15] Introduce rust: In xdiff Ezekiel Newren via GitGitGadget
                   ` (9 preceding siblings ...)
  2025-08-29 19:42 ` [PATCH 10/15] xdiff: use one definition for freeing xdfile_t Ezekiel Newren via GitGitGadget
@ 2025-08-29 19:42 ` Ezekiel Newren via GitGitGadget
  2025-08-29 19:42 ` [PATCH 12/15] xdiff: delete nrec field from xdfile_t Ezekiel Newren via GitGitGadget
                   ` (4 subsequent siblings)
  15 siblings, 0 replies; 100+ messages in thread
From: Ezekiel Newren via GitGitGadget @ 2025-08-29 19:42 UTC (permalink / raw)
  To: git; +Cc: Ezekiel Newren, Ezekiel Newren

From: Ezekiel Newren <ezekielnewren@gmail.com>

xdfile_t currently uses a chastore which functions as a memory pool and
a vector which maps to the allocations created by the chastore. It seems
like xrecord_t used to be a linked list until the recs and nrec fields
were added. I think that xrecord_t.next was meant to be removed, but
was overlooked. This dual data structure setup make the code somewhat
confusing.

Additionally the C type chastore_t isn't FFI friendly. While it could
be implemented in Rust, since the data structure is confusing anyway,
replace it with an ivec whose purpose is to be interoperable. This
makes the fields nrec and recs in xdfile_t redundant, which will be
removed in the next 2 commits.

Signed-off-by: Ezekiel Newren <ezekielnewren@gmail.com>
---
 xdiff/xprepare.c | 34 +++++++++++++++++-----------------
 xdiff/xtypes.h   |  6 ++++--
 2 files changed, 21 insertions(+), 19 deletions(-)

diff --git a/xdiff/xprepare.c b/xdiff/xprepare.c
index 55e1cc3087..3b33186c15 100644
--- a/xdiff/xprepare.c
+++ b/xdiff/xprepare.c
@@ -130,11 +130,11 @@ static int xdl_classify_record(unsigned int pass, xdlclassifier_t *cf, xrecord_t
 
 
 static void xdl_free_ctx(xdfile_t *xdf) {
+	ivec_free(&xdf->record);
 	xdl_free(xdf->rindex);
 	xdl_free(xdf->rchg - 1);
 	xdl_free(xdf->ha);
 	xdl_free(xdf->recs);
-	xdl_cha_free(&xdf->rcha);
 }
 
 
@@ -143,35 +143,35 @@ static int xdl_prepare_ctx(unsigned int pass, mmfile_t *mf, long narec, xpparam_
 	long bsize;
 	unsigned long hav;
 	char const *blk, *cur, *top, *prev;
-	xrecord_t *crec;
 
 	xdf->ha = NULL;
 	xdf->rindex = NULL;
 	xdf->rchg = NULL;
 	xdf->recs = NULL;
 	xdf->nrec = 0;
-
-	if (xdl_cha_init(&xdf->rcha, sizeof(xrecord_t), narec / 4 + 1) < 0)
-		goto abort;
-	if (!XDL_ALLOC_ARRAY(xdf->recs, narec))
-		goto abort;
+	IVEC_INIT(xdf->record);
 
 	if ((cur = blk = xdl_mmfile_first(mf, &bsize))) {
 		for (top = blk + bsize; cur < top; ) {
+			xrecord_t crec;
 			prev = cur;
 			hav = xdl_hash_record(&cur, top, xpp->flags);
-			if (XDL_ALLOC_GROW(xdf->recs, xdf->nrec + 1, narec))
-				goto abort;
-			if (!(crec = xdl_cha_alloc(&xdf->rcha)))
-				goto abort;
-			crec->ptr = (u8 const*) prev;
-			crec->size = (long) (cur - prev);
-			crec->ha = hav;
-			xdf->recs[xdf->nrec++] = crec;
-			if (xdl_classify_record(pass, cf, crec) < 0)
-				goto abort;
+			crec.ptr = (u8 const*) prev;
+			crec.size = cur - prev;
+			crec.ha = hav;
+			ivec_push(&xdf->record, &crec);
 		}
 	}
+	ivec_shrink_to_fit(&xdf->record);
+
+	xdf->nrec = (long) xdf->record.length;
+	if (!XDL_ALLOC_ARRAY(xdf->recs, xdf->record.length))
+		goto abort;
+	for (usize i = 0; i < xdf->record.length; i++) {
+		if (xdl_classify_record(pass, cf, &xdf->record.ptr[i]) < 0)
+			goto abort;
+		xdf->recs[i] = &xdf->record.ptr[i];
+	}
 
 	if (!XDL_CALLOC_ARRAY(xdf->rchg, xdf->nrec + 2))
 		goto abort;
diff --git a/xdiff/xtypes.h b/xdiff/xtypes.h
index 6e5f67ebf3..5028a70b26 100644
--- a/xdiff/xtypes.h
+++ b/xdiff/xtypes.h
@@ -23,7 +23,7 @@
 #if !defined(XTYPES_H)
 #define XTYPES_H
 
-
+#include "../interop/ivec.h"
 
 typedef struct s_chanode {
 	struct s_chanode *next;
@@ -44,8 +44,10 @@ typedef struct s_xrecord {
 	u64 ha;
 } xrecord_t;
 
+DEFINE_IVEC_TYPE(xrecord_t, xrecord);
+
 typedef struct s_xdfile {
-	chastore_t rcha;
+	struct ivec_xrecord record;
 	long nrec;
 	long dstart, dend;
 	xrecord_t **recs;
-- 
gitgitgadget


^ permalink raw reply related	[flat|nested] 100+ messages in thread

* [PATCH 12/15] xdiff: delete nrec field from xdfile_t
  2025-08-29 19:42 [PATCH 00/15] Introduce rust: In xdiff Ezekiel Newren via GitGitGadget
                   ` (10 preceding siblings ...)
  2025-08-29 19:42 ` [PATCH 11/15] xdiff: replace chastore with an ivec in xdfile_t Ezekiel Newren via GitGitGadget
@ 2025-08-29 19:42 ` Ezekiel Newren via GitGitGadget
  2025-08-29 19:42 ` [PATCH 13/15] xdiff: delete recs " Ezekiel Newren via GitGitGadget
                   ` (3 subsequent siblings)
  15 siblings, 0 replies; 100+ messages in thread
From: Ezekiel Newren via GitGitGadget @ 2025-08-29 19:42 UTC (permalink / raw)
  To: git; +Cc: Ezekiel Newren, Ezekiel Newren

From: Ezekiel Newren <ezekielnewren@gmail.com>

Because of the data structure cleanup in the previous commit, the nrec
field is no longer necessary. Use record.length in place of nrec.

This commit is best viewed with --color-words.

Signed-off-by: Ezekiel Newren <ezekielnewren@gmail.com>
---
 xdiff/xdiffi.c    | 10 +++++-----
 xdiff/xemit.c     | 20 ++++++++++----------
 xdiff/xmerge.c    | 10 +++++-----
 xdiff/xpatience.c |  2 +-
 xdiff/xprepare.c  | 34 ++++++++++++++++------------------
 xdiff/xtypes.h    |  1 -
 6 files changed, 37 insertions(+), 40 deletions(-)

diff --git a/xdiff/xdiffi.c b/xdiff/xdiffi.c
index 3b364c61f6..bcab2d7ae5 100644
--- a/xdiff/xdiffi.c
+++ b/xdiff/xdiffi.c
@@ -496,7 +496,7 @@ static void measure_split(const xdfile_t *xdf, long split,
 {
 	long i;
 
-	if (split >= xdf->nrec) {
+	if (split >= (long) xdf->record.length) {
 		m->end_of_file = 1;
 		m->indent = -1;
 	} else {
@@ -519,7 +519,7 @@ static void measure_split(const xdfile_t *xdf, long split,
 
 	m->post_blank = 0;
 	m->post_indent = -1;
-	for (i = split + 1; i < xdf->nrec; i++) {
+	for (i = split + 1; i < (long) xdf->record.length; i++) {
 		m->post_indent = get_indent(xdf->recs[i]);
 		if (m->post_indent != -1)
 			break;
@@ -730,7 +730,7 @@ static void group_init(xdfile_t *xdf, struct xdlgroup *g)
  */
 static inline int group_next(xdfile_t *xdf, struct xdlgroup *g)
 {
-	if (g->end == xdf->nrec)
+	if (g->end == (long) xdf->record.length)
 		return -1;
 
 	g->start = g->end + 1;
@@ -763,7 +763,7 @@ static inline int group_previous(xdfile_t *xdf, struct xdlgroup *g)
  */
 static int group_slide_down(xdfile_t *xdf, struct xdlgroup *g)
 {
-	if (g->end < xdf->nrec &&
+	if (g->end < (long) xdf->record.length &&
 	    recs_match(xdf->recs[g->start], xdf->recs[g->end])) {
 		xdf->rchg[g->start++] = 0;
 		xdf->rchg[g->end++] = 1;
@@ -950,7 +950,7 @@ int xdl_build_script(xdfenv_t *xe, xdchange_t **xscr) {
 	/*
 	 * Trivial. Collects "groups" of changes and creates an edit script.
 	 */
-	for (i1 = xe->xdf1.nrec, i2 = xe->xdf2.nrec; i1 >= 0 || i2 >= 0; i1--, i2--)
+	for (i1 = xe->xdf1.record.length, i2 = xe->xdf2.record.length; i1 >= 0 || i2 >= 0; i1--, i2--)
 		if (rchg1[i1 - 1] || rchg2[i2 - 1]) {
 			for (l1 = i1; rchg1[i1 - 1]; i1--);
 			for (l2 = i2; rchg2[i2 - 1]; i2--);
diff --git a/xdiff/xemit.c b/xdiff/xemit.c
index bbf7b7f8c8..11c2823eca 100644
--- a/xdiff/xemit.c
+++ b/xdiff/xemit.c
@@ -147,7 +147,7 @@ static long get_func_line(xdfenv_t *xe, xdemitconf_t const *xecfg,
 	buf = func_line ? func_line->buf : dummy;
 	size = func_line ? sizeof(func_line->buf) : sizeof(dummy);
 
-	for (l = start; l != limit && 0 <= l && l < xe->xdf1.nrec; l += step) {
+	for (l = start; l != limit && 0 <= l && l < (long) xe->xdf1.record.length; l += step) {
 		long len = match_func_rec(&xe->xdf1, xecfg, l, buf, size);
 		if (len >= 0) {
 			if (func_line)
@@ -191,14 +191,14 @@ pre_context_calculation:
 			long fs1, i1 = xch->i1;
 
 			/* Appended chunk? */
-			if (i1 >= xe->xdf1.nrec) {
+			if (i1 >= (long) xe->xdf1.record.length) {
 				long i2 = xch->i2;
 
 				/*
 				 * We don't need additional context if
 				 * a whole function was added.
 				 */
-				while (i2 < xe->xdf2.nrec) {
+				while (i2 < (long) xe->xdf2.record.length) {
 					if (is_func_rec(&xe->xdf2, xecfg, i2))
 						goto post_context_calculation;
 					i2++;
@@ -208,7 +208,7 @@ pre_context_calculation:
 				 * Otherwise get more context from the
 				 * pre-image.
 				 */
-				i1 = xe->xdf1.nrec - 1;
+				i1 = xe->xdf1.record.length - 1;
 			}
 
 			fs1 = get_func_line(xe, xecfg, NULL, i1, -1);
@@ -240,8 +240,8 @@ pre_context_calculation:
 
  post_context_calculation:
 		lctx = xecfg->ctxlen;
-		lctx = XDL_MIN(lctx, xe->xdf1.nrec - (xche->i1 + xche->chg1));
-		lctx = XDL_MIN(lctx, xe->xdf2.nrec - (xche->i2 + xche->chg2));
+		lctx = XDL_MIN(lctx, (long) xe->xdf1.record.length - (xche->i1 + xche->chg1));
+		lctx = XDL_MIN(lctx, (long) xe->xdf2.record.length - (xche->i2 + xche->chg2));
 
 		e1 = xche->i1 + xche->chg1 + lctx;
 		e2 = xche->i2 + xche->chg2 + lctx;
@@ -249,13 +249,13 @@ pre_context_calculation:
 		if (xecfg->flags & XDL_EMIT_FUNCCONTEXT) {
 			long fe1 = get_func_line(xe, xecfg, NULL,
 						 xche->i1 + xche->chg1,
-						 xe->xdf1.nrec);
+						 xe->xdf1.record.length);
 			while (fe1 > 0 && is_empty_rec(&xe->xdf1, fe1 - 1))
 				fe1--;
 			if (fe1 < 0)
-				fe1 = xe->xdf1.nrec;
+				fe1 = xe->xdf1.record.length;
 			if (fe1 > e1) {
-				e2 = XDL_MIN(e2 + (fe1 - e1), xe->xdf2.nrec);
+				e2 = XDL_MIN(e2 + (fe1 - e1), (long) xe->xdf2.record.length);
 				e1 = fe1;
 			}
 
@@ -266,7 +266,7 @@ pre_context_calculation:
 			 */
 			if (xche->next) {
 				long l = XDL_MIN(xche->next->i1,
-						 xe->xdf1.nrec - 1);
+						 (long) xe->xdf1.record.length - 1);
 				if (l - xecfg->ctxlen <= e1 ||
 				    get_func_line(xe, xecfg, NULL, l, e1) < 0) {
 					xche = xche->next;
diff --git a/xdiff/xmerge.c b/xdiff/xmerge.c
index 6fa6ea61a2..f48549605d 100644
--- a/xdiff/xmerge.c
+++ b/xdiff/xmerge.c
@@ -158,11 +158,11 @@ static int is_eol_crlf(xdfile_t *file, int i)
 {
 	long size;
 
-	if (i < file->nrec - 1)
+	if (i < (isize) file->record.length - 1)
 		/* All lines before the last *must* end in LF */
 		return (size = file->recs[i]->size) > 1 &&
 			file->recs[i]->ptr[size - 2] == '\r';
-	if (!file->nrec)
+	if (!file->record.length)
 		/* Cannot determine eol style from empty file */
 		return -1;
 	if ((size = file->recs[i]->size) &&
@@ -317,7 +317,7 @@ static int xdl_fill_merge_buffer(xdfenv_t *xe1, const char *name1,
 			continue;
 		i = m->i1 + m->chg1;
 	}
-	size += xdl_recs_copy(xe1, i, xe1->xdf2.nrec - i, 0, 0,
+	size += xdl_recs_copy(xe1, i, (int) xe1->xdf2.record.length - i, 0, 0,
 			      dest ? dest + size : NULL);
 	return size;
 }
@@ -622,7 +622,7 @@ static int xdl_do_merge(xdfenv_t *xe1, xdchange_t *xscr1,
 			changes = c;
 		i0 = xscr1->i1;
 		i1 = xscr1->i2;
-		i2 = xscr1->i1 + xe2->xdf2.nrec - xe2->xdf1.nrec;
+		i2 = xscr1->i1 + xe2->xdf2.record.length - xe2->xdf1.record.length;
 		chg0 = xscr1->chg1;
 		chg1 = xscr1->chg2;
 		chg2 = xscr1->chg1;
@@ -637,7 +637,7 @@ static int xdl_do_merge(xdfenv_t *xe1, xdchange_t *xscr1,
 		if (!changes)
 			changes = c;
 		i0 = xscr2->i1;
-		i1 = xscr2->i1 + xe1->xdf2.nrec - xe1->xdf1.nrec;
+		i1 = xscr2->i1 + xe1->xdf2.record.length - xe1->xdf1.record.length;
 		i2 = xscr2->i2;
 		chg0 = xscr2->chg1;
 		chg1 = xscr2->chg1;
diff --git a/xdiff/xpatience.c b/xdiff/xpatience.c
index 986a3a3f74..e1ce9a399f 100644
--- a/xdiff/xpatience.c
+++ b/xdiff/xpatience.c
@@ -370,5 +370,5 @@ static int patience_diff(xpparam_t const *xpp, xdfenv_t *env,
 
 int xdl_do_patience_diff(xpparam_t const *xpp, xdfenv_t *env)
 {
-	return patience_diff(xpp, env, 1, env->xdf1.nrec, 1, env->xdf2.nrec);
+	return patience_diff(xpp, env, 1, env->xdf1.record.length, 1, env->xdf2.record.length);
 }
diff --git a/xdiff/xprepare.c b/xdiff/xprepare.c
index 3b33186c15..9b46523afe 100644
--- a/xdiff/xprepare.c
+++ b/xdiff/xprepare.c
@@ -138,7 +138,7 @@ static void xdl_free_ctx(xdfile_t *xdf) {
 }
 
 
-static int xdl_prepare_ctx(unsigned int pass, mmfile_t *mf, long narec, xpparam_t const *xpp,
+static int xdl_prepare_ctx(unsigned int pass, mmfile_t *mf, xpparam_t const *xpp,
 			   xdlclassifier_t *cf, xdfile_t *xdf) {
 	long bsize;
 	unsigned long hav;
@@ -148,7 +148,6 @@ static int xdl_prepare_ctx(unsigned int pass, mmfile_t *mf, long narec, xpparam_
 	xdf->rindex = NULL;
 	xdf->rchg = NULL;
 	xdf->recs = NULL;
-	xdf->nrec = 0;
 	IVEC_INIT(xdf->record);
 
 	if ((cur = blk = xdl_mmfile_first(mf, &bsize))) {
@@ -164,7 +163,6 @@ static int xdl_prepare_ctx(unsigned int pass, mmfile_t *mf, long narec, xpparam_
 	}
 	ivec_shrink_to_fit(&xdf->record);
 
-	xdf->nrec = (long) xdf->record.length;
 	if (!XDL_ALLOC_ARRAY(xdf->recs, xdf->record.length))
 		goto abort;
 	for (usize i = 0; i < xdf->record.length; i++) {
@@ -173,21 +171,21 @@ static int xdl_prepare_ctx(unsigned int pass, mmfile_t *mf, long narec, xpparam_
 		xdf->recs[i] = &xdf->record.ptr[i];
 	}
 
-	if (!XDL_CALLOC_ARRAY(xdf->rchg, xdf->nrec + 2))
+	if (!XDL_CALLOC_ARRAY(xdf->rchg, xdf->record.length + 2))
 		goto abort;
 
 	if ((XDF_DIFF_ALG(xpp->flags) != XDF_PATIENCE_DIFF) &&
 	    (XDF_DIFF_ALG(xpp->flags) != XDF_HISTOGRAM_DIFF)) {
-		if (!XDL_ALLOC_ARRAY(xdf->rindex, xdf->nrec + 1))
+		if (!XDL_ALLOC_ARRAY(xdf->rindex, xdf->record.length + 1))
 			goto abort;
-		if (!XDL_ALLOC_ARRAY(xdf->ha, xdf->nrec + 1))
+		if (!XDL_ALLOC_ARRAY(xdf->ha, xdf->record.length + 1))
 			goto abort;
 	}
 
 	xdf->rchg += 1;
 	xdf->nreff = 0;
 	xdf->dstart = 0;
-	xdf->dend = xdf->nrec - 1;
+	xdf->dend = xdf->record.length - 1;
 
 	return 0;
 
@@ -274,12 +272,12 @@ static int xdl_cleanup_records(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xd
 	char *dis, *dis1, *dis2;
 	int need_min = !!(cf->flags & XDF_NEED_MINIMAL);
 
-	if (!XDL_CALLOC_ARRAY(dis, xdf1->nrec + xdf2->nrec + 2))
+	if (!XDL_CALLOC_ARRAY(dis, xdf1->record.length + xdf2->record.length + 2))
 		return -1;
 	dis1 = dis;
-	dis2 = dis1 + xdf1->nrec + 1;
+	dis2 = dis1 + xdf1->record.length + 1;
 
-	if ((mlim = xdl_bogosqrt(xdf1->nrec)) > XDL_MAX_EQLIMIT)
+	if ((mlim = xdl_bogosqrt(xdf1->record.length)) > XDL_MAX_EQLIMIT)
 		mlim = XDL_MAX_EQLIMIT;
 	for (i = xdf1->dstart, recs = &xdf1->recs[xdf1->dstart]; i <= xdf1->dend; i++, recs++) {
 		rcrec = cf->rcrecs[(*recs)->ha];
@@ -287,7 +285,7 @@ static int xdl_cleanup_records(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xd
 		dis1[i] = (nm == 0) ? 0: (nm >= mlim && !need_min) ? 2: 1;
 	}
 
-	if ((mlim = xdl_bogosqrt(xdf2->nrec)) > XDL_MAX_EQLIMIT)
+	if ((mlim = xdl_bogosqrt(xdf2->record.length)) > XDL_MAX_EQLIMIT)
 		mlim = XDL_MAX_EQLIMIT;
 	for (i = xdf2->dstart, recs = &xdf2->recs[xdf2->dstart]; i <= xdf2->dend; i++, recs++) {
 		rcrec = cf->rcrecs[(*recs)->ha];
@@ -334,21 +332,21 @@ static int xdl_trim_ends(xdfile_t *xdf1, xdfile_t *xdf2) {
 
 	recs1 = xdf1->recs;
 	recs2 = xdf2->recs;
-	for (i = 0, lim = XDL_MIN(xdf1->nrec, xdf2->nrec); i < lim;
+	for (i = 0, lim = XDL_MIN(xdf1->record.length, xdf2->record.length); i < lim;
 	     i++, recs1++, recs2++)
 		if ((*recs1)->ha != (*recs2)->ha)
 			break;
 
 	xdf1->dstart = xdf2->dstart = i;
 
-	recs1 = xdf1->recs + xdf1->nrec - 1;
-	recs2 = xdf2->recs + xdf2->nrec - 1;
+	recs1 = xdf1->recs + xdf1->record.length - 1;
+	recs2 = xdf2->recs + xdf2->record.length - 1;
 	for (lim -= i, i = 0; i < lim; i++, recs1--, recs2--)
 		if ((*recs1)->ha != (*recs2)->ha)
 			break;
 
-	xdf1->dend = xdf1->nrec - i - 1;
-	xdf2->dend = xdf2->nrec - i - 1;
+	xdf1->dend = xdf1->record.length - i - 1;
+	xdf2->dend = xdf2->record.length - i - 1;
 
 	return 0;
 }
@@ -388,12 +386,12 @@ int xdl_prepare_env(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,
 	if (xdl_init_classifier(&cf, enl1 + enl2 + 1, xpp->flags) < 0)
 		return -1;
 
-	if (xdl_prepare_ctx(1, mf1, enl1, xpp, &cf, &xe->xdf1) < 0) {
+	if (xdl_prepare_ctx(1, mf1, xpp, &cf, &xe->xdf1) < 0) {
 
 		xdl_free_classifier(&cf);
 		return -1;
 	}
-	if (xdl_prepare_ctx(2, mf2, enl2, xpp, &cf, &xe->xdf2) < 0) {
+	if (xdl_prepare_ctx(2, mf2, xpp, &cf, &xe->xdf2) < 0) {
 
 		xdl_free_ctx(&xe->xdf1);
 		xdl_free_classifier(&cf);
diff --git a/xdiff/xtypes.h b/xdiff/xtypes.h
index 5028a70b26..c322e62fbf 100644
--- a/xdiff/xtypes.h
+++ b/xdiff/xtypes.h
@@ -48,7 +48,6 @@ DEFINE_IVEC_TYPE(xrecord_t, xrecord);
 
 typedef struct s_xdfile {
 	struct ivec_xrecord record;
-	long nrec;
 	long dstart, dend;
 	xrecord_t **recs;
 	char *rchg;
-- 
gitgitgadget


^ permalink raw reply related	[flat|nested] 100+ messages in thread

* [PATCH 13/15] xdiff: delete recs field from xdfile_t
  2025-08-29 19:42 [PATCH 00/15] Introduce rust: In xdiff Ezekiel Newren via GitGitGadget
                   ` (11 preceding siblings ...)
  2025-08-29 19:42 ` [PATCH 12/15] xdiff: delete nrec field from xdfile_t Ezekiel Newren via GitGitGadget
@ 2025-08-29 19:42 ` Ezekiel Newren via GitGitGadget
  2025-08-29 19:42 ` [PATCH 14/15] xdiff: make xdfile_t more rust friendly Ezekiel Newren via GitGitGadget
                   ` (2 subsequent siblings)
  15 siblings, 0 replies; 100+ messages in thread
From: Ezekiel Newren via GitGitGadget @ 2025-08-29 19:42 UTC (permalink / raw)
  To: git; +Cc: Ezekiel Newren, Ezekiel Newren

From: Ezekiel Newren <ezekielnewren@gmail.com>

Because of the change from chastore to ivec a few commits ago,
recs now points to record's elements in a 1:1 mapping.
Since both recs and record are vectors, this additional mapping is
superfluous. Remove recs.

This commit is best viewed with --color-words.

Signed-off-by: Ezekiel Newren <ezekielnewren@gmail.com>
---
 xdiff/xdiffi.c     | 30 ++++++++++++------------
 xdiff/xemit.c      |  4 ++--
 xdiff/xhistogram.c |  2 +-
 xdiff/xmerge.c     | 58 +++++++++++++++++++++++-----------------------
 xdiff/xpatience.c  | 14 +++++------
 xdiff/xprepare.c   | 37 +++++++++++++----------------
 xdiff/xtypes.h     |  1 -
 xdiff/xutils.c     | 12 +++++-----
 8 files changed, 76 insertions(+), 82 deletions(-)

diff --git a/xdiff/xdiffi.c b/xdiff/xdiffi.c
index bcab2d7ae5..ebdb724322 100644
--- a/xdiff/xdiffi.c
+++ b/xdiff/xdiffi.c
@@ -501,13 +501,13 @@ static void measure_split(const xdfile_t *xdf, long split,
 		m->indent = -1;
 	} else {
 		m->end_of_file = 0;
-		m->indent = get_indent(xdf->recs[split]);
+		m->indent = get_indent(&xdf->record.ptr[split]);
 	}
 
 	m->pre_blank = 0;
 	m->pre_indent = -1;
 	for (i = split - 1; i >= 0; i--) {
-		m->pre_indent = get_indent(xdf->recs[i]);
+		m->pre_indent = get_indent(&xdf->record.ptr[i]);
 		if (m->pre_indent != -1)
 			break;
 		m->pre_blank += 1;
@@ -520,7 +520,7 @@ static void measure_split(const xdfile_t *xdf, long split,
 	m->post_blank = 0;
 	m->post_indent = -1;
 	for (i = split + 1; i < (long) xdf->record.length; i++) {
-		m->post_indent = get_indent(xdf->recs[i]);
+		m->post_indent = get_indent(&xdf->record.ptr[i]);
 		if (m->post_indent != -1)
 			break;
 		m->post_blank += 1;
@@ -764,7 +764,7 @@ static inline int group_previous(xdfile_t *xdf, struct xdlgroup *g)
 static int group_slide_down(xdfile_t *xdf, struct xdlgroup *g)
 {
 	if (g->end < (long) xdf->record.length &&
-	    recs_match(xdf->recs[g->start], xdf->recs[g->end])) {
+	    recs_match(&xdf->record.ptr[g->start], &xdf->record.ptr[g->end])) {
 		xdf->rchg[g->start++] = 0;
 		xdf->rchg[g->end++] = 1;
 
@@ -785,7 +785,7 @@ static int group_slide_down(xdfile_t *xdf, struct xdlgroup *g)
 static int group_slide_up(xdfile_t *xdf, struct xdlgroup *g)
 {
 	if (g->start > 0 &&
-	    recs_match(xdf->recs[g->start - 1], xdf->recs[g->end - 1])) {
+	    recs_match(&xdf->record.ptr[g->start - 1], &xdf->record.ptr[g->end - 1])) {
 		xdf->rchg[--g->start] = 1;
 		xdf->rchg[--g->end] = 0;
 
@@ -1000,16 +1000,16 @@ static void xdl_mark_ignorable_lines(xdchange_t *xscr, xdfenv_t *xe, long flags)
 
 	for (xch = xscr; xch; xch = xch->next) {
 		int ignore = 1;
-		xrecord_t **rec;
+		xrecord_t *rec;
 		long i;
 
-		rec = &xe->xdf1.recs[xch->i1];
+		rec = &xe->xdf1.record.ptr[xch->i1];
 		for (i = 0; i < xch->chg1 && ignore; i++)
-			ignore = xdl_blankline((const char*) rec[i]->ptr, rec[i]->size, flags);
+			ignore = xdl_blankline((const char*) rec[i].ptr, rec[i].size, flags);
 
-		rec = &xe->xdf2.recs[xch->i2];
+		rec = &xe->xdf2.record.ptr[xch->i2];
 		for (i = 0; i < xch->chg2 && ignore; i++)
-			ignore = xdl_blankline((const char*)rec[i]->ptr, rec[i]->size, flags);
+			ignore = xdl_blankline((const char*)rec[i].ptr, rec[i].size, flags);
 
 		xch->ignore = ignore;
 	}
@@ -1033,7 +1033,7 @@ static void xdl_mark_ignorable_regex(xdchange_t *xscr, const xdfenv_t *xe,
 	xdchange_t *xch;
 
 	for (xch = xscr; xch; xch = xch->next) {
-		xrecord_t **rec;
+		xrecord_t *rec;
 		int ignore = 1;
 		long i;
 
@@ -1043,13 +1043,13 @@ static void xdl_mark_ignorable_regex(xdchange_t *xscr, const xdfenv_t *xe,
 		if (xch->ignore)
 			continue;
 
-		rec = &xe->xdf1.recs[xch->i1];
+		rec = &xe->xdf1.record.ptr[xch->i1];
 		for (i = 0; i < xch->chg1 && ignore; i++)
-			ignore = record_matches_regex(rec[i], xpp);
+			ignore = record_matches_regex(&rec[i], xpp);
 
-		rec = &xe->xdf2.recs[xch->i2];
+		rec = &xe->xdf2.record.ptr[xch->i2];
 		for (i = 0; i < xch->chg2 && ignore; i++)
-			ignore = record_matches_regex(rec[i], xpp);
+			ignore = record_matches_regex(&rec[i], xpp);
 
 		xch->ignore = ignore;
 	}
diff --git a/xdiff/xemit.c b/xdiff/xemit.c
index 11c2823eca..0c9a12a5e8 100644
--- a/xdiff/xemit.c
+++ b/xdiff/xemit.c
@@ -24,9 +24,9 @@
 
 static long xdl_get_rec(xdfile_t *xdf, long ri, char const **rec) {
 
-	*rec = (char const*) xdf->recs[ri]->ptr;
+	*rec = (char const*) xdf->record.ptr[ri].ptr;
 
-	return xdf->recs[ri]->size;
+	return xdf->record.ptr[ri].size;
 }
 
 
diff --git a/xdiff/xhistogram.c b/xdiff/xhistogram.c
index 040d81e0bc..643d1c8b70 100644
--- a/xdiff/xhistogram.c
+++ b/xdiff/xhistogram.c
@@ -86,7 +86,7 @@ struct region {
 	((LINE_MAP(index, ptr))->cnt)
 
 #define REC(env, s, l) \
-	(env->xdf##s.recs[l - 1])
+	(&env->xdf##s.record.ptr[l - 1])
 
 static int cmp_recs(xrecord_t *r1, xrecord_t *r2)
 {
diff --git a/xdiff/xmerge.c b/xdiff/xmerge.c
index f48549605d..0a3e0f28ab 100644
--- a/xdiff/xmerge.c
+++ b/xdiff/xmerge.c
@@ -97,12 +97,12 @@ static int xdl_merge_cmp_lines(xdfenv_t *xe1, int i1, xdfenv_t *xe2, int i2,
 		int line_count, long flags)
 {
 	int i;
-	xrecord_t **rec1 = xe1->xdf2.recs + i1;
-	xrecord_t **rec2 = xe2->xdf2.recs + i2;
+	xrecord_t *rec1 = xe1->xdf2.record.ptr + i1;
+	xrecord_t *rec2 = xe2->xdf2.record.ptr + i2;
 
 	for (i = 0; i < line_count; i++) {
-		int result = xdl_recmatch((const char*) rec1[i]->ptr, rec1[i]->size,
-			(const char*) rec2[i]->ptr, rec2[i]->size, flags);
+		int result = xdl_recmatch((const char*) rec1[i].ptr, rec1[i].size,
+			(const char*) rec2[i].ptr, rec2[i].size, flags);
 		if (!result)
 			return -1;
 	}
@@ -111,20 +111,20 @@ static int xdl_merge_cmp_lines(xdfenv_t *xe1, int i1, xdfenv_t *xe2, int i2,
 
 static int xdl_recs_copy_0(int use_orig, xdfenv_t *xe, int i, int count, int needs_cr, int add_nl, char *dest)
 {
-	xrecord_t **recs;
+	xrecord_t *recs;
 	int size = 0;
 
-	recs = (use_orig ? xe->xdf1.recs : xe->xdf2.recs) + i;
+	recs = (use_orig ? xe->xdf1.record.ptr : xe->xdf2.record.ptr) + i;
 
 	if (count < 1)
 		return 0;
 
-	for (i = 0; i < count; size += recs[i++]->size)
+	for (i = 0; i < count; size += recs[i++].size)
 		if (dest)
-			memcpy(dest + size, recs[i]->ptr, recs[i]->size);
+			memcpy(dest + size, recs[i].ptr, recs[i].size);
 	if (add_nl) {
-		i = recs[count - 1]->size;
-		if (i == 0 || recs[count - 1]->ptr[i - 1] != '\n') {
+		i = recs[count - 1].size;
+		if (i == 0 || recs[count - 1].ptr[i - 1] != '\n') {
 			if (needs_cr) {
 				if (dest)
 					dest[size] = '\r';
@@ -160,22 +160,22 @@ static int is_eol_crlf(xdfile_t *file, int i)
 
 	if (i < (isize) file->record.length - 1)
 		/* All lines before the last *must* end in LF */
-		return (size = file->recs[i]->size) > 1 &&
-			file->recs[i]->ptr[size - 2] == '\r';
+		return (size = file->record.ptr[i].size) > 1 &&
+			file->record.ptr[i].ptr[size - 2] == '\r';
 	if (!file->record.length)
 		/* Cannot determine eol style from empty file */
 		return -1;
-	if ((size = file->recs[i]->size) &&
-			file->recs[i]->ptr[size - 1] == '\n')
+	if ((size = file->record.ptr[i].size) &&
+			file->record.ptr[i].ptr[size - 1] == '\n')
 		/* Last line; ends in LF; Is it CR/LF? */
 		return size > 1 &&
-			file->recs[i]->ptr[size - 2] == '\r';
+			file->record.ptr[i].ptr[size - 2] == '\r';
 	if (!i)
 		/* The only line has no eol */
 		return -1;
 	/* Determine eol from second-to-last line */
-	return (size = file->recs[i - 1]->size) > 1 &&
-		file->recs[i - 1]->ptr[size - 2] == '\r';
+	return (size = file->record.ptr[i - 1].size) > 1 &&
+		file->record.ptr[i - 1].ptr[size - 2] == '\r';
 }
 
 static int is_cr_needed(xdfenv_t *xe1, xdfenv_t *xe2, xdmerge_t *m)
@@ -334,22 +334,22 @@ static int recmatch(xrecord_t *rec1, xrecord_t *rec2, unsigned long flags)
 static void xdl_refine_zdiff3_conflicts(xdfenv_t *xe1, xdfenv_t *xe2, xdmerge_t *m,
 		xpparam_t const *xpp)
 {
-	xrecord_t **rec1 = xe1->xdf2.recs, **rec2 = xe2->xdf2.recs;
+	xrecord_t *rec1 = xe1->xdf2.record.ptr, *rec2 = xe2->xdf2.record.ptr;
 	for (; m; m = m->next) {
 		/* let's handle just the conflicts */
 		if (m->mode)
 			continue;
 
 		while(m->chg1 && m->chg2 &&
-		      recmatch(rec1[m->i1], rec2[m->i2], xpp->flags)) {
+		      recmatch(&rec1[m->i1], &rec2[m->i2], xpp->flags)) {
 			m->chg1--;
 			m->chg2--;
 			m->i1++;
 			m->i2++;
 		}
 		while (m->chg1 && m->chg2 &&
-		       recmatch(rec1[m->i1 + m->chg1 - 1],
-				rec2[m->i2 + m->chg2 - 1], xpp->flags)) {
+		       recmatch(&rec1[m->i1 + m->chg1 - 1],
+				&rec2[m->i2 + m->chg2 - 1], xpp->flags)) {
 			m->chg1--;
 			m->chg2--;
 		}
@@ -381,12 +381,12 @@ static int xdl_refine_conflicts(xdfenv_t *xe1, xdfenv_t *xe2, xdmerge_t *m,
 		 * This probably does not work outside git, since
 		 * we have a very simple mmfile structure.
 		 */
-		t1.ptr = (char *)xe1->xdf2.recs[m->i1]->ptr;
-		t1.size = xe1->xdf2.recs[m->i1 + m->chg1 - 1]->ptr
-			+ xe1->xdf2.recs[m->i1 + m->chg1 - 1]->size - (u8 const*) t1.ptr;
-		t2.ptr = (char *)xe2->xdf2.recs[m->i2]->ptr;
-		t2.size = xe2->xdf2.recs[m->i2 + m->chg2 - 1]->ptr
-			+ xe2->xdf2.recs[m->i2 + m->chg2 - 1]->size - (u8 const*) t2.ptr;
+		t1.ptr = (char *)xe1->xdf2.record.ptr[m->i1].ptr;
+		t1.size = xe1->xdf2.record.ptr[m->i1 + m->chg1 - 1].ptr
+			+ xe1->xdf2.record.ptr[m->i1 + m->chg1 - 1].size - (u8 const*) t1.ptr;
+		t2.ptr = (char *)xe2->xdf2.record.ptr[m->i2].ptr;
+		t2.size = xe2->xdf2.record.ptr[m->i2 + m->chg2 - 1].ptr
+			+ xe2->xdf2.record.ptr[m->i2 + m->chg2 - 1].size - (u8 const*) t2.ptr;
 		if (xdl_do_diff(&t1, &t2, xpp, &xe) < 0)
 			return -1;
 		if (xdl_change_compact(&xe.xdf1, &xe.xdf2, xpp->flags) < 0 ||
@@ -440,8 +440,8 @@ static int line_contains_alnum(const char *ptr, long size)
 static int lines_contain_alnum(xdfenv_t *xe, int i, int chg)
 {
 	for (; chg; chg--, i++)
-		if (line_contains_alnum((char const*) xe->xdf2.recs[i]->ptr,
-				xe->xdf2.recs[i]->size))
+		if (line_contains_alnum((char const*) xe->xdf2.record.ptr[i].ptr,
+				xe->xdf2.record.ptr[i].size))
 			return 1;
 	return 0;
 }
diff --git a/xdiff/xpatience.c b/xdiff/xpatience.c
index e1ce9a399f..31b819ec58 100644
--- a/xdiff/xpatience.c
+++ b/xdiff/xpatience.c
@@ -88,9 +88,9 @@ static int is_anchor(xpparam_t const *xpp, const char *line)
 static void insert_record(xpparam_t const *xpp, int line, struct hashmap *map,
 			  int pass)
 {
-	xrecord_t **records = pass == 1 ?
-		map->env->xdf1.recs : map->env->xdf2.recs;
-	xrecord_t *record = records[line - 1];
+	xrecord_t *records = pass == 1 ?
+		map->env->xdf1.record.ptr : map->env->xdf2.record.ptr;
+	xrecord_t *record = &records[line - 1];
 	/*
 	 * After xdl_prepare_env() (or more precisely, due to
 	 * xdl_classify_record()), the "ha" member of the records (AKA lines)
@@ -121,7 +121,7 @@ static void insert_record(xpparam_t const *xpp, int line, struct hashmap *map,
 		return;
 	map->entries[index].line1 = line;
 	map->entries[index].hash = record->ha;
-	map->entries[index].anchor = is_anchor(xpp, (const char*) map->env->xdf1.recs[line - 1]->ptr);
+	map->entries[index].anchor = is_anchor(xpp, (const char*) map->env->xdf1.record.ptr[line - 1].ptr);
 	if (!map->first)
 		map->first = map->entries + index;
 	if (map->last) {
@@ -246,9 +246,9 @@ static int find_longest_common_sequence(struct hashmap *map, struct entry **res)
 
 static int match(struct hashmap *map, int line1, int line2)
 {
-	xrecord_t *record1 = map->env->xdf1.recs[line1 - 1];
-	xrecord_t *record2 = map->env->xdf2.recs[line2 - 1];
-	return record1->ha == record2->ha;
+	u64 mph1 = map->env->xdf1.record.ptr[line1 - 1].ha;
+	u64 mph2 = map->env->xdf2.record.ptr[line2 - 1].ha;
+	return mph1 == mph2;
 }
 
 static int patience_diff(xpparam_t const *xpp, xdfenv_t *env,
diff --git a/xdiff/xprepare.c b/xdiff/xprepare.c
index 9b46523afe..93370f1c6d 100644
--- a/xdiff/xprepare.c
+++ b/xdiff/xprepare.c
@@ -134,7 +134,6 @@ static void xdl_free_ctx(xdfile_t *xdf) {
 	xdl_free(xdf->rindex);
 	xdl_free(xdf->rchg - 1);
 	xdl_free(xdf->ha);
-	xdl_free(xdf->recs);
 }
 
 
@@ -147,7 +146,6 @@ static int xdl_prepare_ctx(unsigned int pass, mmfile_t *mf, xpparam_t const *xpp
 	xdf->ha = NULL;
 	xdf->rindex = NULL;
 	xdf->rchg = NULL;
-	xdf->recs = NULL;
 	IVEC_INIT(xdf->record);
 
 	if ((cur = blk = xdl_mmfile_first(mf, &bsize))) {
@@ -163,12 +161,9 @@ static int xdl_prepare_ctx(unsigned int pass, mmfile_t *mf, xpparam_t const *xpp
 	}
 	ivec_shrink_to_fit(&xdf->record);
 
-	if (!XDL_ALLOC_ARRAY(xdf->recs, xdf->record.length))
-		goto abort;
 	for (usize i = 0; i < xdf->record.length; i++) {
 		if (xdl_classify_record(pass, cf, &xdf->record.ptr[i]) < 0)
 			goto abort;
-		xdf->recs[i] = &xdf->record.ptr[i];
 	}
 
 	if (!XDL_CALLOC_ARRAY(xdf->rchg, xdf->record.length + 2))
@@ -267,7 +262,7 @@ static int xdl_clean_mmatch(char const *dis, long i, long s, long e) {
  */
 static int xdl_cleanup_records(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xdf2) {
 	long i, nm, nreff, mlim;
-	xrecord_t **recs;
+	xrecord_t *recs;
 	xdlclass_t *rcrec;
 	char *dis, *dis1, *dis2;
 	int need_min = !!(cf->flags & XDF_NEED_MINIMAL);
@@ -279,38 +274,38 @@ static int xdl_cleanup_records(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xd
 
 	if ((mlim = xdl_bogosqrt(xdf1->record.length)) > XDL_MAX_EQLIMIT)
 		mlim = XDL_MAX_EQLIMIT;
-	for (i = xdf1->dstart, recs = &xdf1->recs[xdf1->dstart]; i <= xdf1->dend; i++, recs++) {
-		rcrec = cf->rcrecs[(*recs)->ha];
+	for (i = xdf1->dstart, recs = &xdf1->record.ptr[xdf1->dstart]; i <= xdf1->dend; i++, recs++) {
+		rcrec = cf->rcrecs[recs->ha];
 		nm = rcrec ? rcrec->len2 : 0;
 		dis1[i] = (nm == 0) ? 0: (nm >= mlim && !need_min) ? 2: 1;
 	}
 
 	if ((mlim = xdl_bogosqrt(xdf2->record.length)) > XDL_MAX_EQLIMIT)
 		mlim = XDL_MAX_EQLIMIT;
-	for (i = xdf2->dstart, recs = &xdf2->recs[xdf2->dstart]; i <= xdf2->dend; i++, recs++) {
-		rcrec = cf->rcrecs[(*recs)->ha];
+	for (i = xdf2->dstart, recs = &xdf2->record.ptr[xdf2->dstart]; i <= xdf2->dend; i++, recs++) {
+		rcrec = cf->rcrecs[recs->ha];
 		nm = rcrec ? rcrec->len1 : 0;
 		dis2[i] = (nm == 0) ? 0: (nm >= mlim && !need_min) ? 2: 1;
 	}
 
-	for (nreff = 0, i = xdf1->dstart, recs = &xdf1->recs[xdf1->dstart];
+	for (nreff = 0, i = xdf1->dstart, recs = &xdf1->record.ptr[xdf1->dstart];
 	     i <= xdf1->dend; i++, recs++) {
 		if (dis1[i] == 1 ||
 		    (dis1[i] == 2 && !xdl_clean_mmatch(dis1, i, xdf1->dstart, xdf1->dend))) {
 			xdf1->rindex[nreff] = i;
-			xdf1->ha[nreff] = (*recs)->ha;
+			xdf1->ha[nreff] = recs->ha;
 			nreff++;
 		} else
 			xdf1->rchg[i] = 1;
 	}
 	xdf1->nreff = nreff;
 
-	for (nreff = 0, i = xdf2->dstart, recs = &xdf2->recs[xdf2->dstart];
+	for (nreff = 0, i = xdf2->dstart, recs = &xdf2->record.ptr[xdf2->dstart];
 	     i <= xdf2->dend; i++, recs++) {
 		if (dis2[i] == 1 ||
 		    (dis2[i] == 2 && !xdl_clean_mmatch(dis2, i, xdf2->dstart, xdf2->dend))) {
 			xdf2->rindex[nreff] = i;
-			xdf2->ha[nreff] = (*recs)->ha;
+			xdf2->ha[nreff] = recs->ha;
 			nreff++;
 		} else
 			xdf2->rchg[i] = 1;
@@ -328,21 +323,21 @@ static int xdl_cleanup_records(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xd
  */
 static int xdl_trim_ends(xdfile_t *xdf1, xdfile_t *xdf2) {
 	long i, lim;
-	xrecord_t **recs1, **recs2;
+	xrecord_t *recs1, *recs2;
 
-	recs1 = xdf1->recs;
-	recs2 = xdf2->recs;
+	recs1 = xdf1->record.ptr;
+	recs2 = xdf2->record.ptr;
 	for (i = 0, lim = XDL_MIN(xdf1->record.length, xdf2->record.length); i < lim;
 	     i++, recs1++, recs2++)
-		if ((*recs1)->ha != (*recs2)->ha)
+		if (recs1->ha != recs2->ha)
 			break;
 
 	xdf1->dstart = xdf2->dstart = i;
 
-	recs1 = xdf1->recs + xdf1->record.length - 1;
-	recs2 = xdf2->recs + xdf2->record.length - 1;
+	recs1 = xdf1->record.ptr + xdf1->record.length - 1;
+	recs2 = xdf2->record.ptr + xdf2->record.length - 1;
 	for (lim -= i, i = 0; i < lim; i++, recs1--, recs2--)
-		if ((*recs1)->ha != (*recs2)->ha)
+		if (recs1->ha != recs2->ha)
 			break;
 
 	xdf1->dend = xdf1->record.length - i - 1;
diff --git a/xdiff/xtypes.h b/xdiff/xtypes.h
index c322e62fbf..849f218b32 100644
--- a/xdiff/xtypes.h
+++ b/xdiff/xtypes.h
@@ -49,7 +49,6 @@ DEFINE_IVEC_TYPE(xrecord_t, xrecord);
 typedef struct s_xdfile {
 	struct ivec_xrecord record;
 	long dstart, dend;
-	xrecord_t **recs;
 	char *rchg;
 	long *rindex;
 	long nreff;
diff --git a/xdiff/xutils.c b/xdiff/xutils.c
index 10e4f20b7c..eed88ee6cb 100644
--- a/xdiff/xutils.c
+++ b/xdiff/xutils.c
@@ -416,12 +416,12 @@ int xdl_fall_back_diff(xdfenv_t *diff_env, xpparam_t const *xpp,
 	mmfile_t subfile1, subfile2;
 	xdfenv_t env;
 
-	subfile1.ptr = (char *)diff_env->xdf1.recs[line1 - 1]->ptr;
-	subfile1.size = diff_env->xdf1.recs[line1 + count1 - 2]->ptr +
-		diff_env->xdf1.recs[line1 + count1 - 2]->size - (u8 const*) subfile1.ptr;
-	subfile2.ptr = (char *)diff_env->xdf2.recs[line2 - 1]->ptr;
-	subfile2.size = diff_env->xdf2.recs[line2 + count2 - 2]->ptr +
-		diff_env->xdf2.recs[line2 + count2 - 2]->size - (u8 const*) subfile2.ptr;
+	subfile1.ptr = (char *)diff_env->xdf1.record.ptr[line1 - 1].ptr;
+	subfile1.size = diff_env->xdf1.record.ptr[line1 + count1 - 2].ptr +
+		diff_env->xdf1.record.ptr[line1 + count1 - 2].size - (u8 const*) subfile1.ptr;
+	subfile2.ptr = (char *)diff_env->xdf2.record.ptr[line2 - 1].ptr;
+	subfile2.size = diff_env->xdf2.record.ptr[line2 + count2 - 2].ptr +
+		diff_env->xdf2.record.ptr[line2 + count2 - 2].size - (u8 const*) subfile2.ptr;
 	if (xdl_do_diff(&subfile1, &subfile2, xpp, &env) < 0)
 		return -1;
 
-- 
gitgitgadget


^ permalink raw reply related	[flat|nested] 100+ messages in thread

* [PATCH 14/15] xdiff: make xdfile_t more rust friendly
  2025-08-29 19:42 [PATCH 00/15] Introduce rust: In xdiff Ezekiel Newren via GitGitGadget
                   ` (12 preceding siblings ...)
  2025-08-29 19:42 ` [PATCH 13/15] xdiff: delete recs " Ezekiel Newren via GitGitGadget
@ 2025-08-29 19:42 ` Ezekiel Newren via GitGitGadget
  2025-08-29 19:42 ` [PATCH 15/15] xdiff: implement xdl_trim_ends() in Rust Ezekiel Newren via GitGitGadget
  2025-09-17  1:16 ` [PATCH v2 00/18] Introduce rust: In xdiff Ezekiel Newren via GitGitGadget
  15 siblings, 0 replies; 100+ messages in thread
From: Ezekiel Newren via GitGitGadget @ 2025-08-29 19:42 UTC (permalink / raw)
  To: git; +Cc: Ezekiel Newren, Ezekiel Newren

From: Ezekiel Newren <ezekielnewren@gmail.com>

Convert the remaining ambiguous fields in xdfile_t from C types to Rust
types for interoperability.

Signed-off-by: Ezekiel Newren <ezekielnewren@gmail.com>
---
 xdiff/xdiffi.c | 16 ++++++++--------
 xdiff/xdiffi.h |  8 ++++----
 xdiff/xtypes.h | 10 +++++-----
 3 files changed, 17 insertions(+), 17 deletions(-)

diff --git a/xdiff/xdiffi.c b/xdiff/xdiffi.c
index ebdb724322..0509b48759 100644
--- a/xdiff/xdiffi.c
+++ b/xdiff/xdiffi.c
@@ -42,8 +42,8 @@ typedef struct s_xdpsplit {
  * using this algorithm, so a little bit of heuristic is needed to cut the
  * search and to return a suboptimal point.
  */
-static long xdl_split(unsigned long const *ha1, long off1, long lim1,
-		      unsigned long const *ha2, long off2, long lim2,
+static long xdl_split(u64 const *ha1, long off1, long lim1,
+		      u64 const *ha2, long off2, long lim2,
 		      long *kvdf, long *kvdb, int need_min, xdpsplit_t *spl,
 		      xdalgoenv_t *xenv) {
 	long dmin = off1 - lim2, dmax = lim1 - off2;
@@ -260,7 +260,7 @@ static long xdl_split(unsigned long const *ha1, long off1, long lim1,
 int xdl_recs_cmp(diffdata_t *dd1, long off1, long lim1,
 		 diffdata_t *dd2, long off2, long lim2,
 		 long *kvdf, long *kvdb, int need_min, xdalgoenv_t *xenv) {
-	unsigned long const *ha1 = dd1->ha, *ha2 = dd2->ha;
+	u64 const *ha1 = dd1->ha, *ha2 = dd2->ha;
 
 	/*
 	 * Shrink the box by walking through each diagonal snake (SW and NE).
@@ -273,14 +273,14 @@ int xdl_recs_cmp(diffdata_t *dd1, long off1, long lim1,
 	 * be obviously changed.
 	 */
 	if (off1 == lim1) {
-		char *rchg2 = dd2->rchg;
-		long *rindex2 = dd2->rindex;
+		u8 *rchg2 = dd2->rchg;
+		usize *rindex2 = dd2->rindex;
 
 		for (; off2 < lim2; off2++)
 			rchg2[rindex2[off2]] = 1;
 	} else if (off2 == lim2) {
-		char *rchg1 = dd1->rchg;
-		long *rindex1 = dd1->rindex;
+		u8 *rchg1 = dd1->rchg;
+		usize *rindex1 = dd1->rindex;
 
 		for (; off1 < lim1; off1++)
 			rchg1[rindex1[off1]] = 1;
@@ -944,7 +944,7 @@ int xdl_change_compact(xdfile_t *xdf, xdfile_t *xdfo, long flags) {
 
 int xdl_build_script(xdfenv_t *xe, xdchange_t **xscr) {
 	xdchange_t *cscr = NULL, *xch;
-	char *rchg1 = xe->xdf1.rchg, *rchg2 = xe->xdf2.rchg;
+	u8 *rchg1 = xe->xdf1.rchg, *rchg2 = xe->xdf2.rchg;
 	long i1, i2, l1, l2;
 
 	/*
diff --git a/xdiff/xdiffi.h b/xdiff/xdiffi.h
index 126c9d8ff4..c766ee115c 100644
--- a/xdiff/xdiffi.h
+++ b/xdiff/xdiffi.h
@@ -25,10 +25,10 @@
 
 
 typedef struct s_diffdata {
-	long nrec;
-	unsigned long const *ha;
-	long *rindex;
-	char *rchg;
+	usize nrec;
+	u64 const *ha;
+	usize *rindex;
+	u8 *rchg;
 } diffdata_t;
 
 typedef struct s_xdalgoenv {
diff --git a/xdiff/xtypes.h b/xdiff/xtypes.h
index 849f218b32..66b3dfae8b 100644
--- a/xdiff/xtypes.h
+++ b/xdiff/xtypes.h
@@ -48,11 +48,11 @@ DEFINE_IVEC_TYPE(xrecord_t, xrecord);
 
 typedef struct s_xdfile {
 	struct ivec_xrecord record;
-	long dstart, dend;
-	char *rchg;
-	long *rindex;
-	long nreff;
-	unsigned long *ha;
+	isize dstart, dend;
+	u8 *rchg;
+	usize *rindex;
+	usize nreff;
+	u64 *ha;
 } xdfile_t;
 
 typedef struct s_xdfenv {
-- 
gitgitgadget


^ permalink raw reply related	[flat|nested] 100+ messages in thread

* [PATCH 15/15] xdiff: implement xdl_trim_ends() in Rust
  2025-08-29 19:42 [PATCH 00/15] Introduce rust: In xdiff Ezekiel Newren via GitGitGadget
                   ` (13 preceding siblings ...)
  2025-08-29 19:42 ` [PATCH 14/15] xdiff: make xdfile_t more rust friendly Ezekiel Newren via GitGitGadget
@ 2025-08-29 19:42 ` Ezekiel Newren via GitGitGadget
  2025-09-17  1:16 ` [PATCH v2 00/18] Introduce rust: In xdiff Ezekiel Newren via GitGitGadget
  15 siblings, 0 replies; 100+ messages in thread
From: Ezekiel Newren via GitGitGadget @ 2025-08-29 19:42 UTC (permalink / raw)
  To: git; +Cc: Ezekiel Newren, Ezekiel Newren

From: Ezekiel Newren <ezekielnewren@gmail.com>

Replace the C implementation of xdl_trim_ends() with a Rust
implementation.

Signed-off-by: Ezekiel Newren <ezekielnewren@gmail.com>
---
 rust/xdiff/src/lib.rs      | 15 +++++++++++++++
 rust/xdiff/src/xprepare.rs | 27 +++++++++++++++++++++++++++
 rust/xdiff/src/xtypes.rs   | 19 +++++++++++++++++++
 xdiff/xprepare.c           | 28 +---------------------------
 4 files changed, 62 insertions(+), 27 deletions(-)
 create mode 100644 rust/xdiff/src/xprepare.rs
 create mode 100644 rust/xdiff/src/xtypes.rs

diff --git a/rust/xdiff/src/lib.rs b/rust/xdiff/src/lib.rs
index e69de29bb2..4cc05a7e6b 100644
--- a/rust/xdiff/src/lib.rs
+++ b/rust/xdiff/src/lib.rs
@@ -0,0 +1,15 @@
+pub mod xprepare;
+pub mod xtypes;
+
+use crate::xprepare::trim_ends;
+use crate::xtypes::xdfile;
+
+#[no_mangle]
+unsafe extern "C" fn xdl_trim_ends(xdf1: *mut xdfile, xdf2: *mut xdfile) -> i32 {
+    let xdf1 = xdf1.as_mut().expect("null pointer");
+    let xdf2 = xdf2.as_mut().expect("null pointer");
+
+    trim_ends(xdf1, xdf2);
+
+    0
+}
diff --git a/rust/xdiff/src/xprepare.rs b/rust/xdiff/src/xprepare.rs
new file mode 100644
index 0000000000..f64f60c099
--- /dev/null
+++ b/rust/xdiff/src/xprepare.rs
@@ -0,0 +1,27 @@
+use crate::xtypes::xdfile;
+
+///
+/// Early trim initial and terminal matching records.
+///
+pub(crate) fn trim_ends(xdf1: &mut xdfile, xdf2: &mut xdfile) {
+    let mut lim = std::cmp::min(xdf1.record.len(), xdf2.record.len());
+
+    for i in 0..lim {
+        if xdf1.record[i].ha != xdf2.record[i].ha {
+            xdf1.dstart = i as isize;
+            xdf2.dstart = i as isize;
+            lim -= i;
+            break;
+        }
+    }
+
+    for i in 0..lim {
+        let f1i = xdf1.record.len() - 1 - i;
+        let f2i = xdf2.record.len() - 1 - i;
+        if xdf1.record[f1i].ha != xdf2.record[f2i].ha {
+            xdf1.dend = f1i as isize;
+            xdf2.dend = f2i as isize;
+            break;
+        }
+    }
+}
diff --git a/rust/xdiff/src/xtypes.rs b/rust/xdiff/src/xtypes.rs
new file mode 100644
index 0000000000..3d1ce9742f
--- /dev/null
+++ b/rust/xdiff/src/xtypes.rs
@@ -0,0 +1,19 @@
+use interop::ivec::IVec;
+
+#[repr(C)]
+pub(crate) struct xrecord {
+    pub(crate) ptr: *const u8,
+    pub(crate) size: usize,
+    pub(crate) ha: u64,
+}
+
+#[repr(C)]
+pub(crate) struct xdfile {
+    pub(crate) record: IVec<xrecord>,
+    pub(crate) dstart: isize,
+    pub(crate) dend: isize,
+    pub(crate) rchg: *mut u8,
+    pub(crate) rindex: *mut usize,
+    pub(crate) nreff: usize,
+    pub(crate) ha: *mut u64,
+}
diff --git a/xdiff/xprepare.c b/xdiff/xprepare.c
index 93370f1c6d..2c7480875f 100644
--- a/xdiff/xprepare.c
+++ b/xdiff/xprepare.c
@@ -318,33 +318,7 @@ static int xdl_cleanup_records(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xd
 }
 
 
-/*
- * Early trim initial and terminal matching records.
- */
-static int xdl_trim_ends(xdfile_t *xdf1, xdfile_t *xdf2) {
-	long i, lim;
-	xrecord_t *recs1, *recs2;
-
-	recs1 = xdf1->record.ptr;
-	recs2 = xdf2->record.ptr;
-	for (i = 0, lim = XDL_MIN(xdf1->record.length, xdf2->record.length); i < lim;
-	     i++, recs1++, recs2++)
-		if (recs1->ha != recs2->ha)
-			break;
-
-	xdf1->dstart = xdf2->dstart = i;
-
-	recs1 = xdf1->record.ptr + xdf1->record.length - 1;
-	recs2 = xdf2->record.ptr + xdf2->record.length - 1;
-	for (lim -= i, i = 0; i < lim; i++, recs1--, recs2--)
-		if (recs1->ha != recs2->ha)
-			break;
-
-	xdf1->dend = xdf1->record.length - i - 1;
-	xdf2->dend = xdf2->record.length - i - 1;
-
-	return 0;
-}
+extern i32 xdl_trim_ends(xdfile_t *xdf1, xdfile_t *xdf2);
 
 
 static int xdl_optimize_ctxs(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xdf2) {
-- 
gitgitgadget

^ permalink raw reply related	[flat|nested] 100+ messages in thread

* Re: [PATCH 01/15] doc: add a policy for using Rust
  2025-08-29 19:42 ` [PATCH 01/15] doc: add a policy for using Rust brian m. carlson via GitGitGadget
@ 2025-08-29 20:00   ` brian m. carlson
  2025-08-29 20:11     ` Ezekiel Newren
  0 siblings, 1 reply; 100+ messages in thread
From: brian m. carlson @ 2025-08-29 20:00 UTC (permalink / raw)
  To: brian m. carlson via GitGitGadget; +Cc: git, Ezekiel Newren

[-- Attachment #1: Type: text/plain, Size: 2592 bytes --]

On 2025-08-29 at 19:42:05, brian m. carlson via GitGitGadget wrote:
> +Crates like libc or rustix define types like c_long, but in ways that are not
> +safe across platforms.
> +From https://docs.rs/rustix/latest/rustix/ffi/type.c_long.html:
> +
> +    This type will always be i32 or i64.  Most notably, many Linux-based
> +    systems assume an i64, but Windows assumes i32.  The C standard technically
> +    only requires that this type be a signed integer that is at least 32 bits
> +    and at least the size of an int, although in practice, no system would
> +    have a long that is neither an i32 nor i64.
> +
> +Also, note that other locations, such as
> +https://docs.rs/libc/latest/libc/type.c_long.html, just hardcode c_long as i64
> +even though C may mean i32 on some platforms.
> +
> +As such, using the c_long type would give us portability issues, and
> +perpetuate some of the bugs git has faced across platforms.  Avoid using C's
> +types (long, unsigned, char, etc.), and switch to unambiguous types (e.g. i32
> +or i64) before trying to make C and Rust interoperate.

This makes sense.  I agree fixed-size types are better and less brittle.

> +Crates like libc and rustix may have also traditionally aided interoperability
> +with older versions of Rust (e.g.  when worrying about stat[64] system calls),
> +but the Rust standard library in newer versions of Rust handle these concerns
> +in a platform agnostic way.  There may arise cases where we need to consider
> +these crates, but for now we omit them.

I'm fine with omitting them for now.  However, we may very well need
them in the future.

> +Tools like bindgen and cbindgen create C-styled unsafe Rust code rather than
> +idiomatic Rust; where possible, we prefer to switch to idiomatic Rust.  Any
> +standard C library functions that are needed can be manually wrapped on the
> +Rust side.

I agree that we want to use idiomatic Rust whenever possible.  However,
I don't want to define structures and function definitions in both
languages and rely on people keeping them in sync, since that's a great
way to create brittle, broken code.  Very notably, I have seen these
kinds of misalignments break only on big-endian architectures, which
most of us do not use, so we can really end up causing problems that are
very subtle this way.

I would prefer we wrote these functions with cbindgen to avoid this.  If
we define structures only in Rust and never ever use C structures, then
we can avoid bindgen.
-- 
brian m. carlson (they/them)
Toronto, Ontario, CA

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 262 bytes --]

^ permalink raw reply	[flat|nested] 100+ messages in thread

* Re: [PATCH 01/15] doc: add a policy for using Rust
  2025-08-29 20:00   ` brian m. carlson
@ 2025-08-29 20:11     ` Ezekiel Newren
  2025-09-02 16:39       ` brian m. carlson
  0 siblings, 1 reply; 100+ messages in thread
From: Ezekiel Newren @ 2025-08-29 20:11 UTC (permalink / raw)
  To: brian m. carlson, brian m. carlson via GitGitGadget, git,
	Ezekiel Newren

On Fri, Aug 29, 2025 at 2:00 PM brian m. carlson
<sandals@crustytoothpaste.net> wrote:
> > +Tools like bindgen and cbindgen create C-styled unsafe Rust code rather than
> > +idiomatic Rust; where possible, we prefer to switch to idiomatic Rust.  Any
> > +standard C library functions that are needed can be manually wrapped on the
> > +Rust side.
>
> I agree that we want to use idiomatic Rust whenever possible.  However,
> I don't want to define structures and function definitions in both
> languages and rely on people keeping them in sync, since that's a great
> way to create brittle, broken code.  Very notably, I have seen these
> kinds of misalignments break only on big-endian architectures, which
> most of us do not use, so we can really end up causing problems that are
> very subtle this way.
>
> I would prefer we wrote these functions with cbindgen to avoid this.  If
> we define structures only in Rust and never ever use C structures, then
> we can avoid bindgen.

Could you create a patch with your preferred wording about bindgen and
cbindgen for me? I'd prefer to introduce bindgen/cbindgen in a
different patch series because this one is already doing a lot just to
make Rust exist in Git.

^ permalink raw reply	[flat|nested] 100+ messages in thread

* Re: [PATCH 01/15] doc: add a policy for using Rust
  2025-08-29 20:11     ` Ezekiel Newren
@ 2025-09-02 16:39       ` brian m. carlson
  2025-09-02 18:39         ` Ezekiel Newren
  0 siblings, 1 reply; 100+ messages in thread
From: brian m. carlson @ 2025-09-02 16:39 UTC (permalink / raw)
  To: Ezekiel Newren; +Cc: brian m. carlson via GitGitGadget, git

[-- Attachment #1: Type: text/plain, Size: 848 bytes --]

On 2025-08-29 at 20:11:46, Ezekiel Newren wrote:
> Could you create a patch with your preferred wording about bindgen and
> cbindgen for me? I'd prefer to introduce bindgen/cbindgen in a
> different patch series because this one is already doing a lot just to
> make Rust exist in Git.

I think it's fine to introduce it in a different series.  I'll plan to
do that myself if it doesn't get done sooner.

My change to the text is the following:

Tools like bindgen and cbindgen create C-styled unsafe Rust code rather than
idiomatic Rust; where possible, we prefer to switch to idiomatic Rust.
However, we may use bindgen and cbindgen to share existing Git types as an
interim step.

You can find the patch at https://github.com/bk2204/git.git in the
`rust-policy` branch.
-- 
brian m. carlson (they/them)
Toronto, Ontario, CA

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 262 bytes --]

^ permalink raw reply	[flat|nested] 100+ messages in thread

* Re: [PATCH 01/15] doc: add a policy for using Rust
  2025-09-02 16:39       ` brian m. carlson
@ 2025-09-02 18:39         ` Ezekiel Newren
  2025-09-04 22:55           ` Ezekiel Newren
  0 siblings, 1 reply; 100+ messages in thread
From: Ezekiel Newren @ 2025-09-02 18:39 UTC (permalink / raw)
  To: brian m. carlson, Ezekiel Newren,
	brian m. carlson via GitGitGadget, git

On Tue, Sep 2, 2025 at 10:39 AM brian m. carlson
<sandals@crustytoothpaste.net> wrote:
> I think it's fine to introduce it in a different series.  I'll plan to
> do that myself if it doesn't get done sooner.

Actually now that I'm knee deep in adding cbindgen to Rust it'll make
less sense to add it later. I'm currently working on refactoring my
entire patch series to include cbindgen from the beginning. I haven't
looked into cbindgen until now because I wanted to understand at a
deep level how C <-> Rust ffi worked rather than using an automagical
tool like cbindgen. I now think cbindgen should be part of the
introduction of Rust.

^ permalink raw reply	[flat|nested] 100+ messages in thread

* Re: [PATCH 01/15] doc: add a policy for using Rust
  2025-09-02 18:39         ` Ezekiel Newren
@ 2025-09-04 22:55           ` Ezekiel Newren
  0 siblings, 0 replies; 100+ messages in thread
From: Ezekiel Newren @ 2025-09-04 22:55 UTC (permalink / raw)
  To: brian m. carlson, Ezekiel Newren,
	brian m. carlson via GitGitGadget, git

On Tue, Sep 2, 2025 at 12:39 PM Ezekiel Newren <ezekielnewren@gmail.com> wrote:
> Actually now that I'm knee deep in adding cbindgen to Rust it'll make
> less sense to add it later. I'm currently working on refactoring my
> entire patch series to include cbindgen from the beginning. I haven't
> looked into cbindgen until now because I wanted to understand at a
> deep level how C <-> Rust ffi worked rather than using an automagical
> tool like cbindgen. I now think cbindgen should be part of the
> introduction of Rust.

I've been able to get cbindgen to work with 1.63.0, but I'm not ready
to release my next patch series yet.

^ permalink raw reply	[flat|nested] 100+ messages in thread

* [PATCH v2 00/18] Introduce rust: In xdiff
  2025-08-29 19:42 [PATCH 00/15] Introduce rust: In xdiff Ezekiel Newren via GitGitGadget
                   ` (14 preceding siblings ...)
  2025-08-29 19:42 ` [PATCH 15/15] xdiff: implement xdl_trim_ends() in Rust Ezekiel Newren via GitGitGadget
@ 2025-09-17  1:16 ` Ezekiel Newren via GitGitGadget
  2025-09-17  1:16   ` [PATCH v2 01/18] cleanup: rename variables that collide with Rust primitive type names Ezekiel Newren via GitGitGadget
                     ` (20 more replies)
  15 siblings, 21 replies; 100+ messages in thread
From: Ezekiel Newren via GitGitGadget @ 2025-09-17  1:16 UTC (permalink / raw)
  To: git; +Cc: Ezekiel Newren

This is a continuation of
https://lore.kernel.org/git/pull.1980.v3.git.git.1755921356.gitgitgadget@gmail.com/,
but I am removing the RFC label.

Suggestions on changes that I could make to this series is appreciated.

Changes in v2: High level overview:

 * patch 1: cleanup: rename variables that collide with Rust primitive type
   names
 * patches 2-4: Makefile now produces libgit.a correctly
 * patches 5,6: Documentation from Brian and Patrick
 * patches 7,8: Introduce Rust
 * patches 9-11: github workflows
 * patches 12-14: introduce crates build-helper, and cbindgen
 * patches 15-18: varint test balloon

I would like feed back in two categories: big changes, and little changes.
Is my patch series even going in the right direction, and what are the
little details that I've missed? I know Brian asked for cbindgen and it made
the series several commits longer, but I think it's a great idea to have. I
am not happy with every choice that I made, but I currently don't see a
better way or at least an easier alternative to my approach in bringing Rust
to Git.

Build results for these changes:
https://github.com/git/git/actions/runs/17783386212?pr=2043. Some of these
are failing.

Changes in v1: Changes since the last RFC patch series (range-diff added
below):

 * Addressed feedback:
   * Use self instead of this
   * Update ivec to use Git code style
   * Remove environment variable CI_IS_DOCKER from github workflows
   * Remove PHONY target interop-objs
   * Removed left over asserts in Rust and fix near by code
 * Other changes:
   * Avoid name mangling of ivec if c++ compiler used
   * Fixed a typo in patch 11 commit message

High level overview:

 * patch 1: add a policy for using Rust (brian's patch, with a small tweak)
 * patch 2: introduce Rust to the codebase
 * patches 3-5: adapt CI (github workflows) to build Git with Rust
 * patch 6: introduce the ivec type
 * patches 7-14: xdiff code cleanup in preparation for translating to Rust
 * patch 15: translate a C function into Rust and call it from C

Ezekiel Newren (12):
  cleanup: rename variables that collide with Rust primitive type names
  make: add -fPIE flag
  make: merge xdiff lib into libgit.a
  make: merge reftable lib into libgit.a
  build: introduce rust
  github workflows: install rust
  github workflows: upload Cargo.lock
  build: new crate, build-helper
  build-helper: link against libgit.a and any other required C libraries
  build-helper: cbindgen, let crates generate a header file
  build: new crate, misc
  misc: use BuildHelper

Johannes Schindelin (1):
  win+Meson: do allow linking with the Rust-built xdiff

Patrick Steinhardt (4):
  BreakingChanges: announce Rust becoming mandatory
  help: report on whether or not Rust is enabled
  varint: use explicit width for integers
  misc::varint: reimplement as test balloon for Rust

brian m. carlson (1):
  doc: add a policy for using Rust

 .github/workflows/main.yml                    |  82 +++++++++-
 .gitignore                                    |   4 +
 Documentation/BreakingChanges.adoc            |  35 +++++
 Documentation/Makefile                        |   1 +
 Documentation/technical/platform-support.adoc |   2 +
 Documentation/technical/rust-support.adoc     | 142 +++++++++++++++++
 Makefile                                      | 148 +++++++++++-------
 build_rust.sh                                 |  60 +++++++
 ci/install-dependencies.sh                    |  14 +-
 ci/install-rust-toolchain.sh                  |  30 ++++
 ci/install-rustup.sh                          |  25 +++
 ci/lib.sh                                     |   1 +
 ci/make-test-artifacts.sh                     |   9 ++
 ci/run-build-and-tests.sh                     |  13 ++
 config.mak.uname                              |   4 +
 dir.c                                         |  18 ++-
 help.c                                        |   6 +
 meson.build                                   |  79 ++++++++--
 meson_options.txt                             |   2 +
 odb.c                                         |   8 +-
 read-cache.c                                  |   6 +-
 reftable/record.c                             |   6 +-
 rust/Cargo.toml                               |   6 +
 rust/build-helper/Cargo.toml                  |  11 ++
 rust/build-helper/src/lib.rs                  | 126 +++++++++++++++
 rust/cbindgen-template.toml                   |  16 ++
 rust/misc/Cargo.toml                          |  17 ++
 rust/misc/build.rs                            |   6 +
 rust/misc/src/lib.rs                          |   1 +
 rust/misc/src/varint.rs                       |  92 +++++++++++
 t/helper/test-parse-options.c                 |  12 +-
 varint.c                                      |   6 +-
 varint.h                                      |   4 +-
 xdiff/xtypes.h                                |   2 +-
 xdiff/xutils.c                                |   8 +-
 xdiff/xutils.h                                |   2 +-
 36 files changed, 899 insertions(+), 105 deletions(-)
 create mode 100644 Documentation/technical/rust-support.adoc
 create mode 100755 build_rust.sh
 create mode 100755 ci/install-rust-toolchain.sh
 create mode 100755 ci/install-rustup.sh
 create mode 100644 rust/Cargo.toml
 create mode 100644 rust/build-helper/Cargo.toml
 create mode 100644 rust/build-helper/src/lib.rs
 create mode 100644 rust/cbindgen-template.toml
 create mode 100644 rust/misc/Cargo.toml
 create mode 100644 rust/misc/build.rs
 create mode 100644 rust/misc/src/lib.rs
 create mode 100644 rust/misc/src/varint.rs


base-commit: c44beea485f0f2feaf460e2ac87fdd5608d63cf0
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-git-2043%2Fezekielnewren%2Fintroduce_rust-v2
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-git-2043/ezekielnewren/introduce_rust-v2
Pull-Request: https://github.com/git/git/pull/2043

Range-diff vs v1:

  -:  ---------- >  1:  5f77f1bd5d cleanup: rename variables that collide with Rust primitive type names
  -:  ---------- >  2:  7082e32c59 make: add -fPIE flag
  -:  ---------- >  3:  7fe85f0eac make: merge xdiff lib into libgit.a
 10:  0cd2168512 !  4:  3061cc46c1 xdiff: use one definition for freeing xdfile_t
     @@ Metadata
      Author: Ezekiel Newren <ezekielnewren@gmail.com>
      
       ## Commit message ##
     -    xdiff: use one definition for freeing xdfile_t
     -
     -    Simplify xdl_prepare_ctx() by using xdl_free_ctx() instead of using
     -    local variables with hand rolled memory management.
     +    make: merge reftable lib into libgit.a
      
          Signed-off-by: Ezekiel Newren <ezekielnewren@gmail.com>
      
     - ## xdiff/xprepare.c ##
     -@@ xdiff/xprepare.c: static int xdl_classify_record(unsigned int pass, xdlclassifier_t *cf, xrecord_t
     - }
     - 
     + ## Makefile ##
     +@@ Makefile: export PYTHON_PATH
     + TEST_SHELL_PATH = $(SHELL_PATH)
       
     -+static void xdl_free_ctx(xdfile_t *xdf) {
     -+	xdl_free(xdf->rindex);
     -+	xdl_free(xdf->rchg - 1);
     -+	xdl_free(xdf->ha);
     -+	xdl_free(xdf->recs);
     -+	xdl_cha_free(&xdf->rcha);
     -+}
     -+
     -+
     - static int xdl_prepare_ctx(unsigned int pass, mmfile_t *mf, long narec, xpparam_t const *xpp,
     - 			   xdlclassifier_t *cf, xdfile_t *xdf) {
     --	long nrec, bsize;
     -+	long bsize;
     - 	unsigned long hav;
     - 	char const *blk, *cur, *top, *prev;
     - 	xrecord_t *crec;
     --	xrecord_t **recs;
     --	unsigned long *ha;
     --	char *rchg;
     --	long *rindex;
     + LIB_FILE = libgit.a
     +-REFTABLE_LIB = reftable/libreftable.a
       
     --	ha = NULL;
     --	rindex = NULL;
     --	rchg = NULL;
     --	recs = NULL;
     -+	xdf->ha = NULL;
     -+	xdf->rindex = NULL;
     -+	xdf->rchg = NULL;
     -+	xdf->recs = NULL;
     -+	xdf->nrec = 0;
     + GENERATED_H += command-list.h
     + GENERATED_H += config-list.h
     +@@ Makefile: LIB_OBJS += rebase.o
     + LIB_OBJS += ref-filter.o
     + LIB_OBJS += reflog-walk.o
     + LIB_OBJS += reflog.o
     ++LIB_OBJS += reftable/basics.o
     ++LIB_OBJS += reftable/error.o
     ++LIB_OBJS += reftable/block.o
     ++LIB_OBJS += reftable/blocksource.o
     ++LIB_OBJS += reftable/iter.o
     ++LIB_OBJS += reftable/merged.o
     ++LIB_OBJS += reftable/pq.o
     ++LIB_OBJS += reftable/record.o
     ++LIB_OBJS += reftable/stack.o
     ++LIB_OBJS += reftable/system.o
     ++LIB_OBJS += reftable/table.o
     ++LIB_OBJS += reftable/tree.o
     ++LIB_OBJS += reftable/writer.o
     + LIB_OBJS += refs.o
     + LIB_OBJS += refs/debug.o
     + LIB_OBJS += refs/files-backend.o
     +@@ Makefile: CLAR_TEST_OBJS += $(UNIT_TEST_DIR)/unit-test.o
       
     - 	if (xdl_cha_init(&xdf->rcha, sizeof(xrecord_t), narec / 4 + 1) < 0)
     - 		goto abort;
     --	if (!XDL_ALLOC_ARRAY(recs, narec))
     -+	if (!XDL_ALLOC_ARRAY(xdf->recs, narec))
     - 		goto abort;
     + UNIT_TEST_OBJS += $(UNIT_TEST_DIR)/test-lib.o
       
     --	nrec = 0;
     - 	if ((cur = blk = xdl_mmfile_first(mf, &bsize))) {
     - 		for (top = blk + bsize; cur < top; ) {
     - 			prev = cur;
     - 			hav = xdl_hash_record(&cur, top, xpp->flags);
     --			if (XDL_ALLOC_GROW(recs, nrec + 1, narec))
     -+			if (XDL_ALLOC_GROW(xdf->recs, xdf->nrec + 1, narec))
     - 				goto abort;
     - 			if (!(crec = xdl_cha_alloc(&xdf->rcha)))
     - 				goto abort;
     - 			crec->ptr = (u8 const*) prev;
     - 			crec->size = (long) (cur - prev);
     - 			crec->ha = hav;
     --			recs[nrec++] = crec;
     -+			xdf->recs[xdf->nrec++] = crec;
     - 			if (xdl_classify_record(pass, cf, crec) < 0)
     - 				goto abort;
     - 		}
     - 	}
     +-# reftable lib may depend on what is in libgit.a
     +-GITLIBS = common-main.o $(LIB_FILE) $(REFTABLE_LIB) $(LIB_FILE)
     ++GITLIBS = common-main.o $(LIB_FILE)
     + EXTLIBS =
       
     --	if (!XDL_CALLOC_ARRAY(rchg, nrec + 2))
     -+	if (!XDL_CALLOC_ARRAY(xdf->rchg, xdf->nrec + 2))
     - 		goto abort;
     + GIT_USER_AGENT = git/$(GIT_VERSION)
     +@@ Makefile: reconfigure config.mak.autogen: config.status
     + .PHONY: reconfigure # This is a convenience target.
     + endif
       
     - 	if ((XDF_DIFF_ALG(xpp->flags) != XDF_PATIENCE_DIFF) &&
     - 	    (XDF_DIFF_ALG(xpp->flags) != XDF_HISTOGRAM_DIFF)) {
     --		if (!XDL_ALLOC_ARRAY(rindex, nrec + 1))
     -+		if (!XDL_ALLOC_ARRAY(xdf->rindex, xdf->nrec + 1))
     - 			goto abort;
     --		if (!XDL_ALLOC_ARRAY(ha, nrec + 1))
     -+		if (!XDL_ALLOC_ARRAY(xdf->ha, xdf->nrec + 1))
     - 			goto abort;
     - 	}
     +-REFTABLE_OBJS += reftable/basics.o
     +-REFTABLE_OBJS += reftable/error.o
     +-REFTABLE_OBJS += reftable/block.o
     +-REFTABLE_OBJS += reftable/blocksource.o
     +-REFTABLE_OBJS += reftable/iter.o
     +-REFTABLE_OBJS += reftable/merged.o
     +-REFTABLE_OBJS += reftable/pq.o
     +-REFTABLE_OBJS += reftable/record.o
     +-REFTABLE_OBJS += reftable/stack.o
     +-REFTABLE_OBJS += reftable/system.o
     +-REFTABLE_OBJS += reftable/table.o
     +-REFTABLE_OBJS += reftable/tree.o
     +-REFTABLE_OBJS += reftable/writer.o
     +-
     + TEST_OBJS := $(patsubst %$X,%.o,$(TEST_PROGRAMS)) $(patsubst %,t/helper/%,$(TEST_BUILTINS_OBJS))
       
     --	xdf->nrec = nrec;
     --	xdf->recs = recs;
     --	xdf->rchg = rchg + 1;
     --	xdf->rindex = rindex;
     -+	xdf->rchg += 1;
     - 	xdf->nreff = 0;
     --	xdf->ha = ha;
     - 	xdf->dstart = 0;
     --	xdf->dend = nrec - 1;
     -+	xdf->dend = xdf->nrec - 1;
     + .PHONY: test-objs
     +@@ Makefile: OBJECTS += $(SCALAR_OBJS)
     + OBJECTS += $(PROGRAM_OBJS)
     + OBJECTS += $(TEST_OBJS)
     + OBJECTS += $(FUZZ_OBJS)
     +-OBJECTS += $(REFTABLE_OBJS) $(REFTABLE_TEST_OBJS)
     ++OBJECTS += $(REFTABLE_TEST_OBJS)
     + OBJECTS += $(UNIT_TEST_OBJS)
     + OBJECTS += $(CLAR_TEST_OBJS)
     + OBJECTS += $(patsubst %,$(UNIT_TEST_DIR)/%.o,$(UNIT_TEST_PROGRAMS))
     +@@ Makefile: scalar$X: scalar.o GIT-LDFLAGS $(GITLIBS)
     + $(LIB_FILE): $(LIB_OBJS)
     + 	$(QUIET_AR)$(RM) $@ && $(AR) $(ARFLAGS) $@ $^
       
     - 	return 0;
     +-$(REFTABLE_LIB): $(REFTABLE_OBJS)
     +-	$(QUIET_AR)$(RM) $@ && $(AR) $(ARFLAGS) $@ $^
     +-
     + export DEFAULT_EDITOR DEFAULT_PAGER
       
     - abort:
     --	xdl_free(ha);
     --	xdl_free(rindex);
     --	xdl_free(rchg);
     --	xdl_free(recs);
     --	xdl_cha_free(&xdf->rcha);
     -+	xdl_free_ctx(xdf);
     - 	return -1;
     - }
     + Documentation/GIT-EXCLUDED-PROGRAMS: FORCE
     +@@ Makefile: clean: profile-clean coverage-clean cocciclean
     + 	$(RM) git.rc git.res
     + 	$(RM) $(OBJECTS)
     + 	$(RM) headless-git.o
     +-	$(RM) $(LIB_FILE) $(REFTABLE_LIB)
     ++	$(RM) $(LIB_FILE)
     + 	$(RM) $(ALL_PROGRAMS) $(SCRIPT_LIB) $(BUILT_INS) $(OTHER_PROGRAMS)
     + 	$(RM) $(TEST_PROGRAMS)
     + 	$(RM) $(FUZZ_PROGRAMS)
     +@@ Makefile: endif
       
     + LIBGIT_PUB_OBJS += contrib/libgit-sys/public_symbol_export.o
     + LIBGIT_PUB_OBJS += libgit.a
     +-LIBGIT_PUB_OBJS += reftable/libreftable.a
       
     --static void xdl_free_ctx(xdfile_t *xdf) {
     --	xdl_free(xdf->rindex);
     --	xdl_free(xdf->rchg - 1);
     --	xdl_free(xdf->ha);
     --	xdl_free(xdf->recs);
     --	xdl_cha_free(&xdf->rcha);
     --}
     --
     --
     - void xdl_free_env(xdfenv_t *xe) {
     + LIBGIT_PARTIAL_EXPORT = contrib/libgit-sys/partial_symbol_export.o
       
     - 	xdl_free_ctx(&xe->xdf2);
  1:  6d065f550f !  5:  0fc89c3ec3 doc: add a policy for using Rust
     @@ Documentation/technical/rust-support.adoc (new)
      +these crates, but for now we omit them.
      +
      +Tools like bindgen and cbindgen create C-styled unsafe Rust code rather than
     -+idiomatic Rust; where possible, we prefer to switch to idiomatic Rust.  Any
     -+standard C library functions that are needed can be manually wrapped on the
     -+Rust side.
     ++idiomatic Rust; where possible, we prefer to switch to idiomatic Rust.
     ++However, we may use bindgen and cbindgen to share existing Git types as an
     ++interim step.
      +
      +Rust upstream releases every six weeks and only supports the latest stable
      +release.  While it is nice that upstream is active, we would like our software
  -:  ---------- >  6:  8e030170dd BreakingChanges: announce Rust becoming mandatory
  2:  cc9cf87775 !  7:  6032a8740c xdiff: introduce rust
     @@ Metadata
      Author: Ezekiel Newren <ezekielnewren@gmail.com>
      
       ## Commit message ##
     -    xdiff: introduce rust
     +    build: introduce rust
      
          Upcoming patches will simplify xdiff, while also porting parts of it to
          Rust. In preparation, add some stubs and setup the Rust build. For now,
          it is easier to let cargo build rust and have make or meson merely link
          against the static library that cargo builds. In line with ongoing
          libification efforts, use multiple crates to allow more modularity on
     -    the Rust side. xdiff is the crate that this series will focus on, but
     -    we also introduce the interop crate for future patch series.
     +    the Rust side. The xdiff and ivec crates are the focal point for this
     +    series.
      
     -    In order to facilitate interoperability between C and Rust, introduce C
     -    definitions for Rust primitive types in git-compat-util.h.
     +    cbindgen will be used to generate header files for each Rust crate for
     +    C interoperability.
      
          Signed-off-by: Ezekiel Newren <ezekielnewren@gmail.com>
      
     @@ .gitignore: Release/
       /contrib/buildsystems/out
       /contrib/libgit-rs/target
       /contrib/libgit-sys/target
     -+/.idea/
     ++.idea/
      +/rust/target/
      +/rust/Cargo.lock
      
       ## Makefile ##
     +@@ Makefile: include shared.mak
     + # Define LIBPCREDIR=/foo/bar if your PCRE header and library files are
     + # in /foo/bar/include and /foo/bar/lib directories.
     + #
     ++# == Optional Rust support ==
     ++#
     ++# Define WITH_RUST=false if you don't want to include features and subsystems
     ++# written in Rust into Git. For now, Rust is still an optional feature of the
     ++# build process. With Git 3.0 though, Rust will be mandatory.
     ++#
     ++# Building Rust code requires Cargo.
     ++#
     + # == SHA-1 and SHA-256 defines ==
     + #
     + # === SHA-1 backend ===
      @@ Makefile: TEST_SHELL_PATH = $(SHELL_PATH)
       
       LIB_FILE = libgit.a
     - XDIFF_LIB = xdiff/lib.a
     -+
     + 
      +EXTLIBS =
      +
     ++GIT_BUILD_DIR := $(CURDIR)
     ++export GIT_BUILD_DIR
     ++
     ++RUST_CRATES :=
     ++.PHONY: compile_rust rustclean
     ++
     ++WITH_RUST ?= true
     ++ifeq ($(WITH_RUST),true)
     ++
      +ifeq ($(DEBUG), 1)
     -+  RUST_BUILD_MODE = debug
     ++	RUST_BUILD_MODE := debug
      +else
     -+  RUST_BUILD_MODE = release
     ++	RUST_BUILD_MODE := release
      +endif
      +
     -+RUST_TARGET_DIR = rust/target/$(RUST_BUILD_MODE)
     -+RUST_FLAGS_FOR_C = -L$(RUST_TARGET_DIR)
     ++RUST_TARGET_DIR := $(GIT_BUILD_DIR)/$(RUST_BUILD_MODE)
      +
     -+.PHONY: compile_rust
     -+compile_rust:
     -+	./build_rust.sh . $(RUST_BUILD_MODE) xdiff
     ++RUST_LIBS := $(foreach c,$(RUST_CRATES),$(GIT_BUILD_DIR)/lib$(c).a)
     ++
     ++EXTLIBS += $(RUST_LIBS)
      +
     -+EXTLIBS += ./$(RUST_TARGET_DIR)/libxdiff.a
     ++compile_rust: $(RUST_LIBS)
     ++rustclean:
     ++	$(RM) $(RUST_LIBS)
     ++	cd rust && cargo clean
     ++
     ++$(GIT_BUILD_DIR)/lib%.a:
     ++	./build_rust.sh $(GIT_BUILD_DIR) $(RUST_BUILD_MODE) $*
      +
      +UNAME_S := $(shell uname -s)
      +ifeq ($(UNAME_S),Linux)
     -+  EXTLIBS += -ldl
     ++	EXTLIBS += -ldl
     ++endif
     ++
     ++else ifeq ($(WITH_RUST),false)
     ++compile_rust:
     ++	:
     ++rustclean:
     ++	:
     ++else
     ++$(error 'WITH_RUST' must be true or false)
      +endif
      +
     - REFTABLE_LIB = reftable/libreftable.a
     - 
       GENERATED_H += command-list.h
     -@@ Makefile: UNIT_TEST_OBJS += $(UNIT_TEST_DIR)/lib-reftable.o
     + GENERATED_H += config-list.h
     + GENERATED_H += hook-list.h
     +@@ Makefile: CLAR_TEST_OBJS += $(UNIT_TEST_DIR)/unit-test.o
     + UNIT_TEST_OBJS += $(UNIT_TEST_DIR)/test-lib.o
       
     - # xdiff and reftable libs may in turn depend on what is in libgit.a
     - GITLIBS = common-main.o $(LIB_FILE) $(XDIFF_LIB) $(REFTABLE_LIB) $(LIB_FILE)
     + GITLIBS = common-main.o $(LIB_FILE)
      -EXTLIBS =
     -+
       
       GIT_USER_AGENT = git/$(GIT_VERSION)
       
     +@@ Makefile: CC_LD_DYNPATH = -Wl,-rpath,
     + BASIC_CFLAGS = -I. -fPIE
     + BASIC_LDFLAGS =
     + 
     ++ifeq ($(WITH_RUST),true)
     ++BASIC_CFLAGS += -DWITH_RUST
     ++endif
     + # library flags
     + ARFLAGS = rcs
     + PTHREAD_CFLAGS =
      @@ Makefile: git.sp git.s git.o: EXTRA_CPPFLAGS = \
       	'-DGIT_MAN_PATH="$(mandir_relative_SQ)"' \
       	'-DGIT_INFO_PATH="$(infodir_relative_SQ)"'
     @@ Makefile: $(REMOTE_CURL_ALIASES): $(REMOTE_CURL_PRIMARY)
       	$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) \
       		$(filter %.o,$^) $(LIBS)
       
     -@@ Makefile: $(LIB_FILE): $(LIB_OBJS)
     - $(XDIFF_LIB): $(XDIFF_OBJS)
     - 	$(QUIET_AR)$(RM) $@ && $(AR) $(ARFLAGS) $@ $^
     - 
     -+
     - $(REFTABLE_LIB): $(REFTABLE_OBJS)
     - 	$(QUIET_AR)$(RM) $@ && $(AR) $(ARFLAGS) $@ $^
     - 
      @@ Makefile: perf: all
       
       t/helper/test-tool$X: $(patsubst %,t/helper/%,$(TEST_BUILTINS_OBJS)) $(UNIT_TEST_DIR)/test-lib.o
     @@ Makefile: cocciclean:
       	$(RM) contrib/coccinelle/*.cocci.patch
       
      -clean: profile-clean coverage-clean cocciclean
     -+rustclean:
     -+	cd rust && cargo clean
     -+
      +clean: profile-clean coverage-clean cocciclean rustclean
       	$(RM) -r .build $(UNIT_TEST_BIN)
       	$(RM) GIT-TEST-SUITES
     @@ build_rust.sh (new)
      @@
      +#!/bin/sh
      +
     -+
      +rustc -vV || exit $?
      +cargo --version || exit $?
      +
     @@ build_rust.sh (new)
      +  exit 1
      +fi
      +
     -+cd $dir_rust && cargo clean && pwd && cargo build -p $crate $rust_args; cd $dir_git_root
     -+
      +libfile="lib${crate}.a"
      +if rustup show active-toolchain | grep windows-msvc; then
      +  libfile="${crate}.lib"
     ++  PATH="$(echo $PATH | tr ':' '\n' | grep -Ev "^(/mingw64/bin|/usr/bin)$" | paste -sd: -):/mingw64/bin:/usr/bin"
      +fi
     -+dst=$dir_build/$libfile
      +
     -+if [ "$dir_git_root" != "$dir_build" ]; then
     -+  src=$dir_rust/target/$rust_build_profile/$libfile
     -+  if [ ! -f $src ]; then
     -+    echo >&2 "::error:: cannot find path of static library $src is not a file or does not exist"
     -+    exit 5
     -+  fi
     ++CARGO_TARGET_DIR=$dir_git_root/.build/rust/$crate
     ++export CARGO_TARGET_DIR
     ++
     ++cd $dir_rust && cargo clean && pwd && USE_LINKING="false" cargo build -p $crate $rust_args; cd $dir_git_root
     ++
     ++src=$CARGO_TARGET_DIR/$rust_build_profile/$libfile
     ++dst=$dir_build/$libfile
      +
     -+  rm $dst 2>/dev/null
     -+  mv $src $dst
     ++if [ ! -f $src ]; then
     ++  echo >&2 "::error:: cannot find path of static library $src is not a file or does not exist"
     ++  exit 5
      +fi
     -
     - ## git-compat-util.h ##
     -@@ git-compat-util.h: static inline int is_xplatform_dir_sep(int c)
     - #include "compat/msvc.h"
     - #endif
     - 
     -+/* rust types */
     -+typedef uint8_t   u8;
     -+typedef uint16_t  u16;
     -+typedef uint32_t  u32;
     -+typedef uint64_t  u64;
     -+
     -+typedef int8_t    i8;
     -+typedef int16_t   i16;
     -+typedef int32_t   i32;
     -+typedef int64_t   i64;
     -+
     -+typedef float     f32;
     -+typedef double    f64;
     -+
     -+typedef size_t    usize;
     -+typedef ptrdiff_t isize;
     -+
     - /* used on Mac OS X */
     - #ifdef PRECOMPOSE_UNICODE
     - #include "compat/precompose_utf8.h"
     ++
     ++rm $dst 2>/dev/null
     ++echo mv $src $dst
     ++mv $src $dst
      
       ## meson.build ##
      @@ meson.build: version_gen_environment.set('GIT_DATE', get_option('build_date'))
       version_gen_environment.set('GIT_USER_AGENT', get_option('user_agent'))
       version_gen_environment.set('GIT_VERSION', get_option('version'))
       
     -+if get_option('optimization') in ['2', '3', 's', 'z']
     -+  rust_build_profile = 'release'
     -+else
     -+  rust_build_profile = 'debug'
     -+endif
     ++rust_crates = []
      +
     -+# Run `rustup show active-toolchain` and capture output
     -+rustup_out = run_command('rustup', 'show', 'active-toolchain',
     -+                         check: true).stdout().strip()
     -+
     -+rust_crates = ['xdiff']
      +rust_builds = []
     ++with_rust = get_option('with_rust')
     ++if with_rust
     ++  rustc = find_program('rustc', required : false)
     ++  cargo = find_program('cargo', required : false)
      +
     -+foreach crate : rust_crates
     -+  if rustup_out.contains('windows-msvc')
     -+    libfile = crate + '.lib'
     ++  if not rustc.found() or not cargo.found()
     ++    error('Rust toolchain not found. Reconfigure with -Dwith_rust=false')
     ++  endif
     ++
     ++  if get_option('optimization') in ['2', '3', 's', 'z']
     ++    rust_build_profile = 'release'
      +  else
     -+    libfile = 'lib' + crate + '.a'
     ++    rust_build_profile = 'debug'
      +  endif
      +
     -+  rust_builds += custom_target(
     -+    'rust_build_'+crate,
     -+    output: libfile,
     -+    build_by_default: true,
     -+    build_always_stale: true,
     -+    command: [
     -+      meson.project_source_root() / 'build_rust.sh',
     -+      meson.current_build_dir(), rust_build_profile, crate,
     -+    ],
     -+    install: false,
     -+  )
     -+endforeach
     ++  # Run `rustup show active-toolchain` and capture output
     ++  rustup_out = run_command('rustup', 'show', 'active-toolchain',
     ++                           check: true).stdout().strip()
     ++
     ++  rust_environment = script_environment
     ++
     ++  foreach crate : rust_crates
     ++    if rustup_out.contains('windows-msvc')
     ++      libfile = crate + '.lib'
     ++    else
     ++      libfile = 'lib' + crate + '.a'
     ++    endif
     ++
     ++    rust_builds += custom_target(
     ++      'rust_build_'+crate,
     ++      output: libfile,
     ++      build_by_default: true,
     ++      build_always_stale: true,
     ++      command: [
     ++        meson.project_source_root() / 'build_rust.sh',
     ++        meson.current_build_dir(), rust_build_profile, crate,
     ++      ],
     ++      install: false,
     ++      env: rust_environment,
     ++    )
     ++  endforeach
     ++else
     ++  message('Rust components disabled (-Dwith_rust=false)')
     ++endif
      +
      +
       compiler = meson.get_compiler('c')
       
       libgit_sources = [
     +@@ meson.build: libgit_c_args = [
     +   '-DSHELL_PATH="' + fs.as_posix(target_shell.full_path()) + '"',
     + ]
     + 
     ++if with_rust
     ++  libgit_c_args += '-DWITH_RUST'
     ++endif
     ++
     + system_attributes = get_option('gitattributes')
     + if system_attributes != ''
     +   libgit_c_args += '-DETC_GITATTRIBUTES="' + system_attributes + '"'
     +@@ meson.build: elif host_machine.system() == 'windows'
     +   ]
     + 
     +   libgit_dependencies += compiler.find_library('ntdll')
     ++  libgit_dependencies += compiler.find_library('userenv')
     +   libgit_include_directories += 'compat/win32'
     +   if compiler.get_id() == 'msvc'
     +     libgit_include_directories += 'compat/vcbuild/include'
      @@ meson.build: version_def_h = custom_target(
       libgit_sources += version_def_h
       
     @@ meson.build: version_def_h = custom_target(
         dependencies: libgit_dependencies,
         include_directories: libgit_include_directories,
      
     + ## meson_options.txt ##
     +@@ meson_options.txt: option('zlib_backend', type: 'combo', choices: ['auto', 'zlib', 'zlib-ng'], valu
     +   description: 'The backend used for compressing objects and other data.')
     + 
     + # Build tweaks.
     ++option('with_rust', type: 'boolean', value: true,
     ++       description: 'Enable building with Rust, true by default.')
     + option('breaking_changes', type: 'boolean', value: false,
     +   description: 'Enable upcoming breaking changes.')
     + option('macos_use_homebrew_gettext', type: 'boolean', value: true,
     +
       ## rust/Cargo.toml (new) ##
      @@
      +[workspace]
     -+members = [
     -+    "xdiff",
     -+    "interop",
     -+]
     ++members = []
      +resolver = "2"
     -
     - ## rust/interop/Cargo.toml (new) ##
     -@@
     -+[package]
     -+name = "interop"
     -+version = "0.1.0"
     -+edition = "2021"
     -+
     -+[lib]
     -+name = "interop"
     -+path = "src/lib.rs"
     -+## staticlib to generate xdiff.a for use by gcc
     -+## cdylib (optional) to generate xdiff.so for use by gcc
     -+## rlib is required by the rust unit tests
     -+crate-type = ["staticlib", "rlib"]
     -+
     -+[dependencies]
     -
     - ## rust/interop/src/lib.rs (new) ##
     -
     - ## rust/xdiff/Cargo.toml (new) ##
     -@@
     -+[package]
     -+name = "xdiff"
     -+version = "0.1.0"
     -+edition = "2021"
     -+
     -+[lib]
     -+name = "xdiff"
     -+path = "src/lib.rs"
     -+## staticlib to generate xdiff.a for use by gcc
     -+## cdylib (optional) to generate xdiff.so for use by gcc
     -+## rlib is required by the rust unit tests
     -+crate-type = ["staticlib", "rlib"]
     -+
     -+[dependencies]
     -+interop = { path = "../interop" }
     -
     - ## rust/xdiff/src/lib.rs (new) ##
  -:  ---------- >  8:  cd04ed5aa7 help: report on whether or not Rust is enabled
  3:  6c47401ba0 !  9:  fcdfc55fb7 github workflows: install rust
     @@ Commit message
              the Rust toolchain, the Rust target triple must be set to
              x86_64-pc-windows-gnu for make (win build), and
              x86_64-pc-windows-msvc for meson (win+Meson build).
     +      * MSVC builds: Rearrange PATH to look in /mingw64/bin and /usr/bin
     +        last.
            * On musl: libc differences, such as ftruncate64 vs ftruncate, were
              not accounted for until Rust version 1.72.0. No older version of
              Rust will work on musl for our needs.
     @@ .github/workflows/main.yml: jobs:
           outputs:
             enabled: ${{ steps.check-ref.outputs.enabled }}${{ steps.skip-if-redundant.outputs.enabled }}
             skip_concurrent: ${{ steps.check-ref.outputs.skip_concurrent }}
     -+      rust_version_minimum: 1.61.0
     ++      rust_version_minimum: 1.63.0
      +      rust_version_windows: 1.78.0
      +      rust_version_musl: 1.72.0
      +      ## the rust target is inferred by rustup unless specified
     @@ .github/workflows/main.yml: jobs:
           steps:
           - uses: actions/checkout@v4
           - uses: git-for-windows/setup-git-for-windows-sdk@v1
     ++      with:
     ++        flavor: full
      +    - name: Install rustup via github actions
      +      uses: actions-rs/toolchain@v1
      +      with:
  4:  8e350700bc ! 10:  6eeef4081c win+Meson: do allow linking with the Rust-built xdiff
     @@ Commit message
           handling from an earlier patch]
          Signed-off-by: Ezekiel Newren <ezekielnewren@gmail.com>
      
     - ## .github/workflows/main.yml ##
     -@@ .github/workflows/main.yml: jobs:
     -     steps:
     -     - uses: actions/checkout@v4
     -     - uses: git-for-windows/setup-git-for-windows-sdk@v1
     -+    - name: ensure that libuserenv.a is present
     -+      shell: bash
     -+      run: |
     -+        cd /mingw64/lib && {
     -+          test -f libuserenv.a ||
     -+          /c/Program\ Files/Git/mingw64/bin/curl -Lo libuserenv.a \
     -+            https://github.com/git-for-windows/git-sdk-64/raw/HEAD/mingw64/lib/libuserenv.a
     -+        }
     -     - name: Install rustup via github actions
     -       uses: actions-rs/toolchain@v1
     -       with:
     -
       ## config.mak.uname ##
      @@ config.mak.uname: ifeq ($(uname_S),MINGW)
       		COMPAT_CFLAGS += -D_USE_32BIT_TIME_T
     @@ config.mak.uname: ifeq ($(uname_S),MINGW)
       	CC = gcc
       	COMPAT_CFLAGS += -D__USE_MINGW_ANSI_STDIO=0 -DDETECT_MSYS_TTY \
       		-fstack-protector-strong
     -
     - ## meson.build ##
     -@@ meson.build: elif host_machine.system() == 'windows'
     -   ]
     - 
     -   libgit_dependencies += compiler.find_library('ntdll')
     -+  libgit_dependencies += compiler.find_library('userenv')
     -   libgit_include_directories += 'compat/win32'
     -   if compiler.get_id() == 'msvc'
     -     libgit_include_directories += 'compat/vcbuild/include'
  5:  a00cbf7bcb = 11:  1e13d6c45f github workflows: upload Cargo.lock
  6:  922d506ed6 <  -:  ---------- ivec: create a vector type that is interoperable between C and Rust
  -:  ---------- > 12:  1c923a71f2 build: new crate, build-helper
  7:  3a6f822e2a ! 13:  6a27e07e63 xdiff/xprepare: remove superfluous forward declarations
     @@ Metadata
      Author: Ezekiel Newren <ezekielnewren@gmail.com>
      
       ## Commit message ##
     -    xdiff/xprepare: remove superfluous forward declarations
     +    build-helper: link against libgit.a and any other required C libraries
      
     -    Move xdl_prepare_env() later in the file to avoid the need
     -    for forward declarations.
     +    Don't link against the C libraries when building with Make or Meson.
     +    Run cargo tests like this:
     +    cd rust && cargo clean && USE_LINKING=true cargo test
      
          Signed-off-by: Ezekiel Newren <ezekielnewren@gmail.com>
      
     - ## xdiff/xprepare.c ##
     -@@ xdiff/xprepare.c: typedef struct s_xdlclassifier {
     + ## rust/build-helper/Cargo.toml ##
     +@@ rust/build-helper/Cargo.toml: version = "0.1.0"
     + edition = "2021"
       
     - 
     - 
     --static int xdl_init_classifier(xdlclassifier_t *cf, long size, long flags);
     --static void xdl_free_classifier(xdlclassifier_t *cf);
     --static int xdl_classify_record(unsigned int pass, xdlclassifier_t *cf, xrecord_t **rhash,
     --			       unsigned int hbits, xrecord_t *rec);
     --static int xdl_prepare_ctx(unsigned int pass, mmfile_t *mf, long narec, xpparam_t const *xpp,
     --			   xdlclassifier_t *cf, xdfile_t *xdf);
     --static void xdl_free_ctx(xdfile_t *xdf);
     --static int xdl_clean_mmatch(char const *dis, long i, long s, long e);
     --static int xdl_cleanup_records(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xdf2);
     --static int xdl_trim_ends(xdfile_t *xdf1, xdfile_t *xdf2);
     --static int xdl_optimize_ctxs(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xdf2);
     --
     --
     --
     --
     - static int xdl_init_classifier(xdlclassifier_t *cf, long size, long flags) {
     - 	cf->flags = flags;
     - 
     -@@ xdiff/xprepare.c: static void xdl_free_ctx(xdfile_t *xdf) {
     - }
     - 
     - 
     --int xdl_prepare_env(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,
     --		    xdfenv_t *xe) {
     --	long enl1, enl2, sample;
     --	xdlclassifier_t cf;
     --
     --	memset(&cf, 0, sizeof(cf));
     --
     --	/*
     --	 * For histogram diff, we can afford a smaller sample size and
     --	 * thus a poorer estimate of the number of lines, as the hash
     --	 * table (rhash) won't be filled up/grown. The number of lines
     --	 * (nrecs) will be updated correctly anyway by
     --	 * xdl_prepare_ctx().
     --	 */
     --	sample = (XDF_DIFF_ALG(xpp->flags) == XDF_HISTOGRAM_DIFF
     --		  ? XDL_GUESS_NLINES2 : XDL_GUESS_NLINES1);
     --
     --	enl1 = xdl_guess_lines(mf1, sample) + 1;
     --	enl2 = xdl_guess_lines(mf2, sample) + 1;
     --
     --	if (xdl_init_classifier(&cf, enl1 + enl2 + 1, xpp->flags) < 0)
     --		return -1;
     --
     --	if (xdl_prepare_ctx(1, mf1, enl1, xpp, &cf, &xe->xdf1) < 0) {
     --
     --		xdl_free_classifier(&cf);
     --		return -1;
     --	}
     --	if (xdl_prepare_ctx(2, mf2, enl2, xpp, &cf, &xe->xdf2) < 0) {
     --
     --		xdl_free_ctx(&xe->xdf1);
     --		xdl_free_classifier(&cf);
     --		return -1;
     --	}
     + [dependencies]
      -
     --	if ((XDF_DIFF_ALG(xpp->flags) != XDF_PATIENCE_DIFF) &&
     --	    (XDF_DIFF_ALG(xpp->flags) != XDF_HISTOGRAM_DIFF) &&
     --	    xdl_optimize_ctxs(&cf, &xe->xdf1, &xe->xdf2) < 0) {
     --
     --		xdl_free_ctx(&xe->xdf2);
     --		xdl_free_ctx(&xe->xdf1);
     --		xdl_free_classifier(&cf);
     --		return -1;
     --	}
     --
     --	xdl_free_classifier(&cf);
     --
     --	return 0;
     --}
     --
     --
     - void xdl_free_env(xdfenv_t *xe) {
     - 
     - 	xdl_free_ctx(&xe->xdf2);
     -@@ xdiff/xprepare.c: static int xdl_optimize_ctxs(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xdf2
     - 
     - 	return 0;
     - }
     +
     + ## rust/build-helper/src/lib.rs ##
     +@@
     ++use std::collections::HashMap;
     ++use std::path::PathBuf;
      +
     -+int xdl_prepare_env(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,
     -+		    xdfenv_t *xe) {
     -+	long enl1, enl2, sample;
     -+	xdlclassifier_t cf;
      +
     -+	memset(&cf, 0, sizeof(cf));
     ++fn parse_bool_from_str(value: &str) -> bool {
     ++    match value {
     ++        "1" | "true"  | "yes" | "on"  => true,
     ++        "0" | "false" | "no"  | "off" => false,
     ++        _ => false
     ++    }
     ++}
     ++
     ++fn parse_bool_from_option(value: Option<&String>, default: bool) -> bool {
     ++    match value {
     ++        Some(v) => {
     ++            parse_bool_from_str(v.as_str())
     ++        },
     ++        None => default,
     ++    }
     ++}
      +
     -+	/*
     -+	 * For histogram diff, we can afford a smaller sample size and
     -+	 * thus a poorer estimate of the number of lines, as the hash
     -+	 * table (rhash) won't be filled up/grown. The number of lines
     -+	 * (nrecs) will be updated correctly anyway by
     -+	 * xdl_prepare_ctx().
     -+	 */
     -+	sample = (XDF_DIFF_ALG(xpp->flags) == XDF_HISTOGRAM_DIFF
     -+		  ? XDL_GUESS_NLINES2 : XDL_GUESS_NLINES1);
     ++/// To build without linking against C libraries run `USE_LINKING=false cargo build`
     ++/// To run tests set GIT_BUILD_DIR and run `USE_LINKING=true cargo test`
     ++pub struct BuildHelper {
     ++    crate_env: HashMap<String, String>,
     ++}
      +
     -+	enl1 = xdl_guess_lines(mf1, sample) + 1;
     -+	enl2 = xdl_guess_lines(mf2, sample) + 1;
      +
     -+	if (xdl_init_classifier(&cf, enl1 + enl2 + 1, xpp->flags) < 0)
     -+		return -1;
     ++impl BuildHelper {
     ++    pub fn new(crate_env: HashMap<String, String>) -> Self {
     ++        let it = Self {crate_env};
      +
     -+	if (xdl_prepare_ctx(1, mf1, enl1, xpp, &cf, &xe->xdf1) < 0) {
     ++        let dir_crate = it.dir_crate();
     ++        let dir_workspace = dir_crate.parent().unwrap();
     ++        let dir_git = dir_workspace.parent().unwrap();
     ++        let dir_interop = dir_git.join("interop");
     ++        if !dir_interop.exists() {
     ++            std::fs::create_dir(dir_interop.clone()).unwrap();
     ++        }
      +
     -+		xdl_free_classifier(&cf);
     -+		return -1;
     -+	}
     -+	if (xdl_prepare_ctx(2, mf2, enl2, xpp, &cf, &xe->xdf2) < 0) {
     ++        it
     ++    }
      +
     -+		xdl_free_ctx(&xe->xdf1);
     -+		xdl_free_classifier(&cf);
     -+		return -1;
     -+	}
     ++    pub fn crate_name(&self) -> String {
     ++        self.crate_env["CARGO_PKG_NAME"].clone()
     ++    }
      +
     -+	if ((XDF_DIFF_ALG(xpp->flags) != XDF_PATIENCE_DIFF) &&
     -+	    (XDF_DIFF_ALG(xpp->flags) != XDF_HISTOGRAM_DIFF) &&
     -+	    xdl_optimize_ctxs(&cf, &xe->xdf1, &xe->xdf2) < 0) {
     ++    pub fn dir_crate(&self) -> PathBuf {
     ++        PathBuf::from(self.crate_env["CARGO_MANIFEST_DIR"].clone())
     ++    }
      +
     -+		xdl_free_ctx(&xe->xdf2);
     -+		xdl_free_ctx(&xe->xdf1);
     -+		xdl_free_classifier(&cf);
     -+		return -1;
     -+	    }
     ++    pub fn build(self) {
     ++        let use_linking = parse_bool_from_option(self.crate_env.get("USE_LINKING"), self.crate_env.get("CARGO_TARGET_DIR").is_none());
     ++        let dir_crate = self.dir_crate();
     ++        let dir_git = dir_crate.parent().unwrap().parent().unwrap();
      +
     -+	xdl_free_classifier(&cf);
     ++        println!("cargo:rerun-if-changed={}", dir_git.display());
      +
     -+	return 0;
     ++        if use_linking {
     ++            if let Some(git_build_dir) = self.crate_env.get("GIT_BUILD_DIR") {
     ++                let mut path_git_build_dir = PathBuf::from(git_build_dir);
     ++                path_git_build_dir = path_git_build_dir.canonicalize().unwrap();
     ++                if !path_git_build_dir.is_dir() {
     ++                    panic!("'GIT_BUILD_DIR' is not a directory: {}", path_git_build_dir.display());
     ++                }
     ++                println!("cargo:rustc-link-search=native={}", git_build_dir);
     ++            } else {
     ++                panic!("environment variable 'GIT_BUILD_DIR' is not set");
     ++            }
     ++
     ++            println!("cargo:rustc-link-lib=static=git");
     ++            println!("cargo:rustc-link-lib=pcre2-8");
     ++            if self.crate_env.get("ZLIB_NG").is_some() {
     ++                println!("cargo:rustc-link-lib=z-ng");
     ++            } else {
     ++                println!("cargo:rustc-link-lib=z");
     ++            }
     ++        } else {
     ++            println!("cargo:warning={} is not linking against C objects, `USE_LINKING=true cargo test`", self.crate_env["CARGO_PKG_NAME"]);
     ++        }
     ++    }
      +}
     ++
     ++
  8:  c7cd71dae0 <  -:  ---------- xdiff: delete unnecessary fields from xrecord_t and xdfile_t
  9:  1096c8f0a4 <  -:  ---------- xdiff: make fields of xrecord_t Rust friendly
 11:  7ea2dccd71 <  -:  ---------- xdiff: replace chastore with an ivec in xdfile_t
 12:  d8f561e173 <  -:  ---------- xdiff: delete nrec field from xdfile_t
 13:  15c2854a32 <  -:  ---------- xdiff: delete recs field from xdfile_t
 14:  3dacb2c09d <  -:  ---------- xdiff: make xdfile_t more rust friendly
 15:  9fdd23acf6 <  -:  ---------- xdiff: implement xdl_trim_ends() in Rust
  -:  ---------- > 14:  fa33440568 build-helper: cbindgen, let crates generate a header file
  -:  ---------- > 15:  df837fdb4d varint: use explicit width for integers
  -:  ---------- > 16:  17143ced6f build: new crate, misc
  -:  ---------- > 17:  89bb7c3613 misc: use BuildHelper
  -:  ---------- > 18:  3c521fa2e4 misc::varint: reimplement as test balloon for Rust

-- 
gitgitgadget

^ permalink raw reply	[flat|nested] 100+ messages in thread

* [PATCH v2 01/18] cleanup: rename variables that collide with Rust primitive type names
  2025-09-17  1:16 ` [PATCH v2 00/18] Introduce rust: In xdiff Ezekiel Newren via GitGitGadget
@ 2025-09-17  1:16   ` Ezekiel Newren via GitGitGadget
  2025-09-17  7:42     ` Eric Sunshine
  2025-09-17  1:16   ` [PATCH v2 02/18] make: add -fPIE flag Ezekiel Newren via GitGitGadget
                     ` (19 subsequent siblings)
  20 siblings, 1 reply; 100+ messages in thread
From: Ezekiel Newren via GitGitGadget @ 2025-09-17  1:16 UTC (permalink / raw)
  To: git; +Cc: Ezekiel Newren, Ezekiel Newren

From: Ezekiel Newren <ezekielnewren@gmail.com>

Use a regex to find and rename variables that collide with Rust
primitive integer and float type names:

    git grep -n -E -e '\<([ui](8|16|32|64|size)|(f(32|64)))\>'

Matches were reviewed and renamed. The remaining matches don't count
because:
- Rust source files:
  contrib/libgit-rs/src/config.rs
  contrib/libgit-sys/src/lib.rs
  t/t4018/rust-impl
  t/t4018/rust-trait
- Intentional references:
  t/helper/test-parse-options.c (prints Rust int names)
  t/t0040-parse-options.sh (tests the above)

View with --color-words to highlight the variable renames.

Signed-off-by: Ezekiel Newren <ezekielnewren@gmail.com>
---
 odb.c                         |  8 ++++----
 reftable/record.c             |  6 +++---
 t/helper/test-parse-options.c | 12 ++++++------
 xdiff/xtypes.h                |  2 +-
 xdiff/xutils.c                |  8 ++++----
 xdiff/xutils.h                |  2 +-
 6 files changed, 19 insertions(+), 19 deletions(-)

diff --git a/odb.c b/odb.c
index 2a92a018c4..f0255b4b36 100644
--- a/odb.c
+++ b/odb.c
@@ -913,7 +913,7 @@ void *odb_read_object_peeled(struct object_database *odb,
 {
 	enum object_type type;
 	void *buffer;
-	unsigned long isize;
+	unsigned long isize_;
 	struct object_id actual_oid;
 
 	oidcpy(&actual_oid, oid);
@@ -921,11 +921,11 @@ void *odb_read_object_peeled(struct object_database *odb,
 		int ref_length = -1;
 		const char *ref_type = NULL;
 
-		buffer = odb_read_object(odb, &actual_oid, &type, &isize);
+		buffer = odb_read_object(odb, &actual_oid, &type, &isize_);
 		if (!buffer)
 			return NULL;
 		if (type == required_type) {
-			*size = isize;
+			*size = isize_;
 			if (actual_oid_return)
 				oidcpy(actual_oid_return, &actual_oid);
 			return buffer;
@@ -941,7 +941,7 @@ void *odb_read_object_peeled(struct object_database *odb,
 		}
 		ref_length = strlen(ref_type);
 
-		if (ref_length + odb->repo->hash_algo->hexsz > isize ||
+		if (ref_length + odb->repo->hash_algo->hexsz > isize_ ||
 		    memcmp(buffer, ref_type, ref_length) ||
 		    get_oid_hex_algop((char *) buffer + ref_length, &actual_oid,
 				      odb->repo->hash_algo)) {
diff --git a/reftable/record.c b/reftable/record.c
index fcd387ba5d..e8fc854561 100644
--- a/reftable/record.c
+++ b/reftable/record.c
@@ -675,7 +675,7 @@ static int reftable_log_record_key(const void *r, struct reftable_buf *dest)
 	const struct reftable_log_record *rec =
 		(const struct reftable_log_record *)r;
 	int len = strlen(rec->refname), err;
-	uint8_t i64[8];
+	uint8_t i64_[8];
 	uint64_t ts = 0;
 
 	reftable_buf_reset(dest);
@@ -684,9 +684,9 @@ static int reftable_log_record_key(const void *r, struct reftable_buf *dest)
 		return err;
 
 	ts = (~ts) - rec->update_index;
-	reftable_put_be64(&i64[0], ts);
+	reftable_put_be64(&i64_[0], ts);
 
-	err = reftable_buf_add(dest, i64, sizeof(i64));
+	err = reftable_buf_add(dest, i64_, sizeof(i64_));
 	if (err < 0)
 		return err;
 
diff --git a/t/helper/test-parse-options.c b/t/helper/test-parse-options.c
index 68579d83f3..aa1cb693a2 100644
--- a/t/helper/test-parse-options.c
+++ b/t/helper/test-parse-options.c
@@ -120,8 +120,8 @@ int cmd__parse_options(int argc, const char **argv)
 	};
 	struct string_list expect = STRING_LIST_INIT_NODUP;
 	struct string_list list = STRING_LIST_INIT_NODUP;
-	uint16_t u16 = 0;
-	int16_t i16 = 0;
+	uint16_t u16_ = 0;
+	int16_t i16_ = 0;
 
 	struct option options[] = {
 		OPT_BOOL(0, "yes", &boolean, "get a boolean"),
@@ -142,10 +142,10 @@ int cmd__parse_options(int argc, const char **argv)
 		OPT_NEGBIT(0, "neg-or4", &boolean, "same as --no-or4", 4),
 		OPT_GROUP(""),
 		OPT_INTEGER('i', "integer", &integer, "get a integer"),
-		OPT_INTEGER(0, "i16", &i16, "get a 16 bit integer"),
+		OPT_INTEGER(0, "i16", &i16_, "get a 16 bit integer"),
 		OPT_INTEGER('j', NULL, &integer, "get a integer, too"),
 		OPT_UNSIGNED('u', "unsigned", &unsigned_integer, "get an unsigned integer"),
-		OPT_UNSIGNED(0, "u16", &u16, "get a 16 bit unsigned integer"),
+		OPT_UNSIGNED(0, "u16", &u16_, "get a 16 bit unsigned integer"),
 		OPT_SET_INT(0, "set23", &integer, "set integer to 23", 23),
 		OPT_CMDMODE(0, "mode1", &integer, "set integer to 1 (cmdmode option)", 1),
 		OPT_CMDMODE(0, "mode2", &integer, "set integer to 2 (cmdmode option)", 2),
@@ -225,9 +225,9 @@ int cmd__parse_options(int argc, const char **argv)
 	}
 	show(&expect, &ret, "boolean: %d", boolean);
 	show(&expect, &ret, "integer: %d", integer);
-	show(&expect, &ret, "i16: %"PRIdMAX, (intmax_t) i16);
+	show(&expect, &ret, "i16: %"PRIdMAX, (intmax_t) i16_);
 	show(&expect, &ret, "unsigned: %lu", unsigned_integer);
-	show(&expect, &ret, "u16: %"PRIuMAX, (uintmax_t) u16);
+	show(&expect, &ret, "u16: %"PRIuMAX, (uintmax_t) u16_);
 	show(&expect, &ret, "timestamp: %"PRItime, timestamp);
 	show(&expect, &ret, "string: %s", string ? string : "(not set)");
 	show(&expect, &ret, "abbrev: %d", abbrev);
diff --git a/xdiff/xtypes.h b/xdiff/xtypes.h
index 8442bd436e..ec1dc7548a 100644
--- a/xdiff/xtypes.h
+++ b/xdiff/xtypes.h
@@ -32,7 +32,7 @@ typedef struct s_chanode {
 
 typedef struct s_chastore {
 	chanode_t *head, *tail;
-	long isize, nsize;
+	long isize_, nsize;
 	chanode_t *ancur;
 	chanode_t *sncur;
 	long scurr;
diff --git a/xdiff/xutils.c b/xdiff/xutils.c
index 444a108f87..20dee53dbd 100644
--- a/xdiff/xutils.c
+++ b/xdiff/xutils.c
@@ -71,11 +71,11 @@ long xdl_mmfile_size(mmfile_t *mmf)
 }
 
 
-int xdl_cha_init(chastore_t *cha, long isize, long icount) {
+int xdl_cha_init(chastore_t *cha, long isize_, long icount) {
 
 	cha->head = cha->tail = NULL;
-	cha->isize = isize;
-	cha->nsize = icount * isize;
+	cha->isize_ = isize_;
+	cha->nsize = icount * isize_;
 	cha->ancur = cha->sncur = NULL;
 	cha->scurr = 0;
 
@@ -113,7 +113,7 @@ void *xdl_cha_alloc(chastore_t *cha) {
 	}
 
 	data = (char *) ancur + sizeof(chanode_t) + ancur->icurr;
-	ancur->icurr += cha->isize;
+	ancur->icurr += cha->isize_;
 
 	return data;
 }
diff --git a/xdiff/xutils.h b/xdiff/xutils.h
index fd0bba94e8..b531d2ebff 100644
--- a/xdiff/xutils.h
+++ b/xdiff/xutils.h
@@ -28,7 +28,7 @@
 long xdl_bogosqrt(long n);
 int xdl_emit_diffrec(char const *rec, long size, char const *pre, long psize,
 		     xdemitcb_t *ecb);
-int xdl_cha_init(chastore_t *cha, long isize, long icount);
+int xdl_cha_init(chastore_t *cha, long isize_, long icount);
 void xdl_cha_free(chastore_t *cha);
 void *xdl_cha_alloc(chastore_t *cha);
 long xdl_guess_lines(mmfile_t *mf, long sample);
-- 
gitgitgadget


^ permalink raw reply related	[flat|nested] 100+ messages in thread

* [PATCH v2 02/18] make: add -fPIE flag
  2025-09-17  1:16 ` [PATCH v2 00/18] Introduce rust: In xdiff Ezekiel Newren via GitGitGadget
  2025-09-17  1:16   ` [PATCH v2 01/18] cleanup: rename variables that collide with Rust primitive type names Ezekiel Newren via GitGitGadget
@ 2025-09-17  1:16   ` Ezekiel Newren via GitGitGadget
  2025-09-17  7:44     ` Eric Sunshine
  2025-09-17  1:16   ` [PATCH v2 03/18] make: merge xdiff lib into libgit.a Ezekiel Newren via GitGitGadget
                     ` (18 subsequent siblings)
  20 siblings, 1 reply; 100+ messages in thread
From: Ezekiel Newren via GitGitGadget @ 2025-09-17  1:16 UTC (permalink / raw)
  To: git; +Cc: Ezekiel Newren, Ezekiel Newren

From: Ezekiel Newren <ezekielnewren@gmail.com>

Signed-off-by: Ezekiel Newren <ezekielnewren@gmail.com>
---
 Makefile | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Makefile b/Makefile
index e11340c1ae..d3e034f3be 100644
--- a/Makefile
+++ b/Makefile
@@ -1402,7 +1402,7 @@ endif
 CFLAGS = -g -O2 -Wall
 LDFLAGS =
 CC_LD_DYNPATH = -Wl,-rpath,
-BASIC_CFLAGS = -I.
+BASIC_CFLAGS = -I. -fPIE
 BASIC_LDFLAGS =
 
 # library flags
-- 
gitgitgadget


^ permalink raw reply related	[flat|nested] 100+ messages in thread

* [PATCH v2 03/18] make: merge xdiff lib into libgit.a
  2025-09-17  1:16 ` [PATCH v2 00/18] Introduce rust: In xdiff Ezekiel Newren via GitGitGadget
  2025-09-17  1:16   ` [PATCH v2 01/18] cleanup: rename variables that collide with Rust primitive type names Ezekiel Newren via GitGitGadget
  2025-09-17  1:16   ` [PATCH v2 02/18] make: add -fPIE flag Ezekiel Newren via GitGitGadget
@ 2025-09-17  1:16   ` Ezekiel Newren via GitGitGadget
  2025-09-17  7:46     ` Eric Sunshine
  2025-09-17  1:16   ` [PATCH v2 04/18] make: merge reftable " Ezekiel Newren via GitGitGadget
                     ` (17 subsequent siblings)
  20 siblings, 1 reply; 100+ messages in thread
From: Ezekiel Newren via GitGitGadget @ 2025-09-17  1:16 UTC (permalink / raw)
  To: git; +Cc: Ezekiel Newren, Ezekiel Newren

From: Ezekiel Newren <ezekielnewren@gmail.com>

Signed-off-by: Ezekiel Newren <ezekielnewren@gmail.com>
---
 Makefile | 30 ++++++++++--------------------
 1 file changed, 10 insertions(+), 20 deletions(-)

diff --git a/Makefile b/Makefile
index d3e034f3be..94f38eb4c9 100644
--- a/Makefile
+++ b/Makefile
@@ -670,7 +670,6 @@ BUILTIN_OBJS =
 BUILT_INS =
 COMPAT_CFLAGS =
 COMPAT_OBJS =
-XDIFF_OBJS =
 GENERATED_H =
 EXTRA_CPPFLAGS =
 FUZZ_OBJS =
@@ -916,7 +915,6 @@ export PYTHON_PATH
 TEST_SHELL_PATH = $(SHELL_PATH)
 
 LIB_FILE = libgit.a
-XDIFF_LIB = xdiff/lib.a
 REFTABLE_LIB = reftable/libreftable.a
 
 GENERATED_H += command-list.h
@@ -1207,6 +1205,13 @@ LIB_OBJS += write-or-die.o
 LIB_OBJS += ws.o
 LIB_OBJS += wt-status.o
 LIB_OBJS += xdiff-interface.o
+LIB_OBJS += xdiff/xdiffi.o
+LIB_OBJS += xdiff/xemit.o
+LIB_OBJS += xdiff/xhistogram.o
+LIB_OBJS += xdiff/xmerge.o
+LIB_OBJS += xdiff/xpatience.o
+LIB_OBJS += xdiff/xprepare.o
+LIB_OBJS += xdiff/xutils.o
 
 BUILTIN_OBJS += builtin/add.o
 BUILTIN_OBJS += builtin/am.o
@@ -1385,8 +1390,8 @@ CLAR_TEST_OBJS += $(UNIT_TEST_DIR)/unit-test.o
 
 UNIT_TEST_OBJS += $(UNIT_TEST_DIR)/test-lib.o
 
-# xdiff and reftable libs may in turn depend on what is in libgit.a
-GITLIBS = common-main.o $(LIB_FILE) $(XDIFF_LIB) $(REFTABLE_LIB) $(LIB_FILE)
+# reftable lib may depend on what is in libgit.a
+GITLIBS = common-main.o $(LIB_FILE) $(REFTABLE_LIB) $(LIB_FILE)
 EXTLIBS =
 
 GIT_USER_AGENT = git/$(GIT_VERSION)
@@ -2718,16 +2723,6 @@ reconfigure config.mak.autogen: config.status
 .PHONY: reconfigure # This is a convenience target.
 endif
 
-XDIFF_OBJS += xdiff/xdiffi.o
-XDIFF_OBJS += xdiff/xemit.o
-XDIFF_OBJS += xdiff/xhistogram.o
-XDIFF_OBJS += xdiff/xmerge.o
-XDIFF_OBJS += xdiff/xpatience.o
-XDIFF_OBJS += xdiff/xprepare.o
-XDIFF_OBJS += xdiff/xutils.o
-.PHONY: xdiff-objs
-xdiff-objs: $(XDIFF_OBJS)
-
 REFTABLE_OBJS += reftable/basics.o
 REFTABLE_OBJS += reftable/error.o
 REFTABLE_OBJS += reftable/block.o
@@ -2762,7 +2757,6 @@ OBJECTS += $(GIT_OBJS)
 OBJECTS += $(SCALAR_OBJS)
 OBJECTS += $(PROGRAM_OBJS)
 OBJECTS += $(TEST_OBJS)
-OBJECTS += $(XDIFF_OBJS)
 OBJECTS += $(FUZZ_OBJS)
 OBJECTS += $(REFTABLE_OBJS) $(REFTABLE_TEST_OBJS)
 OBJECTS += $(UNIT_TEST_OBJS)
@@ -2916,9 +2910,6 @@ scalar$X: scalar.o GIT-LDFLAGS $(GITLIBS)
 $(LIB_FILE): $(LIB_OBJS)
 	$(QUIET_AR)$(RM) $@ && $(AR) $(ARFLAGS) $@ $^
 
-$(XDIFF_LIB): $(XDIFF_OBJS)
-	$(QUIET_AR)$(RM) $@ && $(AR) $(ARFLAGS) $@ $^
-
 $(REFTABLE_LIB): $(REFTABLE_OBJS)
 	$(QUIET_AR)$(RM) $@ && $(AR) $(ARFLAGS) $@ $^
 
@@ -3760,7 +3751,7 @@ clean: profile-clean coverage-clean cocciclean
 	$(RM) git.rc git.res
 	$(RM) $(OBJECTS)
 	$(RM) headless-git.o
-	$(RM) $(LIB_FILE) $(XDIFF_LIB) $(REFTABLE_LIB)
+	$(RM) $(LIB_FILE) $(REFTABLE_LIB)
 	$(RM) $(ALL_PROGRAMS) $(SCRIPT_LIB) $(BUILT_INS) $(OTHER_PROGRAMS)
 	$(RM) $(TEST_PROGRAMS)
 	$(RM) $(FUZZ_PROGRAMS)
@@ -3955,7 +3946,6 @@ endif
 LIBGIT_PUB_OBJS += contrib/libgit-sys/public_symbol_export.o
 LIBGIT_PUB_OBJS += libgit.a
 LIBGIT_PUB_OBJS += reftable/libreftable.a
-LIBGIT_PUB_OBJS += xdiff/lib.a
 
 LIBGIT_PARTIAL_EXPORT = contrib/libgit-sys/partial_symbol_export.o
 
-- 
gitgitgadget


^ permalink raw reply related	[flat|nested] 100+ messages in thread

* [PATCH v2 04/18] make: merge reftable lib into libgit.a
  2025-09-17  1:16 ` [PATCH v2 00/18] Introduce rust: In xdiff Ezekiel Newren via GitGitGadget
                     ` (2 preceding siblings ...)
  2025-09-17  1:16   ` [PATCH v2 03/18] make: merge xdiff lib into libgit.a Ezekiel Newren via GitGitGadget
@ 2025-09-17  1:16   ` Ezekiel Newren via GitGitGadget
  2025-09-17  7:46     ` Eric Sunshine
  2025-09-19 19:02     ` Junio C Hamano
  2025-09-17  1:16   ` [PATCH v2 05/18] doc: add a policy for using Rust brian m. carlson via GitGitGadget
                     ` (16 subsequent siblings)
  20 siblings, 2 replies; 100+ messages in thread
From: Ezekiel Newren via GitGitGadget @ 2025-09-17  1:16 UTC (permalink / raw)
  To: git; +Cc: Ezekiel Newren, Ezekiel Newren

From: Ezekiel Newren <ezekielnewren@gmail.com>

Signed-off-by: Ezekiel Newren <ezekielnewren@gmail.com>
---
 Makefile | 39 ++++++++++++++++-----------------------
 1 file changed, 16 insertions(+), 23 deletions(-)

diff --git a/Makefile b/Makefile
index 94f38eb4c9..ffb898b611 100644
--- a/Makefile
+++ b/Makefile
@@ -915,7 +915,6 @@ export PYTHON_PATH
 TEST_SHELL_PATH = $(SHELL_PATH)
 
 LIB_FILE = libgit.a
-REFTABLE_LIB = reftable/libreftable.a
 
 GENERATED_H += command-list.h
 GENERATED_H += config-list.h
@@ -1125,6 +1124,19 @@ LIB_OBJS += rebase.o
 LIB_OBJS += ref-filter.o
 LIB_OBJS += reflog-walk.o
 LIB_OBJS += reflog.o
+LIB_OBJS += reftable/basics.o
+LIB_OBJS += reftable/error.o
+LIB_OBJS += reftable/block.o
+LIB_OBJS += reftable/blocksource.o
+LIB_OBJS += reftable/iter.o
+LIB_OBJS += reftable/merged.o
+LIB_OBJS += reftable/pq.o
+LIB_OBJS += reftable/record.o
+LIB_OBJS += reftable/stack.o
+LIB_OBJS += reftable/system.o
+LIB_OBJS += reftable/table.o
+LIB_OBJS += reftable/tree.o
+LIB_OBJS += reftable/writer.o
 LIB_OBJS += refs.o
 LIB_OBJS += refs/debug.o
 LIB_OBJS += refs/files-backend.o
@@ -1390,8 +1402,7 @@ CLAR_TEST_OBJS += $(UNIT_TEST_DIR)/unit-test.o
 
 UNIT_TEST_OBJS += $(UNIT_TEST_DIR)/test-lib.o
 
-# reftable lib may depend on what is in libgit.a
-GITLIBS = common-main.o $(LIB_FILE) $(REFTABLE_LIB) $(LIB_FILE)
+GITLIBS = common-main.o $(LIB_FILE)
 EXTLIBS =
 
 GIT_USER_AGENT = git/$(GIT_VERSION)
@@ -2723,20 +2734,6 @@ reconfigure config.mak.autogen: config.status
 .PHONY: reconfigure # This is a convenience target.
 endif
 
-REFTABLE_OBJS += reftable/basics.o
-REFTABLE_OBJS += reftable/error.o
-REFTABLE_OBJS += reftable/block.o
-REFTABLE_OBJS += reftable/blocksource.o
-REFTABLE_OBJS += reftable/iter.o
-REFTABLE_OBJS += reftable/merged.o
-REFTABLE_OBJS += reftable/pq.o
-REFTABLE_OBJS += reftable/record.o
-REFTABLE_OBJS += reftable/stack.o
-REFTABLE_OBJS += reftable/system.o
-REFTABLE_OBJS += reftable/table.o
-REFTABLE_OBJS += reftable/tree.o
-REFTABLE_OBJS += reftable/writer.o
-
 TEST_OBJS := $(patsubst %$X,%.o,$(TEST_PROGRAMS)) $(patsubst %,t/helper/%,$(TEST_BUILTINS_OBJS))
 
 .PHONY: test-objs
@@ -2758,7 +2755,7 @@ OBJECTS += $(SCALAR_OBJS)
 OBJECTS += $(PROGRAM_OBJS)
 OBJECTS += $(TEST_OBJS)
 OBJECTS += $(FUZZ_OBJS)
-OBJECTS += $(REFTABLE_OBJS) $(REFTABLE_TEST_OBJS)
+OBJECTS += $(REFTABLE_TEST_OBJS)
 OBJECTS += $(UNIT_TEST_OBJS)
 OBJECTS += $(CLAR_TEST_OBJS)
 OBJECTS += $(patsubst %,$(UNIT_TEST_DIR)/%.o,$(UNIT_TEST_PROGRAMS))
@@ -2910,9 +2907,6 @@ scalar$X: scalar.o GIT-LDFLAGS $(GITLIBS)
 $(LIB_FILE): $(LIB_OBJS)
 	$(QUIET_AR)$(RM) $@ && $(AR) $(ARFLAGS) $@ $^
 
-$(REFTABLE_LIB): $(REFTABLE_OBJS)
-	$(QUIET_AR)$(RM) $@ && $(AR) $(ARFLAGS) $@ $^
-
 export DEFAULT_EDITOR DEFAULT_PAGER
 
 Documentation/GIT-EXCLUDED-PROGRAMS: FORCE
@@ -3751,7 +3745,7 @@ clean: profile-clean coverage-clean cocciclean
 	$(RM) git.rc git.res
 	$(RM) $(OBJECTS)
 	$(RM) headless-git.o
-	$(RM) $(LIB_FILE) $(REFTABLE_LIB)
+	$(RM) $(LIB_FILE)
 	$(RM) $(ALL_PROGRAMS) $(SCRIPT_LIB) $(BUILT_INS) $(OTHER_PROGRAMS)
 	$(RM) $(TEST_PROGRAMS)
 	$(RM) $(FUZZ_PROGRAMS)
@@ -3945,7 +3939,6 @@ endif
 
 LIBGIT_PUB_OBJS += contrib/libgit-sys/public_symbol_export.o
 LIBGIT_PUB_OBJS += libgit.a
-LIBGIT_PUB_OBJS += reftable/libreftable.a
 
 LIBGIT_PARTIAL_EXPORT = contrib/libgit-sys/partial_symbol_export.o
 
-- 
gitgitgadget


^ permalink raw reply related	[flat|nested] 100+ messages in thread

* [PATCH v2 05/18] doc: add a policy for using Rust
  2025-09-17  1:16 ` [PATCH v2 00/18] Introduce rust: In xdiff Ezekiel Newren via GitGitGadget
                     ` (3 preceding siblings ...)
  2025-09-17  1:16   ` [PATCH v2 04/18] make: merge reftable " Ezekiel Newren via GitGitGadget
@ 2025-09-17  1:16   ` brian m. carlson via GitGitGadget
  2025-09-17  1:16   ` [PATCH v2 06/18] BreakingChanges: announce Rust becoming mandatory Patrick Steinhardt via GitGitGadget
                     ` (15 subsequent siblings)
  20 siblings, 0 replies; 100+ messages in thread
From: brian m. carlson via GitGitGadget @ 2025-09-17  1:16 UTC (permalink / raw)
  To: git; +Cc: Ezekiel Newren, brian m. carlson

From: "brian m. carlson" <sandals@crustytoothpaste.net>

Git has historically been written primarily in C, with some shell and
Perl.  However, C is not memory safe, which makes it more likely that
security vulnerabilities or other bugs will be introduced, and it is
also more verbose and less ergonomic than other, more modern languages.

One of the most common modern compiled languages which is easily
interoperable with C is Rust.  It is popular (the most admired language
on the 2024 Stack Overflow Developer Survey), efficient, portable, and
robust.

Introduce a document laying out the incremental introduction of Rust to
Git and provide a detailed rationale for doing so, including the points
above.  Propose a design for this approach that addresses the needs of
downstreams and distributors, as well as contributors.

Since we don't want to carry both a C and Rust version of code and want
to be able to add new features only in Rust, mention that Rust is a
required part of our platform support policy.

It should be noted that a recent discussion at the Berlin Git Merge
Contributor Summit found widespread support for the addition of Rust to
Git.  While of course not all contributors were represented, the
proposal appeared to have the support of a majority of active
contributors.

Signed-off-by: brian m. carlson <sandals@crustytoothpaste.net>
[en: Added some comments about types, and changed the recommondations
     about cbindgen, bindgen, rustix, libc.]
Signed-off-by: Ezekiel Newren <ezekielnewren@gmail.com>
---
 Documentation/Makefile                        |   1 +
 Documentation/technical/platform-support.adoc |   2 +
 Documentation/technical/rust-support.adoc     | 142 ++++++++++++++++++
 3 files changed, 145 insertions(+)
 create mode 100644 Documentation/technical/rust-support.adoc

diff --git a/Documentation/Makefile b/Documentation/Makefile
index df2ce187eb..5e7821db3c 100644
--- a/Documentation/Makefile
+++ b/Documentation/Makefile
@@ -127,6 +127,7 @@ TECH_DOCS += technical/parallel-checkout
 TECH_DOCS += technical/partial-clone
 TECH_DOCS += technical/platform-support
 TECH_DOCS += technical/racy-git
+TECH_DOCS += technical/rust-support
 TECH_DOCS += technical/reftable
 TECH_DOCS += technical/scalar
 TECH_DOCS += technical/send-pack-pipeline
diff --git a/Documentation/technical/platform-support.adoc b/Documentation/technical/platform-support.adoc
index 0a2fb28d62..dc71672dcb 100644
--- a/Documentation/technical/platform-support.adoc
+++ b/Documentation/technical/platform-support.adoc
@@ -33,6 +33,8 @@ meet the following minimum requirements:
 
 * Has active security support (taking security releases of dependencies, etc)
 
+* Supports Rust and the toolchain version specified in link:rust-support.adoc[].
+
 These requirements are a starting point, and not sufficient on their own for the
 Git community to be enthusiastic about supporting your platform. Maintainers of
 platforms which do meet these requirements can follow the steps below to make it
diff --git a/Documentation/technical/rust-support.adoc b/Documentation/technical/rust-support.adoc
new file mode 100644
index 0000000000..5b856bdee6
--- /dev/null
+++ b/Documentation/technical/rust-support.adoc
@@ -0,0 +1,142 @@
+Usage of Rust in Git
+====================
+
+Objective
+---------
+Introduce Rust into Git incrementally to improve security and maintainability.
+
+Background
+----------
+Git has historically been written primarily in C, with some portions in shell,
+Perl, or other languages.  At the time it was originally written, this was
+important for portability and was a logical choice for software development.
+
+:0: link:https://security.googleblog.com/2024/09/eliminating-memory-safety-vulnerabilities-Android.html
+:1: link:https://www.cisa.gov/resources-tools/resources/product-security-bad-practices
+
+However, as time has progressed, we've seen an increased concern with memory
+safety vulnerabilities and the development of newer languages, such as Rust,
+that substantially limit or eliminate this class of vulnerabilities.
+Development in a variety of projects has found that memory safety
+vulnerabilities constitute about 70% of vulnerabilities of software in
+languages that are not memory safe.  For instance, {0}[one survey of Android]
+found that memory safety vulnerabilities decreased from 76% to 24% over six
+years due to an increase in memory safe code.  Similarly, the U.S. government
+is {1}[proposing to classify development in memory unsafe languages as a
+Product Security Bad Practice"].
+
+These risks are even more substantial when we consider the fact that Git is a
+network-facing service.  Many organizations run Git servers internally or use a
+cloud-based forge, and the risk of accidental exposure or compromise of user
+data is substantial.  It's important to ensure that Git, whether it's used
+locally or remotely, is robustly secure.
+
+In addition, C is a difficult language to write well and concisely.  While it
+is of course possible to do anything with C, it lacks built-in support for
+niceties found in modern languages, such as hash tables, generics, typed
+errors, and automatic destruction, and most modern language offer shorter, more
+ergonomic syntax for expressing code.  This is valuable functionality that can
+allow Git to be developed more rapidly, more easily, by more developers of a
+variety of levels, and with more confidence in the correctness of the code.
+
+For these reasons, adding Rust to Git is a sensible and prudent move that will
+allow us to improve the quality of the code and potentially attract new developers.
+
+Goals
+-----
+1. Git continues to build, run, and pass tests on a wide variety of operating
+   systems and architectures.
+2. Transition from C to Rust is incremental; that is, code can be ported as it
+   is convenient and Git does not need to transition all at once.
+3. Git continues to support older operating systems in conformance with the
+   platform support policy.
+
+Non-Goals
+---------
+1. Support for every possible operating system and architecture.  Git already
+   has a platform support policy which defines what is supported and we already
+   exclude some operating systems for various reasons (e.g., lacking enough POSIX
+   tools to pass the test suite).
+2. Implementing C-only versions of Rust code or compiling a C-only Git.  This
+   would be difficult to maintain and would not offer the ergonomic benefits we
+   desire.
+
+Design
+------
+Git will adopt Rust incrementally.  This transition will start with the
+creation of a static library that can be linked into the existing Git binaries.
+At some point, we may wish to expose a dynamic library and compile the Git
+binaries themselves using Rust.  Using an incremental approach allows us to
+determine as we go along how to structure our code in the best way for the
+project and avoids the need to make hard, potentially disruptive, transitions
+caused by porting a binary wholesale from one language to another that might
+introduce bugs.
+
+Crates like libc or rustix define types like c_long, but in ways that are not
+safe across platforms.
+From https://docs.rs/rustix/latest/rustix/ffi/type.c_long.html:
+
+    This type will always be i32 or i64.  Most notably, many Linux-based
+    systems assume an i64, but Windows assumes i32.  The C standard technically
+    only requires that this type be a signed integer that is at least 32 bits
+    and at least the size of an int, although in practice, no system would
+    have a long that is neither an i32 nor i64.
+
+Also, note that other locations, such as
+https://docs.rs/libc/latest/libc/type.c_long.html, just hardcode c_long as i64
+even though C may mean i32 on some platforms.
+
+As such, using the c_long type would give us portability issues, and
+perpetuate some of the bugs git has faced across platforms.  Avoid using C's
+types (long, unsigned, char, etc.), and switch to unambiguous types (e.g. i32
+or i64) before trying to make C and Rust interoperate.
+
+Crates like libc and rustix may have also traditionally aided interoperability
+with older versions of Rust (e.g.  when worrying about stat[64] system calls),
+but the Rust standard library in newer versions of Rust handle these concerns
+in a platform agnostic way.  There may arise cases where we need to consider
+these crates, but for now we omit them.
+
+Tools like bindgen and cbindgen create C-styled unsafe Rust code rather than
+idiomatic Rust; where possible, we prefer to switch to idiomatic Rust.
+However, we may use bindgen and cbindgen to share existing Git types as an
+interim step.
+
+Rust upstream releases every six weeks and only supports the latest stable
+release.  While it is nice that upstream is active, we would like our software
+releases to have a lifespan exceeding six weeks.  To allow compiling our code
+on a variety of systems, we will support the version of Rust in Debian stable,
+plus, for a year after a new Debian stable is released, the version in Debian
+oldstable.
+
+This provides an approximately three-year lifespan of support for a Rust
+release and allows us to support a variety of operating systems and
+architectures, including those for which Rust upstream does not build binaries.
+Debian stable is the benchmark distribution used by many Rust projects when
+determining supported Rust versions, and it is an extremely portable and
+popular free software operating system that is available to the public at no
+charge, which makes it a sensible choice for us as well.
+
+We may change this policy if the Rust project issues long-term support releases
+or the Rust community and distributors agree on releases to target as if they
+were long-term support releases.
+
+This version support policy necessitates that we be very careful about the
+dependencies we include, since many Rust projects support only the latest
+stable version.  However, we typically have been careful about dependencies in
+the first place, so this should not be a major departure from existing policy,
+although it may be a change for some existing Rust developers.
+
+We will avoid including the `Cargo.lock` file in the repository and instead
+specify minimum dependency versions in the `Cargo.toml` file.  We want to allow
+people to use newer versions of dependencies if necessary to support newer
+platforms without needing to force upgrades of dependencies on all users, and
+it provides additional flexibility for distribution maintainers.
+
+We do not plan to support beta or nightly versions of the Rust compiler.  These
+versions may change rapidly and especially parts of the toolchain such as
+Clippy, the lint tool, can have false positives or add additional warnings with
+too great of a frequency to be supportable by the project.  However, we do plan
+to support alternate compilers, such as the rust_codegen_gcc backend and gccrs
+when they are stable and support our desired release versions.  This will
+provide greater support for more operating systems and architectures.
-- 
gitgitgadget


^ permalink raw reply related	[flat|nested] 100+ messages in thread

* [PATCH v2 06/18] BreakingChanges: announce Rust becoming mandatory
  2025-09-17  1:16 ` [PATCH v2 00/18] Introduce rust: In xdiff Ezekiel Newren via GitGitGadget
                     ` (4 preceding siblings ...)
  2025-09-17  1:16   ` [PATCH v2 05/18] doc: add a policy for using Rust brian m. carlson via GitGitGadget
@ 2025-09-17  1:16   ` Patrick Steinhardt via GitGitGadget
  2025-09-17  1:16   ` [PATCH v2 07/18] build: introduce rust Ezekiel Newren via GitGitGadget
                     ` (14 subsequent siblings)
  20 siblings, 0 replies; 100+ messages in thread
From: Patrick Steinhardt via GitGitGadget @ 2025-09-17  1:16 UTC (permalink / raw)
  To: git; +Cc: Ezekiel Newren, Patrick Steinhardt

From: Patrick Steinhardt <ps@pks.im>

Over the last couple of years the appetite for bringing Rust into the
codebase has grown significantly across the developer base. Introducing
Rust is a major change though and has ramifications for the whole
ecosystem:

  - Some platforms have a Rust toolchain available, but have not yet
    integrated it into their build infrastructure.

  - Some platforms don't have any support for Rust at all.

  - Some platforms may have to figure out how to fit Rust into their
    bootstrapping sequence.

Due to this, and given that Git is a critical piece of infrastructure
for the whole industry, we cannot just introduce such a heavyweight
dependency without doing our due diligence.

Instead, preceding commits have introduced a test balloon into our build
infrastructure that convert one tiny subsystem to use Rust. For now,
using Rust to build that subsystem is entirely optional -- if no Rust
support is available, we continue to use the C implementation. This test
balloon has the intention to give distributions time and let them ease
into our adoption of Rust.

Having multiple implementations of the same subsystem is not sustainable
though, and the plan is to eventually be able to use Rust freely all
across our codebase. As such, there is the intent to make Rust become a
mandatory part of our build process.

Add an announcement to our breaking changes that Rust will become
mandatory in Git 3.0. A (very careful and non-binding) estimate might be
that this major release might be released in the second half of next
year, which should give distributors enough time to prepare for the
change.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Ezekiel Newren <ezekielnewren@gmail.com>
---
 Documentation/BreakingChanges.adoc | 35 ++++++++++++++++++++++++++++++
 1 file changed, 35 insertions(+)

diff --git a/Documentation/BreakingChanges.adoc b/Documentation/BreakingChanges.adoc
index f8d2eba061..56bbd5699e 100644
--- a/Documentation/BreakingChanges.adoc
+++ b/Documentation/BreakingChanges.adoc
@@ -165,6 +165,41 @@ A prerequisite for this change is that the ecosystem is ready to support the
 "reftable" format. Most importantly, alternative implementations of Git like
 JGit, libgit2 and Gitoxide need to support it.
 
+* Git will require Rust as a mandatory part of the build process. While Git
+  already started to adopt Rust in Git 2.49, all parts written in Rust are
+  optional for the time being. This includes:
++
+  ** Subsystems that have an alternative implementation in Rust to test
+     interoperability between our C and Rust codebase.
+  ** Newly written features that are not mission critical for a fully functional
+     Git client.
++
+These changes are meant as test balloons to allow distributors of Git to prepare
+for Rust becoming a mandatory part of the build process. There will be multiple
+milestones for the introduction of Rust:
++
+--
+1. In Git 2.52, both build systems will default-enable support for Rust.
+   Consequently, builds will break by default if Rust is not available on the
+   build host. The use of Rust can still be explicitly disabled via build
+   flags.
+2. In Git 3.0, the build options will be removed and support for Rust is
+   mandatory.
+--
++
+Disable building with Rust:
+Meson: `meson configure -Dwith_rust=false`.
+Makefile: `make WITH_RUST=false`,
++
+The Git project will declare the last version before Git 3.0 to be a long-term
+support release. This long-term release will receive important bug fixes for at
+least four release cycles and security fixes for six release cycles. The Git
+project will hand over maintainership of the long-term release to distributors
+in case they need to extend the life of that long-term release even further. In
+that case, the backporting process will be handled by these distributors, but
+the backported patches will be reviewed on the mailing list and pulled in by the
+Git maintainer.
+
 === Removals
 
 * Support for grafting commits has long been superseded by git-replace(1).
-- 
gitgitgadget


^ permalink raw reply related	[flat|nested] 100+ messages in thread

* [PATCH v2 07/18] build: introduce rust
  2025-09-17  1:16 ` [PATCH v2 00/18] Introduce rust: In xdiff Ezekiel Newren via GitGitGadget
                     ` (5 preceding siblings ...)
  2025-09-17  1:16   ` [PATCH v2 06/18] BreakingChanges: announce Rust becoming mandatory Patrick Steinhardt via GitGitGadget
@ 2025-09-17  1:16   ` Ezekiel Newren via GitGitGadget
  2025-09-17  8:26     ` Eric Sunshine
  2025-09-17  1:16   ` [PATCH v2 08/18] help: report on whether or not Rust is enabled Patrick Steinhardt via GitGitGadget
                     ` (13 subsequent siblings)
  20 siblings, 1 reply; 100+ messages in thread
From: Ezekiel Newren via GitGitGadget @ 2025-09-17  1:16 UTC (permalink / raw)
  To: git; +Cc: Ezekiel Newren, Ezekiel Newren

From: Ezekiel Newren <ezekielnewren@gmail.com>

Upcoming patches will simplify xdiff, while also porting parts of it to
Rust. In preparation, add some stubs and setup the Rust build. For now,
it is easier to let cargo build rust and have make or meson merely link
against the static library that cargo builds. In line with ongoing
libification efforts, use multiple crates to allow more modularity on
the Rust side. The xdiff and ivec crates are the focal point for this
series.

cbindgen will be used to generate header files for each Rust crate for
C interoperability.

Signed-off-by: Ezekiel Newren <ezekielnewren@gmail.com>
---
 .gitignore        |  3 ++
 Makefile          | 81 +++++++++++++++++++++++++++++++++++++++--------
 build_rust.sh     | 60 +++++++++++++++++++++++++++++++++++
 meson.build       | 72 ++++++++++++++++++++++++++++++++++++-----
 meson_options.txt |  2 ++
 rust/Cargo.toml   |  3 ++
 6 files changed, 200 insertions(+), 21 deletions(-)
 create mode 100755 build_rust.sh
 create mode 100644 rust/Cargo.toml

diff --git a/.gitignore b/.gitignore
index 04c444404e..24292eb473 100644
--- a/.gitignore
+++ b/.gitignore
@@ -254,3 +254,6 @@ Release/
 /contrib/buildsystems/out
 /contrib/libgit-rs/target
 /contrib/libgit-sys/target
+.idea/
+/rust/target/
+/rust/Cargo.lock
diff --git a/Makefile b/Makefile
index ffb898b611..ef55a8183c 100644
--- a/Makefile
+++ b/Makefile
@@ -483,6 +483,14 @@ include shared.mak
 # Define LIBPCREDIR=/foo/bar if your PCRE header and library files are
 # in /foo/bar/include and /foo/bar/lib directories.
 #
+# == Optional Rust support ==
+#
+# Define WITH_RUST=false if you don't want to include features and subsystems
+# written in Rust into Git. For now, Rust is still an optional feature of the
+# build process. With Git 3.0 though, Rust will be mandatory.
+#
+# Building Rust code requires Cargo.
+#
 # == SHA-1 and SHA-256 defines ==
 #
 # === SHA-1 backend ===
@@ -916,6 +924,51 @@ TEST_SHELL_PATH = $(SHELL_PATH)
 
 LIB_FILE = libgit.a
 
+EXTLIBS =
+
+GIT_BUILD_DIR := $(CURDIR)
+export GIT_BUILD_DIR
+
+RUST_CRATES :=
+.PHONY: compile_rust rustclean
+
+WITH_RUST ?= true
+ifeq ($(WITH_RUST),true)
+
+ifeq ($(DEBUG), 1)
+	RUST_BUILD_MODE := debug
+else
+	RUST_BUILD_MODE := release
+endif
+
+RUST_TARGET_DIR := $(GIT_BUILD_DIR)/$(RUST_BUILD_MODE)
+
+RUST_LIBS := $(foreach c,$(RUST_CRATES),$(GIT_BUILD_DIR)/lib$(c).a)
+
+EXTLIBS += $(RUST_LIBS)
+
+compile_rust: $(RUST_LIBS)
+rustclean:
+	$(RM) $(RUST_LIBS)
+	cd rust && cargo clean
+
+$(GIT_BUILD_DIR)/lib%.a:
+	./build_rust.sh $(GIT_BUILD_DIR) $(RUST_BUILD_MODE) $*
+
+UNAME_S := $(shell uname -s)
+ifeq ($(UNAME_S),Linux)
+	EXTLIBS += -ldl
+endif
+
+else ifeq ($(WITH_RUST),false)
+compile_rust:
+	:
+rustclean:
+	:
+else
+$(error 'WITH_RUST' must be true or false)
+endif
+
 GENERATED_H += command-list.h
 GENERATED_H += config-list.h
 GENERATED_H += hook-list.h
@@ -1403,7 +1456,6 @@ CLAR_TEST_OBJS += $(UNIT_TEST_DIR)/unit-test.o
 UNIT_TEST_OBJS += $(UNIT_TEST_DIR)/test-lib.o
 
 GITLIBS = common-main.o $(LIB_FILE)
-EXTLIBS =
 
 GIT_USER_AGENT = git/$(GIT_VERSION)
 
@@ -1421,6 +1473,9 @@ CC_LD_DYNPATH = -Wl,-rpath,
 BASIC_CFLAGS = -I. -fPIE
 BASIC_LDFLAGS =
 
+ifeq ($(WITH_RUST),true)
+BASIC_CFLAGS += -DWITH_RUST
+endif
 # library flags
 ARFLAGS = rcs
 PTHREAD_CFLAGS =
@@ -2551,7 +2606,7 @@ git.sp git.s git.o: EXTRA_CPPFLAGS = \
 	'-DGIT_MAN_PATH="$(mandir_relative_SQ)"' \
 	'-DGIT_INFO_PATH="$(infodir_relative_SQ)"'
 
-git$X: git.o GIT-LDFLAGS $(BUILTIN_OBJS) $(GITLIBS)
+git$X: git.o GIT-LDFLAGS $(BUILTIN_OBJS) $(GITLIBS) compile_rust
 	$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) \
 		$(filter %.o,$^) $(LIBS)
 
@@ -2876,17 +2931,17 @@ headless-git.o: compat/win32/headless.c GIT-CFLAGS
 headless-git$X: headless-git.o git.res GIT-LDFLAGS
 	$(QUIET_LINK)$(CC) $(ALL_CFLAGS) $(ALL_LDFLAGS) -mwindows -o $@ $< git.res
 
-git-%$X: %.o GIT-LDFLAGS $(GITLIBS)
+git-%$X: %.o GIT-LDFLAGS $(GITLIBS) compile_rust
 	$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(LIBS)
 
-git-imap-send$X: imap-send.o $(IMAP_SEND_BUILDDEPS) GIT-LDFLAGS $(GITLIBS)
+git-imap-send$X: imap-send.o $(IMAP_SEND_BUILDDEPS) GIT-LDFLAGS $(GITLIBS) compile_rust
 	$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
 		$(IMAP_SEND_LDFLAGS) $(LIBS)
 
-git-http-fetch$X: http.o http-walker.o http-fetch.o GIT-LDFLAGS $(GITLIBS)
+git-http-fetch$X: http.o http-walker.o http-fetch.o GIT-LDFLAGS $(GITLIBS) compile_rust
 	$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
 		$(CURL_LIBCURL) $(LIBS)
-git-http-push$X: http.o http-push.o GIT-LDFLAGS $(GITLIBS)
+git-http-push$X: http.o http-push.o GIT-LDFLAGS $(GITLIBS) compile_rust
 	$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
 		$(CURL_LIBCURL) $(EXPAT_LIBEXPAT) $(LIBS)
 
@@ -2896,11 +2951,11 @@ $(REMOTE_CURL_ALIASES): $(REMOTE_CURL_PRIMARY)
 	ln -s $< $@ 2>/dev/null || \
 	cp $< $@
 
-$(REMOTE_CURL_PRIMARY): remote-curl.o http.o http-walker.o GIT-LDFLAGS $(GITLIBS)
+$(REMOTE_CURL_PRIMARY): remote-curl.o http.o http-walker.o GIT-LDFLAGS $(GITLIBS) compile_rust
 	$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
 		$(CURL_LIBCURL) $(EXPAT_LIBEXPAT) $(LIBS)
 
-scalar$X: scalar.o GIT-LDFLAGS $(GITLIBS)
+scalar$X: scalar.o GIT-LDFLAGS $(GITLIBS) compile_rust
 	$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) \
 		$(filter %.o,$^) $(LIBS)
 
@@ -3273,7 +3328,7 @@ perf: all
 
 t/helper/test-tool$X: $(patsubst %,t/helper/%,$(TEST_BUILTINS_OBJS)) $(UNIT_TEST_DIR)/test-lib.o
 
-t/helper/test-%$X: t/helper/test-%.o GIT-LDFLAGS $(GITLIBS)
+t/helper/test-%$X: t/helper/test-%.o GIT-LDFLAGS $(GITLIBS) compile_rust
 	$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(filter %.a,$^) $(LIBS)
 
 check-sha1:: t/helper/test-tool$X
@@ -3738,7 +3793,7 @@ cocciclean:
 	$(RM) -r .build/contrib/coccinelle
 	$(RM) contrib/coccinelle/*.cocci.patch
 
-clean: profile-clean coverage-clean cocciclean
+clean: profile-clean coverage-clean cocciclean rustclean
 	$(RM) -r .build $(UNIT_TEST_BIN)
 	$(RM) GIT-TEST-SUITES
 	$(RM) po/git.pot po/git-core.pot
@@ -3893,13 +3948,13 @@ FUZZ_CXXFLAGS ?= $(ALL_CFLAGS)
 .PHONY: fuzz-all
 fuzz-all: $(FUZZ_PROGRAMS)
 
-$(FUZZ_PROGRAMS): %: %.o oss-fuzz/dummy-cmd-main.o $(GITLIBS) GIT-LDFLAGS
+$(FUZZ_PROGRAMS): %: %.o oss-fuzz/dummy-cmd-main.o $(GITLIBS) GIT-LDFLAGS compile_rust
 	$(QUIET_LINK)$(FUZZ_CXX) $(FUZZ_CXXFLAGS) -o $@ $(ALL_LDFLAGS) \
 		-Wl,--allow-multiple-definition \
 		$(filter %.o,$^) $(filter %.a,$^) $(LIBS) $(LIB_FUZZING_ENGINE)
 
 $(UNIT_TEST_PROGS): $(UNIT_TEST_BIN)/%$X: $(UNIT_TEST_DIR)/%.o $(UNIT_TEST_OBJS) \
-	$(GITLIBS) GIT-LDFLAGS
+	$(GITLIBS) GIT-LDFLAGS compile_rust
 	$(call mkdir_p_parent_template)
 	$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) \
 		$(filter %.o,$^) $(filter %.a,$^) $(LIBS)
@@ -3918,7 +3973,7 @@ $(UNIT_TEST_DIR)/clar.suite: $(UNIT_TEST_DIR)/clar-decls.h $(UNIT_TEST_DIR)/gene
 $(UNIT_TEST_DIR)/clar/clar.o: $(UNIT_TEST_DIR)/clar.suite
 $(CLAR_TEST_OBJS): $(UNIT_TEST_DIR)/clar-decls.h
 $(CLAR_TEST_OBJS): EXTRA_CPPFLAGS = -I$(UNIT_TEST_DIR)
-$(CLAR_TEST_PROG): $(UNIT_TEST_DIR)/clar.suite $(CLAR_TEST_OBJS) $(GITLIBS) GIT-LDFLAGS
+$(CLAR_TEST_PROG): $(UNIT_TEST_DIR)/clar.suite $(CLAR_TEST_OBJS) $(GITLIBS) GIT-LDFLAGS compile_rust
 	$(call mkdir_p_parent_template)
 	$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(LIBS)
 
diff --git a/build_rust.sh b/build_rust.sh
new file mode 100755
index 0000000000..b020f2d37a
--- /dev/null
+++ b/build_rust.sh
@@ -0,0 +1,60 @@
+#!/bin/sh
+
+rustc -vV || exit $?
+cargo --version || exit $?
+
+dir_git_root=${0%/*}
+dir_build=$1
+rust_build_profile=$2
+crate=$3
+
+dir_rust=$dir_git_root/rust
+
+if [ "$dir_git_root" = "" ]; then
+  echo "did not specify the directory for the root of git"
+  exit 1
+fi
+
+if [ "$dir_build" = "" ]; then
+  echo "did not specify the build directory"
+  exit 1
+fi
+
+if [ "$rust_build_profile" = "" ]; then
+  echo "did not specify the rust_build_profile"
+  exit 1
+fi
+
+if [ "$rust_build_profile" = "release" ]; then
+  rust_args="--release"
+  export RUSTFLAGS=''
+elif [ "$rust_build_profile" = "debug" ]; then
+  rust_args=""
+  export RUSTFLAGS='-C debuginfo=2 -C opt-level=1 -C force-frame-pointers=yes'
+else
+  echo "illegal rust_build_profile value $rust_build_profile"
+  exit 1
+fi
+
+libfile="lib${crate}.a"
+if rustup show active-toolchain | grep windows-msvc; then
+  libfile="${crate}.lib"
+  PATH="$(echo $PATH | tr ':' '\n' | grep -Ev "^(/mingw64/bin|/usr/bin)$" | paste -sd: -):/mingw64/bin:/usr/bin"
+fi
+
+CARGO_TARGET_DIR=$dir_git_root/.build/rust/$crate
+export CARGO_TARGET_DIR
+
+cd $dir_rust && cargo clean && pwd && USE_LINKING="false" cargo build -p $crate $rust_args; cd $dir_git_root
+
+src=$CARGO_TARGET_DIR/$rust_build_profile/$libfile
+dst=$dir_build/$libfile
+
+if [ ! -f $src ]; then
+  echo >&2 "::error:: cannot find path of static library $src is not a file or does not exist"
+  exit 5
+fi
+
+rm $dst 2>/dev/null
+echo mv $src $dst
+mv $src $dst
diff --git a/meson.build b/meson.build
index 5dd299b496..cbe1afac83 100644
--- a/meson.build
+++ b/meson.build
@@ -267,6 +267,55 @@ version_gen_environment.set('GIT_DATE', get_option('build_date'))
 version_gen_environment.set('GIT_USER_AGENT', get_option('user_agent'))
 version_gen_environment.set('GIT_VERSION', get_option('version'))
 
+rust_crates = []
+
+rust_builds = []
+with_rust = get_option('with_rust')
+if with_rust
+  rustc = find_program('rustc', required : false)
+  cargo = find_program('cargo', required : false)
+
+  if not rustc.found() or not cargo.found()
+    error('Rust toolchain not found. Reconfigure with -Dwith_rust=false')
+  endif
+
+  if get_option('optimization') in ['2', '3', 's', 'z']
+    rust_build_profile = 'release'
+  else
+    rust_build_profile = 'debug'
+  endif
+
+  # Run `rustup show active-toolchain` and capture output
+  rustup_out = run_command('rustup', 'show', 'active-toolchain',
+                           check: true).stdout().strip()
+
+  rust_environment = script_environment
+
+  foreach crate : rust_crates
+    if rustup_out.contains('windows-msvc')
+      libfile = crate + '.lib'
+    else
+      libfile = 'lib' + crate + '.a'
+    endif
+
+    rust_builds += custom_target(
+      'rust_build_'+crate,
+      output: libfile,
+      build_by_default: true,
+      build_always_stale: true,
+      command: [
+        meson.project_source_root() / 'build_rust.sh',
+        meson.current_build_dir(), rust_build_profile, crate,
+      ],
+      install: false,
+      env: rust_environment,
+    )
+  endforeach
+else
+  message('Rust components disabled (-Dwith_rust=false)')
+endif
+
+
 compiler = meson.get_compiler('c')
 
 libgit_sources = [
@@ -774,6 +823,10 @@ libgit_c_args = [
   '-DSHELL_PATH="' + fs.as_posix(target_shell.full_path()) + '"',
 ]
 
+if with_rust
+  libgit_c_args += '-DWITH_RUST'
+endif
+
 system_attributes = get_option('gitattributes')
 if system_attributes != ''
   libgit_c_args += '-DETC_GITATTRIBUTES="' + system_attributes + '"'
@@ -1266,6 +1319,7 @@ elif host_machine.system() == 'windows'
   ]
 
   libgit_dependencies += compiler.find_library('ntdll')
+  libgit_dependencies += compiler.find_library('userenv')
   libgit_include_directories += 'compat/win32'
   if compiler.get_id() == 'msvc'
     libgit_include_directories += 'compat/vcbuild/include'
@@ -1702,14 +1756,16 @@ version_def_h = custom_target(
 libgit_sources += version_def_h
 
 libgit = declare_dependency(
-  link_with: static_library('git',
-    sources: libgit_sources,
-    c_args: libgit_c_args + [
-      '-DGIT_VERSION_H="' + version_def_h.full_path() + '"',
-    ],
-    dependencies: libgit_dependencies,
-    include_directories: libgit_include_directories,
-  ),
+  link_with: [
+    static_library('git',
+      sources: libgit_sources,
+      c_args: libgit_c_args + [
+        '-DGIT_VERSION_H="' + version_def_h.full_path() + '"',
+      ],
+      dependencies: libgit_dependencies,
+      include_directories: libgit_include_directories,
+    ),
+  ] + rust_builds,
   compile_args: libgit_c_args,
   dependencies: libgit_dependencies,
   include_directories: libgit_include_directories,
diff --git a/meson_options.txt b/meson_options.txt
index 1668f260a1..362e928c79 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -69,6 +69,8 @@ option('zlib_backend', type: 'combo', choices: ['auto', 'zlib', 'zlib-ng'], valu
   description: 'The backend used for compressing objects and other data.')
 
 # Build tweaks.
+option('with_rust', type: 'boolean', value: true,
+       description: 'Enable building with Rust, true by default.')
 option('breaking_changes', type: 'boolean', value: false,
   description: 'Enable upcoming breaking changes.')
 option('macos_use_homebrew_gettext', type: 'boolean', value: true,
diff --git a/rust/Cargo.toml b/rust/Cargo.toml
new file mode 100644
index 0000000000..9bc2f769fb
--- /dev/null
+++ b/rust/Cargo.toml
@@ -0,0 +1,3 @@
+[workspace]
+members = []
+resolver = "2"
-- 
gitgitgadget


^ permalink raw reply related	[flat|nested] 100+ messages in thread

* [PATCH v2 08/18] help: report on whether or not Rust is enabled
  2025-09-17  1:16 ` [PATCH v2 00/18] Introduce rust: In xdiff Ezekiel Newren via GitGitGadget
                     ` (6 preceding siblings ...)
  2025-09-17  1:16   ` [PATCH v2 07/18] build: introduce rust Ezekiel Newren via GitGitGadget
@ 2025-09-17  1:16   ` Patrick Steinhardt via GitGitGadget
  2025-09-17  1:16   ` [PATCH v2 09/18] github workflows: install rust Ezekiel Newren via GitGitGadget
                     ` (12 subsequent siblings)
  20 siblings, 0 replies; 100+ messages in thread
From: Patrick Steinhardt via GitGitGadget @ 2025-09-17  1:16 UTC (permalink / raw)
  To: git; +Cc: Ezekiel Newren, Patrick Steinhardt

From: Patrick Steinhardt <ps@pks.im>

We're about to introduce support for Rust into the core of Git, where
some (trivial) subsystems are converted to Rust. These subsystems will
also retain a C implementation though as Rust is not yet mandatory.
Consequently, it now becomes possible for a Git version to have bugs
that are specific to whether or not it is built with Rust support
overall.

Expose information about whether or not Git was built with Rust via our
build info. This means that both `git version --build-options`, but also
`git bugreport` will now expose that bit of information. Hopefully, this
should make it easier for us to discover any Rust-specific issues.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Ezekiel Newren <ezekielnewren@gmail.com>
---
 help.c | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/help.c b/help.c
index bb20498cfd..5854dd4a7e 100644
--- a/help.c
+++ b/help.c
@@ -791,6 +791,12 @@ void get_version_info(struct strbuf *buf, int show_build_options)
 		strbuf_addf(buf, "shell-path: %s\n", SHELL_PATH);
 		/* NEEDSWORK: also save and output GIT-BUILD_OPTIONS? */
 
+#if defined WITH_RUST
+		strbuf_addstr(buf, "rust: enabled\n");
+#else
+		strbuf_addstr(buf, "rust: disabled\n");
+#endif
+
 		if (fsmonitor_ipc__is_supported())
 			strbuf_addstr(buf, "feature: fsmonitor--daemon\n");
 #if defined LIBCURL_VERSION
-- 
gitgitgadget


^ permalink raw reply related	[flat|nested] 100+ messages in thread

* [PATCH v2 09/18] github workflows: install rust
  2025-09-17  1:16 ` [PATCH v2 00/18] Introduce rust: In xdiff Ezekiel Newren via GitGitGadget
                     ` (7 preceding siblings ...)
  2025-09-17  1:16   ` [PATCH v2 08/18] help: report on whether or not Rust is enabled Patrick Steinhardt via GitGitGadget
@ 2025-09-17  1:16   ` Ezekiel Newren via GitGitGadget
  2025-09-17  8:01     ` Eric Sunshine
  2025-09-17  1:16   ` [PATCH v2 10/18] win+Meson: do allow linking with the Rust-built xdiff Johannes Schindelin via GitGitGadget
                     ` (11 subsequent siblings)
  20 siblings, 1 reply; 100+ messages in thread
From: Ezekiel Newren via GitGitGadget @ 2025-09-17  1:16 UTC (permalink / raw)
  To: git; +Cc: Ezekiel Newren, Ezekiel Newren

From: Ezekiel Newren <ezekielnewren@gmail.com>

Prefer using actions-rs/toolchain@v1 where possible to install rustup,
but for docker targets use a script to install rustup. Consolidate the
Rust toolchain definitions in main.yaml. Use install-rust-toolchain.sh
to ensure the correct toolchain is used. Five overrides are used in
main.yaml:

  * On Windows: Rust didn't resolve the bcrypt library on Windows
    correctly until version 1.78.0. Also since rustup mis-identifies
    the Rust toolchain, the Rust target triple must be set to
    x86_64-pc-windows-gnu for make (win build), and
    x86_64-pc-windows-msvc for meson (win+Meson build).
  * MSVC builds: Rearrange PATH to look in /mingw64/bin and /usr/bin
    last.
  * On musl: libc differences, such as ftruncate64 vs ftruncate, were
    not accounted for until Rust version 1.72.0. No older version of
    Rust will work on musl for our needs.
  * In a 32-bit docker container running on a 64-bit host, we need to
    override the Rust target triple. This is because rustup asks the
    kernel for the bitness of the system and it says 64, even though
    the container is 32-bit. This also allows us to remove the
    BITNESS environment variable in ci/lib.sh.

The logic for selecting library names was initially provided in a patch
from Johannes, but was reworked and squashed into this commit.

Helped-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>
Signed-off-by: Ezekiel Newren <ezekielnewren@gmail.com>
---
 .github/workflows/main.yml   | 62 +++++++++++++++++++++++++++++++++++-
 ci/install-dependencies.sh   | 14 ++++----
 ci/install-rust-toolchain.sh | 30 +++++++++++++++++
 ci/install-rustup.sh         | 25 +++++++++++++++
 ci/lib.sh                    |  1 +
 ci/make-test-artifacts.sh    |  9 ++++++
 ci/run-build-and-tests.sh    | 13 ++++++++
 7 files changed, 146 insertions(+), 8 deletions(-)
 create mode 100755 ci/install-rust-toolchain.sh
 create mode 100755 ci/install-rustup.sh

diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index d122e79415..e986684a2f 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -26,6 +26,13 @@ jobs:
     outputs:
       enabled: ${{ steps.check-ref.outputs.enabled }}${{ steps.skip-if-redundant.outputs.enabled }}
       skip_concurrent: ${{ steps.check-ref.outputs.skip_concurrent }}
+      rust_version_minimum: 1.63.0
+      rust_version_windows: 1.78.0
+      rust_version_musl: 1.72.0
+      ## the rust target is inferred by rustup unless specified
+      rust_target_windows_make: x86_64-pc-windows-gnu
+      rust_target_windows_meson: x86_64-pc-windows-msvc
+      rust_target_32bit_linux: i686-unknown-linux-gnu
     steps:
       - name: try to clone ci-config branch
         run: |
@@ -108,12 +115,28 @@ jobs:
     needs: ci-config
     if: needs.ci-config.outputs.enabled == 'yes'
     runs-on: windows-latest
+    env:
+      CARGO_HOME: "/c/Users/runneradmin/.cargo"
     concurrency:
       group: windows-build-${{ github.ref }}
       cancel-in-progress: ${{ needs.ci-config.outputs.skip_concurrent == 'yes' }}
     steps:
     - uses: actions/checkout@v4
     - uses: git-for-windows/setup-git-for-windows-sdk@v1
+      with:
+        flavor: full
+    - name: Install rustup via github actions
+      uses: actions-rs/toolchain@v1
+      with:
+        toolchain: stable
+        profile: minimal
+        override: false
+    - name: Install Rust toolchain
+      shell: bash
+      env:
+        RUST_VERSION: ${{ needs.ci-config.outputs.rust_version_windows }}
+        RUST_TARGET: ${{ needs.ci-config.outputs.rust_target_windows_make }}
+      run: ci/install-rust-toolchain.sh
     - name: build
       shell: bash
       env:
@@ -254,12 +277,26 @@ jobs:
     needs: ci-config
     if: needs.ci-config.outputs.enabled == 'yes'
     runs-on: windows-latest
+    env:
+      CARGO_HOME: "/c/Users/runneradmin/.cargo"
     concurrency:
       group: windows-meson-build-${{ github.ref }}
       cancel-in-progress: ${{ needs.ci-config.outputs.skip_concurrent == 'yes' }}
     steps:
     - uses: actions/checkout@v4
     - uses: actions/setup-python@v5
+    - name: Install rustup via github actions
+      uses: actions-rs/toolchain@v1
+      with:
+        toolchain: stable
+        profile: minimal
+        override: false
+    - name: Install Rust toolchain
+      shell: bash
+      env:
+        RUST_VERSION: ${{ needs.ci-config.outputs.rust_version_windows }}
+        RUST_TARGET: ${{ needs.ci-config.outputs.rust_target_windows_meson }}
+      run: ci/install-rust-toolchain.sh
     - name: Set up dependencies
       shell: pwsh
       run: pip install meson ninja
@@ -329,11 +366,24 @@ jobs:
       jobname: ${{matrix.vector.jobname}}
       CI_JOB_IMAGE: ${{matrix.vector.pool}}
       TEST_OUTPUT_DIRECTORY: ${{github.workspace}}/t
+      CARGO_HOME: "/Users/runner/.cargo"
     runs-on: ${{matrix.vector.pool}}
     steps:
     - uses: actions/checkout@v4
     - run: ci/install-dependencies.sh
-    - run: ci/run-build-and-tests.sh
+    - name: Install rustup via github actions
+      uses: actions-rs/toolchain@v1
+      with:
+        toolchain: stable
+        profile: minimal
+        override: false
+    - name: Install Rust toolchain
+      shell: bash
+      env:
+        RUST_VERSION: ${{ needs.ci-config.outputs.rust_version_minimum }}
+      run: ci/install-rust-toolchain.sh
+    - name: Run build and tests
+      run: ci/run-build-and-tests.sh
     - name: print test failures
       if: failure() && env.FAILED_TEST_ARTIFACTS != ''
       run: ci/print-test-failures.sh
@@ -393,9 +443,11 @@ jobs:
           cc: gcc
         - jobname: linux-musl-meson
           image: alpine:latest
+          rust_version_override: ${{ needs.ci-config.outputs.rust_version_musl }}
         # Supported until 2025-04-02.
         - jobname: linux32
           image: i386/ubuntu:focal
+          rust_target_override: ${{ needs.ci-config.outputs.rust_target_32bit_linux }}
         - jobname: pedantic
           image: fedora:latest
         # A RHEL 8 compatible distro.  Supported until 2029-05-31.
@@ -409,6 +461,7 @@ jobs:
       CC: ${{matrix.vector.cc}}
       CI_JOB_IMAGE: ${{matrix.vector.image}}
       CUSTOM_PATH: /custom
+      CARGO_HOME: /home/builder/.cargo
     runs-on: ubuntu-latest
     container: ${{matrix.vector.image}}
     steps:
@@ -433,6 +486,13 @@ jobs:
     - run: ci/install-dependencies.sh
     - run: useradd builder --create-home
     - run: chown -R builder .
+    - name: Install rustup via script
+      run: sudo --preserve-env --set-home --user=builder ci/install-rustup.sh
+    - name: Install Rust toolchain
+      env:
+        RUST_VERSION: ${{ matrix.vector.rust_version_override || needs.ci-config.outputs.rust_version_minimum }}
+        RUST_TARGET: ${{ matrix.vector.rust_target_override || '' }}
+      run: sudo --preserve-env --set-home --user=builder ci/install-rust-toolchain.sh
     - run: sudo --preserve-env --set-home --user=builder ci/run-build-and-tests.sh
     - name: print test failures
       if: failure() && env.FAILED_TEST_ARTIFACTS != ''
diff --git a/ci/install-dependencies.sh b/ci/install-dependencies.sh
index d061a47293..7801075821 100755
--- a/ci/install-dependencies.sh
+++ b/ci/install-dependencies.sh
@@ -24,14 +24,14 @@ fi
 
 case "$distro" in
 alpine-*)
-	apk add --update shadow sudo meson ninja-build gcc libc-dev curl-dev openssl-dev expat-dev gettext \
+	apk add --update shadow sudo meson ninja-build gcc libc-dev curl curl-dev openssl-dev expat-dev gettext \
 		zlib-ng-dev pcre2-dev python3 musl-libintl perl-utils ncurses \
 		apache2 apache2-http2 apache2-proxy apache2-ssl apache2-webdav apr-util-dbd_sqlite3 \
 		bash cvs gnupg perl-cgi perl-dbd-sqlite perl-io-tty >/dev/null
 	;;
 fedora-*|almalinux-*)
 	dnf -yq update >/dev/null &&
-	dnf -yq install shadow-utils sudo make gcc findutils diffutils perl python3 gawk gettext zlib-devel expat-devel openssl-devel curl-devel pcre2-devel >/dev/null
+	dnf -yq install shadow-utils sudo make gcc findutils diffutils perl python3 gawk gettext zlib-devel expat-devel openssl-devel curl curl-devel pcre2-devel >/dev/null
 	;;
 ubuntu-*|i386/ubuntu-*|debian-*)
 	# Required so that apt doesn't wait for user input on certain packages.
@@ -55,8 +55,8 @@ ubuntu-*|i386/ubuntu-*|debian-*)
 	sudo apt-get -q update
 	sudo apt-get -q -y install \
 		$LANGUAGES apache2 cvs cvsps git gnupg $SVN \
-		make libssl-dev libcurl4-openssl-dev libexpat-dev wget sudo default-jre \
-		tcl tk gettext zlib1g-dev perl-modules liberror-perl libauthen-sasl-perl \
+		make libssl-dev curl libcurl4-openssl-dev libexpat-dev wget sudo default-jre \
+		tcl tk gettext zlib1g zlib1g-dev perl-modules liberror-perl libauthen-sasl-perl \
 		libemail-valid-perl libio-pty-perl libio-socket-ssl-perl libnet-smtp-ssl-perl libdbd-sqlite3-perl libcgi-pm-perl \
 		libsecret-1-dev libpcre2-dev meson ninja-build pkg-config \
 		${CC_PACKAGE:-${CC:-gcc}} $PYTHON_PACKAGE
@@ -121,13 +121,13 @@ ClangFormat)
 	;;
 StaticAnalysis)
 	sudo apt-get -q update
-	sudo apt-get -q -y install coccinelle libcurl4-openssl-dev libssl-dev \
+	sudo apt-get -q -y install coccinelle curl libcurl4-openssl-dev libssl-dev \
 		libexpat-dev gettext make
 	;;
 sparse)
 	sudo apt-get -q update -q
-	sudo apt-get -q -y install libssl-dev libcurl4-openssl-dev \
-		libexpat-dev gettext zlib1g-dev sparse
+	sudo apt-get -q -y install libssl-dev curl libcurl4-openssl-dev \
+		libexpat-dev gettext zlib1g zlib1g-dev sparse
 	;;
 Documentation)
 	sudo apt-get -q update
diff --git a/ci/install-rust-toolchain.sh b/ci/install-rust-toolchain.sh
new file mode 100755
index 0000000000..06a29c4cfa
--- /dev/null
+++ b/ci/install-rust-toolchain.sh
@@ -0,0 +1,30 @@
+#!/bin/sh
+
+if [ "$CARGO_HOME" = "" ]; then
+  echo >&2 "::error:: CARGO_HOME is not set"
+  exit 2
+fi
+export PATH="$CARGO_HOME/bin:$PATH"
+rustup -vV || exit $?
+
+## Enforce the correct Rust toolchain
+rustup override unset || true
+
+## install a specific version of rust
+if [ "$RUST_TARGET" != "" ]; then
+  rustup default --force-non-host "$RUST_VERSION-$RUST_TARGET" || exit $?
+else
+  rustup default "$RUST_VERSION" || exit $?
+fi
+
+rustc -vV || exit $?
+
+RE_RUST_TARGET="$RUST_TARGET"
+if [ "$RUST_TARGET" = "" ]; then
+  RE_RUST_TARGET="[^ ]+"
+fi
+
+if ! rustup show active-toolchain | grep -E "^$RUST_VERSION-$RE_RUST_TARGET \(default\)$"; then
+  echo >&2 "::error:: wrong Rust toolchain, active-toolchain: $(rustup show active-toolchain)"
+  exit 3
+fi
diff --git a/ci/install-rustup.sh b/ci/install-rustup.sh
new file mode 100755
index 0000000000..0036231aee
--- /dev/null
+++ b/ci/install-rustup.sh
@@ -0,0 +1,25 @@
+#!/bin/sh
+
+## github workflows actions-rs/toolchain@v1 doesn't work for docker
+## targets. This script should only be used if the ci pipeline
+## doesn't support installing rust on a particular target.
+
+if [ "$(id -u)" -eq 0 ]; then
+  echo >&2 "::warning:: installing rust as root"
+fi
+
+if [ "$CARGO_HOME" = "" ]; then
+  echo >&2 "::error:: CARGO_HOME is not set"
+  exit 2
+fi
+
+export RUSTUP_HOME=$CARGO_HOME
+
+## install rustup
+curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- --default-toolchain none -y
+if [ ! -f $CARGO_HOME/env ]; then
+  echo "PATH=$CARGO_HOME/bin:\$PATH" > $CARGO_HOME/env
+fi
+. $CARGO_HOME/env
+
+rustup -vV
diff --git a/ci/lib.sh b/ci/lib.sh
index f561884d40..a7992b22fd 100755
--- a/ci/lib.sh
+++ b/ci/lib.sh
@@ -1,5 +1,6 @@
 # Library of functions shared by all CI scripts
 
+
 if test true = "$GITHUB_ACTIONS"
 then
 	begin_group () {
diff --git a/ci/make-test-artifacts.sh b/ci/make-test-artifacts.sh
index 74141af0cc..e37ed7030c 100755
--- a/ci/make-test-artifacts.sh
+++ b/ci/make-test-artifacts.sh
@@ -7,6 +7,15 @@ mkdir -p "$1" # in case ci/lib.sh decides to quit early
 
 . ${0%/*}/lib.sh
 
+## ensure rustup is in the PATH variable
+if [ "$CARGO_HOME" = "" ]; then
+  echo >&2 "::error:: CARGO_HOME is not set"
+  exit 2
+fi
+export PATH="$CARGO_HOME/bin:$PATH"
+
+rustc -vV
+
 group Build make artifacts-tar ARTIFACTS_DIRECTORY="$1"
 
 check_unignored_build_artifacts
diff --git a/ci/run-build-and-tests.sh b/ci/run-build-and-tests.sh
index 01823fd0f1..22b61e2812 100755
--- a/ci/run-build-and-tests.sh
+++ b/ci/run-build-and-tests.sh
@@ -5,6 +5,15 @@
 
 . ${0%/*}/lib.sh
 
+## ensure rustup is in the PATH variable
+if [ "$CARGO_HOME" = "" ]; then
+  echo >&2 "::error:: CARGO_HOME is not set"
+  exit 2
+fi
+. $CARGO_HOME/env
+
+rustc -vV || exit $?
+
 run_tests=t
 
 case "$jobname" in
@@ -72,5 +81,9 @@ case "$jobname" in
 	;;
 esac
 
+if [ -d "$CARGO_HOME" ]; then
+  rm -rf $CARGO_HOME
+fi
+
 check_unignored_build_artifacts
 save_good_tree
-- 
gitgitgadget


^ permalink raw reply related	[flat|nested] 100+ messages in thread

* [PATCH v2 10/18] win+Meson: do allow linking with the Rust-built xdiff
  2025-09-17  1:16 ` [PATCH v2 00/18] Introduce rust: In xdiff Ezekiel Newren via GitGitGadget
                     ` (8 preceding siblings ...)
  2025-09-17  1:16   ` [PATCH v2 09/18] github workflows: install rust Ezekiel Newren via GitGitGadget
@ 2025-09-17  1:16   ` Johannes Schindelin via GitGitGadget
  2025-09-17  1:16   ` [PATCH v2 11/18] github workflows: upload Cargo.lock Ezekiel Newren via GitGitGadget
                     ` (10 subsequent siblings)
  20 siblings, 0 replies; 100+ messages in thread
From: Johannes Schindelin via GitGitGadget @ 2025-09-17  1:16 UTC (permalink / raw)
  To: git; +Cc: Ezekiel Newren, Johannes Schindelin

From: Johannes Schindelin <johannes.schindelin@gmx.de>

When linking against the Rust-built `xdiff`, there is now a new required
dependency: Without _also_ linking to the system library `userenv`, the
compile would fail with this error message:

  xdiff.lib(std-c85e9beb7923f636.std.df32d1bc89881d89-cgu.0.rcgu.o) :
  error LNK2019: unresolved external symbol __imp_GetUserProfileDirectoryW
  referenced in function _ZN3std3env8home_dir17hfd1c3b6676cd78f6E

Therefore, just like we do in case of Makefile-based builds on Windows,
we now also link to that library when building with Meson.

Note that if we only have Rust depend upon libuserenv then at link time
GCC would complain about:

  undefined reference to `GetUserProfileDirectoryW'

Apparently there is _some_ closure that gets compiled in that requires
this function, and that in turn forces Git to link to libuserenv.

This is a new requirement, and therefore has not been made part of the
"minimal Git for Windows SDK".

In the near future, I intend to include it, but for now let's just
ensure that the file is added manually if it is missing.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
[en: Squashed a few of Johannes's patches, and moved lib userenv
 handling from an earlier patch]
Signed-off-by: Ezekiel Newren <ezekielnewren@gmail.com>
---
 config.mak.uname | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/config.mak.uname b/config.mak.uname
index 1691c6ae6e..9161694dc4 100644
--- a/config.mak.uname
+++ b/config.mak.uname
@@ -759,6 +759,10 @@ ifeq ($(uname_S),MINGW)
 		COMPAT_CFLAGS += -D_USE_32BIT_TIME_T
 		BASIC_LDFLAGS += -Wl,--large-address-aware
         endif
+
+	# Unfortunately now needed because of Rust
+	EXTLIBS += -luserenv
+
 	CC = gcc
 	COMPAT_CFLAGS += -D__USE_MINGW_ANSI_STDIO=0 -DDETECT_MSYS_TTY \
 		-fstack-protector-strong
-- 
gitgitgadget


^ permalink raw reply related	[flat|nested] 100+ messages in thread

* [PATCH v2 11/18] github workflows: upload Cargo.lock
  2025-09-17  1:16 ` [PATCH v2 00/18] Introduce rust: In xdiff Ezekiel Newren via GitGitGadget
                     ` (9 preceding siblings ...)
  2025-09-17  1:16   ` [PATCH v2 10/18] win+Meson: do allow linking with the Rust-built xdiff Johannes Schindelin via GitGitGadget
@ 2025-09-17  1:16   ` Ezekiel Newren via GitGitGadget
  2025-09-17  1:16   ` [PATCH v2 12/18] build: new crate, build-helper Ezekiel Newren via GitGitGadget
                     ` (9 subsequent siblings)
  20 siblings, 0 replies; 100+ messages in thread
From: Ezekiel Newren via GitGitGadget @ 2025-09-17  1:16 UTC (permalink / raw)
  To: git; +Cc: Ezekiel Newren, Ezekiel Newren

From: Ezekiel Newren <ezekielnewren@gmail.com>

Make each ci workflow upload its Cargo.lock file as a build artifact so
that we can audit build dependencies.

Signed-off-by: Ezekiel Newren <ezekielnewren@gmail.com>
---
 .github/workflows/main.yml | 20 ++++++++++++++++++++
 1 file changed, 20 insertions(+)

diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index e986684a2f..ff314d48b7 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -150,6 +150,11 @@ jobs:
       with:
         name: windows-artifacts
         path: artifacts
+    - name: upload Cargo.lock
+      uses: actions/upload-artifact@v4
+      with:
+        name: cargo-lock-windows
+        path: rust/Cargo.lock
   windows-test:
     name: win test
     runs-on: windows-latest
@@ -311,6 +316,11 @@ jobs:
       with:
         name: windows-meson-artifacts
         path: build
+    - name: Upload Cargo.lock
+      uses: actions/upload-artifact@v4
+      with:
+        name: cargo-lock-windows-meson
+        path: rust/Cargo.lock
   windows-meson-test:
     name: win+Meson test
     runs-on: windows-latest
@@ -393,6 +403,11 @@ jobs:
       with:
         name: failed-tests-${{matrix.vector.jobname}}
         path: ${{env.FAILED_TEST_ARTIFACTS}}
+    - name: Upload Cargo.lock
+      uses: actions/upload-artifact@v4
+      with:
+        name: cargo-lock-${{matrix.vector.jobname}}
+        path: rust/Cargo.lock
   fuzz-smoke-test:
     name: fuzz smoke test
     needs: ci-config
@@ -503,6 +518,11 @@ jobs:
       with:
         name: failed-tests-${{matrix.vector.jobname}}
         path: ${{env.FAILED_TEST_ARTIFACTS}}
+    - name: Upload Cargo.lock
+      uses: actions/upload-artifact@v4
+      with:
+        name: cargo-lock-${{matrix.vector.jobname}}
+        path: rust/Cargo.lock
   static-analysis:
     needs: ci-config
     if: needs.ci-config.outputs.enabled == 'yes'
-- 
gitgitgadget


^ permalink raw reply related	[flat|nested] 100+ messages in thread

* [PATCH v2 12/18] build: new crate, build-helper
  2025-09-17  1:16 ` [PATCH v2 00/18] Introduce rust: In xdiff Ezekiel Newren via GitGitGadget
                     ` (10 preceding siblings ...)
  2025-09-17  1:16   ` [PATCH v2 11/18] github workflows: upload Cargo.lock Ezekiel Newren via GitGitGadget
@ 2025-09-17  1:16   ` Ezekiel Newren via GitGitGadget
  2025-09-17  8:58     ` Eric Sunshine
  2025-09-17  1:16   ` [PATCH v2 13/18] build-helper: link against libgit.a and any other required C libraries Ezekiel Newren via GitGitGadget
                     ` (8 subsequent siblings)
  20 siblings, 1 reply; 100+ messages in thread
From: Ezekiel Newren via GitGitGadget @ 2025-09-17  1:16 UTC (permalink / raw)
  To: git; +Cc: Ezekiel Newren, Ezekiel Newren

From: Ezekiel Newren <ezekielnewren@gmail.com>

Make a crate that takes care of the boiler-plate code that most
build.rs files would need to write.

Signed-off-by: Ezekiel Newren <ezekielnewren@gmail.com>
---
 .gitignore                   | 1 +
 rust/Cargo.toml              | 4 +++-
 rust/build-helper/Cargo.toml | 7 +++++++
 rust/build-helper/src/lib.rs | 0
 4 files changed, 11 insertions(+), 1 deletion(-)
 create mode 100644 rust/build-helper/Cargo.toml
 create mode 100644 rust/build-helper/src/lib.rs

diff --git a/.gitignore b/.gitignore
index 24292eb473..e595759d71 100644
--- a/.gitignore
+++ b/.gitignore
@@ -257,3 +257,4 @@ Release/
 .idea/
 /rust/target/
 /rust/Cargo.lock
+
diff --git a/rust/Cargo.toml b/rust/Cargo.toml
index 9bc2f769fb..aaf008b5ee 100644
--- a/rust/Cargo.toml
+++ b/rust/Cargo.toml
@@ -1,3 +1,5 @@
 [workspace]
-members = []
+members = [
+    "build-helper",
+]
 resolver = "2"
diff --git a/rust/build-helper/Cargo.toml b/rust/build-helper/Cargo.toml
new file mode 100644
index 0000000000..4455fff5ce
--- /dev/null
+++ b/rust/build-helper/Cargo.toml
@@ -0,0 +1,7 @@
+[package]
+name = "build-helper"
+version = "0.1.0"
+edition = "2021"
+
+[dependencies]
+
diff --git a/rust/build-helper/src/lib.rs b/rust/build-helper/src/lib.rs
new file mode 100644
index 0000000000..e69de29bb2
-- 
gitgitgadget


^ permalink raw reply related	[flat|nested] 100+ messages in thread

* [PATCH v2 13/18] build-helper: link against libgit.a and any other required C libraries
  2025-09-17  1:16 ` [PATCH v2 00/18] Introduce rust: In xdiff Ezekiel Newren via GitGitGadget
                     ` (11 preceding siblings ...)
  2025-09-17  1:16   ` [PATCH v2 12/18] build: new crate, build-helper Ezekiel Newren via GitGitGadget
@ 2025-09-17  1:16   ` Ezekiel Newren via GitGitGadget
  2025-09-17  8:51     ` Eric Sunshine
  2025-09-17  1:16   ` [PATCH v2 14/18] build-helper: cbindgen, let crates generate a header file Ezekiel Newren via GitGitGadget
                     ` (7 subsequent siblings)
  20 siblings, 1 reply; 100+ messages in thread
From: Ezekiel Newren via GitGitGadget @ 2025-09-17  1:16 UTC (permalink / raw)
  To: git; +Cc: Ezekiel Newren, Ezekiel Newren

From: Ezekiel Newren <ezekielnewren@gmail.com>

Don't link against the C libraries when building with Make or Meson.
Run cargo tests like this:
cd rust && cargo clean && USE_LINKING=true cargo test

Signed-off-by: Ezekiel Newren <ezekielnewren@gmail.com>
---
 rust/build-helper/Cargo.toml |  1 -
 rust/build-helper/src/lib.rs | 84 ++++++++++++++++++++++++++++++++++++
 2 files changed, 84 insertions(+), 1 deletion(-)

diff --git a/rust/build-helper/Cargo.toml b/rust/build-helper/Cargo.toml
index 4455fff5ce..8939b4b876 100644
--- a/rust/build-helper/Cargo.toml
+++ b/rust/build-helper/Cargo.toml
@@ -4,4 +4,3 @@ version = "0.1.0"
 edition = "2021"
 
 [dependencies]
-
diff --git a/rust/build-helper/src/lib.rs b/rust/build-helper/src/lib.rs
index e69de29bb2..70e95d16f5 100644
--- a/rust/build-helper/src/lib.rs
+++ b/rust/build-helper/src/lib.rs
@@ -0,0 +1,84 @@
+use std::collections::HashMap;
+use std::path::PathBuf;
+
+
+fn parse_bool_from_str(value: &str) -> bool {
+    match value {
+        "1" | "true"  | "yes" | "on"  => true,
+        "0" | "false" | "no"  | "off" => false,
+        _ => false
+    }
+}
+
+fn parse_bool_from_option(value: Option<&String>, default: bool) -> bool {
+    match value {
+        Some(v) => {
+            parse_bool_from_str(v.as_str())
+        },
+        None => default,
+    }
+}
+
+/// To build without linking against C libraries run `USE_LINKING=false cargo build`
+/// To run tests set GIT_BUILD_DIR and run `USE_LINKING=true cargo test`
+pub struct BuildHelper {
+    crate_env: HashMap<String, String>,
+}
+
+
+impl BuildHelper {
+    pub fn new(crate_env: HashMap<String, String>) -> Self {
+        let it = Self {crate_env};
+
+        let dir_crate = it.dir_crate();
+        let dir_workspace = dir_crate.parent().unwrap();
+        let dir_git = dir_workspace.parent().unwrap();
+        let dir_interop = dir_git.join("interop");
+        if !dir_interop.exists() {
+            std::fs::create_dir(dir_interop.clone()).unwrap();
+        }
+
+        it
+    }
+
+    pub fn crate_name(&self) -> String {
+        self.crate_env["CARGO_PKG_NAME"].clone()
+    }
+
+    pub fn dir_crate(&self) -> PathBuf {
+        PathBuf::from(self.crate_env["CARGO_MANIFEST_DIR"].clone())
+    }
+
+    pub fn build(self) {
+        let use_linking = parse_bool_from_option(self.crate_env.get("USE_LINKING"), self.crate_env.get("CARGO_TARGET_DIR").is_none());
+        let dir_crate = self.dir_crate();
+        let dir_git = dir_crate.parent().unwrap().parent().unwrap();
+
+        println!("cargo:rerun-if-changed={}", dir_git.display());
+
+        if use_linking {
+            if let Some(git_build_dir) = self.crate_env.get("GIT_BUILD_DIR") {
+                let mut path_git_build_dir = PathBuf::from(git_build_dir);
+                path_git_build_dir = path_git_build_dir.canonicalize().unwrap();
+                if !path_git_build_dir.is_dir() {
+                    panic!("'GIT_BUILD_DIR' is not a directory: {}", path_git_build_dir.display());
+                }
+                println!("cargo:rustc-link-search=native={}", git_build_dir);
+            } else {
+                panic!("environment variable 'GIT_BUILD_DIR' is not set");
+            }
+
+            println!("cargo:rustc-link-lib=static=git");
+            println!("cargo:rustc-link-lib=pcre2-8");
+            if self.crate_env.get("ZLIB_NG").is_some() {
+                println!("cargo:rustc-link-lib=z-ng");
+            } else {
+                println!("cargo:rustc-link-lib=z");
+            }
+        } else {
+            println!("cargo:warning={} is not linking against C objects, `USE_LINKING=true cargo test`", self.crate_env["CARGO_PKG_NAME"]);
+        }
+    }
+}
+
+
-- 
gitgitgadget


^ permalink raw reply related	[flat|nested] 100+ messages in thread

* [PATCH v2 14/18] build-helper: cbindgen, let crates generate a header file
  2025-09-17  1:16 ` [PATCH v2 00/18] Introduce rust: In xdiff Ezekiel Newren via GitGitGadget
                     ` (12 preceding siblings ...)
  2025-09-17  1:16   ` [PATCH v2 13/18] build-helper: link against libgit.a and any other required C libraries Ezekiel Newren via GitGitGadget
@ 2025-09-17  1:16   ` Ezekiel Newren via GitGitGadget
  2025-09-17  9:08     ` Eric Sunshine
  2025-09-17  1:16   ` [PATCH v2 15/18] varint: use explicit width for integers Patrick Steinhardt via GitGitGadget
                     ` (6 subsequent siblings)
  20 siblings, 1 reply; 100+ messages in thread
From: Ezekiel Newren via GitGitGadget @ 2025-09-17  1:16 UTC (permalink / raw)
  To: git; +Cc: Ezekiel Newren, Ezekiel Newren

From: Ezekiel Newren <ezekielnewren@gmail.com>

Signed-off-by: Ezekiel Newren <ezekielnewren@gmail.com>
---
 rust/build-helper/Cargo.toml |  5 ++++
 rust/build-helper/src/lib.rs | 44 +++++++++++++++++++++++++++++++++++-
 rust/cbindgen-template.toml  | 16 +++++++++++++
 3 files changed, 64 insertions(+), 1 deletion(-)
 create mode 100644 rust/cbindgen-template.toml

diff --git a/rust/build-helper/Cargo.toml b/rust/build-helper/Cargo.toml
index 8939b4b876..2e42bb5405 100644
--- a/rust/build-helper/Cargo.toml
+++ b/rust/build-helper/Cargo.toml
@@ -4,3 +4,8 @@ version = "0.1.0"
 edition = "2021"
 
 [dependencies]
+cbindgen = "0.24.0"
+textwrap = "=0.16.1"
+once_cell = "=1.20.3"
+unicode-width = "=0.1.13"
+
diff --git a/rust/build-helper/src/lib.rs b/rust/build-helper/src/lib.rs
index 70e95d16f5..640bc19734 100644
--- a/rust/build-helper/src/lib.rs
+++ b/rust/build-helper/src/lib.rs
@@ -1,5 +1,7 @@
 use std::collections::HashMap;
+use std::io::Write;
 use std::path::PathBuf;
+use cbindgen::Config;
 
 
 fn parse_bool_from_str(value: &str) -> bool {
@@ -23,12 +25,20 @@ fn parse_bool_from_option(value: Option<&String>, default: bool) -> bool {
 /// To run tests set GIT_BUILD_DIR and run `USE_LINKING=true cargo test`
 pub struct BuildHelper {
     crate_env: HashMap<String, String>,
+    generate_header: bool,
+    file_out: PathBuf,
+    config: Config,
 }
 
 
 impl BuildHelper {
     pub fn new(crate_env: HashMap<String, String>) -> Self {
-        let it = Self {crate_env};
+        let mut it = Self {
+            crate_env,
+            generate_header: false,
+            file_out: PathBuf::default(),
+            config: Config::default(),
+        };
 
         let dir_crate = it.dir_crate();
         let dir_workspace = dir_crate.parent().unwrap();
@@ -38,6 +48,12 @@ impl BuildHelper {
             std::fs::create_dir(dir_interop.clone()).unwrap();
         }
 
+        let file_cbindgen = dir_workspace.join("cbindgen-template.toml");
+        it.file_out = dir_interop.join(format!("{}.h", it.crate_name()));
+
+        it.config = Config::from_file(file_cbindgen.display().to_string().as_str()).unwrap();
+        it.config.include_guard = Some(format!("{}_H", it.crate_name().to_uppercase()));
+
         it
     }
 
@@ -49,6 +65,16 @@ impl BuildHelper {
         PathBuf::from(self.crate_env["CARGO_MANIFEST_DIR"].clone())
     }
 
+    pub fn generate_header<F>(mut self, editor: F) -> Self
+    where
+        F: Fn(&mut Config)
+    {
+        self.generate_header = true;
+        editor(&mut self.config);
+
+        self
+    }
+
     pub fn build(self) {
         let use_linking = parse_bool_from_option(self.crate_env.get("USE_LINKING"), self.crate_env.get("CARGO_TARGET_DIR").is_none());
         let dir_crate = self.dir_crate();
@@ -78,6 +104,22 @@ impl BuildHelper {
         } else {
             println!("cargo:warning={} is not linking against C objects, `USE_LINKING=true cargo test`", self.crate_env["CARGO_PKG_NAME"]);
         }
+
+        if self.generate_header {
+            let mut buffer = Vec::<u8>::new();
+            cbindgen::Builder::new()
+                .with_crate(dir_crate.clone())
+                .with_config(self.config)
+                .with_std_types(true)
+                .generate()
+                .expect("Unable to generate bindings")
+                .write(&mut buffer);
+
+            let mut fd = std::fs::File::create(self.file_out).unwrap();
+            fd.write(buffer.as_slice()).unwrap();
+        } else {
+            let _ = std::fs::remove_file(self.file_out);
+        }
     }
 }
 
diff --git a/rust/cbindgen-template.toml b/rust/cbindgen-template.toml
new file mode 100644
index 0000000000..cab83218d0
--- /dev/null
+++ b/rust/cbindgen-template.toml
@@ -0,0 +1,16 @@
+## compat/posix.h includes stdbool.h where git-compat-util.h does not
+## this is mandatory for correct bool C <-> Rust interop. Though the
+## 'includes' (and all other variables in this file) can be
+## overridden in build.rs.
+sys_includes = ["compat/posix.h"]
+
+autogen_warning = "/* Warning, this file is autogenerated by cbindgen. Don't modify this manually. */"
+
+language = "C"
+no_includes = true
+usize_is_size_t = true
+style = "tag"
+tab_width = 4
+
+[parse]
+parse_deps = false
-- 
gitgitgadget


^ permalink raw reply related	[flat|nested] 100+ messages in thread

* [PATCH v2 15/18] varint: use explicit width for integers
  2025-09-17  1:16 ` [PATCH v2 00/18] Introduce rust: In xdiff Ezekiel Newren via GitGitGadget
                     ` (13 preceding siblings ...)
  2025-09-17  1:16   ` [PATCH v2 14/18] build-helper: cbindgen, let crates generate a header file Ezekiel Newren via GitGitGadget
@ 2025-09-17  1:16   ` Patrick Steinhardt via GitGitGadget
  2025-09-17  1:16   ` [PATCH v2 16/18] build: new crate, misc Ezekiel Newren via GitGitGadget
                     ` (5 subsequent siblings)
  20 siblings, 0 replies; 100+ messages in thread
From: Patrick Steinhardt via GitGitGadget @ 2025-09-17  1:16 UTC (permalink / raw)
  To: git; +Cc: Ezekiel Newren, Patrick Steinhardt

From: Patrick Steinhardt <ps@pks.im>

The varint subsystem currently uses implcit widths for integers. On the
one hand we use `uintmax_t` for the actual value. On the other hand, we
use `int` for the length of the encoded varint.

Both of these have known maximum vaules, as we only support at most 16
bytes when encoding varints. Thus, we know that we won't ever exceed
`uint64_t` for the actual value and `uint8_t` for the prefix length.

Refactor the code to use explicit widths. Besides making the logic
platform-independent, it also makes our life a bit easier in the next
commit, where we reimplement "varint.c" in Rust.

Suggested-by: Ezekiel Newren <ezekielnewren@gmail.com>
Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Ezekiel Newren <ezekielnewren@gmail.com>
---
 dir.c        | 18 ++++++++++--------
 read-cache.c |  6 ++++--
 varint.c     |  6 +++---
 varint.h     |  4 ++--
 4 files changed, 19 insertions(+), 15 deletions(-)

diff --git a/dir.c b/dir.c
index dfb4d40103..b92a5d750e 100644
--- a/dir.c
+++ b/dir.c
@@ -3579,7 +3579,8 @@ static void write_one_dir(struct untracked_cache_dir *untracked,
 	struct stat_data stat_data;
 	struct strbuf *out = &wd->out;
 	unsigned char intbuf[16];
-	unsigned int intlen, value;
+	unsigned int value;
+	uint8_t intlen;
 	int i = wd->index++;
 
 	/*
@@ -3632,7 +3633,7 @@ void write_untracked_extension(struct strbuf *out, struct untracked_cache *untra
 	struct ondisk_untracked_cache *ouc;
 	struct write_data wd;
 	unsigned char varbuf[16];
-	int varint_len;
+	uint8_t varint_len;
 	const unsigned hashsz = the_hash_algo->rawsz;
 
 	CALLOC_ARRAY(ouc, 1);
@@ -3738,7 +3739,7 @@ static int read_one_dir(struct untracked_cache_dir **untracked_,
 	struct untracked_cache_dir ud, *untracked;
 	const unsigned char *data = rd->data, *end = rd->end;
 	const unsigned char *eos;
-	unsigned int value;
+	uint64_t value;
 	int i;
 
 	memset(&ud, 0, sizeof(ud));
@@ -3830,7 +3831,8 @@ struct untracked_cache *read_untracked_extension(const void *data, unsigned long
 	struct read_data rd;
 	const unsigned char *next = data, *end = (const unsigned char *)data + sz;
 	const char *ident;
-	int ident_len;
+	uint64_t ident_len;
+	uint64_t varint_len;
 	ssize_t len;
 	const char *exclude_per_dir;
 	const unsigned hashsz = the_hash_algo->rawsz;
@@ -3867,8 +3869,8 @@ struct untracked_cache *read_untracked_extension(const void *data, unsigned long
 	if (next >= end)
 		goto done2;
 
-	len = decode_varint(&next);
-	if (next > end || len == 0)
+	varint_len = decode_varint(&next);
+	if (next > end || varint_len == 0)
 		goto done2;
 
 	rd.valid      = ewah_new();
@@ -3877,9 +3879,9 @@ struct untracked_cache *read_untracked_extension(const void *data, unsigned long
 	rd.data	      = next;
 	rd.end	      = end;
 	rd.index      = 0;
-	ALLOC_ARRAY(rd.ucd, len);
+	ALLOC_ARRAY(rd.ucd, varint_len);
 
-	if (read_one_dir(&uc->root, &rd) || rd.index != len)
+	if (read_one_dir(&uc->root, &rd) || rd.index != varint_len)
 		goto done;
 
 	next = rd.data;
diff --git a/read-cache.c b/read-cache.c
index 06ad74db22..41b44148b1 100644
--- a/read-cache.c
+++ b/read-cache.c
@@ -1807,7 +1807,7 @@ static struct cache_entry *create_from_disk(struct mem_pool *ce_mem_pool,
 
 	if (expand_name_field) {
 		const unsigned char *cp = (const unsigned char *)name;
-		size_t strip_len, previous_len;
+		uint64_t strip_len, previous_len;
 
 		/* If we're at the beginning of a block, ignore the previous name */
 		strip_len = decode_varint(&cp);
@@ -2655,8 +2655,10 @@ static int ce_write_entry(struct hashfile *f, struct cache_entry *ce,
 		hashwrite(f, ce->name, len);
 		hashwrite(f, padding, align_padding_size(size, len));
 	} else {
-		int common, to_remove, prefix_size;
+		int common, to_remove;
+		uint8_t prefix_size;
 		unsigned char to_remove_vi[16];
+
 		for (common = 0;
 		     (common < previous_name->len &&
 		      ce->name[common] &&
diff --git a/varint.c b/varint.c
index 409c4977a1..03cd54416b 100644
--- a/varint.c
+++ b/varint.c
@@ -1,11 +1,11 @@
 #include "git-compat-util.h"
 #include "varint.h"
 
-uintmax_t decode_varint(const unsigned char **bufp)
+uint64_t decode_varint(const unsigned char **bufp)
 {
 	const unsigned char *buf = *bufp;
 	unsigned char c = *buf++;
-	uintmax_t val = c & 127;
+	uint64_t val = c & 127;
 	while (c & 128) {
 		val += 1;
 		if (!val || MSB(val, 7))
@@ -17,7 +17,7 @@ uintmax_t decode_varint(const unsigned char **bufp)
 	return val;
 }
 
-int encode_varint(uintmax_t value, unsigned char *buf)
+uint8_t encode_varint(uint64_t value, unsigned char *buf)
 {
 	unsigned char varint[16];
 	unsigned pos = sizeof(varint) - 1;
diff --git a/varint.h b/varint.h
index f78bb0ca52..eb401935bd 100644
--- a/varint.h
+++ b/varint.h
@@ -1,7 +1,7 @@
 #ifndef VARINT_H
 #define VARINT_H
 
-int encode_varint(uintmax_t, unsigned char *);
-uintmax_t decode_varint(const unsigned char **);
+uint8_t encode_varint(uint64_t, unsigned char *);
+uint64_t decode_varint(const unsigned char **);
 
 #endif /* VARINT_H */
-- 
gitgitgadget


^ permalink raw reply related	[flat|nested] 100+ messages in thread

* [PATCH v2 16/18] build: new crate, misc
  2025-09-17  1:16 ` [PATCH v2 00/18] Introduce rust: In xdiff Ezekiel Newren via GitGitGadget
                     ` (14 preceding siblings ...)
  2025-09-17  1:16   ` [PATCH v2 15/18] varint: use explicit width for integers Patrick Steinhardt via GitGitGadget
@ 2025-09-17  1:16   ` Ezekiel Newren via GitGitGadget
  2025-09-17  9:16     ` Eric Sunshine
  2025-09-19 20:42     ` Ezekiel Newren
  2025-09-17  1:16   ` [PATCH v2 17/18] misc: use BuildHelper Ezekiel Newren via GitGitGadget
                     ` (4 subsequent siblings)
  20 siblings, 2 replies; 100+ messages in thread
From: Ezekiel Newren via GitGitGadget @ 2025-09-17  1:16 UTC (permalink / raw)
  To: git; +Cc: Ezekiel Newren, Ezekiel Newren

From: Ezekiel Newren <ezekielnewren@gmail.com>

Signed-off-by: Ezekiel Newren <ezekielnewren@gmail.com>
---
 Makefile             |  2 +-
 meson.build          |  2 +-
 rust/Cargo.toml      |  1 +
 rust/misc/Cargo.toml | 14 ++++++++++++++
 rust/misc/src/lib.rs |  0
 5 files changed, 17 insertions(+), 2 deletions(-)
 create mode 100644 rust/misc/Cargo.toml
 create mode 100644 rust/misc/src/lib.rs

diff --git a/Makefile b/Makefile
index ef55a8183c..c363a6365d 100644
--- a/Makefile
+++ b/Makefile
@@ -929,7 +929,7 @@ EXTLIBS =
 GIT_BUILD_DIR := $(CURDIR)
 export GIT_BUILD_DIR
 
-RUST_CRATES :=
+RUST_CRATES := misc
 .PHONY: compile_rust rustclean
 
 WITH_RUST ?= true
diff --git a/meson.build b/meson.build
index cbe1afac83..6911625f3b 100644
--- a/meson.build
+++ b/meson.build
@@ -267,7 +267,7 @@ version_gen_environment.set('GIT_DATE', get_option('build_date'))
 version_gen_environment.set('GIT_USER_AGENT', get_option('user_agent'))
 version_gen_environment.set('GIT_VERSION', get_option('version'))
 
-rust_crates = []
+rust_crates = ['misc']
 
 rust_builds = []
 with_rust = get_option('with_rust')
diff --git a/rust/Cargo.toml b/rust/Cargo.toml
index aaf008b5ee..010effef4c 100644
--- a/rust/Cargo.toml
+++ b/rust/Cargo.toml
@@ -1,5 +1,6 @@
 [workspace]
 members = [
     "build-helper",
+    "misc",
 ]
 resolver = "2"
diff --git a/rust/misc/Cargo.toml b/rust/misc/Cargo.toml
new file mode 100644
index 0000000000..7195d7894d
--- /dev/null
+++ b/rust/misc/Cargo.toml
@@ -0,0 +1,14 @@
+[package]
+name = "misc"
+version = "0.1.0"
+edition = "2021"
+
+[lib]
+name = "misc"
+path = "src/lib.rs"
+## staticlib to generate xdiff.a for use by gcc
+## cdylib (optional) to generate xdiff.so for use by gcc
+## rlib is required by the rust unit tests
+crate-type = ["staticlib", "rlib"]
+
+[dependencies]
diff --git a/rust/misc/src/lib.rs b/rust/misc/src/lib.rs
new file mode 100644
index 0000000000..e69de29bb2
-- 
gitgitgadget


^ permalink raw reply related	[flat|nested] 100+ messages in thread

* [PATCH v2 17/18] misc: use BuildHelper
  2025-09-17  1:16 ` [PATCH v2 00/18] Introduce rust: In xdiff Ezekiel Newren via GitGitGadget
                     ` (15 preceding siblings ...)
  2025-09-17  1:16   ` [PATCH v2 16/18] build: new crate, misc Ezekiel Newren via GitGitGadget
@ 2025-09-17  1:16   ` Ezekiel Newren via GitGitGadget
  2025-09-17  1:16   ` [PATCH v2 18/18] misc::varint: reimplement as test balloon for Rust Patrick Steinhardt via GitGitGadget
                     ` (3 subsequent siblings)
  20 siblings, 0 replies; 100+ messages in thread
From: Ezekiel Newren via GitGitGadget @ 2025-09-17  1:16 UTC (permalink / raw)
  To: git; +Cc: Ezekiel Newren, Ezekiel Newren

From: Ezekiel Newren <ezekielnewren@gmail.com>

Signed-off-by: Ezekiel Newren <ezekielnewren@gmail.com>
---
 rust/misc/Cargo.toml | 3 +++
 rust/misc/build.rs   | 6 ++++++
 2 files changed, 9 insertions(+)
 create mode 100644 rust/misc/build.rs

diff --git a/rust/misc/Cargo.toml b/rust/misc/Cargo.toml
index 7195d7894d..0f93aa4ae4 100644
--- a/rust/misc/Cargo.toml
+++ b/rust/misc/Cargo.toml
@@ -12,3 +12,6 @@ path = "src/lib.rs"
 crate-type = ["staticlib", "rlib"]
 
 [dependencies]
+
+[build-dependencies]
+build-helper = { path = "../build-helper" }
diff --git a/rust/misc/build.rs b/rust/misc/build.rs
new file mode 100644
index 0000000000..291923ae1d
--- /dev/null
+++ b/rust/misc/build.rs
@@ -0,0 +1,6 @@
+use build_helper::BuildHelper;
+
+fn main() {
+    BuildHelper::new(std::env::vars().collect())
+        .build();
+}
-- 
gitgitgadget


^ permalink raw reply related	[flat|nested] 100+ messages in thread

* [PATCH v2 18/18] misc::varint: reimplement as test balloon for Rust
  2025-09-17  1:16 ` [PATCH v2 00/18] Introduce rust: In xdiff Ezekiel Newren via GitGitGadget
                     ` (16 preceding siblings ...)
  2025-09-17  1:16   ` [PATCH v2 17/18] misc: use BuildHelper Ezekiel Newren via GitGitGadget
@ 2025-09-17  1:16   ` Patrick Steinhardt via GitGitGadget
  2025-09-17  5:58   ` [PATCH v2 00/18] Introduce rust: In xdiff Patrick Steinhardt
                     ` (2 subsequent siblings)
  20 siblings, 0 replies; 100+ messages in thread
From: Patrick Steinhardt via GitGitGadget @ 2025-09-17  1:16 UTC (permalink / raw)
  To: git; +Cc: Ezekiel Newren, Patrick Steinhardt

From: Patrick Steinhardt <ps@pks.im>

Implement a trivial test balloon for our Rust build infrastructure by
reimplementing the "varint.c" subsystem in Rust. This subsystem is
chosen because it is trivial to convert and because it doesn't have any
dependencies to other components of Git.

If support for Rust is enabled, we stop compiling "varint.c" and instead
compile and use "src/varint.rs".

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Ezekiel Newren <ezekielnewren@gmail.com>
---
 Makefile                |  2 +
 meson.build             |  7 +++-
 rust/misc/src/lib.rs    |  1 +
 rust/misc/src/varint.rs | 92 +++++++++++++++++++++++++++++++++++++++++
 4 files changed, 101 insertions(+), 1 deletion(-)
 create mode 100644 rust/misc/src/varint.rs

diff --git a/Makefile b/Makefile
index c363a6365d..9d50f88809 100644
--- a/Makefile
+++ b/Makefile
@@ -1259,7 +1259,9 @@ LIB_OBJS += urlmatch.o
 LIB_OBJS += usage.o
 LIB_OBJS += userdiff.o
 LIB_OBJS += utf8.o
+ifeq ($(WITH_RUST),false)
 LIB_OBJS += varint.o
+endif
 LIB_OBJS += version.o
 LIB_OBJS += versioncmp.o
 LIB_OBJS += walker.o
diff --git a/meson.build b/meson.build
index 6911625f3b..1b191b12a3 100644
--- a/meson.build
+++ b/meson.build
@@ -571,7 +571,6 @@ libgit_sources = [
   'usage.c',
   'userdiff.c',
   'utf8.c',
-  'varint.c',
   'version.c',
   'versioncmp.c',
   'walker.c',
@@ -591,6 +590,12 @@ libgit_sources = [
   'xdiff/xutils.c',
 ]
 
+if not with_rust
+  libgit_sources += [
+    'varint.c',
+  ]
+endif
+
 libgit_sources += custom_target(
   input: 'command-list.txt',
   output: 'command-list.h',
diff --git a/rust/misc/src/lib.rs b/rust/misc/src/lib.rs
index e69de29bb2..9da70d8b57 100644
--- a/rust/misc/src/lib.rs
+++ b/rust/misc/src/lib.rs
@@ -0,0 +1 @@
+pub mod varint;
diff --git a/rust/misc/src/varint.rs b/rust/misc/src/varint.rs
new file mode 100644
index 0000000000..10c83e1f43
--- /dev/null
+++ b/rust/misc/src/varint.rs
@@ -0,0 +1,92 @@
+#[no_mangle]
+pub unsafe extern "C" fn decode_varint(bufp: *mut *const u8) -> usize {
+    let mut buf = *bufp;
+    let mut c = *buf;
+    let mut val = usize::from(c & 127);
+
+    buf = buf.add(1);
+
+    while (c & 128) != 0 {
+        val = val.wrapping_add(1);
+        if val == 0 || val.leading_zeros() < 7 {
+            return 0; // overflow
+        }
+
+        c = *buf;
+        buf = buf.add(1);
+
+        val = (val << 7) + usize::from(c & 127);
+    }
+
+    *bufp = buf;
+    val
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn encode_varint(value: usize, buf: *mut u8) -> u8 {
+    let mut varint: [u8; 16] = [0; 16];
+    let mut pos = varint.len() - 1;
+
+    varint[pos] = (value & 127) as u8;
+
+    let mut value = value >> 7;
+    while value != 0 {
+        pos -= 1;
+        value -= 1;
+        varint[pos] = 128 | (value & 127) as u8;
+        value >>= 7;
+    }
+
+    if !buf.is_null() {
+        std::ptr::copy_nonoverlapping(varint.as_ptr().add(pos), buf, varint.len() - pos);
+    }
+
+    (varint.len() - pos) as u8
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    #[test]
+    fn test_decode_varint() {
+        unsafe {
+            assert_eq!(decode_varint(&mut [0x00].as_slice().as_ptr()), 0);
+            assert_eq!(decode_varint(&mut [0x01].as_slice().as_ptr()), 1);
+            assert_eq!(decode_varint(&mut [0x7f].as_slice().as_ptr()), 127);
+            assert_eq!(decode_varint(&mut [0x80, 0x00].as_slice().as_ptr()), 128);
+            assert_eq!(decode_varint(&mut [0x80, 0x01].as_slice().as_ptr()), 129);
+            assert_eq!(decode_varint(&mut [0x80, 0x7f].as_slice().as_ptr()), 255);
+
+            // Overflows are expected to return 0.
+            assert_eq!(decode_varint(&mut [0x88; 16].as_slice().as_ptr()), 0);
+        }
+    }
+
+    #[test]
+    fn test_encode_varint() {
+        unsafe {
+            let mut varint: [u8; 16] = [0; 16];
+
+            assert_eq!(encode_varint(0, std::ptr::null_mut()), 1);
+
+            assert_eq!(encode_varint(0, varint.as_mut_slice().as_mut_ptr()), 1);
+            assert_eq!(varint, [0; 16]);
+
+            assert_eq!(encode_varint(10, varint.as_mut_slice().as_mut_ptr()), 1);
+            assert_eq!(varint, [10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
+
+            assert_eq!(encode_varint(127, varint.as_mut_slice().as_mut_ptr()), 1);
+            assert_eq!(varint, [127, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
+
+            assert_eq!(encode_varint(128, varint.as_mut_slice().as_mut_ptr()), 2);
+            assert_eq!(varint, [128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
+
+            assert_eq!(encode_varint(129, varint.as_mut_slice().as_mut_ptr()), 2);
+            assert_eq!(varint, [128, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
+
+            assert_eq!(encode_varint(255, varint.as_mut_slice().as_mut_ptr()), 2);
+            assert_eq!(varint, [128, 127, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
+        }
+    }
+}
-- 
gitgitgadget

^ permalink raw reply related	[flat|nested] 100+ messages in thread

* Re: [PATCH v2 00/18] Introduce rust: In xdiff
  2025-09-17  1:16 ` [PATCH v2 00/18] Introduce rust: In xdiff Ezekiel Newren via GitGitGadget
                     ` (17 preceding siblings ...)
  2025-09-17  1:16   ` [PATCH v2 18/18] misc::varint: reimplement as test balloon for Rust Patrick Steinhardt via GitGitGadget
@ 2025-09-17  5:58   ` Patrick Steinhardt
  2025-09-19 20:57     ` Ezekiel Newren
  2025-09-17 17:07   ` Junio C Hamano
  2025-09-23 18:05   ` Ezekiel Newren
  20 siblings, 1 reply; 100+ messages in thread
From: Patrick Steinhardt @ 2025-09-17  5:58 UTC (permalink / raw)
  To: Ezekiel Newren via GitGitGadget; +Cc: git, Ezekiel Newren

On Wed, Sep 17, 2025 at 01:16:20AM +0000, Ezekiel Newren via GitGitGadget wrote:
> This is a continuation of
> https://lore.kernel.org/git/pull.1980.v3.git.git.1755921356.gitgitgadget@gmail.com/,
> but I am removing the RFC label.
> 
> Suggestions on changes that I could make to this series is appreciated.
> 
> Changes in v2: High level overview:
> 
>  * patch 1: cleanup: rename variables that collide with Rust primitive type
>    names
>  * patches 2-4: Makefile now produces libgit.a correctly
>  * patches 5,6: Documentation from Brian and Patrick
>  * patches 7,8: Introduce Rust
>  * patches 9-11: github workflows
>  * patches 12-14: introduce crates build-helper, and cbindgen
>  * patches 15-18: varint test balloon
> 
> I would like feed back in two categories: big changes, and little changes.
> Is my patch series even going in the right direction, and what are the
> little details that I've missed? I know Brian asked for cbindgen and it made
> the series several commits longer, but I think it's a great idea to have. I
> am not happy with every choice that I made, but I currently don't see a
> better way or at least an easier alternative to my approach in bringing Rust
> to Git.
> 
> Build results for these changes:
> https://github.com/git/git/actions/runs/17783386212?pr=2043. Some of these
> are failing.

Given that this isn't yet ready and given that this patch series is way
bigger than the one I have in flight that focusses on introducing the
policy [1]: how about we first merge that one down and then rebase your
patches on top of it to iterate? It would reduce the scope of your patch
series and allow us to make smaller steps towards our goal.

To be clear: I very much think that most of the steps here are quite
sensible. We definitely want to introduce cbindgen, we obviously need to
introduce support on Windows, and I guess having multiple different
workspaces is also something that could provide benefit.

But there is no reason to have that all at once, so hence my suggestion
to build out the infra one step at a time.

What do you think?

Patrick

[1]: https://lore.kernel.org/git/20250904-b4-pks-rust-breaking-change-v1-0-3af1d25e0be9@pks.im/

^ permalink raw reply	[flat|nested] 100+ messages in thread

* Re: [PATCH v2 01/18] cleanup: rename variables that collide with Rust primitive type names
  2025-09-17  1:16   ` [PATCH v2 01/18] cleanup: rename variables that collide with Rust primitive type names Ezekiel Newren via GitGitGadget
@ 2025-09-17  7:42     ` Eric Sunshine
  2025-09-17 14:32       ` Junio C Hamano
  0 siblings, 1 reply; 100+ messages in thread
From: Eric Sunshine @ 2025-09-17  7:42 UTC (permalink / raw)
  To: Ezekiel Newren via GitGitGadget; +Cc: git, Ezekiel Newren

On Tue, Sep 16, 2025 at 9:17 PM Ezekiel Newren via GitGitGadget
<gitgitgadget@gmail.com> wrote:
> cleanup: rename variables that collide with Rust primitive type names
>
> Use a regex to find and rename variables that collide with Rust
> primitive integer and float type names:
>
>     git grep -n -E -e '\<([ui](8|16|32|64|size)|(f(32|64)))\>'

This explains what the patch is doing but doesn't explain why we want
this change. Please update the commit message to describe the
problem/issue the patch is trying to address, and then (if necessary)
explain what the patch is doing.

> Matches were reviewed and renamed. The remaining matches don't count
> because:
> - Rust source files:
>   contrib/libgit-rs/src/config.rs
>   contrib/libgit-sys/src/lib.rs
>   t/t4018/rust-impl
>   t/t4018/rust-trait
> - Intentional references:
>   t/helper/test-parse-options.c (prints Rust int names)
>   t/t0040-parse-options.sh (tests the above)
>
> View with --color-words to highlight the variable renames.
>
> Signed-off-by: Ezekiel Newren <ezekielnewren@gmail.com>
> ---
> diff --git a/odb.c b/odb.c
> @@ -913,7 +913,7 @@ void *odb_read_object_peeled(struct object_database *odb,
>  {
> -       unsigned long isize;
> +       unsigned long isize_;

Nit: It's minor, but I can't say I'm a fan of this approach to
renaming variables. It would be better to come up with a name that is
more meaningful if possible rather than merely appending an
underscore. In this particular case, the original name, "isize",
already fails to convey much meaning, but appears to have been named
this way simply to avoid a collision with the existing function
argument named "size". So, you could just as easily rename "isize" to
"sz" or some such.

Same comment applies to the other renamed variables...

> -               OPT_UNSIGNED(0, "u16", &u16, "get a 16 bit unsigned integer"),
> +               OPT_UNSIGNED(0, "u16", &u16_, "get a 16 bit unsigned integer"),

... though with some of them, such as this one, it is admittedly more
difficult to come up with a better name since the original name is
already meaningful.

^ permalink raw reply	[flat|nested] 100+ messages in thread

* Re: [PATCH v2 02/18] make: add -fPIE flag
  2025-09-17  1:16   ` [PATCH v2 02/18] make: add -fPIE flag Ezekiel Newren via GitGitGadget
@ 2025-09-17  7:44     ` Eric Sunshine
  2025-09-19 19:48       ` Ezekiel Newren
  0 siblings, 1 reply; 100+ messages in thread
From: Eric Sunshine @ 2025-09-17  7:44 UTC (permalink / raw)
  To: Ezekiel Newren via GitGitGadget; +Cc: git, Ezekiel Newren

On Tue, Sep 16, 2025 at 9:17 PM Ezekiel Newren via GitGitGadget
<gitgitgadget@gmail.com> wrote:
> make: add -fPIE flag
>
> Signed-off-by: Ezekiel Newren <ezekielnewren@gmail.com>
> ---
> diff --git a/Makefile b/Makefile
> @@ -1402,7 +1402,7 @@ endif
>  CC_LD_DYNPATH = -Wl,-rpath,
> -BASIC_CFLAGS = -I.
> +BASIC_CFLAGS = -I. -fPIE
>  BASIC_LDFLAGS =

The commit message explains what the patch is doing (which is obvious
from the patch itself) but doesn't explain why this change is needed,
hence it is very difficult for a reviewer to understand why such a
change is necessary.

^ permalink raw reply	[flat|nested] 100+ messages in thread

* Re: [PATCH v2 03/18] make: merge xdiff lib into libgit.a
  2025-09-17  1:16   ` [PATCH v2 03/18] make: merge xdiff lib into libgit.a Ezekiel Newren via GitGitGadget
@ 2025-09-17  7:46     ` Eric Sunshine
  2025-09-19 19:54       ` Ezekiel Newren
  0 siblings, 1 reply; 100+ messages in thread
From: Eric Sunshine @ 2025-09-17  7:46 UTC (permalink / raw)
  To: Ezekiel Newren via GitGitGadget; +Cc: git, Ezekiel Newren

On Tue, Sep 16, 2025 at 9:17 PM Ezekiel Newren via GitGitGadget
<gitgitgadget@gmail.com> wrote:
> make: merge xdiff lib into libgit.a
>
> Signed-off-by: Ezekiel Newren <ezekielnewren@gmail.com>
> ---

Please extend the commit message to explain why this change is desirable.

^ permalink raw reply	[flat|nested] 100+ messages in thread

* Re: [PATCH v2 04/18] make: merge reftable lib into libgit.a
  2025-09-17  1:16   ` [PATCH v2 04/18] make: merge reftable " Ezekiel Newren via GitGitGadget
@ 2025-09-17  7:46     ` Eric Sunshine
  2025-09-19 19:02     ` Junio C Hamano
  1 sibling, 0 replies; 100+ messages in thread
From: Eric Sunshine @ 2025-09-17  7:46 UTC (permalink / raw)
  To: Ezekiel Newren via GitGitGadget; +Cc: git, Ezekiel Newren

On Tue, Sep 16, 2025 at 9:17 PM Ezekiel Newren via GitGitGadget
<gitgitgadget@gmail.com> wrote:
> make: merge reftable lib into libgit.a
>
> Signed-off-by: Ezekiel Newren <ezekielnewren@gmail.com>
> ---

Same comment as previous patch... please extend the commit message to
explain why this change is desirable.

^ permalink raw reply	[flat|nested] 100+ messages in thread

* Re: [PATCH v2 09/18] github workflows: install rust
  2025-09-17  1:16   ` [PATCH v2 09/18] github workflows: install rust Ezekiel Newren via GitGitGadget
@ 2025-09-17  8:01     ` Eric Sunshine
  0 siblings, 0 replies; 100+ messages in thread
From: Eric Sunshine @ 2025-09-17  8:01 UTC (permalink / raw)
  To: Ezekiel Newren via GitGitGadget; +Cc: git, Ezekiel Newren

On Tue, Sep 16, 2025 at 9:17 PM Ezekiel Newren via GitGitGadget
<gitgitgadget@gmail.com> wrote:
> Prefer using actions-rs/toolchain@v1 where possible to install rustup,
> but for docker targets use a script to install rustup. Consolidate the
> Rust toolchain definitions in main.yaml. Use install-rust-toolchain.sh
> to ensure the correct toolchain is used. Five overrides are used in
> main.yaml:
>
>   * On Windows: Rust didn't resolve the bcrypt library on Windows
>     correctly until version 1.78.0. Also since rustup mis-identifies
>     the Rust toolchain, the Rust target triple must be set to
>     x86_64-pc-windows-gnu for make (win build), and
>     x86_64-pc-windows-msvc for meson (win+Meson build).
>   * MSVC builds: Rearrange PATH to look in /mingw64/bin and /usr/bin
>     last.

Please add an explanation as to why it is necessary to rearrange PATH.
I saw in patch [7/18] that your "build_rust.sh" does the same but the
reason is never spelled out (and it's still a mystery to me).

Also, this patch, [9/18], doesn't seem to touch PATH in the way
described here (unless I somehow overlooked it).

>   * On musl: libc differences, such as ftruncate64 vs ftruncate, were
>     not accounted for until Rust version 1.72.0. No older version of
>     Rust will work on musl for our needs.
>   * In a 32-bit docker container running on a 64-bit host, we need to
>     override the Rust target triple. This is because rustup asks the
>     kernel for the bitness of the system and it says 64, even though
>     the container is 32-bit. This also allows us to remove the
>     BITNESS environment variable in ci/lib.sh.
>
> The logic for selecting library names was initially provided in a patch
> from Johannes, but was reworked and squashed into this commit.
>
> Helped-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>
> Signed-off-by: Ezekiel Newren <ezekielnewren@gmail.com>
> ---
> diff --git a/ci/install-rust-toolchain.sh b/ci/install-rust-toolchain.sh
> @@ -0,0 +1,30 @@
> +#!/bin/sh
> +
> +if [ "$CARGO_HOME" = "" ]; then
> +  echo >&2 "::error:: CARGO_HOME is not set"
> +  exit 2
> +fi

Let's follow project coding guidelines for shell scripts:

    if test "$CARGO_HOME" = ""
    then
        ..
    fi

or even:

    if test -z "$CARGO_HOME"
    then
        ...
    fi

Same comment applies to the remainder of this script and other scripts
in this patch.

> diff --git a/ci/install-rustup.sh b/ci/install-rustup.sh
> @@ -0,0 +1,25 @@
> +if [ ! -f $CARGO_HOME/env ]; then
> +  echo "PATH=$CARGO_HOME/bin:\$PATH" > $CARGO_HOME/env
> +fi

Style: drop space after '>' operator

> diff --git a/ci/lib.sh b/ci/lib.sh
> @@ -1,5 +1,6 @@
>  # Library of functions shared by all CI scripts
>
> +
>  if test true = "$GITHUB_ACTIONS"

Do we need the extra blank line introduced above the `if`?

^ permalink raw reply	[flat|nested] 100+ messages in thread

* Re: [PATCH v2 07/18] build: introduce rust
  2025-09-17  1:16   ` [PATCH v2 07/18] build: introduce rust Ezekiel Newren via GitGitGadget
@ 2025-09-17  8:26     ` Eric Sunshine
  2025-09-17 14:54       ` Junio C Hamano
  2025-09-19 20:11       ` Ezekiel Newren
  0 siblings, 2 replies; 100+ messages in thread
From: Eric Sunshine @ 2025-09-17  8:26 UTC (permalink / raw)
  To: Ezekiel Newren via GitGitGadget; +Cc: git, Ezekiel Newren

On Tue, Sep 16, 2025 at 9:17 PM Ezekiel Newren via GitGitGadget
<gitgitgadget@gmail.com> wrote:
> Upcoming patches will simplify xdiff, while also porting parts of it to
> Rust. In preparation, add some stubs and setup the Rust build. For now,
> it is easier to let cargo build rust and have make or meson merely link
> against the static library that cargo builds. In line with ongoing
> libification efforts, use multiple crates to allow more modularity on
> the Rust side. The xdiff and ivec crates are the focal point for this
> series.
>
> cbindgen will be used to generate header files for each Rust crate for
> C interoperability.
>
> Signed-off-by: Ezekiel Newren <ezekielnewren@gmail.com>
> ---
> diff --git a/.gitignore b/.gitignore
> @@ -254,3 +254,6 @@ Release/
>  /contrib/buildsystems/out
>  /contrib/libgit-rs/target
>  /contrib/libgit-sys/target
> +.idea/
> +/rust/target/
> +/rust/Cargo.lock

Is ".idea" directory detritus from your chosen editor? If so, it
probably ought not be added to this list since we don't otherwise
ignore detritus from foreign tools such as that.

> diff --git a/Makefile b/Makefile
> @@ -916,6 +924,51 @@ TEST_SHELL_PATH = $(SHELL_PATH)
> +RUST_CRATES :=
> +.PHONY: compile_rust rustclean

This is an extremely tiny nit, but multi-word target names in the Git
Makefile are almost universally spelled with a hyphen rather than an
underscore, so "compile-rust" would be more consistent and
appropriate.

> +$(GIT_BUILD_DIR)/lib%.a:
> +       ./build_rust.sh $(GIT_BUILD_DIR) $(RUST_BUILD_MODE) $*

I wonder if this should follow the convention of most (but not quite
all) other local shell script invocations in the Makefile of
referencing SHELL_PATH; for instance:

    $(QUIET_GEN)$(SHELL_PATH) ./build_rust.sh ...etc...

with or without the $(QUIET_GEN) bit.

> diff --git a/build_rust.sh b/build_rust.sh
> @@ -0,0 +1,60 @@
> +if [ "$dir_git_root" = "" ]; then
> +  echo "did not specify the directory for the root of git"
> +  exit 1
> +fi

Let's follow project style conventions for shell scripts:

    if test "$dir_git_root" = ""
    then
        ...
    fi

or even:

    if test -z "$dir_git_root"
    then
        ...
    fi

Same comment applies to the rest of the script.

Also, should the above error message be sent to stderr rather than
stdout? (Genuine question since I'm not sure if other scripts are
necessarily consistent in that regard.)

> +if [ "$rust_build_profile" = "release" ]; then
> +  rust_args="--release"
> +  export RUSTFLAGS=''
> +elif [ "$rust_build_profile" = "debug" ]; then
> +  rust_args=""
> +  export RUSTFLAGS='-C debuginfo=2 -C opt-level=1 -C force-frame-pointers=yes'
> +else
> +  echo "illegal rust_build_profile value $rust_build_profile"
> +  exit 1
> +fi

For maximum portability, we don't use the:

    export FOO=bar

idiom in this project. Instead, do this:

    FOO=bar
    export FOO

The same comment applies to the remainder of the patch, as well.

Also, the empty-string assignments to RUSTFLAGS and rust_args above
inconsistently use a mix of single and double quotes. However, we also
usually just assign nothingness in such cases rather than an empty
string:

    RUSTFLAGS=
    export RUSTFLAGS
    ...
    rust_args=

> +if rustup show active-toolchain | grep windows-msvc; then
> +  libfile="${crate}.lib"
> +  PATH="$(echo $PATH | tr ':' '\n' | grep -Ev "^(/mingw64/bin|/usr/bin)$" | paste -sd: -):/mingw64/bin:/usr/bin"
> +fi

Please add either an in-code comment or a sentence/paragraph to the
commit message explaining why this PATH munging is needed.

> +cd $dir_rust && cargo clean && pwd && USE_LINKING="false" cargo build -p $crate $rust_args; cd $dir_git_root

When formatted like this, it is very easy to overlook the fact that
the final `cd` is (correctly) not &&-chained with the preceding
commands. Reformatting it like this would help:

    cd $dir_rust &&
        cargo clean &&
        pwd &&
        USE_LINKING="false" cargo build -p $crate $rust_args
    cd $dir_git_root

Alternatively, use a subshell and drop the final `cd`:

    (
        cd $dir_rust &&
        cargo clean &&
        pwd &&
        USE_LINKING="false" cargo build -p $crate $rust_args
    )

By the way, is the `pwd` command needed? What is its purpose?

> +rm $dst 2>/dev/null

I think we typically use `rm -f` without redirect to /dev/null to do
the "right thing".

> +echo mv $src $dst
> +mv $src $dst

^ permalink raw reply	[flat|nested] 100+ messages in thread

* Re: [PATCH v2 13/18] build-helper: link against libgit.a and any other required C libraries
  2025-09-17  1:16   ` [PATCH v2 13/18] build-helper: link against libgit.a and any other required C libraries Ezekiel Newren via GitGitGadget
@ 2025-09-17  8:51     ` Eric Sunshine
  2025-09-17 23:07       ` D. Ben Knoble
  2025-09-19 20:25       ` Ezekiel Newren
  0 siblings, 2 replies; 100+ messages in thread
From: Eric Sunshine @ 2025-09-17  8:51 UTC (permalink / raw)
  To: Ezekiel Newren via GitGitGadget; +Cc: git, Ezekiel Newren

On Tue, Sep 16, 2025 at 9:18 PM Ezekiel Newren via GitGitGadget
<gitgitgadget@gmail.com> wrote:
> build-helper: link against libgit.a and any other required C libraries
>
> Don't link against the C libraries when building with Make or Meson.
> Run cargo tests like this:
> cd rust && cargo clean && USE_LINKING=true cargo test
>
> Signed-off-by: Ezekiel Newren <ezekielnewren@gmail.com>
> ---

Perhaps it's because I haven't been following the discussion closely
enough, but the above commit message leaves me entirely in the dark.
After reading and rereading it several times, I suppose it is trying
to address some difference between building with `cargo` vs. building
with Make or Meson, but it gives no explanation of what the
differences are or what problem it is trying to solve. So, please
enhance the commit message to begin with the "why" and then proceed to
the "what" or "how".

> diff --git a/rust/build-helper/Cargo.toml b/rust/build-helper/Cargo.toml
> @@ -4,4 +4,3 @@ version = "0.1.0"
>  edition = "2021"
>
>  [dependencies]
> -

This seems merely to be deleting a blank line which probably shouldn't
have been present in the first place. Rather than fixing the "problem"
here, it would make more sense to eliminate the blank line in the
patch which introduced it in the first place.

> diff --git a/rust/build-helper/src/lib.rs b/rust/build-helper/src/lib.rs
> @@ -0,0 +1,84 @@
> +use std::collections::HashMap;
> +use std::path::PathBuf;
> +
> +

If I'm not mistaken, it is uncommon to have two blank lines like this
in Rust code.

> +fn parse_bool_from_str(value: &str) -> bool {
> +    match value {
> +        "1" | "true"  | "yes" | "on"  => true,
> +        "0" | "false" | "no"  | "off" => false,
> +        _ => false
> +    }
> +}

Or, more simply:

    fn parse_bool_from_str(value: &str) -> bool {
        match value {
            "1" | "true"  | "yes" | "on"  => true,
            _ => false
        }
    }

(Though, admittedly, I'd probably lean toward writing the function the
same way you did.)

> +/// To build without linking against C libraries run `USE_LINKING=false cargo build`
> +/// To run tests set GIT_BUILD_DIR and run `USE_LINKING=true cargo test`
> +pub struct BuildHelper {
> +    crate_env: HashMap<String, String>,
> +}
> +
> +

Nit: unnecessary extra blank line

> +impl BuildHelper {
> +    pub fn build(self) {
> +        let use_linking = parse_bool_from_option(self.crate_env.get("USE_LINKING"), self.crate_env.get("CARGO_TARGET_DIR").is_none());
> +        ...
> +            println!("cargo:warning={} is not linking against C objects, `USE_LINKING=true cargo test`", self.crate_env["CARGO_PKG_NAME"]);

There are more than a few developers on this project (including
myself) who still use 80-column editors and terminals. As a general
style guideline, this project does recommend wrapping code to fit
within 80 columns (except in cases when doing so would severely hurt
readability). I imagine that the same sort of guideline would be
appreciated in Rust code, as well, by those who still stick with 80
columns.

I bring this up because, although it hasn't been such a big deal with
the existing C code, assuming that developers run `rustfmt` on the
code before sending a patch series, then this may become an issue if
different developers have `rustfmt` configured to enforce different
maximum column width, especially since `rustfmt` is likely to reformat
the entire file rather than just the region that has just been edited.
So, if this code gets checked in as-is with these very wide lines, and
then someone else, who has `rustfmt` configured for 80-columns edits
the file, then it becomes a problem.

As such, can we also add a project-wide `rustfmt.toml` which, at
minimum, sets the maximum line width to 80? For instance:

    max_width = 80

^ permalink raw reply	[flat|nested] 100+ messages in thread

* Re: [PATCH v2 12/18] build: new crate, build-helper
  2025-09-17  1:16   ` [PATCH v2 12/18] build: new crate, build-helper Ezekiel Newren via GitGitGadget
@ 2025-09-17  8:58     ` Eric Sunshine
  0 siblings, 0 replies; 100+ messages in thread
From: Eric Sunshine @ 2025-09-17  8:58 UTC (permalink / raw)
  To: Ezekiel Newren via GitGitGadget; +Cc: git, Ezekiel Newren

On Tue, Sep 16, 2025 at 9:18 PM Ezekiel Newren via GitGitGadget
<gitgitgadget@gmail.com> wrote:
> Make a crate that takes care of the boiler-plate code that most
> build.rs files would need to write.
>
> Signed-off-by: Ezekiel Newren <ezekielnewren@gmail.com>
> ---
> diff --git a/.gitignore b/.gitignore
> @@ -257,3 +257,4 @@ Release/
>  .idea/
>  /rust/target/
>  /rust/Cargo.lock
> +

This seems to be adding an unnecessary blank line (for no apparent reason).

> diff --git a/rust/build-helper/Cargo.toml b/rust/build-helper/Cargo.toml
> @@ -0,0 +1,7 @@
> +[package]
> +name = "build-helper"
> +version = "0.1.0"
> +edition = "2021"
> +
> +[dependencies]
> +

I believe I saw a later patch make the sole change to this file of
removing the unnecessary blank line at the end of this file. It would
be better to fix this patch to omit the line in the first place.

^ permalink raw reply	[flat|nested] 100+ messages in thread

* Re: [PATCH v2 14/18] build-helper: cbindgen, let crates generate a header file
  2025-09-17  1:16   ` [PATCH v2 14/18] build-helper: cbindgen, let crates generate a header file Ezekiel Newren via GitGitGadget
@ 2025-09-17  9:08     ` Eric Sunshine
  2025-09-19 20:34       ` Ezekiel Newren
  0 siblings, 1 reply; 100+ messages in thread
From: Eric Sunshine @ 2025-09-17  9:08 UTC (permalink / raw)
  To: Ezekiel Newren via GitGitGadget; +Cc: git, Ezekiel Newren

On Tue, Sep 16, 2025 at 9:18 PM Ezekiel Newren via GitGitGadget
<gitgitgadget@gmail.com> wrote:
> build-helper: cbindgen, let crates generate a header file
>
> Signed-off-by: Ezekiel Newren <ezekielnewren@gmail.com>
> ---

It would be nice to have a bit more explanation of what this patch is
doing and why. I presume that this is adding boilerplate functionality
but that there are no clients of this functionality yet. Is that
correct?

> diff --git a/rust/cbindgen-template.toml b/rust/cbindgen-template.toml
> @@ -0,0 +1,16 @@
> +## compat/posix.h includes stdbool.h where git-compat-util.h does not
> +## this is mandatory for correct bool C <-> Rust interop. Though the
> +## 'includes' (and all other variables in this file) can be
> +## overridden in build.rs.

There seems to be some missing punctuation in the above comment, and
the final sentence seems to be incomplete, both of which make it
difficult to understand what is being said.

> +sys_includes = ["compat/posix.h"]
> +
> +autogen_warning = "/* Warning, this file is autogenerated by cbindgen. Don't modify this manually. */"
> +
> +language = "C"
> +no_includes = true
> +usize_is_size_t = true
> +style = "tag"
> +tab_width = 4

It's a minor thing and probably not super important but I'm wondering
if it would be a good idea to follow project guidelines and make the
TAB width 8 rather than 4. Doing so might be appreciated by people
reading the generated header files.

^ permalink raw reply	[flat|nested] 100+ messages in thread

* Re: [PATCH v2 16/18] build: new crate, misc
  2025-09-17  1:16   ` [PATCH v2 16/18] build: new crate, misc Ezekiel Newren via GitGitGadget
@ 2025-09-17  9:16     ` Eric Sunshine
  2025-09-19 20:42     ` Ezekiel Newren
  1 sibling, 0 replies; 100+ messages in thread
From: Eric Sunshine @ 2025-09-17  9:16 UTC (permalink / raw)
  To: Ezekiel Newren via GitGitGadget; +Cc: git, Ezekiel Newren

On Tue, Sep 16, 2025 at 9:18 PM Ezekiel Newren via GitGitGadget
<gitgitgadget@gmail.com> wrote:
> build: new crate, misc
>
> Signed-off-by: Ezekiel Newren <ezekielnewren@gmail.com>
> ---

Please extend the commit message to at least give _some_ information
about what the "misc" crate is all about since it is not at all
apparent based upon the name.

By the way, is "misc" really a good name? It sounds like it's going to
be a dumping ground for anything which doesn't fit anywhere else.

> diff --git a/rust/misc/Cargo.toml b/rust/misc/Cargo.toml
> @@ -0,0 +1,14 @@
> +[lib]
> +name = "misc"
> +path = "src/lib.rs"
> +## staticlib to generate xdiff.a for use by gcc
> +## cdylib (optional) to generate xdiff.so for use by gcc
> +## rlib is required by the rust unit tests
> +crate-type = ["staticlib", "rlib"]
> +
> +[dependencies]
> diff --git a/rust/misc/src/lib.rs b/rust/misc/src/lib.rs
> new file mode 100644
> index 0000000000..e69de29bb2

Do I understand correctly from the comment that this crate has
something to do with "xdiff", yet there doesn't actually seem to be
anything here referencing "xdiff"? Am I missing something?

^ permalink raw reply	[flat|nested] 100+ messages in thread

* Re: [PATCH v2 01/18] cleanup: rename variables that collide with Rust primitive type names
  2025-09-17  7:42     ` Eric Sunshine
@ 2025-09-17 14:32       ` Junio C Hamano
  2025-09-19 19:36         ` Ezekiel Newren
  0 siblings, 1 reply; 100+ messages in thread
From: Junio C Hamano @ 2025-09-17 14:32 UTC (permalink / raw)
  To: Eric Sunshine; +Cc: Ezekiel Newren via GitGitGadget, git, Ezekiel Newren

Eric Sunshine <sunshine@sunshineco.com> writes:

> Same comment applies to the other renamed variables...
>
>> -               OPT_UNSIGNED(0, "u16", &u16, "get a 16 bit unsigned integer"),
>> +               OPT_UNSIGNED(0, "u16", &u16_, "get a 16 bit unsigned integer"),
>
> ... though with some of them, such as this one, it is admittedly more
> difficult to come up with a better name since the original name is
> already meaningful.

On top of that, just like a name that begins with an underscore is
reserved by C standard, a name taht ends with an underscore is used
for specific purposes by convention in this project.

Typically 'foo_' appears as a parameter to a function, whose type is
specified with our interhal API, and when that type is cumbersome to
work with, we have a local equivalent 'foo' of a more appropriate
type whose value is populated from 'foo_' before being used, and use
of 'foo' thanks to its better type is more ergonomic.

    static int compare_pt(const void *a_, const void *b_)
    {
            struct possible_tag *a = (struct possible_tag *)a_;
            struct possible_tag *b = (struct possible_tag *)b_;
            if (a->depth != b->depth)
                    return a->depth - b->depth;
            if (a->found_order != b->found_order)
                    return a->found_order - b->found_order;
            return 0;
    }

    static int path_is_beyond_symlink(struct apply_state *state, const char *name_)
    {
            int ret;
            struct strbuf name = STRBUF_INIT;

            assert(*name_ != '\0');
            strbuf_addstr(&name, name_);
            ret = path_is_beyond_symlink_1(state, &name);
            strbuf_release(&name);

            return ret;
    }

are examples.

There are existing crappy code that uses foo_ without corresponding foo;
we should clean them up, not emulating or spreading the pattern.

By the way, in this partcular case, why not use "uint16_t u16"?

Isn't the true cause of the trouble the (I might say "misguided")
desire to use "u16" as a type in C code?  As long as we all agree
that the data that can be passed across the ffi barrier should be of
the types of known size, and let C side use uint(8|16|32|64)_t and
Rust side use u(8|16|32|64) consistently, we do not need to have
this "cleanup", do we?

Thanks.

^ permalink raw reply	[flat|nested] 100+ messages in thread

* Re: [PATCH v2 07/18] build: introduce rust
  2025-09-17  8:26     ` Eric Sunshine
@ 2025-09-17 14:54       ` Junio C Hamano
  2025-09-18  7:06         ` Eric Sunshine
  2025-09-19 20:11       ` Ezekiel Newren
  1 sibling, 1 reply; 100+ messages in thread
From: Junio C Hamano @ 2025-09-17 14:54 UTC (permalink / raw)
  To: Eric Sunshine; +Cc: Ezekiel Newren via GitGitGadget, git, Ezekiel Newren

Eric Sunshine <sunshine@sunshineco.com> writes:

>> diff --git a/.gitignore b/.gitignore
>> @@ -254,3 +254,6 @@ Release/
>>  /contrib/buildsystems/out
>>  /contrib/libgit-rs/target
>>  /contrib/libgit-sys/target
>> +.idea/
>> +/rust/target/
>> +/rust/Cargo.lock
>
> Is ".idea" directory detritus from your chosen editor? If so, it
> probably ought not be added to this list since we don't otherwise
> ignore detritus from foreign tools such as that.

I knew that the above was our official stance but somehow thought
that we loosened at some point to add common ones like *~ and
*.sw[op] to make life simpler for majority of developers.  But I was
wrong.  We do not even have *~, which means we haven't officially
loosened.

But there are oddballs that violate this policy like ".cache"
introduced by a5c01603 (gitignore: ignore clangd .cache directory,
2023-08-04).  Three are many other that are *not* our droppings,
between /.vscode/ to /contrib/buildsystems/out in .gitignore file.

    /.vscode/
    /tags
    /TAGS
    /cscope*
    /compile_commands.json
    /.cache/
    *.hcc
    *.obj
    *.lib
    *.sln
    *.sp
    *.suo
    *.ncb
    *.vcproj
    *.user
    *.idb
    *.pdb
    *.ilk
    *.iobj
    *.ipdb
    *.dll
    .vs/
    Debug/
    Release/
    /UpgradeLog*.htm
    /git.VC.VC.opendb
    /git.VC.db
    *.dSYM

Some (like TAGS and *.hcc) are our droppings (in other words, what
"make" with some build targets may produce), but most of these are
tool specific and according to our original official stance, they
should never have been added, but there they are.

I actually do not mind having common ones to the project .gitignore
as long as it does not get bloated too much with droppings from
esoteric tools that majority of us have never heard of.  It seems
that we have been punishing needlessly Emacs and vim users while
being sloppy about others' droppings.  A #leftoverbit may be to
have a brief discussion to gain consensus and add a few common ones
and/or remove too esoteric ones?  I dunno. 

>> +$(GIT_BUILD_DIR)/lib%.a:
>> +       ./build_rust.sh $(GIT_BUILD_DIR) $(RUST_BUILD_MODE) $*
>
> I wonder if this should follow the convention of most (but not quite
> all) other local shell script invocations in the Makefile of
> referencing SHELL_PATH; for instance:
>
>     $(QUIET_GEN)$(SHELL_PATH) ./build_rust.sh ...etc...
>
> with or without the $(QUIET_GEN) bit.

Good suggestion.  This reminds me of my favorite gripe, which is
that "make clean" makes no effort to be quiet by default; perhaps
leaving a #leftover bit marker here would help?

Thanks.

^ permalink raw reply	[flat|nested] 100+ messages in thread

* Re: [PATCH v2 00/18] Introduce rust: In xdiff
  2025-09-17  1:16 ` [PATCH v2 00/18] Introduce rust: In xdiff Ezekiel Newren via GitGitGadget
                     ` (18 preceding siblings ...)
  2025-09-17  5:58   ` [PATCH v2 00/18] Introduce rust: In xdiff Patrick Steinhardt
@ 2025-09-17 17:07   ` Junio C Hamano
  2025-09-17 20:44     ` Junio C Hamano
  2025-09-17 21:34     ` Elijah Newren
  2025-09-23 18:05   ` Ezekiel Newren
  20 siblings, 2 replies; 100+ messages in thread
From: Junio C Hamano @ 2025-09-17 17:07 UTC (permalink / raw)
  To: Ezekiel Newren via GitGitGadget; +Cc: git, Ezekiel Newren

"Ezekiel Newren via GitGitGadget" <gitgitgadget@gmail.com> writes:

> This is a continuation of
> https://lore.kernel.org/git/pull.1980.v3.git.git.1755921356.gitgitgadget@gmail.com/,
> but I am removing the RFC label.

Continuation meaning that these patches build on top of that earlier
15-patch series?

> Suggestions on changes that I could make to this series is appreciated.
>
> Changes in v2: High level overview:
>
>  * patch 1: cleanup: rename variables that collide with Rust primitive type
>    names
>  * patches 2-4: Makefile now produces libgit.a correctly
>  * patches 5,6: Documentation from Brian and Patrick
>  * patches 7,8: Introduce Rust
>  * patches 9-11: github workflows
>  * patches 12-14: introduce crates build-helper, and cbindgen
>  * patches 15-18: varint test balloon
>
> I would like feed back in two categories: big changes, and little changes.

This seems to also mix in some patches from Patrick's series that
are already in flight.  What's the intention of the inclusion?  Do
you expect us to discard Patrick's series and replace with this,
which would lose some from them and then add more from here?  Your
"pull request" may target my "master/main" branch, but it needs to
play well together with other topics in flight that are cooking in
'next' and also with other topics that are aspiring to be in 'next'.

So I can figure out that these patches are designed to apply cleanly
on top of Git v2.51.0, I am somewhat lost what you want to do with
the resulting branch.  Having duplicate commits that happen to do
the same thing in multiple branches "git" the tool can handle just
fine, but that certainly is a bad communication among developers
that we do not want to particularly encourage.

Before talking about "big" and "little" changes, do we need to talk
about the series organization and working well among multiple
developers?

Thanks.

^ permalink raw reply	[flat|nested] 100+ messages in thread

* Re: [PATCH v2 00/18] Introduce rust: In xdiff
  2025-09-17 17:07   ` Junio C Hamano
@ 2025-09-17 20:44     ` Junio C Hamano
  2025-09-17 21:34     ` Elijah Newren
  1 sibling, 0 replies; 100+ messages in thread
From: Junio C Hamano @ 2025-09-17 20:44 UTC (permalink / raw)
  To: Ezekiel Newren via GitGitGadget, Patrick Steinhardt; +Cc: git, Ezekiel Newren

Junio C Hamano <gitster@pobox.com> writes:

> "Ezekiel Newren via GitGitGadget" <gitgitgadget@gmail.com> writes:
>
>> This is a continuation of
>> https://lore.kernel.org/git/pull.1980.v3.git.git.1755921356.gitgitgadget@gmail.com/,
>> but I am removing the RFC label.
>
> Continuation meaning that these patches build on top of that earlier
> 15-patch series?
>
>> Suggestions on changes that I could make to this series is appreciated.
>>
>> Changes in v2: High level overview:
>>
>>  * patch 1: cleanup: rename variables that collide with Rust primitive type
>>    names
>>  * patches 2-4: Makefile now produces libgit.a correctly
>>  * patches 5,6: Documentation from Brian and Patrick
>>  * patches 7,8: Introduce Rust
>>  * patches 9-11: github workflows
>>  * patches 12-14: introduce crates build-helper, and cbindgen
>>  * patches 15-18: varint test balloon
>>
>> I would like feed back in two categories: big changes, and little changes.
>
> This seems to also mix in some patches from Patrick's series that
> are already in flight. ...
> ...  Having duplicate commits that happen to do
> the same thing in multiple branches "git" the tool can handle just
> fine, but that certainly is a bad communication among developers
> that we do not want to particularly encourage.

This seems to be even more confusing than I thought.  The text in
the breaking changes document may be based on Patrick's patch, but
it says something different from any of the five iterations of the
original series.  Since RFC v2 on Sep 5th [*], the introduction
schedule described there had multiple steps before Git 3.0 but the
text we see in this series plans to start Rust "on" by default and
goes directly to Git 3.0 without any other intermediate steps, for
example.  How was [v2 06/18], which attributes authorship to
Patrick, came into this shape?  Is this his update over the [v5 7/9]
of his series sent outside the mailing list, or did you and Patrick
working together to arrive at this updated text?


[References]

* Patrick's rust-balloon series (various iterations)

https://lore.kernel.org/git/20250905-b4-pks-rust-breaking-change-v2-5-6939cbf4a0b8@pks.im/
https://lore.kernel.org/git/20250908-b4-pks-rust-breaking-change-v3-6-1cd7189fed3b@pks.im/
https://lore.kernel.org/git/20250910-b4-pks-rust-breaking-change-v4-7-4a63fc69278d@pks.im/
https://lore.kernel.org/git/20250915-b4-pks-rust-breaking-change-v5-7-dc3a32fbb216@pks.im/


^ permalink raw reply	[flat|nested] 100+ messages in thread

* Re: [PATCH v2 00/18] Introduce rust: In xdiff
  2025-09-17 17:07   ` Junio C Hamano
  2025-09-17 20:44     ` Junio C Hamano
@ 2025-09-17 21:34     ` Elijah Newren
  2025-09-17 22:48       ` Junio C Hamano
  1 sibling, 1 reply; 100+ messages in thread
From: Elijah Newren @ 2025-09-17 21:34 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: Ezekiel Newren via GitGitGadget, git, Ezekiel Newren,
	Patrick Steinhardt

On Wed, Sep 17, 2025 at 10:07 AM Junio C Hamano <gitster@pobox.com> wrote:
>
> "Ezekiel Newren via GitGitGadget" <gitgitgadget@gmail.com> writes:
>
> > This is a continuation of
> > https://lore.kernel.org/git/pull.1980.v3.git.git.1755921356.gitgitgadget@gmail.com/,
> > but I am removing the RFC label.
>
> Continuation meaning that these patches build on top of that earlier
> 15-patch series?

No, continuation just meaning "this is v4, even if not labelled as
such by gitgitgadget".  "Replacement for" would have been clearer.

> > Suggestions on changes that I could make to this series is appreciated.
> >
> > Changes in v2: High level overview:
> >
> >  * patch 1: cleanup: rename variables that collide with Rust primitive type
> >    names
> >  * patches 2-4: Makefile now produces libgit.a correctly
> >  * patches 5,6: Documentation from Brian and Patrick
> >  * patches 7,8: Introduce Rust
> >  * patches 9-11: github workflows
> >  * patches 12-14: introduce crates build-helper, and cbindgen
> >  * patches 15-18: varint test balloon
> >
> > I would like feed back in two categories: big changes, and little changes.
>
> This seems to also mix in some patches from Patrick's series that
> are already in flight.  What's the intention of the inclusion?  Do
> you expect us to discard Patrick's series and replace with this,
> which would lose some from them and then add more from here?  Your
> "pull request" may target my "master/main" branch, but it needs to
> play well together with other topics in flight that are cooking in
> 'next' and also with other topics that are aspiring to be in 'next'.

The lack of context in the cover letter is a good point; I apologize
for not having been available to advise and avoid that problem;
Ezekiel's been doing the best he can on his own for a while now with
only very sporadic comments from me, due to my daughter's recent
medical emergency (and then me being multiple weeks behind on
everything else because of that).

> So I can figure out that these patches are designed to apply cleanly
> on top of Git v2.51.0, I am somewhat lost what you want to do with
> the resulting branch.  Having duplicate commits that happen to do
> the same thing in multiple branches "git" the tool can handle just
> fine, but that certainly is a bad communication among developers
> that we do not want to particularly encourage.
>
> Before talking about "big" and "little" changes, do we need to talk
> about the series organization and working well among multiple
> developers?

I think that would be helpful, including some guidance on next steps
because even I don't know what to advise at this point.  The
background so far:

* Ezekiel contributed a series to introduce Rust[1].  Someone
contributed a competing series and Ezekiel was asked to target
somewhere else[2].
* I made a suggestion for that somewhere else, you agreed that it
sounded reasonable[3], and Ezekiel complied and redid the series
accordingly.
* Patrick wanted the introduction to be different, which is fair.
However, instead of providing feedback or suggesting doing things an
alternative way, Patrick submitted a competing series that redid the
Rust introduction without incorporating any of Ezekiel's work[3], that
was far less complete (e.g. no Makefile support, not running on all CI
platforms) and saying he'd personally add that stuff later[5], and the
series had a few things that both brian and Ezekiel objected to (e.g.
cargo delegation, ambiguous types, minimum version which Ezekiel
already demonstrated was insufficient).  I think the thought here on
Patrick's side may have been that Ezekiel's focus was solely on xdiff,
rather than having a dual focus on xdiff and Rust, but Patrick left no
guidance for Ezekiel on how he could move forward with the other Rust
parts Ezekiel did or even whether he was welcome to make contributions
in the area.
* With no feedback on how to move forward, Ezekiel wanted to try to
merge the relevant parts of the two series, including playing nice by
incorporating some of Patrick's changes -- and commented on Patrick's
series to that effect[6].  Linking that email (or even incorporating
it) into the cover letter would have been helpful, but he's new and
missed that.

(Personally, I think it would have been much better for Patrick to
send in a series with _just_ the BreakingChanges stuff, and then send
patches to Ezekiel with the help & varint stuff, similar to what Dscho
did with git-for-windows & Rust[7], and ask Ezekiel to make a test
balloon for introducing Rust.  Allowing new contributors to get some
credit for their work instead of replacing/discarding it tends to go
over better.  And, to be fully blunt, I think introducing a competing
or replacement series to something actively being worked ought to be
more of a last resort whether or not the other contributor is new; but
I'll stop there since otherwise folks might dig up my blow up -- that
I'm not too proud of -- from some years ago when someone did this to
me.)

[1] https://lore.kernel.org/git/pull.1980.git.git.1752784344.gitgitgadget@gmail.com/
[2] https://lore.kernel.org/git/xmqqldnggt2v.fsf@gitster.g/
[3] https://lore.kernel.org/git/xmqqzfbvfxs6.fsf@gitster.g/
[4] https://lore.kernel.org/git/20250910-b4-pks-rust-breaking-change-v4-0-4a63fc69278d@pks.im/
[5] https://lore.kernel.org/git/aME1ETcGAbhoO49n@pks.im/
[6] https://lore.kernel.org/git/CAH=ZcbB0Qv=b-hdB2EVW-D-dob4NnzyWDYGEThYZm94S0V7OGg@mail.gmail.com/
[7] https://lore.kernel.org/git/ac871bc4-df93-31f4-55f2-d6fc538a422d@gmx.de/

So, how to move forward?

A) Modify Patrick's series to just take patch 7 of his v5.  Patrick
did say that the roadmap was  "the more important discussion compared
to the technical discussion", and merging that patch would achieve his
goal of getting an initial roadmap.  Then Ezekiel could grab other
pieces from Patrick's series (e.g. the help and varint stuff) and
incorporate it into an "introduce rust" series.[*]

B) Merge Patrick's series and tell Ezekiel to rebase, while noting to
Ezekiel that the roadmap is the important bit from Patrick's series[*]
and he can suggest changes to any of the other bits.

C) Create a consolidated "introduce Rust" series with bits of both --
what I think Ezekiel was trying to do with this series.

D) Something else?


I think Ezekiel's willing to be flexible here, but was doing the best
he could as a new contributor with no guidance.  What would you like
to see, Junio?



[*] Either of these routes are slightly funny, though because Patrick
introduced a one-quarter delay on notification to end users of the
upcoming change due to the incompleteness of his series.  With
Ezekiel's changes to build on all platforms in CI, that delay would
serve no purpose (that I see) and only harm users by giving them less
notice of an upcoming change; so Ezekiel would likely want to tweak
the BreakingChanges document in either of these cases to note that
we'll give earlier notification to users.

^ permalink raw reply	[flat|nested] 100+ messages in thread

* Re: [PATCH v2 00/18] Introduce rust: In xdiff
  2025-09-17 21:34     ` Elijah Newren
@ 2025-09-17 22:48       ` Junio C Hamano
  2025-09-22 13:01         ` Patrick Steinhardt
  0 siblings, 1 reply; 100+ messages in thread
From: Junio C Hamano @ 2025-09-17 22:48 UTC (permalink / raw)
  To: Elijah Newren
  Cc: Ezekiel Newren via GitGitGadget, git, Ezekiel Newren,
	Patrick Steinhardt

Elijah Newren <newren@gmail.com> writes:

> So, how to move forward?
>
> A) Modify Patrick's series to just take patch 7 of his v5.  Patrick
> did say that the roadmap was  "the more important discussion compared
> to the technical discussion", and merging that patch would achieve his
> goal of getting an initial roadmap.  Then Ezekiel could grab other
> pieces from Patrick's series (e.g. the help and varint stuff) and
> incorporate it into an "introduce rust" series.[*]
>
> B) Merge Patrick's series and tell Ezekiel to rebase, while noting to
> Ezekiel that the roadmap is the important bit from Patrick's series[*]
> and he can suggest changes to any of the other bits.
>
> C) Create a consolidated "introduce Rust" series with bits of both --
> what I think Ezekiel was trying to do with this series.

Ah, I didn't even realize C was what this series was trying to do.

I do not have particular preference between A and B, but I thought A
was closer to what was being done with this series, and as long as
Ezekiel and Patrick can join forces that way, it would be perfect.

Thanks.

^ permalink raw reply	[flat|nested] 100+ messages in thread

* Re: [PATCH v2 13/18] build-helper: link against libgit.a and any other required C libraries
  2025-09-17  8:51     ` Eric Sunshine
@ 2025-09-17 23:07       ` D. Ben Knoble
  2025-09-17 23:31         ` Eric Sunshine
  2025-09-19 20:25       ` Ezekiel Newren
  1 sibling, 1 reply; 100+ messages in thread
From: D. Ben Knoble @ 2025-09-17 23:07 UTC (permalink / raw)
  To: Eric Sunshine; +Cc: Ezekiel Newren via GitGitGadget, git, Ezekiel Newren

On Wed, Sep 17, 2025 at 4:52 AM Eric Sunshine <sunshine@sunshineco.com> wrote:
>
> On Tue, Sep 16, 2025 at 9:18 PM Ezekiel Newren via GitGitGadget
> > +impl BuildHelper {
> > +    pub fn build(self) {
> > +        let use_linking = parse_bool_from_option(self.crate_env.get("USE_LINKING"), self.crate_env.get("CARGO_TARGET_DIR").is_none());
> > +        ...
> > +            println!("cargo:warning={} is not linking against C objects, `USE_LINKING=true cargo test`", self.crate_env["CARGO_PKG_NAME"]);
>
> There are more than a few developers on this project (including
> myself) who still use 80-column editors and terminals. As a general
> style guideline, this project does recommend wrapping code to fit
> within 80 columns (except in cases when doing so would severely hurt
> readability). I imagine that the same sort of guideline would be
> appreciated in Rust code, as well, by those who still stick with 80
> columns.
>
> I bring this up because, although it hasn't been such a big deal with
> the existing C code, assuming that developers run `rustfmt` on the
> code before sending a patch series, then this may become an issue if
> different developers have `rustfmt` configured to enforce different
> maximum column width, especially since `rustfmt` is likely to reformat
> the entire file rather than just the region that has just been edited.
> So, if this code gets checked in as-is with these very wide lines, and
> then someone else, who has `rustfmt` configured for 80-columns edits
> the file, then it becomes a problem.
>
> As such, can we also add a project-wide `rustfmt.toml` which, at
> minimum, sets the maximum line width to 80? For instance:
>
>     max_width = 80
>

I believe rustfmt and Rust generally uses a 100 column limit.

See
- https://github.com/rust-lang/rust/pull/5340
- https://rust-lang.github.io/rustfmt/?version=v1.8.0&search=#max_width

Perhaps you knew this, and are really suggesting we break with Rust
style for our own style?

For handling personal configs, we could try to maintain a config here
that repeats all the defaults, but that seems like more maintenance
headache than noticing unnecessary reformatting and reminding folks we
use the defaults (except where we have config that specifies
otherwise). Although the docs site doesn’t suggest the configs are
layered like for Git, so perhaps a blank config is sufficient to
enforce « we use the defaults »?

-- 
D. Ben Knoble

^ permalink raw reply	[flat|nested] 100+ messages in thread

* Re: [PATCH v2 13/18] build-helper: link against libgit.a and any other required C libraries
  2025-09-17 23:07       ` D. Ben Knoble
@ 2025-09-17 23:31         ` Eric Sunshine
  0 siblings, 0 replies; 100+ messages in thread
From: Eric Sunshine @ 2025-09-17 23:31 UTC (permalink / raw)
  To: D. Ben Knoble; +Cc: Ezekiel Newren via GitGitGadget, git, Ezekiel Newren

On Wed, Sep 17, 2025 at 7:07 PM D. Ben Knoble <ben.knoble@gmail.com> wrote:
> On Wed, Sep 17, 2025 at 4:52 AM Eric Sunshine <sunshine@sunshineco.com> wrote:
> > There are more than a few developers on this project (including
> > myself) who still use 80-column editors and terminals. As a general
> > style guideline, this project does recommend wrapping code to fit
> > within 80 columns (except in cases when doing so would severely hurt
> > readability). I imagine that the same sort of guideline would be
> > appreciated in Rust code, as well, by those who still stick with 80
> > columns.
> >
> > I bring this up because, although it hasn't been such a big deal with
> > the existing C code, assuming that developers run `rustfmt` on the
> > code before sending a patch series, then this may become an issue if
> > different developers have `rustfmt` configured to enforce different
> > maximum column width, especially since `rustfmt` is likely to reformat
> > the entire file rather than just the region that has just been edited.
> > So, if this code gets checked in as-is with these very wide lines, and
> > then someone else, who has `rustfmt` configured for 80-columns edits
> > the file, then it becomes a problem.
> >
> > As such, can we also add a project-wide `rustfmt.toml` which, at
> > minimum, sets the maximum line width to 80? For instance:
> >
> >     max_width = 80
>
> I believe rustfmt and Rust generally uses a 100 column limit.
>
> Perhaps you knew this, and are really suggesting we break with Rust
> style for our own style?

Correct, I did know that, and I also know that there are a number of
developers on the Git project who still adhere to (perhaps archaic)
80-column editor and terminal window sizes.

Project guidelines have long suggested 80 columns as a desirable
maximum not only for C code, but for pretty much all other resources,
including shell code, Perl code, and documentation files. This
suggested maximum works well for adherents of 80-columns and
(presumably) hasn't been too onerous for developers who use wider
windows; at least we haven't heard people clamoring to increase the
suggested maximum column limit. As such, it does not seem far-fetched
to expect that the project guidelines should/could/would also apply to
Rust code.

> For handling personal configs, we could try to maintain a config here
> that repeats all the defaults, but that seems like more maintenance
> headache than noticing unnecessary reformatting and reminding folks we
> use the defaults (except where we have config that specifies
> otherwise). Although the docs site doesn’t suggest the configs are
> layered like for Git, so perhaps a blank config is sufficient to
> enforce « we use the defaults »?

Sorry, I'm not following what you are asking. I'm simply suggesting
the possibility of adding a toplevel "$GIT/.rustfmt.toml" to the
project with the single line content:

    max_width = 80

That aside, we probably also ought to insist (via documentation) that
contributors run `rustfmt` on Rust code before submitting a patch
series.

^ permalink raw reply	[flat|nested] 100+ messages in thread

* Re: [PATCH v2 07/18] build: introduce rust
  2025-09-17 14:54       ` Junio C Hamano
@ 2025-09-18  7:06         ` Eric Sunshine
  0 siblings, 0 replies; 100+ messages in thread
From: Eric Sunshine @ 2025-09-18  7:06 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Ezekiel Newren via GitGitGadget, git, Ezekiel Newren

On Wed, Sep 17, 2025 at 10:54 AM Junio C Hamano <gitster@pobox.com> wrote:
> Eric Sunshine <sunshine@sunshineco.com> writes:
> >> +.idea/
> >
> > Is ".idea" directory detritus from your chosen editor? If so, it
> > probably ought not be added to this list since we don't otherwise
> > ignore detritus from foreign tools such as that.
>
> I knew that the above was our official stance but somehow thought
> that we loosened at some point to add common ones like *~ and
> *.sw[op] to make life simpler for majority of developers.  But I was
> wrong.  We do not even have *~, which means we haven't officially
> loosened.
>
> But there are oddballs that violate this policy like ".cache"
> introduced by a5c01603 (gitignore: ignore clangd .cache directory,
> 2023-08-04).  Three are many other that are *not* our droppings,
> between /.vscode/ to /contrib/buildsystems/out in .gitignore file.
>
>     /.vscode/
>     /tags
>     /TAGS
>     /cscope*
>     /compile_commands.json
>     /.cache/
>     *.hcc
>     *.obj
>     *.lib
>     *.sln
>     *.sp
>     *.suo
>     *.ncb
>     *.vcproj
>     *.user
>     *.idb
>     *.pdb
>     *.ilk
>     *.iobj
>     *.ipdb
>     *.dll
>     .vs/
>     Debug/
>     Release/
>     /UpgradeLog*.htm
>     /git.VC.VC.opendb
>     /git.VC.db
>     *.dSYM
>
> Some (like TAGS and *.hcc) are our droppings (in other words, what
> "make" with some build targets may produce), but most of these are
> tool specific and according to our original official stance, they
> should never have been added, but there they are.

It's been a while since I had to build code with Visual Studio (the
proprietary Microsoft product, not the open-source VScode), but if I
recall correctly, all the entries from ".obj" through "Release/" are
build detritus from compiling with that tool. Assuming we still
support building Git with Visual Studio (which I believe is the case),
then those entries all fall within the same categorization as "our
droppings" similar to the `make` case, so having these in ".gitignore"
is probably in line with the project's official stance.

Others, such as "/.vscode/", on the other hand, fall into the other
category of "someone's favorite editor or handy tool", which has thus
far been frowned upon.

> I actually do not mind having common ones to the project .gitignore
> as long as it does not get bloated too much with droppings from
> esoteric tools that majority of us have never heard of.  It seems
> that we have been punishing needlessly Emacs and vim users while
> being sloppy about others' droppings.  A #leftoverbit may be to
> have a brief discussion to gain consensus and add a few common ones
> and/or remove too esoteric ones?  I dunno.

I don't have a strong opinion aside from avoiding bloat; I've seen
(and inherited at $DAYJOBS) far too many projects which grab some
overly bloated .gitignore template from somewhere in which most of the
entries are meaningless for the project at hand, yet which is used
as-is rather than pruning out the unneeded entries (typically >95% of
them). As with dead code, those unneeded .gitignore entries tend to be
a source of potential confusion, which is rarely or never the case
with a well-curated .gitignore (or with well-curated code).

That said, I also probably do not mind having the common ones in Git's
.gitignore. A few which come to mind include:

* editor-specific droppings (Emacs, vi / vim, VScode)
* .DS_Store files which macOS's Finder drops into *every* directory it
visits, thus which litter the filesystem

^ permalink raw reply	[flat|nested] 100+ messages in thread

* Re: [PATCH v2 04/18] make: merge reftable lib into libgit.a
  2025-09-17  1:16   ` [PATCH v2 04/18] make: merge reftable " Ezekiel Newren via GitGitGadget
  2025-09-17  7:46     ` Eric Sunshine
@ 2025-09-19 19:02     ` Junio C Hamano
  2025-09-19 20:00       ` Ezekiel Newren
  1 sibling, 1 reply; 100+ messages in thread
From: Junio C Hamano @ 2025-09-19 19:02 UTC (permalink / raw)
  To: Ezekiel Newren via GitGitGadget; +Cc: git, Ezekiel Newren

"Ezekiel Newren via GitGitGadget" <gitgitgadget@gmail.com> writes:

> From: Ezekiel Newren <ezekielnewren@gmail.com>
>
> Signed-off-by: Ezekiel Newren <ezekielnewren@gmail.com>
> ---
>  Makefile | 39 ++++++++++++++++-----------------------
>  1 file changed, 16 insertions(+), 23 deletions(-)

Aside from the comment already given about the fact that the
proposed log message does not explain any reason why these change
are necessary, this step and the previous step are fairly hostile to
merging the topic to play well with other topics, especially given
that there would be topics in flight that may want to add, remove,
or reorder these two existing lists.

I wonder if these could have been arranged like the following instead?

 * Drop "REFTABLE_LIB = reftable/libreftable.a" and the target that
   runs "ar" to mantain that archive.

 * Leave "REFTABLE_OBJS += $objects.o" lines alone.

 * Add them into LIB_OBJS so that they are included in libgit.a,
   perhaps a single line like this:

	LIB_OBJS += $(REFTABLE_OBJS)

Wouldn't that have worked equally well for the (unstated) purpose of
these two patches without incurring unnecessary risk of mismerges?

Similar arrangement for xdiff.

Thanks.

^ permalink raw reply	[flat|nested] 100+ messages in thread

* Re: [PATCH v2 01/18] cleanup: rename variables that collide with Rust primitive type names
  2025-09-17 14:32       ` Junio C Hamano
@ 2025-09-19 19:36         ` Ezekiel Newren
  0 siblings, 0 replies; 100+ messages in thread
From: Ezekiel Newren @ 2025-09-19 19:36 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Eric Sunshine, Ezekiel Newren via GitGitGadget, git

On Wed, Sep 17, 2025 at 8:32 AM Junio C Hamano <gitster@pobox.com> wrote:
> Isn't the true cause of the trouble the (I might say "misguided")
> desire to use "u16" as a type in C code?  As long as we all agree
> that the data that can be passed across the ffi barrier should be of
> the types of known size, and let C side use uint(8|16|32|64)_t and
> Rust side use u(8|16|32|64) consistently, we do not need to have
> this "cleanup", do we?

It appears that I am the only one in favor of using Rust type names in
C. Going forward I'll use [ui]int(8|16|32|64)_t types going forward.
I'll drop this commit.

^ permalink raw reply	[flat|nested] 100+ messages in thread

* Re: [PATCH v2 02/18] make: add -fPIE flag
  2025-09-17  7:44     ` Eric Sunshine
@ 2025-09-19 19:48       ` Ezekiel Newren
  2025-09-19 20:07         ` Junio C Hamano
  0 siblings, 1 reply; 100+ messages in thread
From: Ezekiel Newren @ 2025-09-19 19:48 UTC (permalink / raw)
  To: Eric Sunshine; +Cc: Ezekiel Newren via GitGitGadget, git

On Wed, Sep 17, 2025 at 1:45 AM Eric Sunshine <sunshine@sunshineco.com> wrote:
> The commit message explains what the patch is doing (which is obvious
> from the patch itself) but doesn't explain why this change is needed,
> hence it is very difficult for a reviewer to understand why such a
> change is necessary.

I was trying to get the rust compiler to link against libgit.a. Both
Meson and Makefile produce libgit.a. No changes were to meson.build
were necessary for that to work, but but with Makefile I would get
errors like:
relocation R_X86_64_32 against `.rodata' can not be used when making a
PIE object; recompile with -fPIE
...

I should have added this to the commit message.

^ permalink raw reply	[flat|nested] 100+ messages in thread

* Re: [PATCH v2 03/18] make: merge xdiff lib into libgit.a
  2025-09-17  7:46     ` Eric Sunshine
@ 2025-09-19 19:54       ` Ezekiel Newren
  0 siblings, 0 replies; 100+ messages in thread
From: Ezekiel Newren @ 2025-09-19 19:54 UTC (permalink / raw)
  To: Eric Sunshine; +Cc: Ezekiel Newren via GitGitGadget, git

On Wed, Sep 17, 2025 at 1:46 AM Eric Sunshine <sunshine@sunshineco.com> wrote:
> Please extend the commit message to explain why this change is desirable.

This commit continues the effort of making the Rust compiler link
against libgit.a. Meson already includes xdiff when it builds
libgit.a, but Makefile does not. Rather than telling the Rust compiler
to also link against xdiff/lib.a, if Makefile was used as opposed to
Meson, seemed like a clunkier solution.

I should have asked for a better way to accomplish this as part of my
commit message or as a question on the mailing list.

^ permalink raw reply	[flat|nested] 100+ messages in thread

* Re: [PATCH v2 04/18] make: merge reftable lib into libgit.a
  2025-09-19 19:02     ` Junio C Hamano
@ 2025-09-19 20:00       ` Ezekiel Newren
  2025-09-19 20:14         ` Junio C Hamano
  0 siblings, 1 reply; 100+ messages in thread
From: Ezekiel Newren @ 2025-09-19 20:00 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Ezekiel Newren via GitGitGadget, git

On Fri, Sep 19, 2025 at 1:02 PM Junio C Hamano <gitster@pobox.com> wrote:
> Aside from the comment already given about the fact that the
> proposed log message does not explain any reason why these change
> are necessary, this step and the previous step are fairly hostile to
> merging the topic to play well with other topics, especially given
> that there would be topics in flight that may want to add, remove,
> or reorder these two existing lists.
>
> I wonder if these could have been arranged like the following instead?
>
>  * Drop "REFTABLE_LIB = reftable/libreftable.a" and the target that
>    runs "ar" to mantain that archive.
>
>  * Leave "REFTABLE_OBJS += $objects.o" lines alone.
>
>  * Add them into LIB_OBJS so that they are included in libgit.a,
>    perhaps a single line like this:
>
>         LIB_OBJS += $(REFTABLE_OBJS)
>
> Wouldn't that have worked equally well for the (unstated) purpose of
> these two patches without incurring unnecessary risk of mismerges?
>
> Similar arrangement for xdiff.

Like the previous two commits; This one continues the effort to get
the Rust compiler to link against libgit.a. Meson already includes the
reftable in its libgit.a, but Makefile does not.

The reason why I was trying to get the Rust compiler to link against
libgit.a is because I wanted to get Rusts unit testing to work. If the
Rust code calls a C function from Git then 'cargo test' needs to know
about it.

However I think I'll drop these 3 commits since 'cargo test' doesn't
need to be part of the introduction of Rust. It would be nice for make
to be able to run Rust unit tests at some point though.

^ permalink raw reply	[flat|nested] 100+ messages in thread

* Re: [PATCH v2 02/18] make: add -fPIE flag
  2025-09-19 19:48       ` Ezekiel Newren
@ 2025-09-19 20:07         ` Junio C Hamano
  2025-09-19 21:52           ` Ezekiel Newren
  0 siblings, 1 reply; 100+ messages in thread
From: Junio C Hamano @ 2025-09-19 20:07 UTC (permalink / raw)
  To: Ezekiel Newren; +Cc: Eric Sunshine, Ezekiel Newren via GitGitGadget, git

Ezekiel Newren <ezekielnewren@gmail.com> writes:

> I was trying to get the rust compiler to link against libgit.a. Both
> Meson and Makefile produce libgit.a. No changes were to meson.build
> were necessary for that to work, but but with Makefile I would get
> errors like:
> relocation R_X86_64_32 against `.rodata' can not be used when making a
> PIE object; recompile with -fPIE

This sounds like a workaround that is somewhat platform and compiler
specific limitation, at least to me.  Does this need to be conditional
and if so on what?

Thanks.

^ permalink raw reply	[flat|nested] 100+ messages in thread

* Re: [PATCH v2 07/18] build: introduce rust
  2025-09-17  8:26     ` Eric Sunshine
  2025-09-17 14:54       ` Junio C Hamano
@ 2025-09-19 20:11       ` Ezekiel Newren
  2025-09-19 20:24         ` Eric Sunshine
  1 sibling, 1 reply; 100+ messages in thread
From: Ezekiel Newren @ 2025-09-19 20:11 UTC (permalink / raw)
  To: Eric Sunshine; +Cc: Ezekiel Newren via GitGitGadget, git

On Wed, Sep 17, 2025 at 2:26 AM Eric Sunshine <sunshine@sunshineco.com> wrote:
> Is ".idea" directory detritus from your chosen editor? If so, it
> probably ought not be added to this list since we don't otherwise
> ignore detritus from foreign tools such as that.

Yes. I use the Jetbrains IDE's CLion and RustRover for C and Rust
respectively. Jetbrains has an IDE for MANY languages and all of them
use .idea/ as the folder for IDE specific configuration. I'm fine with
keeping it out of .gitignore, but I wanted to know what the community
thought. Which I should have made it more clear that this patch series
was more of an RFC than proposed code to be merged.

> > diff --git a/Makefile b/Makefile
> > @@ -916,6 +924,51 @@ TEST_SHELL_PATH = $(SHELL_PATH)
> > +RUST_CRATES :=
> > +.PHONY: compile_rust rustclean
>
> This is an extremely tiny nit, but multi-word target names in the Git
> Makefile are almost universally spelled with a hyphen rather than an
> underscore, so "compile-rust" would be more consistent and
> appropriate.
>
> > +$(GIT_BUILD_DIR)/lib%.a:
> > +       ./build_rust.sh $(GIT_BUILD_DIR) $(RUST_BUILD_MODE) $*
>
> I wonder if this should follow the convention of most (but not quite
> all) other local shell script invocations in the Makefile of
> referencing SHELL_PATH; for instance:
>
>     $(QUIET_GEN)$(SHELL_PATH) ./build_rust.sh ...etc...
>
> with or without the $(QUIET_GEN) bit.
>
> > diff --git a/build_rust.sh b/build_rust.sh
> > @@ -0,0 +1,60 @@
> > +if [ "$dir_git_root" = "" ]; then
> > +  echo "did not specify the directory for the root of git"
> > +  exit 1
> > +fi
>
> Let's follow project style conventions for shell scripts:
>
>     if test "$dir_git_root" = ""
>     then
>         ...
>     fi
>
> or even:
>
>     if test -z "$dir_git_root"
>     then
>         ...
>     fi
>
> Same comment applies to the rest of the script.
>
> Also, should the above error message be sent to stderr rather than
> stdout? (Genuine question since I'm not sure if other scripts are
> necessarily consistent in that regard.)
>
> > +if [ "$rust_build_profile" = "release" ]; then
> > +  rust_args="--release"
> > +  export RUSTFLAGS=''
> > +elif [ "$rust_build_profile" = "debug" ]; then
> > +  rust_args=""
> > +  export RUSTFLAGS='-C debuginfo=2 -C opt-level=1 -C force-frame-pointers=yes'
> > +else
> > +  echo "illegal rust_build_profile value $rust_build_profile"
> > +  exit 1
> > +fi
>
> For maximum portability, we don't use the:
>
>     export FOO=bar
>
> idiom in this project. Instead, do this:
>
>     FOO=bar
>     export FOO
>
> The same comment applies to the remainder of the patch, as well.
>
> Also, the empty-string assignments to RUSTFLAGS and rust_args above
> inconsistently use a mix of single and double quotes. However, we also
> usually just assign nothingness in such cases rather than an empty
> string:
>
>     RUSTFLAGS=
>     export RUSTFLAGS
>     ...
>     rust_args=

I'll try to keep these in mind for the future.

> > +if rustup show active-toolchain | grep windows-msvc; then
> > +  libfile="${crate}.lib"
> > +  PATH="$(echo $PATH | tr ':' '\n' | grep -Ev "^(/mingw64/bin|/usr/bin)$" | paste -sd: -):/mingw64/bin:/usr/bin"
> > +fi
>
> Please add either an in-code comment or a sentence/paragraph to the
> commit message explaining why this PATH munging is needed.

I will amend the commit with something like:
On windows when building with msvc using shell scripts it looks for
link in /mingw64/bin|/usr/bin when it actually needs to look somewhere
else for the msvc linker program. Since removing these from PATH would
break everything else in the shell; move them to be at the end of
PATH.

> > +cd $dir_rust && cargo clean && pwd && USE_LINKING="false" cargo build -p $crate $rust_args; cd $dir_git_root
>
> When formatted like this, it is very easy to overlook the fact that
> the final `cd` is (correctly) not &&-chained with the preceding
> commands. Reformatting it like this would help:
>
>     cd $dir_rust &&
>         cargo clean &&
>         pwd &&
>         USE_LINKING="false" cargo build -p $crate $rust_args
>     cd $dir_git_root
>
> Alternatively, use a subshell and drop the final `cd`:
>
>     (
>         cd $dir_rust &&
>         cargo clean &&
>         pwd &&
>         USE_LINKING="false" cargo build -p $crate $rust_args
>     )
>
> By the way, is the `pwd` command needed? What is its purpose?
>
> > +rm $dst 2>/dev/null
>
> I think we typically use `rm -f` without redirect to /dev/null to do
> the "right thing".
>
> > +echo mv $src $dst
> > +mv $src $dst

pwd was for debugging and I forgot to take it out. I'll clean up my script.

^ permalink raw reply	[flat|nested] 100+ messages in thread

* Re: [PATCH v2 04/18] make: merge reftable lib into libgit.a
  2025-09-19 20:00       ` Ezekiel Newren
@ 2025-09-19 20:14         ` Junio C Hamano
  2025-09-19 23:02           ` Ezekiel Newren
  0 siblings, 1 reply; 100+ messages in thread
From: Junio C Hamano @ 2025-09-19 20:14 UTC (permalink / raw)
  To: Ezekiel Newren; +Cc: Ezekiel Newren via GitGitGadget, git

Ezekiel Newren <ezekielnewren@gmail.com> writes:

> On Fri, Sep 19, 2025 at 1:02 PM Junio C Hamano <gitster@pobox.com> wrote:
>> Aside from the comment already given about the fact that the
>> proposed log message does not explain any reason why these change
>> are necessary, this step and the previous step are fairly hostile to
>> merging the topic to play well with other topics, especially given
>> that there would be topics in flight that may want to add, remove,
>> or reorder these two existing lists.
>>
>> I wonder if these could have been arranged like the following instead?
>>
>>  * Drop "REFTABLE_LIB = reftable/libreftable.a" and the target that
>>    runs "ar" to mantain that archive.
>>
>>  * Leave "REFTABLE_OBJS += $objects.o" lines alone.
>>
>>  * Add them into LIB_OBJS so that they are included in libgit.a,
>>    perhaps a single line like this:
>>
>>         LIB_OBJS += $(REFTABLE_OBJS)
>>
>> Wouldn't that have worked equally well for the (unstated) purpose of
>> these two patches without incurring unnecessary risk of mismerges?
>>
>> Similar arrangement for xdiff.
>
> Like the previous two commits; This one continues the effort to get
> ...
> The reason why ...

Neither answers my main question, though.

Instead of rolling everything into LIB_OBJS directly, wouldn't it
have been much easier to work with if reftable-related ones are left
in REFTABLE_OBJS and then RERFTABLE_OBJS gets added to LIB_OBJS?
Wouldn't it have been less prone to mismerges to do it that way?

> However I think I'll drop these 3 commits since 'cargo test' doesn't
> need to be part of the introduction of Rust. It would be nice for make
> to be able to run Rust unit tests at some point though.

As we can always extend things more, getting something close to the
minimally viable set with some tests for sanity checking would be a
good first goal.  If you pare down way too much, however, we may end
up to be pretty close to what Patrick sent out originally with the
varint conversion, so let's make sure we do not drop below the
minimum that still demonstrates that we have Rust integration that
is viable going forward.

Thanks.

^ permalink raw reply	[flat|nested] 100+ messages in thread

* Re: [PATCH v2 07/18] build: introduce rust
  2025-09-19 20:11       ` Ezekiel Newren
@ 2025-09-19 20:24         ` Eric Sunshine
  2025-09-19 21:28           ` Ezekiel Newren
  0 siblings, 1 reply; 100+ messages in thread
From: Eric Sunshine @ 2025-09-19 20:24 UTC (permalink / raw)
  To: Ezekiel Newren; +Cc: Ezekiel Newren via GitGitGadget, git

On Fri, Sep 19, 2025 at 4:11 PM Ezekiel Newren <ezekielnewren@gmail.com> wrote:
> On Wed, Sep 17, 2025 at 2:26 AM Eric Sunshine <sunshine@sunshineco.com> wrote:
> > Is ".idea" directory detritus from your chosen editor? If so, it
> > probably ought not be added to this list since we don't otherwise
> > ignore detritus from foreign tools such as that.
>
> Yes. I use the Jetbrains IDE's CLion and RustRover for C and Rust
> respectively. Jetbrains has an IDE for MANY languages and all of them
> use .idea/ as the folder for IDE specific configuration. I'm fine with
> keeping it out of .gitignore, but I wanted to know what the community
> thought. [...]

There is a bit of discussion about this later in this same email
thread. If the project does ultimately decide to accept these
.gitignore entries, adding them would be done via a patch or series of
patches specifically aimed at that goal. Hence, I'd recommend omitting
the ".idea" entry from this particular patch series.

> > > +if rustup show active-toolchain | grep windows-msvc; then
> > > +  libfile="${crate}.lib"
> > > +  PATH="$(echo $PATH | tr ':' '\n' | grep -Ev "^(/mingw64/bin|/usr/bin)$" | paste -sd: -):/mingw64/bin:/usr/bin"
> > > +fi
> >
> > Please add either an in-code comment or a sentence/paragraph to the
> > commit message explaining why this PATH munging is needed.
>
> I will amend the commit with something like:
> On windows when building with msvc using shell scripts it looks for
> link in /mingw64/bin|/usr/bin when it actually needs to look somewhere
> else for the msvc linker program. Since removing these from PATH would
> break everything else in the shell; move them to be at the end of
> PATH.

I had to read and reread this several times but I think I get what it
is saying. To paraphrase your explanation...

When building with `cargo` (I presume), and it comes time to link the
program, the build process is looking for the Microsoft linker named
LINK.exe but, due to PATH order, is instead finding the Unix command
`link` (which is a specialized invocation of the more common `ln`
command). As such, the build process incorrectly invokes the Unix
`link` rather than the Microsoft LINK.exe and fails. To work around
this problem, you move the standard Unix command-containing paths to
the end of PATH so that the Microsoft LINK.exe is found first.

...does that sound correct?

^ permalink raw reply	[flat|nested] 100+ messages in thread

* Re: [PATCH v2 13/18] build-helper: link against libgit.a and any other required C libraries
  2025-09-17  8:51     ` Eric Sunshine
  2025-09-17 23:07       ` D. Ben Knoble
@ 2025-09-19 20:25       ` Ezekiel Newren
  1 sibling, 0 replies; 100+ messages in thread
From: Ezekiel Newren @ 2025-09-19 20:25 UTC (permalink / raw)
  To: Eric Sunshine; +Cc: Ezekiel Newren via GitGitGadget, git

On Wed, Sep 17, 2025 at 2:51 AM Eric Sunshine <sunshine@sunshineco.com> wrote:
>
> On Tue, Sep 16, 2025 at 9:18 PM Ezekiel Newren via GitGitGadget
> <gitgitgadget@gmail.com> wrote:
> > build-helper: link against libgit.a and any other required C libraries
> >
> > Don't link against the C libraries when building with Make or Meson.
> > Run cargo tests like this:
> > cd rust && cargo clean && USE_LINKING=true cargo test
> >
> > Signed-off-by: Ezekiel Newren <ezekielnewren@gmail.com>
> > ---
>
> Perhaps it's because I haven't been following the discussion closely
> enough, but the above commit message leaves me entirely in the dark.
> After reading and rereading it several times, I suppose it is trying
> to address some difference between building with `cargo` vs. building
> with Make or Meson, but it gives no explanation of what the
> differences are or what problem it is trying to solve. So, please
> enhance the commit message to begin with the "why" and then proceed to
> the "what" or "how".

As I partially explained in previous comments about my commits. I was
trying to get the Rust compiler to link against libgit.a so that any
Rust code that references C code in Git would work in Rust unit tests.
The way that Cargo invokes rustc and links to libraries can be
controlled through build.rs. To use an example; When build.rs prints
the following lines to stdout:
cargo:rustc-link-search=native=/path/to/git/build/dir
cargo:rustc-link-lib=static=git
The first line tells cargo to tell the Rust compiler to look in
'/path/to/git/build/dir' for objects. The second line tells Cargo to
tell the Rust compiler to link against libgit.a. Telling Rust how to
link against libgit.a is only needed for Rust unit tests that
reference Git defined C code.

I'll drop build-helper for this series, since it only helps with
linking against libgit.a for Rust unit tests.

^ permalink raw reply	[flat|nested] 100+ messages in thread

* Re: [PATCH v2 14/18] build-helper: cbindgen, let crates generate a header file
  2025-09-17  9:08     ` Eric Sunshine
@ 2025-09-19 20:34       ` Ezekiel Newren
  0 siblings, 0 replies; 100+ messages in thread
From: Ezekiel Newren @ 2025-09-19 20:34 UTC (permalink / raw)
  To: Eric Sunshine; +Cc: Ezekiel Newren via GitGitGadget, git

On Wed, Sep 17, 2025 at 3:09 AM Eric Sunshine <sunshine@sunshineco.com> wrote:
> It would be nice to have a bit more explanation of what this patch is
> doing and why. I presume that this is adding boilerplate functionality
> but that there are no clients of this functionality yet. Is that
> correct?

I included cbindgen at Brian's request. Its job is to generate C
header files based on Rust code. This eliminates errors of manually
keeping definitions in sync between Rust and C. You are correct that
nothing in my code uses cbindgen yet. I wanted people to see what
would be required to make cbindgen exist in Git. I think I'll drop
cbindgen for this series since varint is so trivial.

I think it would be worthwhile to go over some Rust terminology:
[rustc]: The rust compiler.
[cargo]: Canonical build system + package manager. Even rustc uses
cargo to build itself.
[rustup]: Rust toolchain manager. This provides rustc and cargo + other stuff.
[crate]: The unit of compilation. In C it's akin to a single library
file or executable. It follows the structure of
my_crate
├── Cargo.toml
├── build.rs
└── src
    ├── do_that.rs
    ├── do_this.rs
    └── lib.rs
Where src/lib.rs (the entry point) means it's a library crate and
main.rs (the entry point) would mean it's an executable crate (though
you can define both in the same crate).

This means for each crate there will be lib<crate>.a and optionally
interop/<crate>.h. So places like xdiff and reftable would be easy to
fit into the concept of a crate. The rest of Git would take some doing
to organize into crates.

> > diff --git a/rust/cbindgen-template.toml b/rust/cbindgen-template.toml
> > @@ -0,0 +1,16 @@
> > +## compat/posix.h includes stdbool.h where git-compat-util.h does not
> > +## this is mandatory for correct bool C <-> Rust interop. Though the
> > +## 'includes' (and all other variables in this file) can be
> > +## overridden in build.rs.
>
> There seems to be some missing punctuation in the above comment, and
> the final sentence seems to be incomplete, both of which make it
> difficult to understand what is being said.

I'll fix that wording.

> > +sys_includes = ["compat/posix.h"]
> > +
> > +autogen_warning = "/* Warning, this file is autogenerated by cbindgen. Don't modify this manually. */"
> > +
> > +language = "C"
> > +no_includes = true
> > +usize_is_size_t = true
> > +style = "tag"
> > +tab_width = 4
>
> It's a minor thing and probably not super important but I'm wondering
> if it would be a good idea to follow project guidelines and make the
> TAB width 8 rather than 4. Doing so might be appreciated by people
> reading the generated header files.

I'm fine with changing this value.

I was aware that Git uses a tab width of 8, but the problem is that
Git uses tab characters instead of spaces and as far as I can tell
cbindgen doesn't support using a tab character for indentation. So
tab_width in cbindgen-template.toml means X space characters per
indent.

^ permalink raw reply	[flat|nested] 100+ messages in thread

* Re: [PATCH v2 16/18] build: new crate, misc
  2025-09-17  1:16   ` [PATCH v2 16/18] build: new crate, misc Ezekiel Newren via GitGitGadget
  2025-09-17  9:16     ` Eric Sunshine
@ 2025-09-19 20:42     ` Ezekiel Newren
  2025-09-19 20:50       ` Eric Sunshine
  1 sibling, 1 reply; 100+ messages in thread
From: Ezekiel Newren @ 2025-09-19 20:42 UTC (permalink / raw)
  To: Ezekiel Newren via GitGitGadget; +Cc: git

On Tue, Sep 16, 2025 at 7:17 PM Ezekiel Newren via GitGitGadget
<gitgitgadget@gmail.com> wrote:
To: Eric Sunshine

It looks like my gmail never received your email, so I'll respond to
my own patch.
> Please extend the commit message to at least give _some_ information
> about what the "misc" crate is all about since it is not at all
> apparent based upon the name.
>
> By the way, is "misc" really a good name? It sounds like it's going to
> be a dumping ground for anything which doesn't fit anywhere else.

I don't like the name misc either. What should we call the crate that
will be the new home for .c and .h files that live in the root of the
Git repository? varint is so tiny that creating a crate just for it
seems unjustified.

> Do I understand correctly from the comment that this crate has
> something to do with "xdiff", yet there doesn't actually seem to be
> anything here referencing "xdiff"? Am I missing something?

This crate has nothing to do with xdiff. I copy pasted the Cargo.toml
file from the xdiff crate and forgot to refactor the comment.

^ permalink raw reply	[flat|nested] 100+ messages in thread

* Re: [PATCH v2 16/18] build: new crate, misc
  2025-09-19 20:42     ` Ezekiel Newren
@ 2025-09-19 20:50       ` Eric Sunshine
  2025-09-19 21:54         ` Ezekiel Newren
  0 siblings, 1 reply; 100+ messages in thread
From: Eric Sunshine @ 2025-09-19 20:50 UTC (permalink / raw)
  To: Ezekiel Newren; +Cc: Ezekiel Newren via GitGitGadget, git

On Fri, Sep 19, 2025 at 4:42 PM Ezekiel Newren <ezekielnewren@gmail.com> wrote:
> On Tue, Sep 16, 2025 at 7:17 PM Ezekiel Newren via GitGitGadget
> <gitgitgadget@gmail.com> wrote:
> To: Eric Sunshine
> It looks like my gmail never received your email, so I'll respond to
> my own patch.

Indeed, by sending reviews about earlier patches in this series, it
somehow triggered Gmail to think that I was spamming you, hence Gmail
thenceforth refused to deliver all subsequent emails to you (and
several other Gmail addresses in the recipient list, including Ben and
GitGitGadget). Hence, you would (presumably) have only received my
review emails and other responses indirectly via the Git mailing list
itself (assuming you are subscribed). As far as I know, Gmail still
considers my email address as being a spammer to you, so I'm using a
different email address to respond here.

> > Please extend the commit message to at least give _some_ information
> > about what the "misc" crate is all about since it is not at all
> > apparent based upon the name.
> >
> > By the way, is "misc" really a good name? It sounds like it's going to
> > be a dumping ground for anything which doesn't fit anywhere else.
>
> I don't like the name misc either. What should we call the crate that
> will be the new home for .c and .h files that live in the root of the
> Git repository? varint is so tiny that creating a crate just for it
> seems unjustified.

Would the name `gitcore` or `git-core` be suitable?

^ permalink raw reply	[flat|nested] 100+ messages in thread

* Re: [PATCH v2 00/18] Introduce rust: In xdiff
  2025-09-17  5:58   ` [PATCH v2 00/18] Introduce rust: In xdiff Patrick Steinhardt
@ 2025-09-19 20:57     ` Ezekiel Newren
  2025-09-22 13:01       ` Patrick Steinhardt
  0 siblings, 1 reply; 100+ messages in thread
From: Ezekiel Newren @ 2025-09-19 20:57 UTC (permalink / raw)
  To: Patrick Steinhardt; +Cc: Ezekiel Newren via GitGitGadget, git

On Tue, Sep 16, 2025 at 11:58 PM Patrick Steinhardt <ps@pks.im> wrote:
> Given that this isn't yet ready and given that this patch series is way
> bigger than the one I have in flight that focusses on introducing the
> policy [1]: how about we first merge that one down and then rebase your
> patches on top of it to iterate? It would reduce the scope of your patch
> series and allow us to make smaller steps towards our goal.
>
> To be clear: I very much think that most of the steps here are quite
> sensible. We definitely want to introduce cbindgen, we obviously need to
> introduce support on Windows, and I guess having multiple different
> workspaces is also something that could provide benefit.
>
> But there is no reason to have that all at once, so hence my suggestion
> to build out the infra one step at a time.
>
> What do you think?

I think I made a big mistake of not making it clear that I intended v2
as more of an RFC. My worry (that I expressed very poorly or not at
all) is how hard will it be to apply my patches on top of your
foundation? I don't know if minor or major changes to your current
path would be needed, so I proposed a different way to Introduce Rust
while trying to incorporate work from others.

I wanted feedback on:
  * Cleaning up Rust type name collisions
    * People don't like it, so I'll drop that
  * Have Makefile produce libgit.a correctly.
    * I think this is a good idea, but it doesn't belong in this patch series.
  * Adding Rust unit test which required fixing Make and adding
build-helper to reduce boilerplate code
    * I think this is a good idea, but should be added later.
  * Introduce cbindgen to avoid desync errors between Rust and C.
    * LIke other points above; This should be added later.
  * What should we call the crate that will be the new home for .c and
.h files in the root of Git?

^ permalink raw reply	[flat|nested] 100+ messages in thread

* Re: [PATCH v2 07/18] build: introduce rust
  2025-09-19 20:24         ` Eric Sunshine
@ 2025-09-19 21:28           ` Ezekiel Newren
  0 siblings, 0 replies; 100+ messages in thread
From: Ezekiel Newren @ 2025-09-19 21:28 UTC (permalink / raw)
  To: Eric Sunshine; +Cc: Ezekiel Newren via GitGitGadget, git

On Fri, Sep 19, 2025 at 2:24 PM Eric Sunshine <ericsunshine@gmail.com> wrote:
> When building with `cargo` (I presume), and it comes time to link the
> program, the build process is looking for the Microsoft linker named
> LINK.exe but, due to PATH order, is instead finding the Unix command
> `link` (which is a specialized invocation of the more common `ln`
> command). As such, the build process incorrectly invokes the Unix
> `link` rather than the Microsoft LINK.exe and fails. To work around
> this problem, you move the standard Unix command-containing paths to
> the end of PATH so that the Microsoft LINK.exe is found first.

Your understanding is correct.

^ permalink raw reply	[flat|nested] 100+ messages in thread

* Re: [PATCH v2 02/18] make: add -fPIE flag
  2025-09-19 20:07         ` Junio C Hamano
@ 2025-09-19 21:52           ` Ezekiel Newren
  2025-09-19 23:43             ` Junio C Hamano
  0 siblings, 1 reply; 100+ messages in thread
From: Ezekiel Newren @ 2025-09-19 21:52 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Eric Sunshine, Ezekiel Newren via GitGitGadget, git

On Fri, Sep 19, 2025 at 2:07 PM Junio C Hamano <gitster@pobox.com> wrote:
>
> Ezekiel Newren <ezekielnewren@gmail.com> writes:
>
> > I was trying to get the rust compiler to link against libgit.a. Both
> > Meson and Makefile produce libgit.a. No changes were to meson.build
> > were necessary for that to work, but but with Makefile I would get
> > errors like:
> > relocation R_X86_64_32 against `.rodata' can not be used when making a
> > PIE object; recompile with -fPIE
>
> This sounds like a workaround that is somewhat platform and compiler
> specific limitation, at least to me.  Does this need to be conditional
> and if so on what?

I don't know, but I have another question to add to yours. Why does
Make need -fPIE in order for the Rust compiler to link against
libgit.a created by Make, when Meson doesn't seem to specify PIE
anywhere and yet the Rust compiler can work with Meson's libgit.a
output?

^ permalink raw reply	[flat|nested] 100+ messages in thread

* Re: [PATCH v2 16/18] build: new crate, misc
  2025-09-19 20:50       ` Eric Sunshine
@ 2025-09-19 21:54         ` Ezekiel Newren
  0 siblings, 0 replies; 100+ messages in thread
From: Ezekiel Newren @ 2025-09-19 21:54 UTC (permalink / raw)
  To: Eric Sunshine; +Cc: Ezekiel Newren via GitGitGadget, git

On Fri, Sep 19, 2025 at 2:50 PM Eric Sunshine <ericsunshine@gmail.com> wrote:
> Would the name `gitcore` or `git-core` be suitable?

The only other name I could think of was `util`, but I think I prefer
`gitcore`. Does anybody else have any name suggestions?

^ permalink raw reply	[flat|nested] 100+ messages in thread

* Re: [PATCH v2 04/18] make: merge reftable lib into libgit.a
  2025-09-19 20:14         ` Junio C Hamano
@ 2025-09-19 23:02           ` Ezekiel Newren
  0 siblings, 0 replies; 100+ messages in thread
From: Ezekiel Newren @ 2025-09-19 23:02 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Ezekiel Newren via GitGitGadget, git

On Fri, Sep 19, 2025 at 2:14 PM Junio C Hamano <gitster@pobox.com> wrote:
> Instead of rolling everything into LIB_OBJS directly, wouldn't it
> have been much easier to work with if reftable-related ones are left
> in REFTABLE_OBJS and then RERFTABLE_OBJS gets added to LIB_OBJS?
> Wouldn't it have been less prone to mismerges to do it that way?

I didn't do it that way because I didn't think of that. I prefer your
way because it's much cleaner.

> As we can always extend things more, getting something close to the
> minimally viable set with some tests for sanity checking would be a
> good first goal.  If you pare down way too much, however, we may end
> up to be pretty close to what Patrick sent out originally with the
> varint conversion, so let's make sure we do not drop below the
> minimum that still demonstrates that we have Rust integration that
> is viable going forward.

I'm torn between A and B from [1]:
I'm thinking of choosing A because my approach and Patrick's approach
are incompatible. In order to use multiple crates and ensure that Rust
is built and tested the same way with Make and Meson I'd have to rip
out lots of things from Patrick's patch series.

But maybe I should go with B because there's a lot of prep work that
should be done with Git before I can start doing what I'd like to do
in Rust. So let Patrick's stuff merge while I work on cleaning up
xdiff and updating Makefile's libgit.a build process and then deal
with ugly merging later.

I can't decide between the two.

[1] https://lore.kernel.org/git/CABPp-BHJUkSERQon6xx=sHrhN7i=6ekv+Hz1+P+2mh0=Xw15Mg@mail.gmail.com/

^ permalink raw reply	[flat|nested] 100+ messages in thread

* Re: [PATCH v2 02/18] make: add -fPIE flag
  2025-09-19 21:52           ` Ezekiel Newren
@ 2025-09-19 23:43             ` Junio C Hamano
  2025-09-19 23:59               ` Collin Funk
  0 siblings, 1 reply; 100+ messages in thread
From: Junio C Hamano @ 2025-09-19 23:43 UTC (permalink / raw)
  To: Ezekiel Newren
  Cc: Eric Sunshine, Patrick Steinhardt,
	Ezekiel Newren via GitGitGadget, git

Ezekiel Newren <ezekielnewren@gmail.com> writes:

> On Fri, Sep 19, 2025 at 2:07 PM Junio C Hamano <gitster@pobox.com> wrote:
>>
>> Ezekiel Newren <ezekielnewren@gmail.com> writes:
>>
>> > I was trying to get the rust compiler to link against libgit.a. Both
>> > Meson and Makefile produce libgit.a. No changes were to meson.build
>> > were necessary for that to work, but but with Makefile I would get
>> > errors like:
>> > relocation R_X86_64_32 against `.rodata' can not be used when making a
>> > PIE object; recompile with -fPIE
>>
>> This sounds like a workaround that is somewhat platform and compiler
>> specific limitation, at least to me.  Does this need to be conditional
>> and if so on what?
>
> I don't know, but I have another question to add to yours. Why does
> Make need -fPIE in order for the Rust compiler to link against
> libgit.a created by Make, when Meson doesn't seem to specify PIE
> anywhere and yet the Rust compiler can work with Meson's libgit.a
> output?

I do not know what the build procedure using meson exactly does to
create a library archive.  On the Make side, we do not compile for
position independence, and don't do anything other than bog standard
"ar rcs".  Patrick, any hint?


^ permalink raw reply	[flat|nested] 100+ messages in thread

* Re: [PATCH v2 02/18] make: add -fPIE flag
  2025-09-19 23:43             ` Junio C Hamano
@ 2025-09-19 23:59               ` Collin Funk
  2025-09-20 16:44                 ` Junio C Hamano
  0 siblings, 1 reply; 100+ messages in thread
From: Collin Funk @ 2025-09-19 23:59 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: Ezekiel Newren, Eric Sunshine, Patrick Steinhardt,
	Ezekiel Newren via GitGitGadget, git

Junio C Hamano <gitster@pobox.com> writes:

> Ezekiel Newren <ezekielnewren@gmail.com> writes:
>
>> On Fri, Sep 19, 2025 at 2:07 PM Junio C Hamano <gitster@pobox.com> wrote:
>>>
>>> Ezekiel Newren <ezekielnewren@gmail.com> writes:
>>>
>>> > I was trying to get the rust compiler to link against libgit.a. Both
>>> > Meson and Makefile produce libgit.a. No changes were to meson.build
>>> > were necessary for that to work, but but with Makefile I would get
>>> > errors like:
>>> > relocation R_X86_64_32 against `.rodata' can not be used when making a
>>> > PIE object; recompile with -fPIE
>>>
>>> This sounds like a workaround that is somewhat platform and compiler
>>> specific limitation, at least to me.  Does this need to be conditional
>>> and if so on what?
>>
>> I don't know, but I have another question to add to yours. Why does
>> Make need -fPIE in order for the Rust compiler to link against
>> libgit.a created by Make, when Meson doesn't seem to specify PIE
>> anywhere and yet the Rust compiler can work with Meson's libgit.a
>> output?
>
> I do not know what the build procedure using meson exactly does to
> create a library archive.  On the Make side, we do not compile for
> position independence, and don't do anything other than bog standard
> "ar rcs".  Patrick, any hint?

Not too familiar with Rust, but looks like the answer is here [1].
Specificially, this part:

    pic - fully relocatable position independent code, machine
    instructions need to use relative addressing modes. Equivalent to
    the "uppercase" -fPIC or -fPIE options in other compilers, depending
    on the produced crate types. This is the default model for majority
    of supported targets.

Collin

[1] https://doc.rust-lang.org/rustc/codegen-options/index.html#relocation-model

^ permalink raw reply	[flat|nested] 100+ messages in thread

* Re: [PATCH v2 02/18] make: add -fPIE flag
  2025-09-19 23:59               ` Collin Funk
@ 2025-09-20 16:44                 ` Junio C Hamano
  2025-09-21  1:14                   ` Ramsay Jones
  0 siblings, 1 reply; 100+ messages in thread
From: Junio C Hamano @ 2025-09-20 16:44 UTC (permalink / raw)
  To: Collin Funk
  Cc: Ezekiel Newren, Eric Sunshine, Patrick Steinhardt,
	Ezekiel Newren via GitGitGadget, git

Collin Funk <collin.funk1@gmail.com> writes:

> Junio C Hamano <gitster@pobox.com> writes:
>
>> Ezekiel Newren <ezekielnewren@gmail.com> writes:
>>
>>> I don't know, but I have another question to add to yours. Why does
>>> Make need -fPIE in order for the Rust compiler to link against
>>> libgit.a created by Make, when Meson doesn't seem to specify PIE
>>> anywhere and yet the Rust compiler can work with Meson's libgit.a
>>> output?
>>
>> I do not know what the build procedure using meson exactly does to
>> create a library archive.  On the Make side, we do not compile for
>> position independence, and don't do anything other than bog standard
>> "ar rcs".  Patrick, any hint?
>
> Not too familiar with Rust, but looks like the answer is here [1].
> Specificially, this part:
>
>     pic - fully relocatable position independent code, machine
>     instructions need to use relative addressing modes. Equivalent to
>     the "uppercase" -fPIC or -fPIE options in other compilers, depending
>     on the produced crate types. This is the default model for majority
>     of supported targets.
>
> Collin
>
> [1] https://doc.rust-lang.org/rustc/codegen-options/index.html#relocation-model

Well, thanks for joining the conversation, but that much I think is
already shared by those who are involved in this thread.

The background was that Ezekiel did not have to do anything special
as far as he knows to get things compiled in a relocatable way with
meson based build, and with make based build, (which I know has
never used nor required any PIE-ness) he of course needs to add it
in a way that is portable across platforms and compilers we care
about if we wanted to have PIE objects.

And the question was what, if anything, does meson do specially to
do so in a portable way to enable PIE.  We'd want to see if it meets
our needs (it could be that Ezekiel is getting lucky and meson based
build covered the platform+compiler combinations that he happened to
be working with) and if so mimick that on the Makefile side.


^ permalink raw reply	[flat|nested] 100+ messages in thread

* Re: [PATCH v2 02/18] make: add -fPIE flag
  2025-09-20 16:44                 ` Junio C Hamano
@ 2025-09-21  1:14                   ` Ramsay Jones
  0 siblings, 0 replies; 100+ messages in thread
From: Ramsay Jones @ 2025-09-21  1:14 UTC (permalink / raw)
  To: Junio C Hamano, Collin Funk
  Cc: Ezekiel Newren, Eric Sunshine, Patrick Steinhardt,
	Ezekiel Newren via GitGitGadget, git



On 20/09/2025 17:44, Junio C Hamano wrote:
> Collin Funk <collin.funk1@gmail.com> writes:
> 
>> Junio C Hamano <gitster@pobox.com> writes:
>>
>>> Ezekiel Newren <ezekielnewren@gmail.com> writes:
[snip]
>> Not too familiar with Rust, but looks like the answer is here [1].
>> Specificially, this part:
>>
>>     pic - fully relocatable position independent code, machine
>>     instructions need to use relative addressing modes. Equivalent to
>>     the "uppercase" -fPIC or -fPIE options in other compilers, depending
>>     on the produced crate types. This is the default model for majority
>>     of supported targets.
>>
>> Collin
>>
>> [1] https://doc.rust-lang.org/rustc/codegen-options/index.html#relocation-model
> 
> Well, thanks for joining the conversation, but that much I think is
> already shared by those who are involved in this thread.
> 
> The background was that Ezekiel did not have to do anything special
> as far as he knows to get things compiled in a relocatable way with
> meson based build, and with make based build, (which I know has
> never used nor required any PIE-ness) he of course needs to add it
> in a way that is portable across platforms and compilers we care
> about if we wanted to have PIE objects.
> 
> And the question was what, if anything, does meson do specially to
> do so in a portable way to enable PIE.  We'd want to see if it meets
> our needs (it could be that Ezekiel is getting lucky and meson based
> build covered the platform+compiler combinations that he happened to
> be working with) and if so mimick that on the Makefile side.


Yes, meson defaults the compiler flags to -fPIC (see e.g. near the end
of the original cover letter of say [1]). You can change the compiler
flags that meson uses (can't remember off-hand how you do that, but it
is documented), you can even set -fPIE, or remove -fPIC etc,.  ;)

So, I don't know why rust (seems to) require -fPIC/-fPIE to be able to
link to libgit.a; maybe we need to add some flags to rustc to _not_ use
the equivalent of -fPIC/-fPIE. (I haven't looked, but I will note that
the GNU nm program complains about the rust built library!)


[1] https://lore.kernel.org/git/20250416231835.2492562-1-ramsay@ramsayjones.plus.com/


ATB,
Ramsay Jones




^ permalink raw reply	[flat|nested] 100+ messages in thread

* Re: [PATCH v2 00/18] Introduce rust: In xdiff
  2025-09-19 20:57     ` Ezekiel Newren
@ 2025-09-22 13:01       ` Patrick Steinhardt
  2025-09-22 15:31         ` Ezekiel Newren
  0 siblings, 1 reply; 100+ messages in thread
From: Patrick Steinhardt @ 2025-09-22 13:01 UTC (permalink / raw)
  To: Ezekiel Newren; +Cc: Ezekiel Newren via GitGitGadget, git

On Fri, Sep 19, 2025 at 02:57:58PM -0600, Ezekiel Newren wrote:
> On Tue, Sep 16, 2025 at 11:58 PM Patrick Steinhardt <ps@pks.im> wrote:
> > Given that this isn't yet ready and given that this patch series is way
> > bigger than the one I have in flight that focusses on introducing the
> > policy [1]: how about we first merge that one down and then rebase your
> > patches on top of it to iterate? It would reduce the scope of your patch
> > series and allow us to make smaller steps towards our goal.
> >
> > To be clear: I very much think that most of the steps here are quite
> > sensible. We definitely want to introduce cbindgen, we obviously need to
> > introduce support on Windows, and I guess having multiple different
> > workspaces is also something that could provide benefit.
> >
> > But there is no reason to have that all at once, so hence my suggestion
> > to build out the infra one step at a time.
> >
> > What do you think?
> 
> I think I made a big mistake of not making it clear that I intended v2
> as more of an RFC. My worry (that I expressed very poorly or not at
> all) is how hard will it be to apply my patches on top of your
> foundation? I don't know if minor or major changes to your current
> path would be needed, so I proposed a different way to Introduce Rust
> while trying to incorporate work from others.

Ah, fair enough. Thanks for clarifying your intentions!

I don't think there'd have to be major changes to the current version
of my patch series. The idea of that patch series is very much getting
buy-in regarding our roadmap and focus less on the actual build infra.
So the changes introduced are mostly an MVC, and I very much think that
we'll have to iterate quite a bit on it, but that's intended.

I think that a lot of the steps you outline below are logical next steps
to get there. If I can change anything to make these next steps easier
for you I'm happy to do so. But I also don't think it's too bad if we
have to change the current infra quite significantly to get there.

> I wanted feedback on:
>   * Cleaning up Rust type name collisions
>     * People don't like it, so I'll drop that

I don't have a strong opinion on this. If it creates issues I personally
don't mind fixing it.

>   * Have Makefile produce libgit.a correctly.
>     * I think this is a good idea, but it doesn't belong in this patch series.
>   * Adding Rust unit test which required fixing Make and adding
> build-helper to reduce boilerplate code
>     * I think this is a good idea, but should be added later.
>   * Introduce cbindgen to avoid desync errors between Rust and C.
>     * LIke other points above; This should be added later.

Agreed, these all make sense to me.

>   * What should we call the crate that will be the new home for .c and
> .h files in the root of Git?

We could call this something like "libgit-ffi", but I don't care too
much.

Thanks!

Patrick

^ permalink raw reply	[flat|nested] 100+ messages in thread

* Re: [PATCH v2 00/18] Introduce rust: In xdiff
  2025-09-17 22:48       ` Junio C Hamano
@ 2025-09-22 13:01         ` Patrick Steinhardt
  2025-09-22 15:18           ` Ezekiel Newren
  0 siblings, 1 reply; 100+ messages in thread
From: Patrick Steinhardt @ 2025-09-22 13:01 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: Elijah Newren, Ezekiel Newren via GitGitGadget, git,
	Ezekiel Newren

On Wed, Sep 17, 2025 at 03:48:04PM -0700, Junio C Hamano wrote:
> Elijah Newren <newren@gmail.com> writes:
> 
> > So, how to move forward?
> >
> > A) Modify Patrick's series to just take patch 7 of his v5.  Patrick
> > did say that the roadmap was  "the more important discussion compared
> > to the technical discussion", and merging that patch would achieve his
> > goal of getting an initial roadmap.  Then Ezekiel could grab other
> > pieces from Patrick's series (e.g. the help and varint stuff) and
> > incorporate it into an "introduce rust" series.[*]
> >
> > B) Merge Patrick's series and tell Ezekiel to rebase, while noting to
> > Ezekiel that the roadmap is the important bit from Patrick's series[*]
> > and he can suggest changes to any of the other bits.
> >
> > C) Create a consolidated "introduce Rust" series with bits of both --
> > what I think Ezekiel was trying to do with this series.
> 
> Ah, I didn't even realize C was what this series was trying to do.
> 
> I do not have particular preference between A and B, but I thought A
> was closer to what was being done with this series, and as long as
> Ezekiel and Patrick can join forces that way, it would be perfect.

I personally think either (A) or (B) would be good choices. I would
slightly lean towards (B) just so that we have something that we can
already play around with while building the next steps.

By the way: I'm also happy to change attribution of some of the patches
in my patch series to mention Ezekiel as author. I don't care much who
is listed for the initial patches that introduce Rust, but would retain
my own authorship for the "varint" and "BreakingChanges" commits.

Patrick

^ permalink raw reply	[flat|nested] 100+ messages in thread

* Re: [PATCH v2 00/18] Introduce rust: In xdiff
  2025-09-22 13:01         ` Patrick Steinhardt
@ 2025-09-22 15:18           ` Ezekiel Newren
  2025-09-22 16:15             ` Patrick Steinhardt
  0 siblings, 1 reply; 100+ messages in thread
From: Ezekiel Newren @ 2025-09-22 15:18 UTC (permalink / raw)
  To: Patrick Steinhardt
  Cc: Junio C Hamano, Elijah Newren, Ezekiel Newren via GitGitGadget,
	git

On Mon, Sep 22, 2025 at 7:01 AM Patrick Steinhardt <ps@pks.im> wrote:
> I personally think either (A) or (B) would be good choices. I would
> slightly lean towards (B) just so that we have something that we can
> already play around with while building the next steps.

I'm fine with B if you fix the wording in your Breaking Changes about
Rust being introduced in version 2.52. Rust was introduced to Git in
2.49.

Elijah points this out in 1 and 2:
[1] https://lore.kernel.org/git/CABPp-BFXRbaHk9U3BX+d12bZ+ryGOp+btR0ODMw+HtD7xd+MBQ@mail.gmail.com/
[2] https://lore.kernel.org/git/CABPp-BEiK49f_UB5UPe3qM9O7vQGGFJ8Nshw1f6W_6Lw7HRL6Q@mail.gmail.com/

> By the way: I'm also happy to change attribution of some of the patches
> in my patch series to mention Ezekiel as author. I don't care much who
> is listed for the initial patches that introduce Rust, but would retain
> my own authorship for the "varint" and "BreakingChanges" commits.

My only other concern is with varint. You use usize on the Rust side
and then uint64_t on the C side, but I'm ok with fixing that later as
it only breaks 'linux32 (i386/ubuntu:focal)' in the github workflows.

^ permalink raw reply	[flat|nested] 100+ messages in thread

* Re: [PATCH v2 00/18] Introduce rust: In xdiff
  2025-09-22 13:01       ` Patrick Steinhardt
@ 2025-09-22 15:31         ` Ezekiel Newren
  2025-09-22 16:08           ` Patrick Steinhardt
  2025-09-22 16:47           ` Junio C Hamano
  0 siblings, 2 replies; 100+ messages in thread
From: Ezekiel Newren @ 2025-09-22 15:31 UTC (permalink / raw)
  To: Patrick Steinhardt; +Cc: Ezekiel Newren via GitGitGadget, git

On Mon, Sep 22, 2025 at 7:01 AM Patrick Steinhardt <ps@pks.im> wrote:
> I don't think there'd have to be major changes to the current version
> of my patch series. The idea of that patch series is very much getting
> buy-in regarding our roadmap and focus less on the actual build infra.
> So the changes introduced are mostly an MVC, and I very much think that
> we'll have to iterate quite a bit on it, but that's intended.

So long as you're flexible with the build infra details then I'm ok
with rebasing on your work.

What does MVC stand for?

> I think that a lot of the steps you outline below are logical next steps
> to get there. If I can change anything to make these next steps easier
> for you I'm happy to do so. But I also don't think it's too bad if we
> have to change the current infra quite significantly to get there.

I think we can discuss those code and infra changes in a later patch series.

> > I wanted feedback on:
> >   * Cleaning up Rust type name collisions
> >     * People don't like it, so I'll drop that
>
> I don't have a strong opinion on this. If it creates issues I personally
> don't mind fixing it.

Junio doesn't like it, so I'm not going to do it.

> >   * Have Makefile produce libgit.a correctly.
> >     * I think this is a good idea, but it doesn't belong in this patch series.
> >   * Adding Rust unit test which required fixing Make and adding
> > build-helper to reduce boilerplate code
> >     * I think this is a good idea, but should be added later.
> >   * Introduce cbindgen to avoid desync errors between Rust and C.
> >     * LIke other points above; This should be added later.
>
> Agreed, these all make sense to me.

I think the changes to Makefile to build ligbit.a correctly can be an
orthogonal patch series. The rest can come later.

> >   * What should we call the crate that will be the new home for .c and
> > .h files in the root of Git?
>
> We could call this something like "libgit-ffi", but I don't care too
> much.

I prefer Eric Sunshine's suggestion of gitcore, no hyphen.

^ permalink raw reply	[flat|nested] 100+ messages in thread

* Re: [PATCH v2 00/18] Introduce rust: In xdiff
  2025-09-22 15:31         ` Ezekiel Newren
@ 2025-09-22 16:08           ` Patrick Steinhardt
  2025-09-22 16:47           ` Junio C Hamano
  1 sibling, 0 replies; 100+ messages in thread
From: Patrick Steinhardt @ 2025-09-22 16:08 UTC (permalink / raw)
  To: Ezekiel Newren; +Cc: Ezekiel Newren via GitGitGadget, git

On Mon, Sep 22, 2025 at 09:31:10AM -0600, Ezekiel Newren wrote:
> On Mon, Sep 22, 2025 at 7:01 AM Patrick Steinhardt <ps@pks.im> wrote:
> > I don't think there'd have to be major changes to the current version
> > of my patch series. The idea of that patch series is very much getting
> > buy-in regarding our roadmap and focus less on the actual build infra.
> > So the changes introduced are mostly an MVC, and I very much think that
> > we'll have to iterate quite a bit on it, but that's intended.
> 
> So long as you're flexible with the build infra details then I'm ok
> with rebasing on your work.
> 
> What does MVC stand for?

Minimum viable... candidate? I guess I rather meant "product", so MVP
would be the better term.

[snip]
> > >   * What should we call the crate that will be the new home for .c and
> > > .h files in the root of Git?
> >
> > We could call this something like "libgit-ffi", but I don't care too
> > much.
> 
> I prefer Eric Sunshine's suggestion of gitcore, no hyphen.

Sounds reasonable to me.

Patrick

^ permalink raw reply	[flat|nested] 100+ messages in thread

* Re: [PATCH v2 00/18] Introduce rust: In xdiff
  2025-09-22 15:18           ` Ezekiel Newren
@ 2025-09-22 16:15             ` Patrick Steinhardt
  2025-09-22 16:27               ` Ezekiel Newren
  0 siblings, 1 reply; 100+ messages in thread
From: Patrick Steinhardt @ 2025-09-22 16:15 UTC (permalink / raw)
  To: Ezekiel Newren
  Cc: Junio C Hamano, Elijah Newren, Ezekiel Newren via GitGitGadget,
	git

On Mon, Sep 22, 2025 at 09:18:14AM -0600, Ezekiel Newren wrote:
> On Mon, Sep 22, 2025 at 7:01 AM Patrick Steinhardt <ps@pks.im> wrote:
> > I personally think either (A) or (B) would be good choices. I would
> > slightly lean towards (B) just so that we have something that we can
> > already play around with while building the next steps.
> 
> I'm fine with B if you fix the wording in your Breaking Changes about
> Rust being introduced in version 2.52. Rust was introduced to Git in
> 2.49.
> 
> Elijah points this out in 1 and 2:
> [1] https://lore.kernel.org/git/CABPp-BFXRbaHk9U3BX+d12bZ+ryGOp+btR0ODMw+HtD7xd+MBQ@mail.gmail.com/
> [2] https://lore.kernel.org/git/CABPp-BEiK49f_UB5UPe3qM9O7vQGGFJ8Nshw1f6W_6Lw7HRL6Q@mail.gmail.com/

Will adjust.

> > By the way: I'm also happy to change attribution of some of the patches
> > in my patch series to mention Ezekiel as author. I don't care much who
> > is listed for the initial patches that introduce Rust, but would retain
> > my own authorship for the "varint" and "BreakingChanges" commits.
> 
> My only other concern is with varint. You use usize on the Rust side
> and then uint64_t on the C side, but I'm ok with fixing that later as
> it only breaks 'linux32 (i386/ubuntu:focal)' in the github workflows.

Oh, this is actually an oversight, good catch! I refactored "varint.c"
to use `uint64_t`, but then forgot to adjust the Rust side in the same
spirit. Will fix.

I suggested in [1] that I can change authorship of the patches that
introduce the initial infrastructure into Meson and our Makefile (so I
guess patches 1 and 3) to instead list you as author and myself as
Co-authored-by. Is that something you want? Given that you have
kickstarted the whole effort around introducing Rust again I wouldn't
mind that at all.

In any case, I'll send a new version of the series tomorrow.

Thanks!

Patrick

[1]: <aNFIwFD6E6Lngy5M@pks.im>

^ permalink raw reply	[flat|nested] 100+ messages in thread

* Re: [PATCH v2 00/18] Introduce rust: In xdiff
  2025-09-22 16:15             ` Patrick Steinhardt
@ 2025-09-22 16:27               ` Ezekiel Newren
  2025-09-23  5:11                 ` Patrick Steinhardt
  0 siblings, 1 reply; 100+ messages in thread
From: Ezekiel Newren @ 2025-09-22 16:27 UTC (permalink / raw)
  To: Patrick Steinhardt
  Cc: Junio C Hamano, Elijah Newren, Ezekiel Newren via GitGitGadget,
	git

On Mon, Sep 22, 2025 at 10:16 AM Patrick Steinhardt <ps@pks.im> wrote:
> On Mon, Sep 22, 2025 at 09:18:14AM -0600, Ezekiel Newren wrote:
> > On Mon, Sep 22, 2025 at 7:01 AM Patrick Steinhardt <ps@pks.im> wrote:
> > > I personally think either (A) or (B) would be good choices. I would
> > > slightly lean towards (B) just so that we have something that we can
> > > already play around with while building the next steps.
> >
> > I'm fine with B if you fix the wording in your Breaking Changes about
> > Rust being introduced in version 2.52. Rust was introduced to Git in
> > 2.49.
> >
> > Elijah points this out in 1 and 2:
> > [1] https://lore.kernel.org/git/CABPp-BFXRbaHk9U3BX+d12bZ+ryGOp+btR0ODMw+HtD7xd+MBQ@mail.gmail.com/
> > [2] https://lore.kernel.org/git/CABPp-BEiK49f_UB5UPe3qM9O7vQGGFJ8Nshw1f6W_6Lw7HRL6Q@mail.gmail.com/
>
> Will adjust.

Thank you.

> > > By the way: I'm also happy to change attribution of some of the patches
> > > in my patch series to mention Ezekiel as author. I don't care much who
> > > is listed for the initial patches that introduce Rust, but would retain
> > > my own authorship for the "varint" and "BreakingChanges" commits.
> >
> > My only other concern is with varint. You use usize on the Rust side
> > and then uint64_t on the C side, but I'm ok with fixing that later as
> > it only breaks 'linux32 (i386/ubuntu:focal)' in the github workflows.
>
> Oh, this is actually an oversight, good catch! I refactored "varint.c"
> to use `uint64_t`, but then forgot to adjust the Rust side in the same
> spirit. Will fix.

You also missed updating varint.h.

> I suggested in [1] that I can change authorship of the patches that
> introduce the initial infrastructure into Meson and our Makefile (so I
> guess patches 1 and 3) to instead list you as author and myself as
> Co-authored-by. Is that something you want? Given that you have
> kickstarted the whole effort around introducing Rust again I wouldn't
> mind that at all.

It doesn't make sense to me to list myself as the author of any of
your commits, but I would like my name referenced in your commit
messages.

Thanks.

Ezekiel.

^ permalink raw reply	[flat|nested] 100+ messages in thread

* Re: [PATCH v2 00/18] Introduce rust: In xdiff
  2025-09-22 15:31         ` Ezekiel Newren
  2025-09-22 16:08           ` Patrick Steinhardt
@ 2025-09-22 16:47           ` Junio C Hamano
  2025-09-22 17:23             ` Ezekiel Newren
  1 sibling, 1 reply; 100+ messages in thread
From: Junio C Hamano @ 2025-09-22 16:47 UTC (permalink / raw)
  To: Ezekiel Newren; +Cc: Patrick Steinhardt, Ezekiel Newren via GitGitGadget, git

Ezekiel Newren <ezekielnewren@gmail.com> writes:

>> > I wanted feedback on:
>> >   * Cleaning up Rust type name collisions
>> >   * People don't like it, so I'll drop that
>>
>> I don't have a strong opinion on this. If it creates issues I personally
>> don't mind fixing it.
>
> Junio doesn't like it, so I'm not going to do it.

It was not "I do not line u16 as a typename when a perfectly well
established uint16_t is available", though.

It was more about asking to explain the reason behind insisting to
use u(8|16|32|64) types in C code.  Perhaps there is a compelling
reason to do so that I was missing.

I know that the kernel has used these types for a long time, but
that way predates their more recent flirt with Rust.  If your answer
was "the kernel uses them", then I'd want that answer to cover a few
additional questions, like 

 - Have they benefitted from their use of u(8|16|32|64) when they
   started working with Rust and if so how?  

 - Would we be expected to reap the same benefit if we used these
   types?

for example.

Thanks.

^ permalink raw reply	[flat|nested] 100+ messages in thread

* Re: [PATCH v2 00/18] Introduce rust: In xdiff
  2025-09-22 16:47           ` Junio C Hamano
@ 2025-09-22 17:23             ` Ezekiel Newren
  2025-09-22 17:32               ` Ezekiel Newren
  2025-09-22 18:12               ` Junio C Hamano
  0 siblings, 2 replies; 100+ messages in thread
From: Ezekiel Newren @ 2025-09-22 17:23 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Patrick Steinhardt, Ezekiel Newren via GitGitGadget, git

On Mon, Sep 22, 2025 at 10:47 AM Junio C Hamano <gitster@pobox.com> wrote:
> Ezekiel Newren <ezekielnewren@gmail.com> writes:
>
> >> > I wanted feedback on:
> >> >   * Cleaning up Rust type name collisions
> >> >   * People don't like it, so I'll drop that
> >>
> >> I don't have a strong opinion on this. If it creates issues I personally
> >> don't mind fixing it.
> >
> > Junio doesn't like it, so I'm not going to do it.
>
> It was not "I do not like u16 as a typename when a perfectly well
> established uint16_t is available", though.
>
> It was more about asking to explain the reason behind insisting to
> use u(8|16|32|64) types in C code.  Perhaps there is a compelling
> reason to do so that I was missing.

I listed 5 reasons in my commit message[1], and I just thought of
another one now.
  * Pseudo reserved keywords: 'new' is not a reserved keyword in C,
but Git treats it as such. Since Rust will likely be added to Git, the
Rust primitive types should also be treated as reserved keywords.
Cbindgen parse's Rust and generates C header files; If a field in a
struct uses u16 as the name then Rust won't compile, and cbindgen
can't create the C header file. Using [ui](8|16|32|64|size) as the
type in C also spreads awareness that those are reserved keywords and
should not be used as variable names.

If these 6 reasons are not enough to explain why we should be using
the Rust primitive type names in C, then please explain how it is
insufficient.

> I know that the kernel has used these types for a long time, but
> that way predates their more recent flirt with Rust.  If your answer
> was "the kernel uses them", then I'd want that answer to cover a few
> additional questions, like
>
>  - Have they benefited from their use of u(8|16|32|64) when they
>    started working with Rust and if so how?

I did not know this.

[1] https://lore.kernel.org/git/2a7d5b05c18d4a96f1905b7043d47c62d367cd2a.1757274320.git.gitgitgadget@gmail.com/

^ permalink raw reply	[flat|nested] 100+ messages in thread

* Re: [PATCH v2 00/18] Introduce rust: In xdiff
  2025-09-22 17:23             ` Ezekiel Newren
@ 2025-09-22 17:32               ` Ezekiel Newren
  2025-09-22 18:17                 ` Junio C Hamano
  2025-09-22 18:12               ` Junio C Hamano
  1 sibling, 1 reply; 100+ messages in thread
From: Ezekiel Newren @ 2025-09-22 17:32 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Patrick Steinhardt, Ezekiel Newren via GitGitGadget, git

On Mon, Sep 22, 2025 at 11:23 AM Ezekiel Newren <ezekielnewren@gmail.com> wrote:
> Cbindgen parse's Rust and generates C header files; If a field in a
> struct uses u16 as the name then Rust won't compile, and cbindgen
> can't create the C header file.

I just tried this in Rust and it turns out you actually can use u16 as
a struct field name. My bad. I think that's a bad idea and should be
discouraged though.

^ permalink raw reply	[flat|nested] 100+ messages in thread

* Re: [PATCH v2 00/18] Introduce rust: In xdiff
  2025-09-22 17:23             ` Ezekiel Newren
  2025-09-22 17:32               ` Ezekiel Newren
@ 2025-09-22 18:12               ` Junio C Hamano
  1 sibling, 0 replies; 100+ messages in thread
From: Junio C Hamano @ 2025-09-22 18:12 UTC (permalink / raw)
  To: Ezekiel Newren; +Cc: Patrick Steinhardt, Ezekiel Newren via GitGitGadget, git

Ezekiel Newren <ezekielnewren@gmail.com> writes:

> If a field in a
> struct uses u16 as the name then Rust won't compile, and cbindgen
> can't create the C header file.

Do you mean that cbindgen fails to help us make a C code we have
that says "struct { uint16_t u16; }" to work with Rust?

Declaring "typedef uint16_t u16" would not help such a case at all,
as it would not make "struct { u16 u16; }" an invalid C, and would
not force us to avoid such names that cbindgen may have problems
with.

So regardless of what to do with type names, we would need to adjust
some variable names to avoid clashes with Rust, which I am fine with.

> Using [ui](8|16|32|64|size) as the type in C also spreads
> awareness that those are reserved keywords and should not be used
> as variable names.

We certainly need to train our developers to avoid problematic names
like "u16" just like we do so for "new".

I am skeptical that using u16 as a type would have a good chance to
contribute to that effort (otherwise we would have added "new" as a
type to solve this issue already), but I am willing to be talked
into trying, with a few conditions to prevent unnecessary churning,
i.e. We do "typedef uint16_t u16" and friends, and new and old code
that are written to directly interact with Rust written code would
be better written with u16 and friends, so the same thing is called
similarly across the wall, but we want to avoid replacing uint16_t
with u16 blindly.


^ permalink raw reply	[flat|nested] 100+ messages in thread

* Re: [PATCH v2 00/18] Introduce rust: In xdiff
  2025-09-22 17:32               ` Ezekiel Newren
@ 2025-09-22 18:17                 ` Junio C Hamano
  2025-09-22 18:33                   ` Ezekiel Newren
  0 siblings, 1 reply; 100+ messages in thread
From: Junio C Hamano @ 2025-09-22 18:17 UTC (permalink / raw)
  To: Ezekiel Newren; +Cc: Patrick Steinhardt, Ezekiel Newren via GitGitGadget, git

Ezekiel Newren <ezekielnewren@gmail.com> writes:

> On Mon, Sep 22, 2025 at 11:23 AM Ezekiel Newren <ezekielnewren@gmail.com> wrote:
>> Cbindgen parse's Rust and generates C header files; If a field in a
>> struct uses u16 as the name then Rust won't compile, and cbindgen
>> can't create the C header file.
>
> I just tried this in Rust and it turns out you actually can use u16 as
> a struct field name. My bad. I think that's a bad idea and should be
> discouraged though.

That's a great news.

It means we do not have to worry about existing variables and
structure member names at all while working with cbindgen.


^ permalink raw reply	[flat|nested] 100+ messages in thread

* Re: [PATCH v2 00/18] Introduce rust: In xdiff
  2025-09-22 18:17                 ` Junio C Hamano
@ 2025-09-22 18:33                   ` Ezekiel Newren
  2025-09-22 18:41                     ` Junio C Hamano
  0 siblings, 1 reply; 100+ messages in thread
From: Ezekiel Newren @ 2025-09-22 18:33 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Patrick Steinhardt, Ezekiel Newren via GitGitGadget, git

On Mon, Sep 22, 2025 at 12:17 PM Junio C Hamano <gitster@pobox.com> wrote:
> > I just tried this in Rust and it turns out you actually can use u16 as
> > a struct field name. My bad. I think that's a bad idea and should be
> > discouraged though.
>
> That's a great news.
>
> It means we do not have to worry about existing variables and
> structure member names at all while working with cbindgen.

You've convinced me that we shouldn't use Rust type names in C. I've
already refactored my code to use [ui]int(8|16|32|64)_t in part 2 of
my xdiff cleanup.

^ permalink raw reply	[flat|nested] 100+ messages in thread

* Re: [PATCH v2 00/18] Introduce rust: In xdiff
  2025-09-22 18:33                   ` Ezekiel Newren
@ 2025-09-22 18:41                     ` Junio C Hamano
  0 siblings, 0 replies; 100+ messages in thread
From: Junio C Hamano @ 2025-09-22 18:41 UTC (permalink / raw)
  To: Ezekiel Newren; +Cc: Patrick Steinhardt, Ezekiel Newren via GitGitGadget, git

Ezekiel Newren <ezekielnewren@gmail.com> writes:

> already refactored my code to use [ui]int(8|16|32|64)_t in part 2 of
> my xdiff cleanup.

Noted.  I think [u]int(8|16|32|64)_t would be familiar to both C
writers and Rust folks who need to peek into C for working with it.

Thanks.

^ permalink raw reply	[flat|nested] 100+ messages in thread

* Re: [PATCH v2 00/18] Introduce rust: In xdiff
  2025-09-22 16:27               ` Ezekiel Newren
@ 2025-09-23  5:11                 ` Patrick Steinhardt
  2025-09-23 16:32                   ` Ezekiel Newren
  0 siblings, 1 reply; 100+ messages in thread
From: Patrick Steinhardt @ 2025-09-23  5:11 UTC (permalink / raw)
  To: Ezekiel Newren
  Cc: Junio C Hamano, Elijah Newren, Ezekiel Newren via GitGitGadget,
	git

On Mon, Sep 22, 2025 at 10:27:32AM -0600, Ezekiel Newren wrote:
> On Mon, Sep 22, 2025 at 10:16 AM Patrick Steinhardt <ps@pks.im> wrote:
> > On Mon, Sep 22, 2025 at 09:18:14AM -0600, Ezekiel Newren wrote:
> > > > By the way: I'm also happy to change attribution of some of the patches
> > > > in my patch series to mention Ezekiel as author. I don't care much who
> > > > is listed for the initial patches that introduce Rust, but would retain
> > > > my own authorship for the "varint" and "BreakingChanges" commits.
> > >
> > > My only other concern is with varint. You use usize on the Rust side
> > > and then uint64_t on the C side, but I'm ok with fixing that later as
> > > it only breaks 'linux32 (i386/ubuntu:focal)' in the github workflows.
> >
> > Oh, this is actually an oversight, good catch! I refactored "varint.c"
> > to use `uint64_t`, but then forgot to adjust the Rust side in the same
> > spirit. Will fix.
> 
> You also missed updating varint.h.

Hm, am I missing anything? It does use `uint64_t`, and if it didn't it
would cause a compiler error due to mismatching declarations.

> > I suggested in [1] that I can change authorship of the patches that
> > introduce the initial infrastructure into Meson and our Makefile (so I
> > guess patches 1 and 3) to instead list you as author and myself as
> > Co-authored-by. Is that something you want? Given that you have
> > kickstarted the whole effort around introducing Rust again I wouldn't
> > mind that at all.
> 
> It doesn't make sense to me to list myself as the author of any of
> your commits, but I would like my name referenced in your commit
> messages.

Okay, will do. Is it sufficient if I say something "Based-on-patch-by"
or "Inspired-by"? Don't really have much of a better idea for how to
include it, but please let me know in case you have any preference.

Patrick

^ permalink raw reply	[flat|nested] 100+ messages in thread

* Re: [PATCH v2 00/18] Introduce rust: In xdiff
  2025-09-23  5:11                 ` Patrick Steinhardt
@ 2025-09-23 16:32                   ` Ezekiel Newren
  0 siblings, 0 replies; 100+ messages in thread
From: Ezekiel Newren @ 2025-09-23 16:32 UTC (permalink / raw)
  To: Patrick Steinhardt
  Cc: Junio C Hamano, Elijah Newren, Ezekiel Newren via GitGitGadget,
	git

On Mon, Sep 22, 2025 at 11:11 PM Patrick Steinhardt <ps@pks.im> wrote:
>
> On Mon, Sep 22, 2025 at 10:27:32AM -0600, Ezekiel Newren wrote:
> > On Mon, Sep 22, 2025 at 10:16 AM Patrick Steinhardt <ps@pks.im> wrote:
> > > On Mon, Sep 22, 2025 at 09:18:14AM -0600, Ezekiel Newren wrote:
> > > > > By the way: I'm also happy to change attribution of some of the patches
> > > > > in my patch series to mention Ezekiel as author. I don't care much who
> > > > > is listed for the initial patches that introduce Rust, but would retain
> > > > > my own authorship for the "varint" and "BreakingChanges" commits.
> > > >
> > > > My only other concern is with varint. You use usize on the Rust side
> > > > and then uint64_t on the C side, but I'm ok with fixing that later as
> > > > it only breaks 'linux32 (i386/ubuntu:focal)' in the github workflows.
> > >
> > > Oh, this is actually an oversight, good catch! I refactored "varint.c"
> > > to use `uint64_t`, but then forgot to adjust the Rust side in the same
> > > spirit. Will fix.
> >
> > You also missed updating varint.h.

Oh, I think when I was editing your patches locally varint.h got out of sync.

> Hm, am I missing anything? It does use `uint64_t`, and if it didn't it
> would cause a compiler error due to mismatching declarations.

unsigned char should be replaced with uint8_t. I don't know the exact
location that causes linux32 (i386/ubuntu:focal) to fail, I think it
had to do with specifying uint64_t on the C side, and usize on the
Rust side. usize is 32-bits on i386/ubuntu:focal.

> > > I suggested in [1] that I can change authorship of the patches that
> > > introduce the initial infrastructure into Meson and our Makefile (so I
> > > guess patches 1 and 3) to instead list you as author and myself as
> > > Co-authored-by. Is that something you want? Given that you have
> > > kickstarted the whole effort around introducing Rust again I wouldn't
> > > mind that at all.
> >
> > It doesn't make sense to me to list myself as the author of any of
> > your commits, but I would like my name referenced in your commit
> > messages.
>
> Okay, will do. Is it sufficient if I say something "Based-on-patch-by"
> or "Inspired-by"? Don't really have much of a better idea for how to
> include it, but please let me know in case you have any preference.

Inspired-by would be my preference.

^ permalink raw reply	[flat|nested] 100+ messages in thread

* Re: [PATCH v2 00/18] Introduce rust: In xdiff
  2025-09-17  1:16 ` [PATCH v2 00/18] Introduce rust: In xdiff Ezekiel Newren via GitGitGadget
                     ` (19 preceding siblings ...)
  2025-09-17 17:07   ` Junio C Hamano
@ 2025-09-23 18:05   ` Ezekiel Newren
  2025-09-23 21:04     ` Junio C Hamano
  20 siblings, 1 reply; 100+ messages in thread
From: Ezekiel Newren @ 2025-09-23 18:05 UTC (permalink / raw)
  To: Ezekiel Newren via GitGitGadget; +Cc: git

On Tue, Sep 16, 2025 at 7:16 PM Ezekiel Newren via GitGitGadget
<gitgitgadget@gmail.com> wrote:
DROP PATCH SERIES: I am dropping this patch series in favor of
Patrick's. Everything I want to add can be rebased on top of his work.

^ permalink raw reply	[flat|nested] 100+ messages in thread

* Re: [PATCH v2 00/18] Introduce rust: In xdiff
  2025-09-23 18:05   ` Ezekiel Newren
@ 2025-09-23 21:04     ` Junio C Hamano
  0 siblings, 0 replies; 100+ messages in thread
From: Junio C Hamano @ 2025-09-23 21:04 UTC (permalink / raw)
  To: Ezekiel Newren; +Cc: Ezekiel Newren via GitGitGadget, git

Ezekiel Newren <ezekielnewren@gmail.com> writes:

> On Tue, Sep 16, 2025 at 7:16 PM Ezekiel Newren via GitGitGadget
> <gitgitgadget@gmail.com> wrote:
> DROP PATCH SERIES: I am dropping this patch series in favor of
> Patrick's. Everything I want to add can be rebased on top of his work.

OK.  Thanks for expressing intentions clearly.  Will adjust my end
by ejecting them from 'seen'.


^ permalink raw reply	[flat|nested] 100+ messages in thread

end of thread, other threads:[~2025-09-23 21:04 UTC | newest]

Thread overview: 100+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-08-29 19:42 [PATCH 00/15] Introduce rust: In xdiff Ezekiel Newren via GitGitGadget
2025-08-29 19:42 ` [PATCH 01/15] doc: add a policy for using Rust brian m. carlson via GitGitGadget
2025-08-29 20:00   ` brian m. carlson
2025-08-29 20:11     ` Ezekiel Newren
2025-09-02 16:39       ` brian m. carlson
2025-09-02 18:39         ` Ezekiel Newren
2025-09-04 22:55           ` Ezekiel Newren
2025-08-29 19:42 ` [PATCH 02/15] xdiff: introduce rust Ezekiel Newren via GitGitGadget
2025-08-29 19:42 ` [PATCH 03/15] github workflows: install rust Ezekiel Newren via GitGitGadget
2025-08-29 19:42 ` [PATCH 04/15] win+Meson: do allow linking with the Rust-built xdiff Johannes Schindelin via GitGitGadget
2025-08-29 19:42 ` [PATCH 05/15] github workflows: upload Cargo.lock Ezekiel Newren via GitGitGadget
2025-08-29 19:42 ` [PATCH 06/15] ivec: create a vector type that is interoperable between C and Rust Ezekiel Newren via GitGitGadget
2025-08-29 19:42 ` [PATCH 07/15] xdiff/xprepare: remove superfluous forward declarations Ezekiel Newren via GitGitGadget
2025-08-29 19:42 ` [PATCH 08/15] xdiff: delete unnecessary fields from xrecord_t and xdfile_t Ezekiel Newren via GitGitGadget
2025-08-29 19:42 ` [PATCH 09/15] xdiff: make fields of xrecord_t Rust friendly Ezekiel Newren via GitGitGadget
2025-08-29 19:42 ` [PATCH 10/15] xdiff: use one definition for freeing xdfile_t Ezekiel Newren via GitGitGadget
2025-08-29 19:42 ` [PATCH 11/15] xdiff: replace chastore with an ivec in xdfile_t Ezekiel Newren via GitGitGadget
2025-08-29 19:42 ` [PATCH 12/15] xdiff: delete nrec field from xdfile_t Ezekiel Newren via GitGitGadget
2025-08-29 19:42 ` [PATCH 13/15] xdiff: delete recs " Ezekiel Newren via GitGitGadget
2025-08-29 19:42 ` [PATCH 14/15] xdiff: make xdfile_t more rust friendly Ezekiel Newren via GitGitGadget
2025-08-29 19:42 ` [PATCH 15/15] xdiff: implement xdl_trim_ends() in Rust Ezekiel Newren via GitGitGadget
2025-09-17  1:16 ` [PATCH v2 00/18] Introduce rust: In xdiff Ezekiel Newren via GitGitGadget
2025-09-17  1:16   ` [PATCH v2 01/18] cleanup: rename variables that collide with Rust primitive type names Ezekiel Newren via GitGitGadget
2025-09-17  7:42     ` Eric Sunshine
2025-09-17 14:32       ` Junio C Hamano
2025-09-19 19:36         ` Ezekiel Newren
2025-09-17  1:16   ` [PATCH v2 02/18] make: add -fPIE flag Ezekiel Newren via GitGitGadget
2025-09-17  7:44     ` Eric Sunshine
2025-09-19 19:48       ` Ezekiel Newren
2025-09-19 20:07         ` Junio C Hamano
2025-09-19 21:52           ` Ezekiel Newren
2025-09-19 23:43             ` Junio C Hamano
2025-09-19 23:59               ` Collin Funk
2025-09-20 16:44                 ` Junio C Hamano
2025-09-21  1:14                   ` Ramsay Jones
2025-09-17  1:16   ` [PATCH v2 03/18] make: merge xdiff lib into libgit.a Ezekiel Newren via GitGitGadget
2025-09-17  7:46     ` Eric Sunshine
2025-09-19 19:54       ` Ezekiel Newren
2025-09-17  1:16   ` [PATCH v2 04/18] make: merge reftable " Ezekiel Newren via GitGitGadget
2025-09-17  7:46     ` Eric Sunshine
2025-09-19 19:02     ` Junio C Hamano
2025-09-19 20:00       ` Ezekiel Newren
2025-09-19 20:14         ` Junio C Hamano
2025-09-19 23:02           ` Ezekiel Newren
2025-09-17  1:16   ` [PATCH v2 05/18] doc: add a policy for using Rust brian m. carlson via GitGitGadget
2025-09-17  1:16   ` [PATCH v2 06/18] BreakingChanges: announce Rust becoming mandatory Patrick Steinhardt via GitGitGadget
2025-09-17  1:16   ` [PATCH v2 07/18] build: introduce rust Ezekiel Newren via GitGitGadget
2025-09-17  8:26     ` Eric Sunshine
2025-09-17 14:54       ` Junio C Hamano
2025-09-18  7:06         ` Eric Sunshine
2025-09-19 20:11       ` Ezekiel Newren
2025-09-19 20:24         ` Eric Sunshine
2025-09-19 21:28           ` Ezekiel Newren
2025-09-17  1:16   ` [PATCH v2 08/18] help: report on whether or not Rust is enabled Patrick Steinhardt via GitGitGadget
2025-09-17  1:16   ` [PATCH v2 09/18] github workflows: install rust Ezekiel Newren via GitGitGadget
2025-09-17  8:01     ` Eric Sunshine
2025-09-17  1:16   ` [PATCH v2 10/18] win+Meson: do allow linking with the Rust-built xdiff Johannes Schindelin via GitGitGadget
2025-09-17  1:16   ` [PATCH v2 11/18] github workflows: upload Cargo.lock Ezekiel Newren via GitGitGadget
2025-09-17  1:16   ` [PATCH v2 12/18] build: new crate, build-helper Ezekiel Newren via GitGitGadget
2025-09-17  8:58     ` Eric Sunshine
2025-09-17  1:16   ` [PATCH v2 13/18] build-helper: link against libgit.a and any other required C libraries Ezekiel Newren via GitGitGadget
2025-09-17  8:51     ` Eric Sunshine
2025-09-17 23:07       ` D. Ben Knoble
2025-09-17 23:31         ` Eric Sunshine
2025-09-19 20:25       ` Ezekiel Newren
2025-09-17  1:16   ` [PATCH v2 14/18] build-helper: cbindgen, let crates generate a header file Ezekiel Newren via GitGitGadget
2025-09-17  9:08     ` Eric Sunshine
2025-09-19 20:34       ` Ezekiel Newren
2025-09-17  1:16   ` [PATCH v2 15/18] varint: use explicit width for integers Patrick Steinhardt via GitGitGadget
2025-09-17  1:16   ` [PATCH v2 16/18] build: new crate, misc Ezekiel Newren via GitGitGadget
2025-09-17  9:16     ` Eric Sunshine
2025-09-19 20:42     ` Ezekiel Newren
2025-09-19 20:50       ` Eric Sunshine
2025-09-19 21:54         ` Ezekiel Newren
2025-09-17  1:16   ` [PATCH v2 17/18] misc: use BuildHelper Ezekiel Newren via GitGitGadget
2025-09-17  1:16   ` [PATCH v2 18/18] misc::varint: reimplement as test balloon for Rust Patrick Steinhardt via GitGitGadget
2025-09-17  5:58   ` [PATCH v2 00/18] Introduce rust: In xdiff Patrick Steinhardt
2025-09-19 20:57     ` Ezekiel Newren
2025-09-22 13:01       ` Patrick Steinhardt
2025-09-22 15:31         ` Ezekiel Newren
2025-09-22 16:08           ` Patrick Steinhardt
2025-09-22 16:47           ` Junio C Hamano
2025-09-22 17:23             ` Ezekiel Newren
2025-09-22 17:32               ` Ezekiel Newren
2025-09-22 18:17                 ` Junio C Hamano
2025-09-22 18:33                   ` Ezekiel Newren
2025-09-22 18:41                     ` Junio C Hamano
2025-09-22 18:12               ` Junio C Hamano
2025-09-17 17:07   ` Junio C Hamano
2025-09-17 20:44     ` Junio C Hamano
2025-09-17 21:34     ` Elijah Newren
2025-09-17 22:48       ` Junio C Hamano
2025-09-22 13:01         ` Patrick Steinhardt
2025-09-22 15:18           ` Ezekiel Newren
2025-09-22 16:15             ` Patrick Steinhardt
2025-09-22 16:27               ` Ezekiel Newren
2025-09-23  5:11                 ` Patrick Steinhardt
2025-09-23 16:32                   ` Ezekiel Newren
2025-09-23 18:05   ` Ezekiel Newren
2025-09-23 21:04     ` Junio C Hamano

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).