60 comments

[ 4.0 ms ] story [ 45.9 ms ] thread
In short, newer versions of git implemented the three most important use cases of `git rebase --interactive` as separate lower-friction commands. Apparently they only work when there are no conflicts.
Wonder if the history command is all that useful.

I prefer the interactive rebase and use it frequently.

Would much rather “visually” move commits around than accidentally aim “git history” at an orphaned commit hash no longer in my local branch.

> Working with lots of changes in parallel on git can be painful. You end up juggling branches and commits, and running scary rebase -i commands that can leave your tree in a half-broken state if you so much as sneeze.

I think that only happens when you work on code as text files (i.e character streams) instead of code (i.e structured content with meaning). Like you have commit A and commit B that is in conflict, you should be glad because that's a rough signal that the intent of A and B differs. Your goal should be to think about how to compose A and B so that both intent survives (unless one supersedes the other). Which means you should be at least familiar with A and B.

The issue I found with people that fears conflict is that they often don't understand either A or B (or both). So they are a bad candidate to actually do the operation. It's not a matter of git's cli interface, it's a matter of codebase comprehension and how well you're familiar with the changes in question.

Git is the one treating code like a text file instead of code.
I pull out difftastic when it’s all too hard for this. Diffs based off tree sitter
Git store whole files. The diff and the merge algorithm works by line by default, but that's because line is a rough unit of code (statement, expression, and definition happens mostly within one line).
I can fully understand a conflict, know it's coming, and fear it anyway because I'll have to deal with Git being Git to fix it.
> scary rebase -i commands that can leave your tree in a half-broken state if you so much as sneeze

`git rebase --abort` exists. One can also drop a tag or something before doing the rebase, do whatever, then `git reset --hard $dropped_tag`. Nothing to be scared of.

I have so many branches named `temp` or `before-rebase` for exactly that reason; I'm using them effectively as tags, but branches can be moved around with less ceremony than tags (since tags are designed to be for things like v1.2.3, placed once and then almost never moved again), so I usually just do `git branch before-rebase/some-feature` before running a big `rebase -i`.

I've almost never needed to run `get reset before-rebase`. But I have often done `git log -p before-rebase` and compared that to the post-rebase state of the branch, to ensure that the merge-conflict resolution(s) that came up during the rebase haven't accidentally introduced an unintended change.

> but branches can be moved around with less ceremony than tags

`git tag -f` to move a tag.

Personally, I just do `git show` when I'm feeling cautious, but I can generally just scroll up to find the last `git commit` I did with the hash in the output. `git reflog` should also have record of it, so everything else is kind of extra.

Good point, that's no more ceremony than moving a branch. I guess I've just gotten "branches are movable tags" so deep into my hindbrain that I absorbed "tags are hard to move". But that's not actually true, and for what I'm doing (save this point in history for a while) a tag makes slightly more sense, semantically, than a branch.
(comment deleted)
Diffing diffs is so valuable for that reason, seriously underrated. Once I even found a use case for diffing diff diffs.

I also often wish to edit commits or resolve rebase conflicts or whatever by editing the patch rather than the files.

`git history split` is really going to help me help juniors break up their large PRs into smaller more concise changes. If only it had the option to split an entire branch in two easily.
Maybe the issue is they think of a PR as an expensive thing. Would be best if they could just do the small thing and make a PR of that from the get-go. If they want to base future changes on the ones they just did, they can just create a new feature branch from right there, and just not create the PR of the second feature until the first is merged, or create it and add a note to the reviewer that it includes the changes of the other PR so they should review that one first. Should they want to add changes to the first feature, they just need to checkout that branch, do the changes, and merge to the second feature branch so they're available there.
I like to be like an accountant. No editing history. Create a new "journal entry" (i.e. commit) to fix.
You probably shouldn’t be committing things that are broken…
With tools like GitHub Actions and some added constraints, it's not always possible. You literally need a commit to trigger the CI workflow and it starts to trash your branch. Besides, aren't we all familiar with git commit -m "typo"?
Commit yes you absolutely can. Merge to trunk? Probably not, depends on your strategy.
This is likely saying you shouldn't save a word document that contains spelling mistakes.
Commit graph is just a data structure. Sometimes it represents a "history", sometimes other things.

Personally, I like it when project's repository represents the history of the project rather than the history of random things developers do on their machines, but you do you.

