Volume XXII, number 279Tuesday, October 6, 2026Latest message 19 minutes ago

The Git List

News and archive of git@vger.kernel.org, since April 2005

patch, 4 partsAdd a compile-time option to use the new, very fast sha1dc Rust crate

9 messages between Sep 28, 2026 and Oct 3, 2026, from Johannes Schindelin via GitGitGadget, Johannes Schindelin, Junio C Hamano, brian m. carlson.

Plain Markdown or JSON for tools and agents. Diffs are folded; open one to read it.

Johannes Schindelin via GitGitGadgetSep 28, 2026, 15:51 UTC on lore

I stumbled across this new Rust crate last week. Its performance numbers are quite impressive. Naturally, I want to make use of this and get for Windows, which is used on many monorepos where this makes a real difference: In a pretty fast and loose test, I verified that a git index-pack runs roughly three times faster solely due to using those SIMD-based optimizations!

As a safety precaution, because this sha1dc crate is quite new, I wanted to introduce an escape hatch: core.sha1dcBackend=c, but turn it on by default, which is the reason for the three additional patches. Should these patches be undesirable for the Git project? I would not be mad at all if they were simply dropped.

Johannes Schindelin (4):
  libgitcore: add `sha1dc` as an optional feature
  sha1dc: allow selecting the C backend without rebuilding
  pthread: provide `pthread_once()` shims for Windows and for
    NO_PTHREADS
  sha1dc: make `sha1dc_init()` thread-safe
 Cargo.toml                     |   4 ++
 Documentation/config/core.adoc |   5 ++
 Makefile                       |  29 +++++++++
 compat/win32/pthread.c         |  16 +++++
 compat/win32/pthread.h         |   5 ++
 hash.h                         |   5 ++
 sha1dc_git.c                   | 109 +++++++++++++++++++++++++++++++--
 sha1dc_git.h                   |   5 +-
 sha1dc_rs.h                    |  37 +++++++++++
 src/lib.rs                     |   2 +
 src/sha1dc_rs.rs               |  78 +++++++++++++++++++++++
 t/helper/test-sha1.c           |  19 +++++-
 t/t0013-sha1dc.sh              |  21 ++++++-
 thread-utils.h                 |  16 +++++
 14 files changed, 343 insertions(+), 8 deletions(-)
 create mode 100644 sha1dc_rs.h
 create mode 100644 src/sha1dc_rs.rs
base-commit: 3bc0341126508f78f5869cbfc0005e987efdf0c7
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-2240%2Fdscho%2Foptionally-use-sha1dc-rs-v1
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-2240/dscho/optionally-use-sha1dc-rs-v1
Pull-Request: https://github.com/gitgitgadget/git/pull/2240
-- 
gitgitgadget
Johannes Schindelin via GitGitGadgetSep 28, 2026, 15:51 UTC in reply to Johannes Schindelin via GitGitGadget on lore

[PATCH 1/4] libgitcore: add `sha1dc` as an optional feature

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

