Git 2.54: New History Rewriting Tools and Config-Based Hooks
Git 2.54 introduces git history for surgical commit rewrites and config-based hooks that finally work across repositories. Plus geometric repacking becomes the default maintenance strategy.
TL;DR
- New
git historycommand simplifies targeted rewrites without touching your working tree - Hooks can now live in config files instead of per-repo scripts — finally shareable across projects
- Geometric repacking becomes the default maintenance strategy for better performance
- 137 contributors shipped improvements to interactive staging, HTTP retries, and line history tracing
The Big Picture
Git 2.54 landed with a focus on making common workflows less painful. The headline feature is git history, an experimental command that handles simple history rewrites without the ceremony of interactive rebase. If you've ever wanted to fix a typo three commits back or split one commit into two, you know the dance: set up a rebase todo list, mark commits for editing, resolve conflicts, continue. It works, but it's overkill for quick fixes.
The other major shift is config-based hooks. For years, sharing hooks across repositories meant symlinks, wrapper scripts, or third-party managers. Now hooks live in your ~/.gitconfig like any other setting. Define a linter once, run it everywhere. No more copying scripts into .git/hooks for every project.
These aren't flashy features. They're the kind of quality-of-life improvements that compound over time — the difference between "I can do this" and "I actually want to do this."
How It Works
git history: Surgical rewrites
The git history command currently supports two operations: reword and split. Both operate without touching your working tree or index, which means they work in bare repositories and won't leave you in a conflicted state.
git history reword <commit> opens your editor with the specified commit message and rewrites it in place. Any branches descending from that commit get updated automatically. No rebase todo list, no interactive session — just edit and done.
git history split <commit> lets you carve hunks out of a commit into a new parent. The interface mirrors git add -p: you select which changes belong in the new commit, and Git creates a two-commit sequence with the same net diff. The original commit keeps whatever you didn't select.
The command refuses to operate on histories with merge commits and won't perform operations that would cause conflicts. This is intentional. git history is designed for targeted, non-interactive rewrites — the 80% case where you know exactly what you want to change and don't need the full power of git rebase -i.
Under the hood, it's built on git replay's machinery, which was extracted into a library as part of this work. That foundation is why git history can operate without a working tree — it's replaying commits at the object level, not checking them out.
Config-based hooks: Finally shareable
Hooks have always been scripts in .git/hooks or wherever core.hooksPath points. That made them per-repository by default and awkward to share. The new config-based approach changes the model entirely.
Instead of placing a script at .git/hooks/pre-commit, you write configuration:
[hook "linter"]
event = pre-commit
command = ~/bin/linter --cpp20The hook.<name>.command key specifies what to run, and hook.<name>.event specifies when. Since this is just config, it can live in ~/.gitconfig, /etc/gitconfig, or a repository's local config. Define hooks once in your user config and they apply everywhere.
Multiple hooks for the same event now work naturally. Want both a linter and a secrets scanner on pre-commit? Configure them independently and Git runs them in order. The traditional script-based hooks still work and run last, so existing setups are unaffected.
You can see which hooks are active with git hook list, and disable individual hooks with hook.<name>.enabled = false without removing their configuration. This is particularly useful when a system-level hook doesn't apply to a specific repository.
Many built-in hooks that were previously invoked through ad-hoc code paths — pre-push, post-rewrite, and the various receive-pack hooks — have been migrated to use the new hook API. They all benefit from the config-based machinery now.
Geometric repacking by default
Git 2.52 introduced the geometric maintenance strategy as an opt-in alternative to traditional garbage collection. In 2.54, it becomes the default for manual maintenance. When you run git maintenance run without specifying a strategy, Git now uses geometric repacking instead of gc.
The geometric approach inspects your packfiles and combines them when they can form a geometric progression by object count. This avoids the expensive all-into-one repacks that gc performs, instead combining packs incrementally and falling back to a full gc only when it would consolidate everything into a single pack. Along the way, it keeps your commit-graph, reflogs, and other auxiliary structures up to date.
If you were already using maintenance.strategy = geometric, nothing changes. If you hadn't set a strategy, you'll start seeing the benefits automatically. The gc strategy is still available via maintenance.strategy = gc if you prefer it.
What This Changes For Developers
The practical impact depends on your workflow. If you frequently rewrite history — fixing commit messages, splitting commits, cleaning up branches before pushing — git history removes friction. No more setting up rebase sessions for one-line changes. No more worrying about conflicts when all you wanted was to reword a message.
Config-based hooks solve a different problem: consistency across repositories. If you run the same linter, formatter, or security scanner on every project, you can now define it once in ~/.gitconfig instead of copying scripts around. Teams can distribute hooks via system-level config without requiring per-repository setup. The ability to run multiple hooks for the same event means you can compose tools instead of writing wrapper scripts.
The geometric repacking change is mostly invisible but improves performance over time. Repositories that accumulate many small packfiles will see faster maintenance runs and better object access patterns. If you're using GitHub CLI or other tools that interact with Git repositories programmatically, they'll benefit from the improved maintenance strategy automatically.
Try It Yourself
Reword a commit message without interactive rebase:
git history reword HEAD~3Split a commit into two by selecting hunks interactively:
git history split HEADDefine a pre-commit hook in your global config:
git config --global hook.linter.event pre-commit
git config --global hook.linter.command "~/bin/linter --strict"List all configured hooks for an event:
git hook list pre-commitTrace line history with pickaxe filtering (new in 2.54):
git log -L :function_name:file.c -S variable_name --onelineThe Bottom Line
Use git history if you do targeted history rewrites and want to skip the rebase ceremony. Skip it if you need to handle merge commits or complex conflict resolution — that's still git rebase -i territory. The real opportunity here is config-based hooks: if you've been manually syncing hook scripts across repositories or avoiding hooks because they're too annoying to set up, this changes the equation. The risk is minimal — both features are additive and don't break existing workflows.
Geometric repacking as the default is a pure win unless you have specific reasons to prefer traditional gc. Most developers won't notice the change except that maintenance runs faster and repositories stay better organized over time.
The command is marked experimental, so expect the interface to evolve. But the foundation is solid — it's built on git replay, which has been maturing for several releases. If you're tired of interactive rebase for simple fixes or you've been wanting shareable hooks, Git 2.54 delivers.
Source: GitHub Blog