惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

月光博客
月光博客
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
N
Netflix TechBlog - Medium
大猫的无限游戏
大猫的无限游戏
爱范儿
爱范儿
Martin Fowler
Martin Fowler
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
The Register - Security
The Register - Security
IT之家
IT之家
博客园_首页
Microsoft Security Blog
Microsoft Security Blog
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
博客园 - 三生石上(FineUI控件)
I
InfoQ
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
Jina AI
Jina AI
Apple Machine Learning Research
Apple Machine Learning Research
M
MIT News - Artificial intelligence
博客园 - Franky
C
Check Point Blog
T
The Blog of Author Tim Ferriss
V
Visual Studio Blog
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
T
Tailwind CSS Blog
Recent Announcements
Recent Announcements
云风的 BLOG
云风的 BLOG
美团技术团队
The Cloudflare Blog
Y
Y Combinator Blog
H
Hackread – Cybersecurity News, Data Breaches, AI and More
MyScale Blog
MyScale Blog
The GitHub Blog
The GitHub Blog
D
DataBreaches.Net
Google DeepMind News
Google DeepMind News
V
V2EX
aimingoo的专栏
aimingoo的专栏
GbyAI
GbyAI
G
Google Developers Blog
S
SegmentFault 最新的问题
Hugging Face - Blog
Hugging Face - Blog
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
U
Unit 42
罗磊的独立博客
量子位
MongoDB | Blog
MongoDB | Blog
Last Week in AI
Last Week in AI
Stack Overflow Blog
Stack Overflow Blog
小众软件
小众软件
D
Docker
人人都是产品经理
人人都是产品经理

Butler's Log

Agentic Version Control Benchmarks Grit: rewriting Git in Rust with agents Git Merge 2026 Agent-safe Git with GitButler We’ve raised $17M to build what comes after Git Announcing the GitButler CLI for Linux The Great CSS Expansion A couple of git nits Simplifying Git by Using GitButler Introducing the GitButler CLI GitButler 0.19 - "Commander Keen" But Head: Crafting a Custom Font MCP vs RAG: Two Very Different Ways to Gain Context Getting Started With GitButler Agents Using the GitButler MCP Server to Build Better AI-Driven Git Workflows Using GitButler With Multiple GitHub Accounts Advent of Code! Upcoming GitButler Events Use GitButler for your Gerrit workflow Integrating GitButler and GitHub Enterprise Butler Flow: shipping code faster (but less like Alfred, more like CI on steroids) - Part 3 Butler Flow: shipping code faster (but less like Alfred, more like CI on steroids) - Part 2 Butler Flow: shipping code faster (but less like Alfred, more like CI on steroids) - Part 1 Grid Happens: Because Flexbox Wasn’t Enough Using Cursor Hooks for automatic version control Deep Dive into the new Cursor Hooks A Responsive Item Counter with CSS only GitButler 0.16 - "Sweet Sixteen" GitButler's Claude Code tab GitButler's Annual Open Source Pledge Report Git Mini Summit 2025 Videos Automate Your AI Workflows with Claude Code Hooks Managing Multiple Claude Code Sessions Without Worktrees GitButler 0.15 - "Quirky Quinceañera" 20 years of Git. Still weird, still wonderful. GitButler's new patch based Code Review (Beta) Going down the rabbit hole of Git's new bundle-uri How to do patch-based review with git range-diff How Core Git Developers Configure Git Why is Git Autocorrect too fast for Formula One drivers? Stacked Branches with GitButler Git Merge 2024 Talks are Up GitButler 0.13 - "Lucky Baseball" Fearless Rebasing Git Merge 2024 Why GitHub Actually Won GitButler is joining the Open Source Pledge The New Era of Town Hall Chat The Future of Open Source GitButler is now Fair Source Git Merge 2024 GitButler 0.12 - "Stingy Baker" The Birth of THE MERGE GitButler for Windows The Git Zeitgeist Git Worktrees and GitButler DevWorld Git Slides Git Tips and Tricks Git Tips 1: Oldies but Goodies Git Tips 2: New Stuff in Git Git Tips 3: Really Large Repositories FOSDEM Git Talk Opening Up GitButler Debugging Tauri in VS Code Advent of GitButler Code Signing Commits in Git, Explained Virtual Branches Alpha Our We Are Developers Adventure Building Virtual Branches DevDays in Vilnius The Future of Software and Open Source Introducing GitButler
Fixing up Git with Autosquash
Scott Chacon · 2024-03-11 · via Butler's Log

This year I've been giving talks all over the place, pointing out some cool features of Git that I've found aren't super well known and writing up some of these features here. Previously I've written about worktrees, large repository features and a few other things.

Today I want to show you a really handy way to incorporate fixups into a series of commits using git commit --fixup and git rebase --autosquash.

But first, let's take a look at the problem that this is designed to solve.

As you may or may not know, Git actually doesn't store patches or changesets, it stores snapshots. However, lots of Git commands can convert a list of them into the differences between each one, which makes it seem like each commit is a patch.

What makes things a little more confusing is that the commit message generally describes this difference, rather than the actual data that it points to.

This is more or less what Git was originally written to do, keep a series of snapshots of the Linux kernel and easily convert them into a series of patches that can easily be e-mailed to a mailing list and re-applied locally.

So this is the generally intended original workflow of a Git user. You make a change and document it. You make another change and document it. Then you use git send-email to mail those changes to the mailing list for feedback.

Today this is often done as a branch with a Pull Request on GitHub, but the idea is the same: you have commits that describe your changes that people can inspect in isolation.

