A happy new year to everyone! And for `gitoxide`, it feels like it starts with a bang!

## A new dawn for error handling: `exn` via `gix-error`

It all started with [this blog post](https://fast.github.io/blog/stop-forwarding-errors-start-designing-them/) which inspired the massive changes that are in progress now.

The underlying problem is that `thiserror` is

* making any change to an error enum in any crate breaking unless they are set to `#[non_exhaustive]`.
* making it very likely to introduce breaking changes as it suggests to collect context into variants, changing any of these is breaking.
* making it extremely cumbersome to use when code with `thiserror` errors is called, as each call needs a variant in a custom error struct.

Getting away from that was a goal for a long time, but I was lacking the means.

`anyhow` is something that *could* work, but it's also quite large and a bit too much of the other end of the spectrum where errors are very unstructured and not meant for introspection, a feature that is needed here. Also, it doesn't implement `std::error::Error` which makes it completely unsuitable for library crates.

[`exn`](https://docs.rs/exn/latest/exn/) was a breath of fresh air as it's somewhere in the middle, while being very explicit about everything. And, as a huge bonus, it records the creation location of each error with `#[track_caller]`.

`gix-error` completely forks `exn` to add what's needed to work in `gitoxide`, but work is underway to eventually reintegrated the `exn` crate.
Here is the new system:

1. `gix-error` provides simple standard error types for common situations, like `Message` and `ParseError`. These implement `std::error::Error` and can be used like `Result<T, Message>`.
2. If calls are made to functions that produce `std::error::Error` or `Exn<Error>`, these routines will return something like `Result<T, Exn<Message>>` to capture the error chain (or error tree). `Exn` does not implement `std::error::Error` though, as a limitation of the Rust typesystem, and is similar to `anyhow` in that regard.
3. `gix_error::Error` is returned by `gix` and it's essentially erasing types from `Exn` or any other errors, while providing facilities to introspect the call chain. This makes it similar to `git2::Error` for instance, which has proven to be so convenient I wouldn't want it any other way anymore. It also serves as a way to turn error trees powered by `Exn` back into call chains that work well with `anyhow`.

The conversion from `thiserror` is time-consuming, and AI thus far couldn't do it, so I end up doing all of that by hand, while retaining the hope that one day AI can help with this arduous process.

But what it does is to allow designing errors properly, making them easy to do in the implementations, that can also consciously think about what they want to expose for introspection later. And it turns out that we don't need everything, like what `thiserror` makes possible. Also, in `gix` one can choose to just use `?` everywhere, without the need for hundreds of error types just to allow making calls.

Overall, I can't wait for fully removing `thiserror` and solve this huge issue once and for all.

## Community

### Incubating SHA256 support: testing

 [Christoph Rüßler](https://github.com/cruessler) keeps chipping away at this seemingly giant topic single-handedly and recently introduced a way to run the existing fixtures with optional SHA-256 support. This creates a whole new repository that is set to SHA-256 on which tests can run.

The concept here is similar to what Git does, even though for now we are not running the entire test-suite twice, but instead take it on a crate-by-crate basis. Let's see where that goes and how long it takes. It feels somewhat similar in effort like the error conversion.

### Bugfix in `gix-pack` that surfaced with `zlib-rs`

This is a great fix of code that feels like it's as old as `gitoxide` itself, and related to [this issue](https://github.com/GitoxideLabs/gitoxide/issues/2044): sometimes pack entries couldn't be decoded even though Git was fine with them, an issue that started to occur with `zlib-rs` becoming the default.

It turned out that `gix-pack` was relying on ZIP never writing more to the output than what it needed to consume the input stream, something that is an implementation defined behavior. 
`gitoxide` also never cared about how many output bytes were written, and due to its 'one buffer for everything' architecture `zlib-rs` was able to write over the undesignated output boundary into space where delta buffers were lined up for decoding… or something like it.

This is [the commit](https://github.com/GitoxideLabs/gitoxide/commit/d99352bd3b7c75791826b40df20afdd4b0d8b26e) that fixes it, limiting the buffer that `zlib-rs` can write to.

A small fix, with large consequences, and a clear mistake on my end.

### Gix in Cargo

There is nothing new here, but let's keep the horizon active:

> With GitButler slated to have its checkout driven by a tailor-made implementation of 'reset' in `gitoxide`, this coincidentally is exactly what Cargo would need to also greatly speed up its checkouts and make them more compatible, too. We are talking proper Git filter support, and speedups in the realms of ~7x (see the `GitButler` paragraph for details).

Cheers
Sebastian

PS: The latest timesheets can be found [here (2026)](https://github.com/Byron/byron/blob/main/timesheets/2026.csv).
