back
95 comments
Nice to see some seemingly jujutsu inspired features getting into Git core.

  git history reword ~= jj describe

  git history split ~= jj split
https://git-scm.com/docs/git-history

https://www.jj-vcs.dev/latest/cli-reference/#jj-describe

https://www.jj-vcs.dev/latest/cli-reference/#jj-split

It's not documented, but unfortunately neither of these re-signs the commits even if GPG signing is configured. They are based on `git replay`, and that doesn't sign either (at least yet).
jj is a "unofficial" Google project that one has to sign a Google CLA to contribute to. I don't think it would be a good idea to trust/transition to a system like that from git.
what has one to do with the other...?
Not familiar with jj and don't want to get into bike shedding, but how is describe supposed to be a good name for history rewrites?
jj describe gives a name to a commit. In jj, everything rewrites the history, so there's no real point in calling it out in the command name since it's just the default behavior.
describe is also the command you can use to edit the commit message of the change you're currently drafting. In jj there's no staging area, every modification to the working tree immediately gets integrated into the current commit. (This means if you have no diff in your working tree, you're actually on an empty commit.)
Not really familiar too, but jj has everything committed by default (no index, staging area, and uncommitted changes). You use ‘jj new’ to stop adding changes to the current commit.

‘jj describe’ lets you add a message to a commit as it’s not there by default.

in jj the history has mutable and immutable commits.
Yeah it’s a direct inspiration.
If this is meant to be a dig, you should keep in mind how much jj owes to git.
Doesn't have to be a dig. One of the great things about having alternatives is that they can learn from each other.
I don't know if it was meant to be a dig but I hope you don't think the fact that Git was a big advancement on SVN means it doesn't deserve any criticism.

It's very widely remarked that the Git CLI is pretty miserable, and as soon as a better (so I hear) alternative comes along they suddenly realise and start improving it... This happens all the time in software.

Some software has crap UX or missing obvious features (e.g. comments in package.json). People try and improve it but are rebuffed because the maintainers claim it's fine as it is. Effort goes elsewhere to a competitor project which starts to attract users. Maintains of the original project have a sudden and coincidental change of heart!

and now git owes jj a couple ergonomic improvements. this is a net win for both.
I have always had this problem with hooks and new contributors: since hooks don't run by default if you just clone the repository, my open source projects get many PRs from new contributors that did not run the linting and commit hooks. I understand there's a security reason for this but what workflows have worked best for you to get everyone to run the hooks? And do you think the new config-based hooks can help new contributors?
> what workflows have worked best for you to get everyone to run the hooks

By running the linters and any other checks on CI instead.

Why waste a round trip, build time, loss of flow and CI machine queue wait time when you can catch things early?

CI should also run all the checks but CI checks are not a replacement for local hooks. LFS and things like it can't be implemented as remote CI checks.

Why are we acting like a James Bond villain, slowly lowering the changes into the vat of sharks after we've left the room? I want the hooks. Can we talk about making that easy, assuming some people want them?

autoformatter and autofix linter results can be committed and pushed by CI into the PR branch itself. this is a pain sometimes, but as a repo owner it should protect your sanity.
We do run the linter on CI as well, but I think our comitters would get faster feedback if they ran those checks locally.
As well, not instead. Just add `pre-commit run -a` to your CI. Job done.

It's still annoying for new contributors though because they might not know how to set up pre-commit (which was quite a pain until recently because it's written in Python).

Many projects have used "hook managers" like Husky for this to install hooks to run based on repository-stored metadata.

These new config-based hooks are definitely a step down the road towards obsoleting the third-party hook managers. One of the things they are for is for managing scripts to support multiple hooks for the same event. The new config format supports multiple hooks for the same event natively. (Which also helps in stacking personal ones versus repository ones.)

The only thing missing is that the repository's .git/config isn't itself source controlled in that repository, so for now there would still be an "install step", but it's now a lot simpler of an "install step" with the install being "append .example-gitconfig into your .git/config" rather than "set X files to X different contents in .git/hooks/*" where X is the number of event hooks to be concerned about.

It does open the door further to if there should be a ".gitconfig" in the Repository working tree that can also contribute repository-wide shared config, what config it can or cannot contribute, and how you secure that as a reviewable opt-in (especially change notifications). But a smartly built secure UX there would be a massive improvement over, say, shell scripts in npm postinstall operations touching .git/hooks "for you" (which is how many of the current hook managers auto-install, as side effects in dependency installs).

(ETA: Though most "install scripts" now just use the very scriptable `git config` command and so just be `git config set --local hook.$name.$field $value` sequences, which is also a simpler improvement over previous ways to install and/or merge hooks files by hooks managers.)

I wish that git would auto check for a `.githooks` directory in the repo root and prompt on first clone if the `core.hooksPath` should be changed for this repositry and when pulling any tracked file in hooksPath causes a warning (though this still leaves out the case that some hook just invokes a script in the repo outside the dir).
I don't want you to run arbitrary hooks on my machine. As with CI/CD... your hooks should simply point to a script instead
I add an autogen.sh script to all my repositories that does things like this as it's first action.
I always considered hooks a nice to have feature for devs to already validate that their PRs will probably satisfy certain CI checks. If they don't install or run them for whatever reason, it's on them to do another iteration and update the code to make it mergeable if CI complains. So I usually considered it fine that they are only opt-in, since the merge will be gated by a CI outside of the dev's control anyway.
In PHP, an established tool is adding GrumPHP [0] to your dependencies.

It will then handle git hooks on each commit via composer script by default (but can be omitted per commit).

[0] https://github.com/phpro/grumphp