The new Rust crate `sha1dc` (https://crates.io/crates/sha1dc) promises not only type safety but also much better performance compared to the collision-detecting SHA-1 library Git uses at the moment. This performance comes mostly from a SIMD-centric design that relies on features provided by many x86_64 and aarch64 CPUs.

Let's optionally use this crate, toggled by the build option `DC_SHA1_RS`.

To avoid requiring a shim around the `sha1dc_rs_final()` function just to call `die()` upon a detected collision, pass a pointer to that function to Rust and let it call it directly. This is safe: `die()` is a variadic function, but the Rust code calls it with a simple string without any interpolation required.

In a pretty unscientific test on a moderately busy Windows Ryzen 7 machine (UCRT64 GCC 16.2), Rust-backed `git-index-pack.exe` using `sha1dc` 0.1.3 was over three times faster than the C backend by median wall time. Here are the results running five iterations of the `sha1dc` C, Rust v0.1.2, and Rust v0.1.3 backends in randomized, balanced order with `--verify --no-rev-index --threads=1 --object-format=sha1`:

SHA1DC backend Median Best of five -------------- ------ ------------ C (default) 32.9s 32.1s Rust 0.1.2 10.9s 10.4s Rust 0.1.3 10.7s 10.5s

Version 0.1.3 finished first in four of five triplets, but its median advantage over 0.1.2 was only about 2.4%, and the best 0.1.2 run was faster. In other words, the difference is mostly in the noise.

On the same machine, using WSL ("Windows Subsystem for Linux") with the same packfile copied to Linux' ext4 filesystem:

SHA1DC backend Median Best of five -------------- ------ ------------ C (default) 23.971s 23.435s Rust 0.1.2 9.123s 8.492s Rust 0.1.3 9.020s 8.491s

This is overall faster because of the ext4 vs NTFS performance characteristics, but the same finding holds true: the Rust version of `sha1dc` is dramatically faster.

Studying the runs with the Linux perf tools reveals that with the C backend, over 70% of the total time is spent in `git_hash_update()`, with either version of the Rust backend it is around 30%.

A comparable test on an M4 Mac yields these results:

SHA1DC backend Median Best of five -------------- ------ ------------ C (default) 8.035s 8.009s Rust 0.1.2 4.359s 4.338s Rust 0.1.3 4.361s 4.268s

Note that the `sha1dc` crate still requires a significantly newer Rust version than Git's existing Rust support requires: 1.87 instead of 1.63 (https://crates.io/api/v1/crates/sha1dc/0.1.3).

Assisted-by: GPT-6 Sol
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
---
 Cargo.toml       |  4 +++
 Makefile         | 29 +++++++++++++++++++
 hash.h           |  5 ++++
 sha1dc_rs.h      | 23 ++++++++++++++++
 src/lib.rs       |  2 ++
 src/sha1dc_rs.rs | 72 ++++++++++++++++++++++++++++++++++++++++++++++++
 6 files changed, 135 insertions(+)
 create mode 100644 sha1dc_rs.h
 create mode 100644 src/sha1dc_rs.rs
Show changes to 6 files +135 −0

Cargo.toml, Makefile, hash.h, sha1dc_rs.h, src/lib.rs, src/sha1dc_rs.rs

diff --git a/Cargo.toml b/Cargo.toml
index 2f51bf5d5f..0a953dd481 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -8,3 +8,7 @@ rust-version = "1.49.0"
 crate-type = ["staticlib"]
 
 [dependencies]
+sha1dc = { version = "0.1.3", optional = true }
+
+[features]
+sha1dc-rs = ["sha1dc"]
diff --git a/Makefile b/Makefile
index c649c93c51..0c321494cf 100644
--- a/Makefile
+++ b/Makefile
@@ -567,6 +567,9 @@ include shared.mak
 # by the git project to migrate to using sha1collisiondetection as a
 # submodule.
 #
+# Define DC_SHA1_RS to use the sha1dc Rust crate instead of the default
+# C implementation. This requires Rust 1.87 or newer.
+#
 # === SHA-256 backend ===
 #
 # ==== Security ====
@@ -2137,6 +2140,23 @@ ifdef PPC_SHA1
 $(error the PPC_SHA1 flag has been removed along with the PowerPC-specific SHA-1 implementation.)
 endif
 
+ifdef DC_SHA1_RS
+ifdef NO_RUST
+$(error DC_SHA1_RS requires Rust support)
+endif
+ifneq ($(strip $(OPENSSL_SHA1)$(BLK_SHA1)$(APPLE_COMMON_CRYPTO_SHA1)),)
+$(error DC_SHA1_RS cannot be combined with another SHA-1 backend)
+endif
+ifdef DC_SHA1_EXTERNAL
+$(error Only set DC_SHA1_RS or DC_SHA1_EXTERNAL, not both)
+endif
+ifdef DC_SHA1_SUBMODULE
+ifneq ($(DC_SHA1_SUBMODULE),auto)
+$(error Only set DC_SHA1_RS or DC_SHA1_SUBMODULE, not both)
+endif
+endif
+endif
+
 ifdef OPENSSL_SHA1
 	EXTLIBS += $(LIB_4_CRYPTO)
 	BASIC_CFLAGS += -DSHA1_OPENSSL
@@ -2150,6 +2170,14 @@ ifdef APPLE_COMMON_CRYPTO_SHA1
 	BASIC_CFLAGS += -DSHA1_APPLE
 else
 	BASIC_CFLAGS += -DSHA1_DC
+ifdef DC_SHA1_RS
+	BASIC_CFLAGS += -DDC_SHA1_RS
+	CARGO_ARGS += --features sha1dc-rs
+	RUST_SOURCES += src/sha1dc_rs.rs
+ifeq ($(uname_S),MINGW)
+	EXTLIBS += -luserenv
+endif
+else
 	LIB_OBJS += sha1dc_git.o
 ifdef DC_SHA1_EXTERNAL
         ifdef DC_SHA1_SUBMODULE
@@ -2177,6 +2205,7 @@ endif
 endif
 endif
 endif
+endif
 
 ifdef OPENSSL_SHA1_UNSAFE
 ifndef OPENSSL_SHA1
diff --git a/hash.h b/hash.h
index cf94ad5700..dd2e66e1c9 100644
--- a/hash.h
+++ b/hash.h
@@ -12,8 +12,13 @@
 #    include "sha1/openssl.h"
 #  endif
 #elif defined(SHA1_DC)
+#ifdef DC_SHA1_RS
+#define SHA1_BACKEND "SHA1_DC-rs"
+#include "sha1dc_rs.h"
+#else
 #define SHA1_BACKEND "SHA1_DC"
 #include "sha1dc_git.h"
+#endif
 #else /* SHA1_BLK */
 #define SHA1_BACKEND "SHA1_BLK (No collision detection)"
 #include "block-sha1/sha1.h"
diff --git a/sha1dc_rs.h b/sha1dc_rs.h
new file mode 100644
index 0000000000..35e3865d72
--- /dev/null
+++ b/sha1dc_rs.h
@@ -0,0 +1,23 @@
+#ifndef SHA1DC_RS_H
+#define SHA1DC_RS_H
+
+#define platform_SHA_IS_SHA1DC /* used by "test-tool sha1-is-sha1dc" */
+
+typedef struct sha1dc_rs_hasher *SHA1_CTX;
+
+void sha1dc_rs_init(SHA1_CTX *);
+void sha1dc_rs_clone(SHA1_CTX *, const SHA1_CTX *);
+void sha1dc_rs_update(SHA1_CTX *, const void *, size_t);
+void sha1dc_rs_final(unsigned char [20], SHA1_CTX *,
+		     void (*die_fn)(const char *, ...));
+void sha1dc_rs_discard(SHA1_CTX *);
+
+#define platform_SHA_CTX SHA1_CTX
+#define platform_SHA1_Init sha1dc_rs_init
+#define platform_SHA1_Update sha1dc_rs_update
+#define platform_SHA1_Final(hash, ctx) sha1dc_rs_final((hash), (ctx), die)
+#define SHA1_NEEDS_CLONE_HELPER
+#define platform_SHA1_Clone sha1dc_rs_clone
+#define platform_SHA1_Discard sha1dc_rs_discard
+
+#endif
diff --git a/src/lib.rs b/src/lib.rs
index 0c598298b1..a34f4d489c 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,4 +1,6 @@
 pub mod csum_file;
 pub mod hash;
 pub mod loose;
+#[cfg(feature = "sha1dc-rs")]
+mod sha1dc_rs;
 pub mod varint;
diff --git a/src/sha1dc_rs.rs b/src/sha1dc_rs.rs
new file mode 100644
index 0000000000..df075a5d83
--- /dev/null
+++ b/src/sha1dc_rs.rs
@@ -0,0 +1,72 @@
+use sha1dc::Hasher;
+use std::ffi::CString;
+use std::os::raw::c_char;
+use std::{ptr, slice};
+
+/// Initialize a collision-detecting SHA-1 context.
+///
+/// # Safety
+/// `ctx` must point to an uninitialized SHA-1 context.
+#[no_mangle]
+pub unsafe extern "C" fn sha1dc_rs_init(ctx: *mut *mut Hasher) {
+    *ctx = Box::into_raw(Box::new(Hasher::new()));
+}
+
+/// Replace a SHA-1 context with a clone of another.
+///
+/// # Safety
+/// Both contexts must be initialized.
+#[no_mangle]
+pub unsafe extern "C" fn sha1dc_rs_clone(dst: *mut *mut Hasher, src: *const *mut Hasher) {
+    let hasher = Box::new((**src).clone());
+    drop(Box::from_raw(*dst));
+    *dst = Box::into_raw(hasher);
+}
+
+/// Update the SHA-1 hasher with the given bytes.
+///
+/// # Safety
+/// `ctx` must be initialized and `data` must point to `len` bytes unless
+/// `len` is zero.
+#[no_mangle]
+pub unsafe extern "C" fn sha1dc_rs_update(ctx: *mut *mut Hasher, data: *const c_char, len: usize) {
+    if len != 0 {
+        (**ctx).update(slice::from_raw_parts(data.cast::<u8>(), len));
+    }
+}
+
+/// Finalize SHA-1, reporting detected collisions through `die`.
+///
+/// # Safety
+/// `ctx` must be initialized, `hash` must point to at least 20 bytes, and
+/// `die` must be a non-returning C variadic function.
+#[no_mangle]
+pub unsafe extern "C" fn sha1dc_rs_final(
+    hash: *mut u8,
+    ctx: *mut *mut Hasher,
+    die: unsafe extern "C" fn(*const c_char, ...) -> !,
+) {
+    let hasher = *Box::from_raw(*ctx);
+    *ctx = ptr::null_mut();
+    match hasher.finalize() {
+        Ok(digest) => ptr::copy_nonoverlapping(digest.as_bytes().as_ptr(), hash, 20),
+        Err(collision) => {
+            let message = CString::new(format!(
+                "SHA-1 appears to be part of a collision attack: {}",
+                collision.digest()
+            ))
+            .expect("collision message contains no NUL");
+            die(message.as_ptr());
+        }
+    }
+}
+
+/// Discard a SHA-1 context without producing a digest.
+///
+/// # Safety
+/// `ctx` must be initialized.
+#[no_mangle]
+pub unsafe extern "C" fn sha1dc_rs_discard(ctx: *mut *mut Hasher) {
+    drop(Box::from_raw(*ctx));
+    *ctx = ptr::null_mut();
+}
-- 
gitgitgadget
Johannes Schindelin via GitGitGadgetSep 28, 2026, 15:51 UTC in reply to Johannes Schindelin via GitGitGadget on lore

[PATCH 2/4] sha1dc: allow selecting the C backend without rebuilding

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

The Rust `sha1dc` create is really new. While it produced only correct hashes in my hands, before unleashing this to the masses, we need to provide an "escape hatch" in case it doesn't do the right thing.

Therefore, when building with `DC_SHA1_RS`, use the Rust `sha1dc` by default, yet also offer to use the C version of `sha1dc` via `core.sha1dcBackend=c` (and `core.sha1dcBackend=rust` to select Rust explicitly).

This is made possible by a set of function pointers that are initialized upon the first call to the `git_hash_init()` function.

Note that the order in which `hex.h` and `sha1dc_git.h` are included in `sha1dc_git.c` now have to be turned the other way round, to avoid redefining the `platform_SHA*` constants.

Assisted-by: GPT-6 Sol
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
---
 Documentation/config/core.adoc |  5 +++
 Makefile                       | 10 +++---
 sha1dc_git.c                   | 60 +++++++++++++++++++++++++++++++---
 sha1dc_git.h                   |  5 +--
 sha1dc_rs.h                    | 30 ++++++++++++-----
 src/sha1dc_rs.rs               | 18 ++++++----
 t/helper/test-sha1.c           | 19 ++++++++++-
 t/t0013-sha1dc.sh              | 21 +++++++++++-
 8 files changed, 141 insertions(+), 27 deletions(-)
Show changes to 8 files +141 −27

Documentation/config/core.adoc, Makefile, sha1dc_git.c, sha1dc_git.h, sha1dc_rs.h, src/sha1dc_rs.rs, t/helper/test-sha1.c, t/t0013-sha1dc.sh

diff --git a/Documentation/config/core.adoc b/Documentation/config/core.adoc
index 340329edc3..2ef56a107e 100644
--- a/Documentation/config/core.adoc
+++ b/Documentation/config/core.adoc
@@ -382,6 +382,11 @@ core.repositoryFormatVersion::
 	Internal variable identifying the repository format and layout
 	version. See linkgit:gitrepository-layout[5].
 
+core.sha1dcBackend::
+	Select the collision-detecting SHA-1 implementation when Git is built
+	with `DC_SHA1_RS`: valid values are `rust` (the default) and `c` (the C
+	fallback). This setting has no effect with other SHA-1 backends.
+
 core.sharedRepository::
 	When 'group' (or 'true'), the repository is made shareable between
 	several users in a group (making sure all the files and objects are
diff --git a/Makefile b/Makefile
index 0c321494cf..31afa7d917 100644
--- a/Makefile
+++ b/Makefile
@@ -567,8 +567,9 @@ include shared.mak
 # by the git project to migrate to using sha1collisiondetection as a
 # submodule.
 #
-# Define DC_SHA1_RS to use the sha1dc Rust crate instead of the default
-# C implementation. This requires Rust 1.87 or newer.
+# Define DC_SHA1_RS to use the sha1dc Rust crate by default, with the C
+# implementation available via core.sha1dcBackend=c. This requires Rust
+# 1.87 or newer.
 #
 # === SHA-256 backend ===
 #
@@ -2170,6 +2171,7 @@ ifdef APPLE_COMMON_CRYPTO_SHA1
 	BASIC_CFLAGS += -DSHA1_APPLE
 else
 	BASIC_CFLAGS += -DSHA1_DC
+	LIB_OBJS += sha1dc_git.o
 ifdef DC_SHA1_RS
 	BASIC_CFLAGS += -DDC_SHA1_RS
 	CARGO_ARGS += --features sha1dc-rs
@@ -2177,8 +2179,7 @@ ifdef DC_SHA1_RS
 ifeq ($(uname_S),MINGW)
 	EXTLIBS += -luserenv
 endif
-else
-	LIB_OBJS += sha1dc_git.o
+endif
 ifdef DC_SHA1_EXTERNAL
         ifdef DC_SHA1_SUBMODULE
                 ifneq ($(DC_SHA1_SUBMODULE),auto)
@@ -2205,7 +2206,6 @@ endif
 endif
 endif
 endif
-endif
 
 ifdef OPENSSL_SHA1_UNSAFE
 ifndef OPENSSL_SHA1
diff --git a/sha1dc_git.c b/sha1dc_git.c
index fe58d7962a..dcc5c1ca8e 100644
--- a/sha1dc_git.c
+++ b/sha1dc_git.c
@@ -1,6 +1,13 @@
+#ifdef DC_SHA1_RS
+#define USE_THE_REPOSITORY_VARIABLE
+#endif
 #include "git-compat-util.h"
-#include "sha1dc_git.h"
 #include "hex.h"
+#include "sha1dc_git.h"
+#ifdef DC_SHA1_RS
+#include "config.h"
+#include "repository.h"
+#endif
 
 #ifdef DC_SHA1_EXTERNAL
 /*
@@ -16,12 +23,13 @@ void git_SHA1DCInit(SHA1_CTX *ctx)
 /*
  * Same as SHA1DCFinal, but convert collision attack case into a verbose die().
  */
-void git_SHA1DCFinal(unsigned char hash[20], SHA1_CTX *ctx)
+void git_SHA1DCFinal(unsigned char hash[20], SHA1_CTX *ctx,
+		     void (*die_fn)(const char *, ...))
 {
 	if (!SHA1DCFinal(hash, ctx))
 		return;
-	die("SHA-1 appears to be part of a collision attack: %s",
-	    hash_to_hex_algop(hash, &hash_algos[GIT_HASH_SHA1]));
+	die_fn("SHA-1 appears to be part of a collision attack: %s",
+	       hash_to_hex_algop(hash, &hash_algos[GIT_HASH_SHA1]));
 }
 
 /*
@@ -37,3 +45,47 @@ void git_SHA1DCUpdate(SHA1_CTX *ctx, const void *vdata, size_t len)
 	}
 	SHA1DCUpdate(ctx, data, len);
 }
+
+#ifdef DC_SHA1_RS
+static void sha1dc_c_clone(SHA1_CTX *dst, const SHA1_CTX *src)
+{
+	*dst = *src;
+}
+
+static void sha1dc_c_discard(SHA1_CTX *ctx UNUSED)
+{
+	/* The C context owns no resources. */
+}
+
+/* The first SHA-1 initialization must precede concurrent hashing. */
+static void sha1dc_choose(SHA1_CTX *ctx);
+
+void (*sha1dc_init)(SHA1_CTX *) = sha1dc_choose;
+void (*sha1dc_clone)(SHA1_CTX *, const SHA1_CTX *);
+void (*sha1dc_update)(SHA1_CTX *, const void *, size_t);
+void (*sha1dc_final)(unsigned char [20], SHA1_CTX *,
+		     void (*die_fn)(const char *, ...));
+void (*sha1dc_discard)(SHA1_CTX *);
+
+static void sha1dc_choose(SHA1_CTX *ctx)
+{
+	const char *backend;
+	int use_c = 0;
+
+	if (!repo_config_get_string_tmp(the_repository, "core.sha1dcbackend",
+					&backend)) {
+		if (!strcasecmp(backend, "c"))
+			use_c = 1;
+		else if (strcasecmp(backend, "rust"))
+			die("invalid value for core.sha1dcBackend: '%s'",
+			    backend);
+	}
+
+	sha1dc_clone = use_c ? sha1dc_c_clone : sha1dc_rs_clone;
+	sha1dc_update = use_c ? git_SHA1DCUpdate : sha1dc_rs_update;
+	sha1dc_final = use_c ? git_SHA1DCFinal : sha1dc_rs_final;
+	sha1dc_discard = use_c ? sha1dc_c_discard : sha1dc_rs_discard;
+	sha1dc_init = use_c ? git_SHA1DCInit : sha1dc_rs_init;
+	sha1dc_init(ctx);
+}
+#endif
diff --git a/sha1dc_git.h b/sha1dc_git.h
index 0bcf1aa84b..4c4aefe3e8 100644
--- a/sha1dc_git.h
+++ b/sha1dc_git.h
@@ -14,7 +14,8 @@ void git_SHA1DCInit(SHA1_CTX *);
 #define git_SHA1DCInit	SHA1DCInit
 #endif
 
-void git_SHA1DCFinal(unsigned char [20], SHA1_CTX *);
+void git_SHA1DCFinal(unsigned char [20], SHA1_CTX *,
+		    void (*die_fn)(const char *, ...));
 void git_SHA1DCUpdate(SHA1_CTX *ctx, const void *data, size_t len);
 
 #define platform_SHA_IS_SHA1DC /* used by "test-tool sha1-is-sha1dc" */
@@ -23,5 +24,5 @@ void git_SHA1DCUpdate(SHA1_CTX *ctx, const void *data, size_t len);
 #define platform_SHA_CTX SHA1_CTX
 #define platform_SHA1_Init git_SHA1DCInit
 #define platform_SHA1_Update git_SHA1DCUpdate
-#define platform_SHA1_Final git_SHA1DCFinal
+#define platform_SHA1_Final(hash, ctx) git_SHA1DCFinal((hash), (ctx), die)
 #endif
diff --git a/sha1dc_rs.h b/sha1dc_rs.h
index 35e3865d72..e5d6345363 100644
--- a/sha1dc_rs.h
+++ b/sha1dc_rs.h
@@ -1,9 +1,15 @@
 #ifndef SHA1DC_RS_H
 #define SHA1DC_RS_H
 
-#define platform_SHA_IS_SHA1DC /* used by "test-tool sha1-is-sha1dc" */
+#define platform_SHA_CTX union sha1dc_ctx
+#include "sha1dc_git.h"
 
-typedef struct sha1dc_rs_hasher *SHA1_CTX;
+typedef struct sha1dc_rs_hasher *sha1dc_rs_ctx;
+
+union sha1dc_ctx {
+	SHA1_CTX c;
+	sha1dc_rs_ctx rs;
+};
 
 void sha1dc_rs_init(SHA1_CTX *);
 void sha1dc_rs_clone(SHA1_CTX *, const SHA1_CTX *);
@@ -12,12 +18,20 @@ void sha1dc_rs_final(unsigned char [20], SHA1_CTX *,
 		     void (*die_fn)(const char *, ...));
 void sha1dc_rs_discard(SHA1_CTX *);
 
-#define platform_SHA_CTX SHA1_CTX
-#define platform_SHA1_Init sha1dc_rs_init
-#define platform_SHA1_Update sha1dc_rs_update
-#define platform_SHA1_Final(hash, ctx) sha1dc_rs_final((hash), (ctx), die)
+extern void (*sha1dc_init)(SHA1_CTX *);
+extern void (*sha1dc_clone)(SHA1_CTX *, const SHA1_CTX *);
+extern void (*sha1dc_update)(SHA1_CTX *, const void *, size_t);
+extern void (*sha1dc_final)(unsigned char [20], SHA1_CTX *,
+			    void (*die_fn)(const char *, ...));
+extern void (*sha1dc_discard)(SHA1_CTX *);
+
+#define platform_SHA1_Init(ctx) sha1dc_init(&(ctx)->c)
+#define platform_SHA1_Update(ctx, data, len) \
+	sha1dc_update(&(ctx)->c, (data), (len))
+#define platform_SHA1_Final(hash, ctx) \
+	sha1dc_final((hash), &(ctx)->c, die)
 #define SHA1_NEEDS_CLONE_HELPER
-#define platform_SHA1_Clone sha1dc_rs_clone
-#define platform_SHA1_Discard sha1dc_rs_discard
+#define platform_SHA1_Clone(dst, src) sha1dc_clone(&(dst)->c, &(src)->c)
+#define platform_SHA1_Discard(ctx) sha1dc_discard(&(ctx)->c)
 
 #endif
diff --git a/src/sha1dc_rs.rs b/src/sha1dc_rs.rs
index df075a5d83..b9c430d0dc 100644
--- a/src/sha1dc_rs.rs
+++ b/src/sha1dc_rs.rs
@@ -1,5 +1,5 @@
 use sha1dc::Hasher;
-use std::ffi::CString;
+use std::ffi::{c_void, CString};
 use std::os::raw::c_char;
 use std::{ptr, slice};
 
@@ -8,7 +8,8 @@ use std::{ptr, slice};
 /// # Safety
 /// `ctx` must point to an uninitialized SHA-1 context.
 #[no_mangle]
-pub unsafe extern "C" fn sha1dc_rs_init(ctx: *mut *mut Hasher) {
+pub unsafe extern "C" fn sha1dc_rs_init(ctx: *mut c_void) {
+    let ctx = ctx.cast::<*mut Hasher>();
     *ctx = Box::into_raw(Box::new(Hasher::new()));
 }
 
@@ -17,7 +18,9 @@ pub unsafe extern "C" fn sha1dc_rs_init(ctx: *mut *mut Hasher) {
 /// # Safety
 /// Both contexts must be initialized.
 #[no_mangle]
-pub unsafe extern "C" fn sha1dc_rs_clone(dst: *mut *mut Hasher, src: *const *mut Hasher) {
+pub unsafe extern "C" fn sha1dc_rs_clone(dst: *mut c_void, src: *const c_void) {
+    let dst = dst.cast::<*mut Hasher>();
+    let src = src.cast::<*mut Hasher>();
     let hasher = Box::new((**src).clone());
     drop(Box::from_raw(*dst));
     *dst = Box::into_raw(hasher);
@@ -29,7 +32,8 @@ pub unsafe extern "C" fn sha1dc_rs_clone(dst: *mut *mut Hasher, src: *const *mut
 /// `ctx` must be initialized and `data` must point to `len` bytes unless
 /// `len` is zero.
 #[no_mangle]
-pub unsafe extern "C" fn sha1dc_rs_update(ctx: *mut *mut Hasher, data: *const c_char, len: usize) {
+pub unsafe extern "C" fn sha1dc_rs_update(ctx: *mut c_void, data: *const c_void, len: usize) {
+    let ctx = ctx.cast::<*mut Hasher>();
     if len != 0 {
         (**ctx).update(slice::from_raw_parts(data.cast::<u8>(), len));
     }
@@ -43,9 +47,10 @@ pub unsafe extern "C" fn sha1dc_rs_update(ctx: *mut *mut Hasher, data: *const c_
 #[no_mangle]
 pub unsafe extern "C" fn sha1dc_rs_final(
     hash: *mut u8,
-    ctx: *mut *mut Hasher,
+    ctx: *mut c_void,
     die: unsafe extern "C" fn(*const c_char, ...) -> !,
 ) {
+    let ctx = ctx.cast::<*mut Hasher>();
     let hasher = *Box::from_raw(*ctx);
     *ctx = ptr::null_mut();
     match hasher.finalize() {
@@ -66,7 +71,8 @@ pub unsafe extern "C" fn sha1dc_rs_final(
 /// # Safety
 /// `ctx` must be initialized.
 #[no_mangle]
-pub unsafe extern "C" fn sha1dc_rs_discard(ctx: *mut *mut Hasher) {
+pub unsafe extern "C" fn sha1dc_rs_discard(ctx: *mut c_void) {
+    let ctx = ctx.cast::<*mut Hasher>();
     drop(Box::from_raw(*ctx));
     *ctx = ptr::null_mut();
 }
diff --git a/t/helper/test-sha1.c b/t/helper/test-sha1.c
index 349540c4df..827fd2d6d5 100644
--- a/t/helper/test-sha1.c
+++ b/t/helper/test-sha1.c
@@ -1,13 +1,30 @@
+#define USE_THE_REPOSITORY_VARIABLE
 #include "test-tool.h"
 #include "hash.h"
+#include "setup.h"
 
 int cmd__sha1(int ac, const char **av)
 {
 	return cmd_hash_impl(ac, av, GIT_HASH_SHA1, 0);
 }
 
-int cmd__sha1_is_sha1dc(int argc UNUSED, const char **argv UNUSED)
+int cmd__sha1_is_sha1dc(int argc, const char **argv)
 {
+#ifdef DC_SHA1_RS
+	if (argc == 2 && !strcmp(argv[1], "--backend")) {
+		git_SHA_CTX ctx;
+		int nongit;
+
+		setup_git_directory_gently(the_repository, &nongit);
+		git_SHA1_Init(&ctx);
+		puts(sha1dc_init == git_SHA1DCInit ? "c" : "rust");
+		git_SHA1_Discard(&ctx);
+		return 0;
+	}
+#else
+	if (argc == 2 && !strcmp(argv[1], "--backend"))
+		return 1;
+#endif
 #ifdef platform_SHA_IS_SHA1DC
 	return 0;
 #endif
diff --git a/t/t0013-sha1dc.sh b/t/t0013-sha1dc.sh
index 3ea3169d92..9f6b72f8ef 100755
--- a/t/t0013-sha1dc.sh
+++ b/t/t0013-sha1dc.sh
@@ -13,10 +13,29 @@ then
 	test_done
 fi
 
+test_lazy_prereq SHA1DC_RS '
+	test rust = "$(GIT_CONFIG_PARAMETERS="${SQ}core.sha1dcBackend=rust${SQ}" \
+		test-tool sha1-is-sha1dc --backend)"
+'
+
 test_expect_success 'test-sha1 detects shattered pdf' '
 	test_must_fail test-tool sha1 <"$TEST_DATA/shattered-1.pdf" 2>err &&
 	test_grep collision err &&
-	test_grep 38762cf7f55934b34d179ae6a4c80cadccbb7f0a err
+	test_grep 38762cf7f55934b34d179ae6a4c80cadccbb7f0a err &&
+	if test_have_prereq SHA1DC_RS
+	then
+		test_must_fail env \
+			GIT_CONFIG_PARAMETERS="${SQ}core.sha1dcBackend=c${SQ}" \
+			test-tool sha1 <"$TEST_DATA/shattered-1.pdf" 2>err &&
+		test_grep collision err &&
+		test_grep 38762cf7f55934b34d179ae6a4c80cadccbb7f0a err
+	fi
+'
+
+test_expect_success SHA1DC_RS 'select SHA1DC backend via config' '
+	test rust = "$(test-tool sha1-is-sha1dc --backend)" &&
+	test_config core.sha1dcBackend c &&
+	test c = "$(test-tool sha1-is-sha1dc --backend)"
 '
 
 test_done
-- 
gitgitgadget
Johannes Schindelin via GitGitGadgetSep 28, 2026, 15:51 UTC in reply to Johannes Schindelin via GitGitGadget on lore

[PATCH 3/4] pthread: provide `pthread_once()` shims for Windows and for NO_PTHREADS

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

I am about to introduce logic that needs to perform some initialization once, and once only, even if called concurrently.

This is a perfect job for `pthread_once()`, but Git's source code currently lacks a Win32 shim. So let's add one!

Also provide a trivial shim for `NO_PTHREAD` builds.
Assisted-by: GPT-6 Sol
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
---
 compat/win32/pthread.c | 16 ++++++++++++++++
 compat/win32/pthread.h |  5 +++++
 thread-utils.h         | 16 ++++++++++++++++
 3 files changed, 37 insertions(+)
Show changes to 3 files +37 −0

compat/win32/pthread.c, compat/win32/pthread.h, thread-utils.h

diff --git a/compat/win32/pthread.c b/compat/win32/pthread.c
index 398caa9602..5af95edd3b 100644
--- a/compat/win32/pthread.c
+++ b/compat/win32/pthread.c
@@ -60,6 +60,22 @@ pthread_t pthread_self(void)
 	return t;
 }
 
+static BOOL CALLBACK win32_pthread_once(PINIT_ONCE once UNUSED,
+					PVOID parameter,
+					PVOID *context UNUSED)
+{
+	(*(void (**)(void))parameter)();
+	return TRUE;
+}
+
+int pthread_once(pthread_once_t *once_control, void (*init_routine)(void))
+{
+	if (!InitOnceExecuteOnce(once_control, win32_pthread_once,
+				 &init_routine, NULL))
+		return err_win_to_posix(GetLastError());
+	return 0;
+}
+
 int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)
 {
 	if (SleepConditionVariableCS(cond, mutex, INFINITE) == 0)
diff --git a/compat/win32/pthread.h b/compat/win32/pthread.h
index d80df8d12a..79a6bd9680 100644
--- a/compat/win32/pthread.h
+++ b/compat/win32/pthread.h
@@ -26,6 +26,11 @@ static inline int return_0(int i UNUSED) {
 #define pthread_mutex_lock EnterCriticalSection
 #define pthread_mutex_unlock LeaveCriticalSection
 
+typedef INIT_ONCE pthread_once_t;
+#define PTHREAD_ONCE_INIT INIT_ONCE_STATIC_INIT
+
+int pthread_once(pthread_once_t *once_control, void (*init_routine)(void));
+
 typedef int pthread_mutexattr_t;
 #define pthread_mutexattr_init(a) (*(a) = 0)
 #define pthread_mutexattr_destroy(a) do {} while (0)
diff --git a/thread-utils.h b/thread-utils.h
index 4961487ed9..98b574eff4 100644
--- a/thread-utils.h
+++ b/thread-utils.h
@@ -19,6 +19,22 @@
 #define pthread_mutex_t int
 #define pthread_cond_t int
 #define pthread_key_t int
+#define pthread_once_t int
+#undef PTHREAD_ONCE_INIT
+#define PTHREAD_ONCE_INIT 0
+
+static inline int dummy_pthread_once(pthread_once_t *once_control,
+				      void (*init_routine)(void))
+{
+	if (!*once_control) {
+		init_routine();
+		*once_control = 1;
+	}
+	return 0;
+}
+
+#define pthread_once(once_control, init_routine) \
+	dummy_pthread_once((once_control), (init_routine))
 
 #define pthread_mutex_init(mutex, attr) dummy_pthread_init(mutex)
 #define pthread_mutex_lock(mutex)
-- 
gitgitgadget
Johannes Schindelin via GitGitGadgetSep 28, 2026, 15:51 UTC in reply to Johannes Schindelin via GitGitGadget on lore

[PATCH 4/4] sha1dc: make `sha1dc_init()` thread-safe

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

The `sha1dc_init` function pointer initially points to a function that determines which sha1dc backend to use. Naturally, this initialization should only run once.

To allow for that function to be called concurrently in multiple threads, we need to use a pthread primitive to ensure that the `sha1dc_*()` function pointers are initialized exactly once.

Unfortunately, this requires quite a bit of non-DRY code to prevent data races when different threads run `initial_init()` concurrently (see https://en.cppreference.com/c/language/memory_model#Threads_and_data_races).

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
---
 sha1dc_git.c | 63 ++++++++++++++++++++++++++++++++++++++++++++++------
 1 file changed, 56 insertions(+), 7 deletions(-)
Show changes to sha1dc_git.c +56 −7
diff --git a/sha1dc_git.c b/sha1dc_git.c
index dcc5c1ca8e..eba62b12ab 100644
--- a/sha1dc_git.c
+++ b/sha1dc_git.c
@@ -7,6 +7,7 @@
 #ifdef DC_SHA1_RS
 #include "config.h"
 #include "repository.h"
+#include "thread-utils.h"
 #endif
 
 #ifdef DC_SHA1_EXTERNAL
@@ -58,16 +59,21 @@ static void sha1dc_c_discard(SHA1_CTX *ctx UNUSED)
 }
 
 /* The first SHA-1 initialization must precede concurrent hashing. */
-static void sha1dc_choose(SHA1_CTX *ctx);
+static void initial_init(SHA1_CTX *);
+static void initial_clone(SHA1_CTX *, const SHA1_CTX *);
+static void initial_update(SHA1_CTX *, const void *, size_t);
+static void initial_final(unsigned char [20], SHA1_CTX *,
+			  void (*die_fn)(const char *, ...));
+static void initial_discard(SHA1_CTX *ctx);
 
-void (*sha1dc_init)(SHA1_CTX *) = sha1dc_choose;
-void (*sha1dc_clone)(SHA1_CTX *, const SHA1_CTX *);
-void (*sha1dc_update)(SHA1_CTX *, const void *, size_t);
+void (*sha1dc_init)(SHA1_CTX *) = initial_init;
+void (*sha1dc_clone)(SHA1_CTX *, const SHA1_CTX *) = initial_clone;
+void (*sha1dc_update)(SHA1_CTX *, const void *, size_t) = initial_update;
 void (*sha1dc_final)(unsigned char [20], SHA1_CTX *,
-		     void (*die_fn)(const char *, ...));
-void (*sha1dc_discard)(SHA1_CTX *);
+		     void (*die_fn)(const char *, ...)) = initial_final;
+void (*sha1dc_discard)(SHA1_CTX *) = initial_discard;
 
-static void sha1dc_choose(SHA1_CTX *ctx)
+static void sha1dc_choose(void)
 {
 	const char *backend;
 	int use_c = 0;
@@ -86,6 +92,49 @@ static void sha1dc_choose(SHA1_CTX *ctx)
 	sha1dc_final = use_c ? git_SHA1DCFinal : sha1dc_rs_final;
 	sha1dc_discard = use_c ? sha1dc_c_discard : sha1dc_rs_discard;
 	sha1dc_init = use_c ? git_SHA1DCInit : sha1dc_rs_init;
+}
+
+static pthread_once_t once = PTHREAD_ONCE_INIT;
+
+static void initial_init(SHA1_CTX *ctx)
+{
+	int ret = pthread_once(&once, sha1dc_choose);
+	if (ret)
+		die("cannot initialize SHA-1 backend: %s", strerror(ret));
 	sha1dc_init(ctx);
 }
+
+static void initial_clone(SHA1_CTX *dst, const SHA1_CTX *src)
+{
+	int ret = pthread_once(&once, sha1dc_choose);
+	if (ret)
+		die("cannot initialize SHA-1 backend: %s", strerror(ret));
+	sha1dc_clone(dst, src);
+}
+
+static void initial_update(SHA1_CTX *ctx, const void *buf, size_t len)
+{
+	int ret = pthread_once(&once, sha1dc_choose);
+	if (ret)
+		die("cannot initialize SHA-1 backend: %s", strerror(ret));
+	sha1dc_update(ctx, buf, len);
+}
+
+static void initial_final(unsigned char hash[20], SHA1_CTX *ctx,
+			  void (*die_fn)(const char *, ...))
+{
+	int ret = pthread_once(&once, sha1dc_choose);
+	if (ret)
+		die("cannot initialize SHA-1 backend: %s", strerror(ret));
+	sha1dc_final(hash, ctx, die_fn);
+}
+
+static void initial_discard(SHA1_CTX *ctx)
+{
+	int ret = pthread_once(&once, sha1dc_choose);
+	if (ret)
+		die("cannot initialize SHA-1 backend: %s", strerror(ret));
+	sha1dc_discard(ctx);
+}
+
 #endif
-- 
gitgitgadget
Johannes SchindelinSep 28, 2026, 16:20 UTC in reply to Johannes Schindelin via GitGitGadget on lore

Re: [PATCH 0/4] Add a compile-time option to use the new, very fast sha1dc Rust crate

Hi,
On Mon, 28 Sep 2026, Johannes Schindelin via GitGitGadget wrote:
> I stumbled across this new Rust crate last week. Its performance numbers are
> quite impressive. Naturally, I want to make use of this and get for Windows,
                                                          ^^^^^^^^^^^^^^^^^^^
							  in Git for Windows

My sincerest apologies; I am using Cohere Transcribe to compensate for my inadequate typing speed, which typically works very, very well for me, yet missed this typo. Likewise:

Show 8 quoted lines
> which is used on many monorepos where this makes a real difference: In a
> pretty fast and loose test, I verified that a git index-pack runs roughly
> three times faster solely due to using those SIMD-based optimizations!
> 
> As a safety precaution, because this sha1dc crate is quite new, I wanted to
> introduce an escape hatch: core.sha1dcBackend=c, but turn it on by default,
> which is the reason for the three additional patches. Should these patches
> be undesirable for the Git project? I would not be mad at all if they were
                                    ^
				    , and

Sorry about that, Johannes

Show 35 quoted lines
> simply dropped.
> 
> Johannes Schindelin (4):
>   libgitcore: add `sha1dc` as an optional feature
>   sha1dc: allow selecting the C backend without rebuilding
>   pthread: provide `pthread_once()` shims for Windows and for
>     NO_PTHREADS
>   sha1dc: make `sha1dc_init()` thread-safe
> 
>  Cargo.toml                     |   4 ++
>  Documentation/config/core.adoc |   5 ++
>  Makefile                       |  29 +++++++++
>  compat/win32/pthread.c         |  16 +++++
>  compat/win32/pthread.h         |   5 ++
>  hash.h                         |   5 ++
>  sha1dc_git.c                   | 109 +++++++++++++++++++++++++++++++--
>  sha1dc_git.h                   |   5 +-
>  sha1dc_rs.h                    |  37 +++++++++++
>  src/lib.rs                     |   2 +
>  src/sha1dc_rs.rs               |  78 +++++++++++++++++++++++
>  t/helper/test-sha1.c           |  19 +++++-
>  t/t0013-sha1dc.sh              |  21 ++++++-
>  thread-utils.h                 |  16 +++++
>  14 files changed, 343 insertions(+), 8 deletions(-)
>  create mode 100644 sha1dc_rs.h
>  create mode 100644 src/sha1dc_rs.rs
> 
> 
> base-commit: 3bc0341126508f78f5869cbfc0005e987efdf0c7
> Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-2240%2Fdscho%2Foptionally-use-sha1dc-rs-v1
> Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-2240/dscho/optionally-use-sha1dc-rs-v1
> Pull-Request: https://github.com/gitgitgadget/git/pull/2240
> -- 
> gitgitgadget
> 
Junio C HamanoSep 29, 2026, 07:30 UTC in reply to Johannes Schindelin via GitGitGadget on lore

Re: [PATCH 0/4] Add a compile-time option to use the new, very fast sha1dc Rust crate

"Johannes Schindelin via GitGitGadget" <gitgitgadget@gmail.com> writes:

Show 18 quoted lines
> I stumbled across this new Rust crate last week. Its performance numbers are
> quite impressive. Naturally, I want to make use of this and get for Windows,
> which is used on many monorepos where this makes a real difference: In a
> pretty fast and loose test, I verified that a git index-pack runs roughly
> three times faster solely due to using those SIMD-based optimizations!
>
> As a safety precaution, because this sha1dc crate is quite new, I wanted to
> introduce an escape hatch: core.sha1dcBackend=c, but turn it on by default,
> which is the reason for the three additional patches. Should these patches
> be undesirable for the Git project? I would not be mad at all if they were
> simply dropped.
>
> Johannes Schindelin (4):
>   libgitcore: add `sha1dc` as an optional feature
>   sha1dc: allow selecting the C backend without rebuilding
>   pthread: provide `pthread_once()` shims for Windows and for
>     NO_PTHREADS
>   sha1dc: make `sha1dc_init()` thread-safe

The feature sha1dc_choose() means that you can between Rust and C implementations of sha1dc pick at runtime and I was confused by the "compile-time" in the topic title, which is misleading. From the end-user's point of view, being able to choose between the two at runtime gives them a lot bigger value, even though from the point of view of the developer who added the feature to allow users to do so, that feature being a compile-time choice might matter more.

How close are these two implementations? Do they implement the same idea but the details may differ? Do they both faithfully implement what the same paper wrote and given the same fudged input they will always detect the attempted attack the same way?

Johannes SchindelinOct 3, 2026, 10:23 UTC in reply to Junio C Hamano on lore

Re: [PATCH 0/4] Add a compile-time option to use the new, very fast sha1dc Rust crate

Hi Junio,
On Tue, 29 Sep 2026, Junio C Hamano wrote:
Show 26 quoted lines
> "Johannes Schindelin via GitGitGadget" <gitgitgadget@gmail.com>
> writes:
> 
> > I stumbled across this new Rust crate last week. Its performance
> > numbers are quite impressive. Naturally, I want to make use of this
> > and get for Windows, which is used on many monorepos where this makes
> > a real difference: In a pretty fast and loose test, I verified that a
> > git index-pack runs roughly three times faster solely due to using
> > those SIMD-based optimizations!
> >
> > As a safety precaution, because this sha1dc crate is quite new, I
> > wanted to introduce an escape hatch: core.sha1dcBackend=c, but turn it
> > on by default, which is the reason for the three additional patches.
> > Should these patches be undesirable for the Git project? I would not
> > be mad at all if they were simply dropped.
> >
> > Johannes Schindelin (4):
> >   libgitcore: add `sha1dc` as an optional feature
> >   sha1dc: allow selecting the C backend without rebuilding
> >   pthread: provide `pthread_once()` shims for Windows and for
> >     NO_PTHREADS
> >   sha1dc: make `sha1dc_init()` thread-safe
> 
> The feature sha1dc_choose() means that you can between Rust and C
> implementations of sha1dc pick at runtime and I was confused by the
> "compile-time" in the topic title, which is misleading.

Right. I was almost certain that you'd reject the runtime flag, which is really only interesting for binary-first distribution vectors such as Git for Windows but not source-code-only releases such as core Git's.

Show 9 quoted lines
> From the end-user's point of view, being able to choose between the two
> at runtime gives them a lot bigger value, even though from the point of
> view of the developer who added the feature to allow users to do so,
> that feature being a compile-time choice might matter more.
> 
> How close are these two implementations?  Do they implement the same
> idea but the details may differ?  Do they both faithfully implement
> what the same paper wrote and given the same fudged input they will
> always detect the attempted attack the same way?

Those two implementations are quite different. As the author of the Rust crate detailed in https://sam.dev/blog/faster-sha1-collision-detection, they first tried to accelerate the quite faithful Rust port of the library that is used by Git, and while there were some gains to be made, a more fundamental approach proved to offer way bigger wins.

While I would have _loved_ to have the time to dig into this myself, armed with pencil and paper only, and doing maths again for once, I simply could not afford the time to assess the validity of the Rust `sha1dc` implementation without AI assistance. With that disclaimer out of the way (which should actually _increase_ your confidence, because I haven't been in the math business in a very, very long time, so the double-teaming with GPT-5.5 Sol and Opus 5.5 probably increased the soundness of my analysis), here are my findings:

- The Rust implementation chooses a different approach from the C
  implementation. The idea is the same, though: to dismiss as quickly as
  possible as many of the DV vectors (each check can cover several of
  those at once). It's just that with SIMD, the technique differs, and
  that informs about the order and the grouping of those checks.
  (In more technical terms: The UBC filter is different, not the overall
  collision-detection algorithm. Both Rust and C implementation cover the
  same per-DV affine solution space over GF(2), albeit with different
  equations).
- While the approach is different, exploiting SIMD-specific advantages to
  great speed-wise effects, the covered DV vectors are exactly the same,
  and the filtering and recompression checks are equivalent; therefore
  both C and Rust implementation detect the very same class of collisions
  under the paper's assumptions.
- The Rust implementation is robust and correct. I performed a light
  (well, for me, not so much for the AI models) static analysis, and then
  I ran some substantial tests. My plan was to exercise Git's entire test
  suite (with a patched-in mode that would exercise both C and Rust and
  validate that they compute the same SHA-1), but I haven't managed to
  kick off _those_ particular AI-assisted sessions yet (I would want to
  exercise both x86_64 and aarch64, of course).
- The reason why I posted this before I finished _all_ the tests? To allow
  other Git contributors an early look, and to inspire (see e.g. Scott's
  alternative), to invite collaboration on this patch series.

Ciao, Johannes

brian m. carlsonOct 3, 2026, 21:01 UTC in reply to Johannes Schindelin via GitGitGadget on lore

Re: [PATCH 1/4] libgitcore: add `sha1dc` as an optional feature

On 2026-09-28 at 15:51:28, Johannes Schindelin via GitGitGadget wrote:
> Note that the `sha1dc` crate still requires a significantly newer Rust
> version than Git's existing Rust support requires: 1.87 instead of
> 1.63 (https://crates.io/api/v1/crates/sha1dc/0.1.3).

I think this is going to be a problem. Yes, this is optional, but we declare compatibility with Rust 1.49.0 in Cargo.toml and we want everything to work there.

The goal was to have everything work with gccrs, but I think we're nearing Git 3.0 and gccrs has not made enough progress for it to be viable. This is not a surprise to me, but that was our goal.

The approach I've been advocating is that we support the version in Debian stable, plus the version in Debian oldstable for a year after the new stable comes out. That would get us to Rust 1.85.1, since Debian 13 (trixie) came out over a year ago, but not to Rust 1.87.

In any event, if we want to raise the version of Rust, we should probably discuss that in a separate series that adds or updates a policy document and bumps the version in Cargo.toml.

-- 
brian m. carlson (they/them)
Toronto, Ontario, CA

Back to recent threads