Git development
 help / color / mirror / Atom feed
* Default object format for new repositories in Git v3
@ 2026-11-06 16:50 Adam Dinwoodie
  2026-11-06 20:46 ` brian m. carlson
  2026-11-08  1:16 ` [PATCH] setup: default hash algo to GIT_HASH_DEFAULT brian m. carlson
  0 siblings, 2 replies; 7+ messages in thread
From: Adam Dinwoodie @ 2026-11-06 16:50 UTC (permalink / raw)
  To: Git Mailing List

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

Hi folks,

As part of preparing for Git v3, in particular for Nixpkgs, I've been
running a bunch of tests using Git built with
`WITH_BREAKING_CHANGES=1`. Somewhat to my surprise, `git init` seems
to still be creating new repositories using sha1 rather than sha256.
Is this expected?

For reference, I've attached the test script I've produced as a repro
that can be run from the root of a Git checkout, although it does
require Nix. I've confirmed the behaviour at v2.56.0, next
(972d6e3ab651d838b30a8d5d70d8ef6ced05a986) and main
(0ffa3e35c7c82ab8d0de226149fb03b3f7674d05).

For a simpler repro that doesn't require Nix, build Git with
`WITH_BREAKING_CHANGES=1`, then run:

```
git init tmpdir
git -C tmpdir rev-parse --show-object-format
```

This will print "sha1", when I'd expect "sha256".