Adding configuration to the config makes things feel far less exotic. I think these changes certainly improve things, but there's still plenty of room to go further.

I think there should probably be a way to specify canonical git configuration for things like hooks and LFS and all of that. It would be nice if when you clone, git prompts you to trust the remote config or to ask you to accept each new change as they come or fully reject them.

Having to scrape through the readme of every repo and then run arbitrary scripts doesn't seem like the most secure solution. When there's a canonical flow gitlab GitHub and all the tooling can support it and have proper permissions around it.

It's really disappointing how much lack of empathy there is when talking about new git features. Forget empathy. There's outright disdain for discussing alternative workflows.

The approach some JS projects have taken is to use Husky, which automatically sets up the git hooks when you install the project's dependencies during development.
> I understand there's a security reason for this

While I understand the security concerns, too, wouldn't this be solved by including a `trust` command like in direnv and mise? (I.e. have the user review the hooks before executing them for the first time.)

My project needs other things on setup as well, so I just have a setup script in my repo. `mv hooks/foo .git/hooks` is then just yet another step.
I agree with the other replies saying to just run the checks in CI and have the CI error message mention how to install the pre-commit hook.

I'm glad cloning a repo doesn't automatically install hooks since I strongly dislike them: I often use Git commands in the terminal but sometimes I use the VS Code UI to commit, and it's extremely frustrating when simply creating a commit runs for several seconds because of some pre-commit hook.

Use a dev container
Config based hooks seem to miss the mark though?

The per-repo config is in `.git/config`, so that can still not be checked into the repo itself, unless I'm missing something?

So not very useful at all...

I get the security implications, but there could be a checked in `$REPO/.githooks`, and a prompt asking to allow running those hooks , with an approval marker being stored inside `.git/`.

This seems to be a first step in that direction, though. Merging repo suggestions into one per-repo `.git/config` is still a big improvement over N `.git/hooks/$EVENT` files. Plus the new config format is directly mergeable, whereas the old hooks files often weren't without extra work (they had to point to one and only one shell command, which might be an arbitrary binary rather than a mergeable shell script; hence why "hooks managers" are as much shell script managers as hooks file maintainers and often were generally mutually exclusive/do not cooperate).

Obviously it still leaves the door open if there should be a `.githooks` or `.gitconfig` file in the committed worktree that can suggest changes to `.git/config` with fewer manual steps, but this first effort in minimizing the number of manual steps and simplifying those manual steps to a simpler merge is still quite useful.

Which is also before pointing out that the `git config` command is already quite scriptable as a simple merge tool. You could in theory replace a "hooks manager" install script with just a sequence of `git config set --local hook.$name.*` lines. You still have to figure out how to get the user to run that script just like installing any other hooks manager, but that's an easy script now to write.

Excited for the new release!

Though looks like `git history split` won't yet replace my typical workflow:

    $ git rebase -i // with e on commit in question
    $ git reset HEAD~
    $ git add -p
As I can't edit the hunk.

`git history reword` will be handy once I wire up a script to pipe the log of recent commits through fzf for interactive selection (not sure if it's just me, but I prefer interactive selection over any other form of reference to a git hash).

And I bet I'll get some use out of `git rebase --trailer`.

The new additions to `git add -p` seem pretty neat. Staging changes with `-p` is seriously underrated!
git history makes me think of git revise^1, which gives you a bunch of handy tools to changes your history, move commits around, remove some of them, cut them (which git history now does). I found it very handy in the past

1: https://git-revise.readthedocs.io/en/latest/man.html

Support for config based hooks is very nice.

Only a few days ago, I was just looking for some way to automatically check (and fail) if there are inactive hooks when I try to commit. I already use `advice.ignoredhook`, but it's easy to miss the warning if you commit through VSCode, and possibly through other IDEs.

With this, I can just write a simple script to perform that check, and add it to my global config

`git history reword` is great. Using `git rebase -i` just to fix a spelling error is overkill and doesn’t actually do what I want.
Why doesn't it do what you what?

I hate to be the worry wart, but I am worried folks are going to avoid `git rebase -i` even more now. It is such an excellent excellent UI in my opinion: it shows you the history of what is clearly, and let's you modify it as you please!

Is there any simple non-hacky way to "save" a snapshot of the working state, without clearing the working state like `stash` does?

I sometimes go in too far deep with a bunch of changes that should be committed separately but need to be worked on together, and I need to save it occasionally in case of data loss etc, and the only way I have/know is the good old "compress entire folder to a .zip"

Those new git history commands will save me an average of maybe a minute a day, but it's still definitely handy nonetheless! After 2 months, that's an hour back!

The git log -L change is nice to see as well. Anything that makes git more filterable gets my vote.

> ... rewrites any descendent branches to point at the updated history.

But what about local heads referred to only by a "soft" tag? Is their history rewritten, or is it left to refer to the old history?

git history is a genuinely welcome addition - rebase -i has always been a sledgehammer when sometimes you just need a scalpel, and it's nice to see git finally acknowledging that not every history edit needs to be a whole production
the new git history command seems to be useful for quick reword, altho since i use lazygit/magit i don't really see much of a problem to me
Wish reword took a commit range though
I do almost no direct git work myself these days. Using claude in Conductor. Working on a team. I'll tell claude what do do in git sometimes, but there doesn't seem to be much need to do it myself anymore, even with complicated rebases, reflogs, etc.
I'd advise to do what you're doing, but to check the commands it runs and figuring out why it does these things. The first step is usally "what does git [command] do?" followed by `man git-[command]` and see what it does.

That way you're still "blazingly-fast with your SOTA-LLM!!!" while also understanding why :)