(comment deleted)
I am thinking of remote. Edits before pushing OK with me (they ate equivalent of recloning and redoing anyway)
Granted, I have a perspective of a game dev, but this kind of repo defiling just gives me the willies. IMO instead of finding a common ancestor and altering it, just make the desired change upstream and merge it to where it’s needed. If you can’t do that then you have already made a deal with the devil and might reconsider your approach. KISS
> That last part goes further than git rebase --update-refs, which only moves refs sitting inside the range you’re actively rebasing. git history instead finds and rewrites every local branch descended from the commit (while also having an option to limit it to only the current branch).

I'm reading that to mean that when I use `git rebase --update-refs` in this situation, where I've currently checked out `D` and update `B` to `B'`:

  A ──► B ──► C ──► D  
        │              
        └───► E        
I'll end up with this state, where `E` remains untouched?

  A ──► B' ─► C' ─► D'  
  │                   
  └───► B ──► E        
(EDIT: Originally I had `E` point to `B'`, which doesn't make sense)

If I use `git history fixup`, it would also update `E` and end up with this?

  A ──► B' ─► C' ─► D'  
        │              
        └───► E'        
If that's the case, is there a way to get `git rebase` to have the same behavior? I've got decades of `git rebase` burned into my fingers at this point.
I don’t consider myself a coder or programmer, but learning git was like an organizational superpower for my particular brain wiring. I use it for websites, design projects, electronics engineering, music composition, personal knowledge bases, remote administration scripts, config management, snippets, so many applications and so many features for one system. It’s not always perfect, but I tell everyone I work with they should learn it.
How do you use it for music composition?
I like `git history reword`, would've found that useful in the past (e.g. fixing a bad typo in an older commit).

It sounds like there was probably an equivalent using an existing command but this is easier for me to understand. Thanks for sharing!

I don't get all the effort people spend in perfectly curating git history. No one is ever going back and reading individual commits. Just squash everything before merging and call it a day.
> No one is ever going back and reading individual commits.

Straight from the git-log, maybe not, but sometimes you see code that makes you wonder how it came to be and it can help a lot to see it in context of the commit that introduced it. That'd be less helpful if that commit were some huge thing making lots of different changes at once.

Of course we do and it helps tremendously when the graph is reasonable.
One good reason is to keep your tests separate from the fixes that make your tests pass. That way you can check your test fails before the next commit makes it pass, eliminating the risk of a false negative (test passes that would have anyway).
Having a well curated one-logical-change-per-commit history is incredibly valuable when bisecting a regression.
Didn't you mean "dissecting"?

Edit: thanks guys, that's very useful knowledge! Could have saved me many times in the past

> No one is ever going back and reading individual commits

I do, regularly! In a repository where care has been taken, it can be super valuable when tracking down a bug or regression, and understanding the intent of the author

> No one is ever going back and reading individual commits.

I violently disagree with this.

At a minimum, when I review PRs I look at the commit history to understand what's up. If the path that was taken to commit this is full of "oops" and "fix" messages, it's an immediate reject for me. The commits tell the story and it's a kindness to your human reviewers to not make them work harder to understand the point you're trying to get across.

Mine aren’t full of “oops” and “fix” messages, because I squashed them.
What's up with the fix commits? Maybe I misunderstood you, but there ain't nothing wrong in fixing stuff you offer in your PR. And there can also be multiple commits even before the PR while you're developing your PR.
> What's up with the fix commits?

They shouldn't show up in the commit history. In a PR, you merge them in the commit that they actually fix. Otherwise when you use git blame to get the context of why a line of code was changed, all you see is a useless "fixup" message that is worse than having nothing.

Anyone can do better than a fixup commit. And doing metter means merging them into the actual commits that are fixed.

NGL AI usage is driven by friction in presentation and communication over petty details.
Depends what the git history is supposed to show. Personally, I prefer people to leave their mistakes and reversions - though I'd require more description messages than "oops" or "fix", something that explained why it was being reverted or swapped out would be the minimum.

Sometimes you try things one way and they don't work out, so you go in a different direction. Capturing why this happened and when can go a long way towards explaining downstream decisions that might seem confusing to someone with a fresh perspective.

Oh that is such a bad heuristic ! The commits and history of how a PR was put together is no indicator of the quality of the PR or the thought process that led to it. Thats the equivalent of rejecting a (handwritten) essay for having too many corrections. ridiculous.

The code is all that should matter. Maybe comments for being nice to others and my future self. thats it.

> The commits and history of how a PR was put together is no indicator of the quality of the PR or the thought process that led to it.

It is, because it means the person posting the PR didn't even bothered to review the changes they are forcing others to review.

Just clean after yourself before asking others to read your stuff.

yea I look at commits several times a week at least, especially when commits are tied to a ticketing system/project it helps a lot going back months later on a large codebase going “how/why did this change happen”

I do tend to squash or make my entire change in one commit though so maybe I misunderstood your comment. If I have a fix commit often I’ll just tag a separate PR/ticket to keep the change history/change control clean

Sorry, no. Just because you've never done it doesn't mean that no one ever will.

Beyond the archival benefits, I've found plenty of bugs by going back through my "wip" commits and creating a sane history from them.

I almost never went back to read the history, but now I often have Claude go through the history when I wonder how we got to a certain point. It can point me to the relevant issues as well. Squashing is fine, up to a point.
I do not agree at all. When you have multiple repos accross different services, commits are the best way to follow up with differents changes. We adopted conventional commits guidelines a few months ago and everyone is happy. Even ClaudeCode is able to keep up and auto fix stuffs with the proper commit messages. The changelog is dynamically updated. Everything so smooth when those commits messages are perfectly synchronized.
That is like saying once the program is done, throw away the source.

Lemme guess all your for commit say "wip"

One use-case for curating commits other than git history is carefully structuring code reviews to be easy to review. Eg "commit 1 just rearranges existing code, no business logic changes"..."change 2 modifies business logic, but in one localized place as the refactoring has already been done"..."change 3 only modifies comments"
> No one is ever going back and reading individual commits.

Your assumption doesn't match the real world practices I've experienced for years across multiple jobs. Even at the PR stage a clean commit history is of critical importance. Nowadays, with ai coding assistants assuming a central role in developing software, commit history is even used as input with context signal, allowing for flows such as "evaluate the changes in commit X and Y and apply the same pattern to project Z".

Just because you don't use a tool properly that doesn't mean everyone around you makes the same mistake.

I do multiple times a week, with repos that have barely been touched in a decade and all the original devs are gone. Squashing would make figuring out why something is the way it is a lot more painful, so I'm glad these repos are svn where squashing wasn't an option. Several times I've discovered bugs that were introduced in linting commits that would have been squashed, so the fix ends up trivial since the intention is already there in the previous commit.
Disagree. You need small, git bisectable commits. Use stgit to do it and it's nearly effortless.
I've been theoretically a big fan of these commands; I use the `jj` equivalents all the time. The roadblock I've run into is that I as far as I can tell (from the man pages and the git source code) there's no way to get `git history` commands to sign commits they modify:

    $ git log --oneline --show-signature  # look ma, I signed my commits!
    3a1dd8f gpg: Signature made Mon 13 Jul 2026 10:45:50 PM CDT
    gpg:                using RSA key FBF32CDBCC134B44FD29B66FA851D929D52FB93F
    gpg:                issuer "chandler@chandlerswift.com"
    gpg: Good signature from "Chandler Swift <chandler@chandlerswift.com>" [ultimate]
    Second commit
    03c3f6e gpg: Signature made Mon 13 Jul 2026 10:45:16 PM CDT
    gpg:                using RSA key FBF32CDBCC134B44FD29B66FA851D929D52FB93F
    gpg:                issuer "chandler@chandlerswift.com"
    gpg: Good signature from "Chandler Swift <chandler@chandlerswift.com>" [ultimate]
    Initial commit
    $ git history reword HEAD~
    $ git log --oneline --show-signature  # oops! where'd they go?
    5662b2c (HEAD -> main) Second commit
    6bf6830 Initial commit amended
This has pushed me back to the time-honored `git rebase -i` since I do want to keep my commits signed.
Great write-up. `git log` really is underrated—learning the flags like --oneline, --graph, -p, and -S turns it into a powerful debugging and code-archaeology tool.
> Working with lots of changes in parallel on git can be painful. You end up juggling branches and commits, and running scary rebase -i commands that can leave your tree in a half-broken state if you so much as sneeze.

I mean, just use stgit[1] to maintain a stack of patches instead of parallel branches, it's so much better. You can easily wrangle thousands of patches (aka commits) with stgit.

[1] https://stacked-git.github.io/

TIL, thx :-)

somewhat related Q:

how do you give two files the same ancestor? so git log will for each show the history to the beginnings of the originally unsplit file? useful for splitting large files.