[-- Attachment #2: test.sh --]
[-- Type: text/x-sh, Size: 1832 bytes --]

#!/usr/bin/env nix
#!nix shell
#!nix --override-flake nixpkgs https://releases.nixos.org/nixos/unstable/nixos-26.11pre1105037.1c89083e451e/nixexprs.tar.zst
#!nix --ignore-env
#!nix nixpkgs#bash nixpkgs#coreutils nixpkgs#nix nixpkgs#findutils nixpkgs#gnused nixpkgs#autoconf nixpkgs#gnumake nixpkgs#gcc nixpkgs#cargo
#!nix --command bash

set -euo pipefail

tmpdir="$(mktemp --dir)"
trap 'rm -rf -- "$tmpdir"' EXIT

# Get the paths we need for zlib-ng
export NIX_PATH=nixpkgs=https://releases.nixos.org/nixos/unstable/nixos-26.11pre1105037.1c89083e451e/nixexprs.tar.zst
nix-build '<nixpkgs>' -A zlib-ng -A zlib-ng.dev -o "$tmpdir"/zlib-ng
zlib_lib="$(realpath "$tmpdir"/zlib-ng)"
zlib_dev="$(realpath "$tmpdir"/zlib-ng-dev)"

# Work out how much to parallelise the builds
jobs="$(nproc)"

# Make sure the build process can find zlib-ng
export CPPFLAGS="-isystem $zlib_dev/include" LDFLAGS="-L$zlib_lib/lib -Wl,-rpath,$zlib_lib"

# Set options: disable features we don't need for speed of building, use
# zlib-ng, enable breaking changes.
export ZLIB_NG=1 WITH_BREAKING_CHANGES=1 NO_TCLTK=1 NO_GETTEXT=1

# Build git
make -j"$jobs" configure
./configure
make -j"$jobs"

# Make sure Git config doesn't have anything unexpected
export GIT_CONFIG_GLOBAL=/dev/null
export GIT_CONFIG_SYSTEM=/dev/null
export GIT_CONFIG=/dev/null
export GIT_CONFIG_COUNT=2
export GIT_CONFIG_KEY_0=user.name GIT_CONFIG_VALUE_0=Adam
export GIT_CONFIG_KEY_1=user.email GIT_CONFIG_VALUE_1=adam@localhost
export GIT_PAGER=cat

# Make sure calling `git` gets the right executable
export PATH="$PWD":"$PATH"

# Set up a simple repository with a single commit
git init "$tmpdir"/repo
cd "$tmpdir"/repo
touch test-file
git add test-file
git commit -m 'demo commit'

# Show everything's sha1 rather than sha256
git log --format=raw
git rev-parse --show-object-format

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

* Re: Default object format for new repositories in Git v3
  2026-11-06 16:50 Default object format for new repositories in Git v3 Adam Dinwoodie
@ 2026-11-06 20:46 ` brian m. carlson
  2026-11-08  1:16 ` [PATCH] setup: default hash algo to GIT_HASH_DEFAULT brian m. carlson
  1 sibling, 0 replies; 7+ messages in thread
From: brian m. carlson @ 2026-11-06 20:46 UTC (permalink / raw)
  To: Adam Dinwoodie; +Cc: Git Mailing List

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

On 2026-11-06 at 16:50:03, Adam Dinwoodie wrote:
> Hi folks,
> 
> As part of preparing for Git v3, in particular for Nixpkgs, I've been
> running a bunch of tests using Git built with
> `WITH_BREAKING_CHANGES=1`. Somewhat to my surprise, `git init` seems
> to still be creating new repositories using sha1 rather than sha256.
> Is this expected?
> 
> For reference, I've attached the test script I've produced as a repro
> that can be run from the root of a Git checkout, although it does
> require Nix. I've confirmed the behaviour at v2.56.0, next
> (972d6e3ab651d838b30a8d5d70d8ef6ced05a986) and main
> (0ffa3e35c7c82ab8d0de226149fb03b3f7674d05).
> 
> For a simpler repro that doesn't require Nix, build Git with
> `WITH_BREAKING_CHANGES=1`, then run:
> 
> ```
> git init tmpdir
> git -C tmpdir rev-parse --show-object-format
> ```
> 
> This will print "sha1", when I'd expect "sha256".

This does appear to be broken.  I have a patch that should fix it that's
running through CI right now.  I'll send it once it's green, either
later tonight or tomorrow.
-- 
brian m. carlson (they/them)
Toronto, Ontario, CA
Speaking for myself only

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

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

* [PATCH] setup: default hash algo to GIT_HASH_DEFAULT
  2026-11-06 16:50 Default object format for new repositories in Git v3 Adam Dinwoodie
  2026-11-06 20:46 ` brian m. carlson
@ 2026-11-08  1:16 ` brian m. carlson
  2026-11-08  4:42   ` Junio C Hamano
  2026-11-08 18:22   ` Junio C Hamano
  1 sibling, 2 replies; 7+ messages in thread
From: brian m. carlson @ 2026-11-08  1:16 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Adam Dinwoodie

When we have no other hash algorithm to choose, we set no hash
algorithm, which ends up with us defaulting to SHA-1.  Instead, default
to GIT_HASH_DEFAULT.  This means that we will choose SHA-256 in breaking
changes mode and in Git 3.0, which is what we want.

Reported-by: Adam Dinwoodie <adam@dinwoodie.org>
Signed-off-by: brian m. carlson <sandals@crustytoothpaste.net>
---
 setup.c         |  2 ++
 t/t0001-init.sh | 16 ++++++++++++++++
 2 files changed, 18 insertions(+)

diff --git a/setup.c b/setup.c
index 61bcdafe82..4a0dfc8908 100644
--- a/setup.c
+++ b/setup.c
@@ -2830,6 +2830,8 @@ static void repository_format_configure(struct repository_format *repo_fmt,
 			repo_fmt->hash_algo = env_algo;
 	} else if (cfg.hash != GIT_HASH_UNKNOWN) {
 		repo_fmt->hash_algo = cfg.hash;
+	} else {
+		repo_fmt->hash_algo = GIT_HASH_DEFAULT;
 	}
 
 	/*
diff --git a/t/t0001-init.sh b/t/t0001-init.sh
index f1d227f271..98c7562f32 100755
--- a/t/t0001-init.sh
+++ b/t/t0001-init.sh
@@ -516,6 +516,22 @@ test_expect_success 're-init from a linked worktree' '
 	)
 '
 
+test_expect_success 'git init uses default object format' '
+	test_when_finished "rm -rf repo" &&
+	(
+		sane_unset GIT_DEFAULT_HASH &&
+		git init repo &&
+		if test_have_prereq WITH_BREAKING_CHANGES
+		then
+			echo sha256
+		else
+			echo sha1
+		fi >expect &&
+		git -C repo rev-parse --show-object-format >actual &&
+		test_cmp expect actual
+	)
+'
+
 test_expect_success 'init honors GIT_DEFAULT_HASH' '
 	test_when_finished "rm -rf sha1 sha256" &&
 	GIT_DEFAULT_HASH=sha1 git init sha1 &&

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

* Re: [PATCH] setup: default hash algo to GIT_HASH_DEFAULT
  2026-11-08  1:16 ` [PATCH] setup: default hash algo to GIT_HASH_DEFAULT brian m. carlson
@ 2026-11-08  4:42   ` Junio C Hamano
  2026-11-08 14:38     ` brian m. carlson
  2026-11-08 18:22   ` Junio C Hamano
  1 sibling, 1 reply; 7+ messages in thread
From: Junio C Hamano @ 2026-11-08  4:42 UTC (permalink / raw)
  To: brian m. carlson; +Cc: git, Adam Dinwoodie

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

> When we have no other hash algorithm to choose, we set no hash
> algorithm, which ends up with us defaulting to SHA-1.  Instead, default
> to GIT_HASH_DEFAULT.  This means that we will choose SHA-256 in breaking
> changes mode and in Git 3.0, which is what we want.
>
> Reported-by: Adam Dinwoodie <adam@dinwoodie.org>
> Signed-off-by: brian m. carlson <sandals@crustytoothpaste.net>
> ---
>  setup.c         |  2 ++
>  t/t0001-init.sh | 16 ++++++++++++++++
>  2 files changed, 18 insertions(+)

With Git 3.0, we promised that SHA-1/SHA-256 interop will not have
to be ready, and that SHA-256 would become the default.  And this
patch would be a good thing to have for the second promise.

But didn't we also discuss "empty/initial repository whose hash
algorithm is undecided can take a push of either SHA-1 or SHA-256
payload and that first push can decide and freeze the hash algorithm
used in the repository" or something along that line?

I am wondering if we want to reserve 'HASH_UNKNOWN' for such a "was
just created, not quite finished initializing yet" state.

The patch looks good for the goal stated in the log message.

Thanks.




> diff --git a/setup.c b/setup.c
> index 61bcdafe82..4a0dfc8908 100644
> --- a/setup.c
> +++ b/setup.c
> @@ -2830,6 +2830,8 @@ static void repository_format_configure(struct repository_format *repo_fmt,
>  			repo_fmt->hash_algo = env_algo;
>  	} else if (cfg.hash != GIT_HASH_UNKNOWN) {
>  		repo_fmt->hash_algo = cfg.hash;
> +	} else {
> +		repo_fmt->hash_algo = GIT_HASH_DEFAULT;
>  	}
>  
>  	/*
> diff --git a/t/t0001-init.sh b/t/t0001-init.sh
> index f1d227f271..98c7562f32 100755
> --- a/t/t0001-init.sh
> +++ b/t/t0001-init.sh
> @@ -516,6 +516,22 @@ test_expect_success 're-init from a linked worktree' '
>  	)
>  '
>  
> +test_expect_success 'git init uses default object format' '
> +	test_when_finished "rm -rf repo" &&
> +	(
> +		sane_unset GIT_DEFAULT_HASH &&
> +		git init repo &&
> +		if test_have_prereq WITH_BREAKING_CHANGES
> +		then
> +			echo sha256
> +		else
> +			echo sha1
> +		fi >expect &&
> +		git -C repo rev-parse --show-object-format >actual &&
> +		test_cmp expect actual
> +	)
> +'
> +
>  test_expect_success 'init honors GIT_DEFAULT_HASH' '
>  	test_when_finished "rm -rf sha1 sha256" &&
>  	GIT_DEFAULT_HASH=sha1 git init sha1 &&

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

* Re: [PATCH] setup: default hash algo to GIT_HASH_DEFAULT
  2026-11-08  4:42   ` Junio C Hamano
@ 2026-11-08 14:38     ` brian m. carlson
  0 siblings, 0 replies; 7+ messages in thread
From: brian m. carlson @ 2026-11-08 14:38 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git, Adam Dinwoodie, Jeff King

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

On 2026-11-08 at 04:42:56, Junio C Hamano wrote:
> "brian m. carlson" <sandals@crustytoothpaste.net> writes:
> 
> > When we have no other hash algorithm to choose, we set no hash
> > algorithm, which ends up with us defaulting to SHA-1.  Instead, default
> > to GIT_HASH_DEFAULT.  This means that we will choose SHA-256 in breaking
> > changes mode and in Git 3.0, which is what we want.
> >
> > Reported-by: Adam Dinwoodie <adam@dinwoodie.org>
> > Signed-off-by: brian m. carlson <sandals@crustytoothpaste.net>
> > ---
> >  setup.c         |  2 ++
> >  t/t0001-init.sh | 16 ++++++++++++++++
> >  2 files changed, 18 insertions(+)
> 
> With Git 3.0, we promised that SHA-1/SHA-256 interop will not have
> to be ready, and that SHA-256 would become the default.  And this
> patch would be a good thing to have for the second promise.
> 
> But didn't we also discuss "empty/initial repository whose hash
> algorithm is undecided can take a push of either SHA-1 or SHA-256
> payload and that first push can decide and freeze the hash algorithm
> used in the repository" or something along that line?

We discussed that as a possibility, but it requires protocol changes and
nobody has stepped up to implement that.  I think Peff was thinking
about maybe implementing that, but I don't think anyone came to a
decision.

> I am wondering if we want to reserve 'HASH_UNKNOWN' for such a "was
> just created, not quite finished initializing yet" state.

I think we concluded that we'd need to initialize a repository to _some_
hash algorithm even if we had the limbo state because otherwise people
could `git init` and then work locally and we need to have some existing
hash algorithm for that case.  That would be true even with bare
repositories.

So I think the limbo functionality, if we implemented it, would be a
separate option (maybe `extensions.potentialObjectFormat`?) and this
patch should go in either way.
-- 
brian m. carlson (they/them)
Toronto, Ontario, CA
Speaking for myself only

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

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

* Re: [PATCH] setup: default hash algo to GIT_HASH_DEFAULT
  2026-11-08  1:16 ` [PATCH] setup: default hash algo to GIT_HASH_DEFAULT brian m. carlson
  2026-11-08  4:42   ` Junio C Hamano
@ 2026-11-08 18:22   ` Junio C Hamano
  2026-11-09  2:21     ` Junio C Hamano
  1 sibling, 1 reply; 7+ messages in thread
From: Junio C Hamano @ 2026-11-08 18:22 UTC (permalink / raw)
  To: brian m. carlson; +Cc: git, Adam Dinwoodie, Patrick Steinhardt

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

> When we have no other hash algorithm to choose, we set no hash
> algorithm, which ends up with us defaulting to SHA-1.  Instead, default
> to GIT_HASH_DEFAULT.  This means that we will choose SHA-256 in breaking
> changes mode and in Git 3.0, which is what we want.
>
> Reported-by: Adam Dinwoodie <adam@dinwoodie.org>
> Signed-off-by: brian m. carlson <sandals@crustytoothpaste.net>
> ---
>  setup.c         |  2 ++
>  t/t0001-init.sh | 16 ++++++++++++++++
>  2 files changed, 18 insertions(+)
>
> diff --git a/setup.c b/setup.c
> index 61bcdafe82..4a0dfc8908 100644
> --- a/setup.c
> +++ b/setup.c
> @@ -2830,6 +2830,8 @@ static void repository_format_configure(struct repository_format *repo_fmt,
>  			repo_fmt->hash_algo = env_algo;
>  	} else if (cfg.hash != GIT_HASH_UNKNOWN) {
>  		repo_fmt->hash_algo = cfg.hash;
> +	} else {
> +		repo_fmt->hash_algo = GIT_HASH_DEFAULT;
>  	}

Hmph, maybe I am not reading the code flow correctly, but does this
not break running "git init" to reinitialize an already initialized
repository?  I mimicked the new test in your patch and wrote the
following just immediately after it, which

 * figures out our hash and the other hash
 * create a new repository with the "other" hash
 * run "git init" without specifying any hash

It is debatable what should happen when you run "git init" from Git
3.0 without any specialization in an existing SHA-1 repository.

Possibilities include:

 1. turn an existing SHA-1 repository into SHA-256 including format
    conversion.

 2. notice that the repository is already set up to use the SHA-1
    format, and that the user did not explicitly say what format to
    use, and behave as if --object-format=sha1 were given with a
    notice or warning.

 3. notice that the repository is already set up to use the SHA-1
    format, but our default format is SHA-256.  Because the user did
    not countermand via an explicit --object-format=sha1 on the
    command line, we should fail loudly, "cannot convert this SHA-1
    repository to SHA-256".

I think #1 is impractical, #2 is probably the best, and #3 would
also be reasonable.

But the behaviour I am seeing with

    $ make WITH_BREAKING_CHANGES=YesPlease
    $ cd t && WITH_BREAKING_CHANGES=YesPlease sh t0001-init.sh -i -v

with the additional patch is different from any of the three (well,
technically, because the repository is empty, we _could_ argue that
we did #1, but ...).

I wonder what the "git init" command does wrt the reference storage
format (Patrick CC'ed for this)?  I am sure we do not convert from
reffiles to reftable on the fly at "git init" time.


diff --git i/t/t0001-init.sh w/t/t0001-init.sh
index bc30cf853b..1d48a712ae 100755
--- i/t/t0001-init.sh
+++ w/t/t0001-init.sh
@@ -532,6 +532,31 @@ test_expect_success 'git init uses default object format' '
 	)
 '
 
+test_expect_success 'git (re)init does not clobber object format' '
+	test_when_finished "rm -rf repo" &&
+	(
+		sane_unset GIT_DEFAULT_HASH &&
+		if test_have_prereq WITH_BREAKING_CHANGES
+		then
+			ours=sha256 opposite=sha1
+		else
+			ours=sha1 opposite=sha256
+		fi &&
+
+		# initialize to non-default format
+		git init --object-format=$opposite repo &&
+		git -C repo rev-parse --show-object-format >actual &&
+		echo "$opposite" >expect &&
+		test_cmp expect actual &&
+
+		# reinitialize without configuration
+		git init repo &&
+		git -C repo rev-parse --show-object-format >actual &&
+		echo "$opposite" >expect &&
+		test_cmp expect actual
+	)
+'
+
 test_expect_success 'init honors GIT_DEFAULT_HASH' '
 	test_when_finished "rm -rf sha1 sha256" &&
 	GIT_DEFAULT_HASH=sha1 git init sha1 &&

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

* Re: [PATCH] setup: default hash algo to GIT_HASH_DEFAULT
  2026-11-08 18:22   ` Junio C Hamano
@ 2026-11-09  2:21     ` Junio C Hamano
  0 siblings, 0 replies; 7+ messages in thread
From: Junio C Hamano @ 2026-11-09  2:21 UTC (permalink / raw)
  To: brian m. carlson; +Cc: git, Adam Dinwoodie, Patrick Steinhardt

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

> I wonder what the "git init" command does wrt the reference storage
> format (Patrick CC'ed for this)?  I am sure we do not convert from
> reffiles to reftable on the fly at "git init" time.

I looked at setup.c:repository_format_configure() and found why
your patch regresses, as well as a latent bug in the existing code.

First, REPOSITORY_FORMAT_INIT sets .hash_algo = GIT_HASH_DEFAULT,
but read_repository_format() forces it to GIT_HASH_SHA1_LEGACY
when version == -1 (no config yet), which is why new repositories
kept getting SHA-1.

Second, unlike the ref-storage block right below it, which checks
"else if (repo_fmt->version >= 0)" once between the explicit
overrides and all default sources, the hash-algo block guards only
the GIT_DEFAULT_HASH branch (repo_fmt->version < 0), leaving "else
if (cfg.hash != GIT_HASH_UNKNOWN)" unguarded.

So init.defaultObjectFormat in ~/.gitconfig already corrupts an
existing repository on re-init, and an unguarded "else" for
GIT_HASH_DEFAULT falls into the same trap even without config.

Instead of starting the if/else if cascade like this:

	if (repo_fmt->version >= 0 &&
	    hash != GIT_HASH_UNKNOWN &&
	    hash != repo_fmt->hash_algo)
		die(_("attempt to reinitialize repository with different hash"));

we should have started it like so:

	if (repo_fmt->version >= 0) {
		if (hash != GIT_HASH_UNKNOWN && hash != repo_fmt->hash_algo)
			die(_("attempt to reinitialize repository with different hash"));
		/* otherwise happy */
	} else if ...

so that all the "set a new value from different sources like command
line, environment, ~/.gitconfig, or the built-in default" would
happen only when (repo_fmt->version < 0), probably?


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

end of thread, other threads:[~2026-11-09  2:21 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-11-06 16:50 Default object format for new repositories in Git v3 Adam Dinwoodie
2026-11-06 20:46 ` brian m. carlson
2026-11-08  1:16 ` [PATCH] setup: default hash algo to GIT_HASH_DEFAULT brian m. carlson
2026-11-08  4:42   ` Junio C Hamano
2026-11-08 14:38     ` brian m. carlson
2026-11-08 18:22   ` Junio C Hamano
2026-11-09  2:21     ` 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