If you're using GitHub and PRs to collaborate on changes, it's possible that you think of the atomic unit of work as the branch rather than the commit for this reason. That the PR description is actually what's most important, not individual commit messages, largely because GitHub makes the PR reviewable and not individual commits. Whereas in mailing lists, the individual commits are quite important for review purposes.

In a mailing list, one normally sends a patch series, gets feedback on each patch and then you need to re-roll the series and re-send it. This article is about trying to do that in an easier way. Maintain a nice series of commits as "patches" and incorporate feedback into that series, rather than just pushing new commits on top of the branch.

Ok, so let's imagine a scenario. Let's say that you have two commits that implement some feature and document it.

❯ git log --oneline sc-feature-A ^origin/master
18e9c6b997a (sc-feature-A) add documentation for new feature
e57c40da089 add new feature

We can also see that each change has modified some files (with --stat):

❯ git log --oneline --stat sc-feature-A ^origin/master
18e9c6b997a (sc-feature-A) add documentation for new feature
 README.md | 1 +
 1 file changed, 1 insertion(+)
e57c40da089 add new feature
 gitbutler-app/src/virtual_branches/errors.rs              | 1 +
 gitbutler-app/src/virtual_branches/files.rs               | 1 +
 gitbutler-ui/src/lib/components/Differ/CodeHighlighter.ts | 2 ++
 3 files changed, 4 insertions(+)

You send your patch series to a mailing list (or push them to a Pull Request).

Now you have two commits that are viewed as patches and commented on. The feedback is valuable and you want to incorporate changes to address the feedback into your commits.

Here is where things become a little difficult. You want to still have two commits with the same good descriptive commit messages, but you need to incorporate changes from your feedback into different files in each commit.

One way to do this is to interactively rebase, stopping at each commit, incorporating your changes destined for that commit, and then continuing. This is a little difficult because you can't really make the changes to make sure they work and then do this very easily.

Or, you could make the changes, commit it into temporary commits, then rebase interactively, rearrange the commits and squash them together.

We make our temporary commits:

❯ # fix the documenation
❯ git commit -am 'feedback on documentation'

❯ # fix the feature
❯ git commit -am 'feedback on feature'

❯ git log --oneline sc-feature-A ^origin/master
d7c50392f51 (sc-feature-A) feedback on feature
c679989f4a7 feedback on documentation
18e9c6b997a add documentation for new feature
e57c40da089 add new feature

Then we interactively rebase and rearrange the pick lines:

❯ git rebase -i origin/master

# REBASE FILE:

---
pick e57c40da08 add new feature
pick 18e9c6b997 add documentation for new feature
pick c679989f4a feedback on documentation
pick d7c50392f5 feedback on feature
---

# BECOMES:

---
pick e57c40da08 add new feature
squash d7c50392f5 feedback on feature
pick 18e9c6b997 add documentation for new feature
squash c679989f4a feedback on documentation
---

This rebase should work without issues (here I also changed the commit message to add w / feedback, just to be clear that it's incorporated the changes)

❯ git rebase -i origin/master
[detached HEAD 3224c58f81] add new feature, w/feedback
Date: Mon Mar 11 11:17:21 2024 +0100
 3 files changed, 5 insertions(+)
[detached HEAD d0a90ff020] add documentation, w/ feedback
 Date: Mon Mar 11 11:17:30 2024 +0100
 1 file changed, 2 insertions(+)
Successfully rebased and updated refs/heads/sc-feature-A.

And viola, we have two commits again, this time with the feedback incorporated.

❯ git log --oneline sc-feature-A ^origin/master
eb4262e9e46 (sc-feature-A) add documentation, w/ feedback
3224c58f81c add new feature, w/feedback

Now we can re-send the series to the mailing list, or force-push to our branch.

However, this is common enough of an issue, that Git has a clever way to do this a little more automatically. Instead of committing something that lets us know that we fixed up a previous commit, we can let Git know that a commit is a fixup.

With, you may have guessed it, the --fixup flag. So let's say we get more feedback on our series and need to fixup both commits again for a third round of the series. This time, we'll use the --fixup flag:

❯ # do more work on documentation
❯ git commit -a --fixup=eb4262e9e46
[sc-feature-A f77a3f5909] fixup! add documentation, w/ feedback
 1 file changed, 1 insertion(+)

❯ # do more work on the feature

❯ git commit -a --fixup=3224c58f81c
[sc-feature-A 6149ef8966] fixup! add new feature, w/feedback
 1 file changed, 1 insertion(+)

❯ git log --oneline sc-feature-A ^origin/master
6149ef89666 (sc-feature-A) fixup! add new feature, w/feedback
f77a3f5909a fixup! add documentation, w/ feedback
eb4262e9e46 add documentation, w/ feedback
3224c58f81c add new feature, w/feedback

It's not super complicated, but in the case of committing with a --fixup=[sha] flag, Git will write a specially formatted commit message that it can recognize to mean "this commit fixes up this other commit".

Now, instead of manually interactively rebasing and rearranging the pick/squash commands, you can simply run git rebase --autosquash:

❯ git rebase --autosquash origin/master
Successfully rebased and updated refs/heads/sc-feature-A.

❯ git log --oneline sc-feature-A ^origin/master
0273a58c32e (sc-feature-A) add documentation, w/ feedback
7c102015437 add new feature, w/feedback

It basically does exactly what we just did, but automatically - no editor is opened, no instructions manually moved around.

💡

The --fixup option also takes amend and reword options, to handle rewording the commit message as well. For example, git commit --fixup:amend=[sha]. Check out the Git commit docs for more options.

If you're trying to maintain a clean patch series as a continuously updated set of commits, --fixup and --autosquash may make things a little simpler. Enjoy your squashing!