<?xml version="1.0" encoding="utf-8" standalone="yes"?><?xml-stylesheet href="/feed.css?v=208f42b55cff" type="text/css"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:site="https://lalitm.com/feed/ns#"><channel><title>Lalit Maganti (Tag: Git)</title><link>https://lalitm.com/tags/git/</link><description>Recent content tagged Git on Lalit Maganti</description><site:notice>This is a feed.
Feeds let you subscribe to updates from this site using a feed reader. Copy this page's URL from your address bar and paste it into your reader.
New to feeds? Read: https://aboutfeeds.com</site:notice><docs>https://aboutfeeds.com</docs><generator>Hugo -- gohugo.io</generator><language>en</language><lastBuildDate>Sat, 01 Aug 2026 16:00:00 +0100</lastBuildDate><atom:link href="https://lalitm.com/tags/git/index.xml" rel="self" type="application/rss+xml"/><item><title>GitHub has alternatives, but no replacement</title><link>https://lalitm.com/post/github-alternatives/</link><pubDate>Sat, 01 Aug 2026 16:00:00 +0100</pubDate><guid>https://lalitm.com/post/github-alternatives/</guid><description>Codeberg, a Git code hosting platform, recently took a decision to prohibit projects that mostly consist of generative-AI-written code which has prompted concern and extensive discussion elsewhere.
The decision does not surprise me, and I don’t mean that as a criticism. Codeberg has always presented itself as a mission-driven alternative to GitHub, not neutral infrastructure.1
What interests me is the disappointment in the response. Many people reacted as though one of the few plausible GitHub replacements had ruled them or their projects out. They wanted Codeberg to be a universal alternative, a better GitHub and the obvious place to go when leaving it.
To me, that exposes a big gap in the open-source space. There are plenty of places to host a Git repository, but remarkably few places to host an open-source community. GitHub gives projects a shared pool of identities, habits and paths to discovery. None of the alternatives has reproduced that at a similar scale.</description><content:encoded>&lt;p&gt;&lt;a href="https://codeberg.org/"&gt;Codeberg&lt;/a&gt;, a Git code hosting platform, recently took a
&lt;a href="https://blog.codeberg.org/protecting-our-floss-commons-from-llms.html"&gt;decision to prohibit projects that mostly consist of generative-AI-written code&lt;/a&gt;
which has prompted
&lt;a href="https://xn--gckvb8fzb.com/i-regret-migrating-to-codeberg/"&gt;concern&lt;/a&gt; and
extensive &lt;a href="https://news.ycombinator.com/item?id=49021856"&gt;discussion&lt;/a&gt;
&lt;a href="https://lobste.rs/s/ax914v/protecting_our_floss_commons_from_llms"&gt;elsewhere&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The decision does not surprise me, and I don&amp;rsquo;t mean that as a criticism.
Codeberg has always presented itself as a mission-driven alternative to GitHub,
not neutral infrastructure.&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;What interests me is the disappointment in the response. Many people reacted as
though one of the few plausible GitHub replacements had ruled them or their
projects out. They wanted Codeberg to be a universal alternative, a better
GitHub and the obvious place to go when leaving it.&lt;/p&gt;
&lt;p&gt;To me, that exposes a big gap in the open-source space. There are plenty of
places to host a Git repository, but remarkably few places to host an
open-source community. GitHub gives projects a shared pool of identities, habits
and paths to discovery. None of the alternatives has reproduced that at a
similar scale.&lt;/p&gt;
&lt;p&gt;I don&amp;rsquo;t think the answer has to be another centralized platform, or that every
project should live in one place. But decentralization is not enough on its own.
Whatever replaces GitHub still needs a shared social layer: identities
contributors already have, conventions they understand and ways to discover
projects across the network.&lt;/p&gt;
&lt;h2 id="why-not-self-hosting"&gt;Why not self-hosting?&lt;a class="heading-anchor" href="#why-not-self-hosting" aria-label="Permalink to Why not self-hosting?"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;Whenever dissatisfaction with GitHub comes up, someone inevitably says: &amp;ldquo;Git is
decentralized. Just self-host a forge.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve self-hosted Gitea for years, so this is an argument I&amp;rsquo;m very familiar with.
Self-hosting works well for personal projects, but I wouldn&amp;rsquo;t use it for
something I wanted strangers to contribute to. On GitHub, most people already
have an account and understand how issues and pull requests work. On my forge,
even reporting a small bug means creating another account, learning how my forge
works and what conventions I want you to follow. Unless someone really cared,
they probably wouldn&amp;rsquo;t bother. I know I wouldn&amp;rsquo;t.&lt;/p&gt;
&lt;p&gt;And contribution is only half of it. GitHub used to be genuinely good at
discovery. I regularly found projects because someone I followed starred them,
often in areas I would never have searched for myself. It felt like a social
network built around people making things.&lt;/p&gt;
&lt;p&gt;GitHub has since redesigned that feed, and I almost never visit it anymore.
Defaults are powerful: once discovery stopped being part of the experience
GitHub put in front of me, it largely disappeared from my workflow.&lt;/p&gt;
&lt;h2 id="why-not-github"&gt;Why not GitHub?&lt;a class="heading-anchor" href="#why-not-github" aria-label="Permalink to Why not GitHub?"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;The basic experience of GitHub has been getting steadily worse. It is slow,
things regularly fail to load and notifications are unreliable. GitHub itself
recently described two major incidents as
&lt;a href="https://github.blog/news-insights/company-news/an-update-on-github-availability/"&gt;&amp;ldquo;not acceptable&amp;rdquo;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Its pull request experience has been awful too. Large PRs are painfully slow to
navigate and review. Stacked PRs&lt;sup id="fnref:2"&gt;&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref"&gt;2&lt;/a&gt;&lt;/sup&gt; have been common inside large
software companies for well over a decade, but only &lt;em&gt;just&lt;/em&gt;
&lt;a href="https://github.blog/changelog/2026-07-30-stacked-pull-requests-are-now-in-public-preview/"&gt;became a thing&lt;/a&gt;
with GitHub and, even then, seems to be
&lt;a href="https://news.ycombinator.com/item?id=49113452"&gt;quite&lt;/a&gt;
&lt;a href="https://news.ycombinator.com/item?id=49122109"&gt;buggy&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;What frustrates me about GitHub&amp;rsquo;s push towards AI is that the core forge feels
neglected while Copilot appears everywhere. An agent writing more code doesn&amp;rsquo;t
help when the interface for reviewing it is already struggling.&lt;/p&gt;
&lt;p&gt;Ghostty exemplifies this frustration. In late April, Mitchell Hashimoto
announced that
&lt;a href="https://mitchellh.com/writing/ghostty-leaving-github"&gt;Ghostty is leaving GitHub&lt;/a&gt;
because frequent outages were preventing its maintainers from working reliably:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;On the day I am writing this post, I&amp;rsquo;ve been unable to do any PR review for ~2
hours because there is a GitHub Actions outage. This is no longer a place for
serious work if it just blocks you out for hours per day, every day.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Interestingly, he &lt;em&gt;also&lt;/em&gt; makes the point that GitHub is more than hosting:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;To the &amp;ldquo;Git is distributed!&amp;rdquo; crowd: the issue isn&amp;rsquo;t Git, it&amp;rsquo;s the
infrastructure we rely on around it: issues, PRs, Actions, etc.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Hashimoto said Ghostty was in discussions with multiple commercial and FOSS
providers and planned an incremental migration. The fact that such a prominent
project had to shop around, rather than move to an obvious default, is exactly
the gap I mean.&lt;/p&gt;
&lt;h2 id="why-not-the-alternatives"&gt;Why not the alternatives?&lt;a class="heading-anchor" href="#why-not-the-alternatives" aria-label="Permalink to Why not the alternatives?"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;It&amp;rsquo;s worth going through the alternatives and the problems I see with each:&lt;/p&gt;
&lt;p&gt;&lt;a href="https://about.gitlab.com/"&gt;GitLab&lt;/a&gt; is capable, but it feels incredibly
corporate, even more so than GitHub.&lt;sup id="fnref:3"&gt;&lt;a href="#fn:3" class="footnote-ref" role="doc-noteref"&gt;3&lt;/a&gt;&lt;/sup&gt; Nor have I found it as
good as GitHub at helping people stumble across projects and developers.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://sr.ht/"&gt;SourceHut&lt;/a&gt; is focused and transparent and, like Codeberg,
openly values-driven.&lt;sup id="fnref:4"&gt;&lt;a href="#fn:4" class="footnote-ref" role="doc-noteref"&gt;4&lt;/a&gt;&lt;/sup&gt; Its email-oriented workflow, while
battle-tested by projects like the Linux kernel, is unfamiliar to most GitHub
users.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://forgejo.org/"&gt;Forgejo&lt;/a&gt;&amp;rsquo;s federation project may eventually connect
self-hosted instances into a shared network. It looks promising, but has been in
development for quite some time, remains experimental&lt;sup id="fnref:5"&gt;&lt;a href="#fn:5" class="footnote-ref" role="doc-noteref"&gt;5&lt;/a&gt;&lt;/sup&gt; and
is not yet a practical answer to the social fragmentation of self-hosting.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://radicle.xyz/"&gt;Radicle&lt;/a&gt; is technologically interesting: repositories are
replicated peer to peer, while issues and patches are stored alongside them. But
it still feels too immature to replace GitHub for a public project. For example,
its public web interface lets people browse repositories, but contributing
requires them to install its CLI or desktop application. Someone encountering a
bug should not have to install the forge’s software merely to report
it.&lt;sup id="fnref:6"&gt;&lt;a href="#fn:6" class="footnote-ref" role="doc-noteref"&gt;6&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;The project I&amp;rsquo;m most interested in is &lt;a href="https://tangled.org/"&gt;Tangled&lt;/a&gt;, based
mostly on gut feel. I like its focus on the social experience around code. For
example, its home page immediately shows me a bunch of cool projects, exactly
like old GitHub used to do. But it is still in alpha, and it remains to be seen
whether it can blossom into a true alternative.&lt;/p&gt;
&lt;h2 id="could-a-boring-company-fill-the-gap"&gt;Could a boring company fill the gap?&lt;a class="heading-anchor" href="#could-a-boring-company-fill-the-gap" aria-label="Permalink to Could a boring company fill the gap?"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;One thing that stands out is that, apart from GitLab, &lt;em&gt;every&lt;/em&gt; competitor is
built around either decentralization or a social mission. That makes each of
them fundamentally less &amp;ldquo;straightforward&amp;rdquo; than a for-profit company. It makes me
wonder whether there is room for one.&lt;/p&gt;
&lt;p&gt;Perhaps it could look more like &lt;a href="https://bunny.net/"&gt;bunny.net&lt;/a&gt; than a
venture-backed startup: a deliberately boring company with no ambition to become
the operating system for software development or to reorganize programming
around whatever technology investors currently find exciting. It might simply
concentrate on making open-source collaboration pleasant and reliable, charge
developers and smaller organizations directly, and grow at whatever pace that
revenue supports.&lt;/p&gt;
&lt;p&gt;The open question is whether this could be a viable business. The difficult part
is exactly what I keep saying is missing: shared identity, shared conventions
and discovery only become valuable once a platform has reached scale. Better
repository hosting alone would not solve that cold-start problem.&lt;sup id="fnref:7"&gt;&lt;a href="#fn:7" class="footnote-ref" role="doc-noteref"&gt;7&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;I don&amp;rsquo;t know who, if anyone, will solve it. But a real replacement will have to
treat the social layer as the product, not as something that appears
automatically once enough repositories are hosted.&lt;/p&gt;
&lt;div class="footnotes" role="doc-endnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;The
&lt;a href="https://codeberg.org/Codeberg/org/src/branch/main/en/bylaws.md"&gt;English translation of the bylaws&lt;/a&gt;
says Codeberg&amp;rsquo;s purpose &amp;ldquo;also intends to raise awareness for the social and
philosophical questions&amp;rdquo; connected to free content and free software.&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:2"&gt;
&lt;p&gt;Stacked PRs split a large change into a sequence of dependent, individually
reviewable changes.&amp;#160;&lt;a href="#fnref:2" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:3"&gt;
&lt;p&gt;For example, &lt;a href="https://about.gitlab.com/"&gt;GitLab&amp;rsquo;s homepage&lt;/a&gt; describes it as
&amp;ldquo;your intelligent orchestration platform for DevSecOps.&amp;rdquo;&amp;#160;&lt;a href="#fnref:3" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:4"&gt;
&lt;p&gt;For example, its &lt;a href="https://man.sr.ht/terms.md"&gt;Terms of Service&lt;/a&gt; prohibit
cryptocurrency and blockchain projects.&amp;#160;&lt;a href="#fnref:4" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:5"&gt;
&lt;p&gt;Forgejo says
&lt;a href="https://forgejo.org/faq/#what-about-forge-federation"&gt;federation is under active development and considered experimental&lt;/a&gt;.
Moderation and access control have not yet been developed, and breaking
changes are expected.&amp;#160;&lt;a href="#fnref:5" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:6"&gt;
&lt;p&gt;Radicle&amp;rsquo;s
&lt;a href="https://wiki.archlinux.org/title/Radicle"&gt;web interface is read-only&lt;/a&gt;, so
interaction requires the CLI or desktop application. Features such as
releases and bundled package registries are absent. CI is possible through
&lt;a href="https://radicle.network/nodes/rosa.radicle.network/rad%3AzwTxygwuz5LDGBq255RA2CbNGrz8"&gt;community projects&lt;/a&gt;,
but is neither official nor tightly integrated.&amp;#160;&lt;a href="#fnref:6" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:7"&gt;
&lt;p&gt;&lt;a href="https://bitbucket.org/product/"&gt;Bitbucket&lt;/a&gt; is the obvious existing
counterexample: a conventional commercial forge that has never developed
into a shared open-source community comparable to GitHub.&amp;#160;&lt;a href="#fnref:7" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</content:encoded></item><item><title>The git history command deserves more attention</title><link>https://lalitm.com/post/git-history/</link><pubDate>Mon, 13 Jul 2026 19:40:00 +0100</pubDate><guid>https://lalitm.com/post/git-history/</guid><description>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.
jj, an alternative to git, gets discussed a lot these days (1, 2, 3, 4) and is often pitched as a solution. While I’m very sold on the problems jj is trying to solve, the way it solves them hasn’t quite hit home with me. Every 3 months, for the last 1.5 years, I try it out for a few days, really trying to make it part of my workflow but eventually I give up and go back to git.1
That’s where git history comes in. It’s an experimental command that arrived across two releases, 2.54 (April, reword and split subcommands) and 2.55 (June, fixup subcommand). It got a flurry of attention on each release day, and then, as far as I can tell, not much community discussion since. Which is a shame, because IMO it already delivers several of the benefits people tout for jj without needing to switch your whole workflow. And the cool thing is that it’s part of the core git distribution, so you can try it without installing anything.</description><content:encoded>&lt;p&gt;Working with lots of changes in parallel on git can be painful. You end up
juggling branches and commits, and running scary &lt;code&gt;rebase -i&lt;/code&gt; commands that can
leave your tree in a half-broken state if you so much as sneeze.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/jj-vcs/jj"&gt;&lt;code&gt;jj&lt;/code&gt;&lt;/a&gt;, an alternative to &lt;code&gt;git&lt;/code&gt;, gets discussed a
lot these days (&lt;a href="https://news.ycombinator.com/item?id=48692083"&gt;1&lt;/a&gt;,
&lt;a href="https://lobste.rs/s/beqyuc/evan_s_jujutsu_tutorial"&gt;2&lt;/a&gt;,
&lt;a href="https://lobste.rs/s/fg3sgh/jj_tui_terminal_user_interface_jujutsu"&gt;3&lt;/a&gt;,
&lt;a href="https://lobste.rs/s/27dxjg/petty_reason_we_didn_t_end_up_using_jj"&gt;4&lt;/a&gt;) and is
often pitched as a solution. While I&amp;rsquo;m very sold on the &lt;em&gt;problems&lt;/em&gt; &lt;code&gt;jj&lt;/code&gt; is
trying to solve, the way it solves them hasn&amp;rsquo;t quite hit home with me. Every 3
months, for the last 1.5 years, I try it out for a few days, really trying to
make it part of my workflow but eventually I give up and go back to git.&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s where &lt;code&gt;git history&lt;/code&gt; comes in. It&amp;rsquo;s an &lt;strong&gt;experimental&lt;/strong&gt;
&lt;a href="https://git-scm.com/docs/git-history"&gt;command&lt;/a&gt; that arrived across two
releases,
&lt;a href="https://github.com/git/git/blob/master/Documentation/RelNotes/2.54.0.adoc"&gt;2.54&lt;/a&gt;
(April, &lt;code&gt;reword&lt;/code&gt; and &lt;code&gt;split&lt;/code&gt; subcommands) and
&lt;a href="https://github.com/git/git/blob/master/Documentation/RelNotes/2.55.0.adoc"&gt;2.55&lt;/a&gt;
(June, &lt;code&gt;fixup&lt;/code&gt; subcommand). It got a flurry of attention on each release day,
and then, as far as I can tell, not much community discussion since. Which is a
shame, because IMO it already delivers several of the benefits people tout for
&lt;code&gt;jj&lt;/code&gt; without needing to switch your whole workflow. And the cool thing is that
it&amp;rsquo;s part of the core git distribution, so you can try it without installing
anything.&lt;/p&gt;
&lt;p&gt;There are three subcommands: &lt;code&gt;fixup&lt;/code&gt;, &lt;code&gt;reword&lt;/code&gt; and &lt;code&gt;split&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id="fixup"&gt;fixup&lt;a class="heading-anchor" href="#fixup" aria-label="Permalink to fixup"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://git-scm.com/docs/git-history#Documentation/git-history.txt-fixupcommit"&gt;&lt;code&gt;git history fixup&lt;/code&gt;&lt;/a&gt;
fixes an old commit that has something wrong in it, then autorebases &lt;em&gt;all&lt;/em&gt; your
branches to match.&lt;/p&gt;
&lt;p&gt;You stage the fix as usual with &lt;code&gt;git add&lt;/code&gt;, then run &lt;code&gt;git history fixup &amp;lt;commit&amp;gt;&lt;/code&gt;
to fold those staged changes into the target commit. It&amp;rsquo;s like a
&lt;code&gt;git commit --fixup&lt;/code&gt; plus an autosquash rebase but with the extra magic that it
&lt;em&gt;also&lt;/em&gt; updates any other branch which contained that commit.&lt;/p&gt;
&lt;p&gt;That last part goes further than &lt;code&gt;git rebase --update-refs&lt;/code&gt;, which only moves
refs sitting inside the range you&amp;rsquo;re actively rebasing. &lt;code&gt;git history&lt;/code&gt; instead
finds and rewrites &lt;em&gt;every&lt;/em&gt; local branch descended from the commit (while also
having an option to limit it to only the current branch). On the other hand it
does &lt;em&gt;not&lt;/em&gt; work in the presence of merge commits which, for some usages of git,
is going to be a dealbreaker.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s how it works in practice:&lt;/p&gt;
&lt;p&gt;Before, with a fix staged for &lt;code&gt;B&lt;/code&gt;:&lt;/p&gt;
&lt;span class="diagram-wrap"&gt;
&lt;img class="diagram-light" src="https://lalitm.com/diagrams/fixup-before-light.svg" alt="Commits A, B (buggy), C on feat-1 and D on feat-2 in a line, with a staged fix sitting on top of the tip D." loading="lazy" decoding="async"&gt;
&lt;img class="diagram-dark" src="https://lalitm.com/diagrams/fixup-before-dark.svg" alt="Commits A, B (buggy), C on feat-1 and D on feat-2 in a line, with a staged fix sitting on top of the tip D." loading="lazy" decoding="async"&gt;
&lt;/span&gt;
&lt;p&gt;After &lt;code&gt;git history fixup B&lt;/code&gt;:&lt;/p&gt;
&lt;span class="diagram-wrap"&gt;
&lt;img class="diagram-light" src="https://lalitm.com/diagrams/fixup-after-light.svg" alt="After fixup: A unchanged; B (now fixed), C and D rewritten with new hashes, branch tips feat-1 and feat-2 following." loading="lazy" decoding="async"&gt;
&lt;img class="diagram-dark" src="https://lalitm.com/diagrams/fixup-after-dark.svg" alt="After fixup: A unchanged; B (now fixed), C and D rewritten with new hashes, branch tips feat-1 and feat-2 following." loading="lazy" decoding="async"&gt;
&lt;/span&gt;
&lt;p&gt;&lt;em&gt;&lt;code&gt;B*&lt;/code&gt; is &lt;code&gt;B&lt;/code&gt; with the fix folded in. Rewriting a commit gives it a new hash, so
&lt;code&gt;C&lt;/code&gt; and &lt;code&gt;D&lt;/code&gt; are automatically re-created on top as &lt;code&gt;C*&lt;/code&gt; and &lt;code&gt;D*&lt;/code&gt;, and the
&lt;code&gt;feat-1&lt;/code&gt; and &lt;code&gt;feat-2&lt;/code&gt; branch tips move with them.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;The most important property, common to all three commands, is that it&amp;rsquo;s atomic:
it never leaves your tree in a half-broken state. It manages this by refusing
any operation that could produce a conflict.&lt;/p&gt;
&lt;p&gt;To be clear, this is strictly &lt;em&gt;less&lt;/em&gt; powerful than &lt;code&gt;jj&lt;/code&gt;. &lt;code&gt;jj&lt;/code&gt;
&lt;a href="https://docs.jj-vcs.dev/latest/conflicts/"&gt;treats conflicts as first class&lt;/a&gt; so
it can carry a conflicted state through a rebase and let you sort it out later.
&lt;code&gt;git history&lt;/code&gt; doesn&amp;rsquo;t do this yet &lt;em&gt;but&lt;/em&gt; the
&lt;a href="https://git-scm.com/docs/git-history#_limitations"&gt;docs&lt;/a&gt; leave the door open:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;ldquo;This limitation is by design as history rewrites are not intended to be
stateful operations. The limitation can be lifted once (if) Git learns about
first-class conflicts.&amp;rdquo;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;So basically, this limitation may change in the future; excited to see if it
does!&lt;/p&gt;
&lt;h2 id="reword"&gt;reword&lt;a class="heading-anchor" href="#reword" aria-label="Permalink to reword"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://git-scm.com/docs/git-history#Documentation/git-history.txt-rewordcommit"&gt;&lt;code&gt;git history reword&lt;/code&gt;&lt;/a&gt;
updates the commit message on an old commit and automatically rebases everything
on top. This is very useful for going back and
fixing commit messages when the design shifts as you iterate.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;git history reword &amp;lt;commit&amp;gt;&lt;/code&gt; opens your editor with that commit&amp;rsquo;s existing
message. You edit it, save, and the rest of the stack is rebuilt on top with the
branches following along. It&amp;rsquo;s exactly like fixup but for commit messages
instead of the tree contents.&lt;/p&gt;
&lt;p&gt;Because it only changes a message, &lt;code&gt;reword&lt;/code&gt; (like &lt;code&gt;split&lt;/code&gt; later) never touches
your index or working tree at all; it works purely on the commit graph. So both
let you rewrite a commit on a branch you don&amp;rsquo;t have checked out without
disturbing whatever you&amp;rsquo;re in the middle of.&lt;/p&gt;
&lt;p&gt;Before:&lt;/p&gt;
&lt;span class="diagram-wrap"&gt;
&lt;img class="diagram-light" src="https://lalitm.com/diagrams/reword-before-light.svg" alt="Commits A, B with message &amp;#39;fix bug&amp;#39;, and C on feat-1." loading="lazy" decoding="async"&gt;
&lt;img class="diagram-dark" src="https://lalitm.com/diagrams/reword-before-dark.svg" alt="Commits A, B with message &amp;#39;fix bug&amp;#39;, and C on feat-1." loading="lazy" decoding="async"&gt;
&lt;/span&gt;
&lt;p&gt;After &lt;code&gt;git history reword B&lt;/code&gt;:&lt;/p&gt;
&lt;span class="diagram-wrap"&gt;
&lt;img class="diagram-light" src="https://lalitm.com/diagrams/reword-after-light.svg" alt="After reword: A unchanged; B rewritten with message &amp;#39;fix null deref in parser&amp;#39; and C rewritten on top." loading="lazy" decoding="async"&gt;
&lt;img class="diagram-dark" src="https://lalitm.com/diagrams/reword-after-dark.svg" alt="After reword: A unchanged; B rewritten with message &amp;#39;fix null deref in parser&amp;#39; and C rewritten on top." loading="lazy" decoding="async"&gt;
&lt;/span&gt;
&lt;p&gt;&lt;em&gt;Only &lt;code&gt;B&lt;/code&gt;&amp;rsquo;s message changes, but that still gives it a new hash, so &lt;code&gt;C&lt;/code&gt; is
rebuilt on top as &lt;code&gt;C*&lt;/code&gt; and &lt;code&gt;feat-1&lt;/code&gt; follows along.&lt;/em&gt;&lt;/p&gt;
&lt;h2 id="split"&gt;split&lt;a class="heading-anchor" href="#split" aria-label="Permalink to split"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://git-scm.com/docs/git-history#Documentation/git-history.txt-splitcommit--pathspec"&gt;&lt;code&gt;git history split&lt;/code&gt;&lt;/a&gt;
takes one commit and splits it into two, interactively picking what you care
about from each. It&amp;rsquo;s the equivalent of &lt;code&gt;git add -p&lt;/code&gt;, but
without needing gymnastics with &lt;code&gt;git rebase&lt;/code&gt;. I&amp;rsquo;ve found this to be the most
specialized of the three, but invaluable when I need it.&lt;/p&gt;
&lt;p&gt;Specifically, &lt;code&gt;git history split &amp;lt;commit&amp;gt;&lt;/code&gt; drops you into a hunk-by-hunk prompt
over that commit&amp;rsquo;s diff. The hunks you keep make up the first commit, the rest
fall into the second.&lt;/p&gt;
&lt;p&gt;Before, with &lt;code&gt;B&lt;/code&gt; bundling two unrelated changes:&lt;/p&gt;
&lt;span class="diagram-wrap"&gt;
&lt;img class="diagram-light" src="https://lalitm.com/diagrams/split-before-light.svg" alt="Commit B bundling two unrelated changes, between A and C on feat-1." loading="lazy" decoding="async"&gt;
&lt;img class="diagram-dark" src="https://lalitm.com/diagrams/split-before-dark.svg" alt="Commit B bundling two unrelated changes, between A and C on feat-1." loading="lazy" decoding="async"&gt;
&lt;/span&gt;
&lt;p&gt;After &lt;code&gt;git history split B&lt;/code&gt;:&lt;/p&gt;
&lt;span class="diagram-wrap"&gt;
&lt;img class="diagram-light" src="https://lalitm.com/diagrams/split-after-light.svg" alt="After split: A unchanged; B split into B1 and B2, and C rewritten on top as C*." loading="lazy" decoding="async"&gt;
&lt;img class="diagram-dark" src="https://lalitm.com/diagrams/split-after-dark.svg" alt="After split: A unchanged; B split into B1 and B2, and C rewritten on top as C*." loading="lazy" decoding="async"&gt;
&lt;/span&gt;
&lt;p&gt;&lt;em&gt;&lt;code&gt;B&lt;/code&gt; becomes &lt;code&gt;B1&lt;/code&gt; and &lt;code&gt;B2&lt;/code&gt;, and &lt;code&gt;C&lt;/code&gt; is rebuilt on top of the pair as &lt;code&gt;C*&lt;/code&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;a class="heading-anchor" href="#conclusion" aria-label="Permalink to Conclusion"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;Judging by how many people are using &lt;code&gt;jj&lt;/code&gt;, I do think there&amp;rsquo;s still some key
mental shift which I&amp;rsquo;m not yet making. And to be clear, &lt;code&gt;git history&lt;/code&gt; doesn&amp;rsquo;t
close the full gap: &lt;code&gt;jj&lt;/code&gt; still gives you an
&lt;a href="https://docs.jj-vcs.dev/latest/operation-log/"&gt;operation log with easy undo&lt;/a&gt;,
&lt;a href="https://docs.jj-vcs.dev/latest/working-copy/"&gt;models your working copy as a commit&lt;/a&gt;,
and can
&lt;a href="https://docs.jj-vcs.dev/latest/conflicts/"&gt;carry conflicts through a rebase&lt;/a&gt;,
none of which this is trying to do.&lt;/p&gt;
&lt;p&gt;But for now, &lt;code&gt;git history&lt;/code&gt; is a &lt;em&gt;big&lt;/em&gt; step forward in adopting many of the
pieces that attract people to &lt;code&gt;jj&lt;/code&gt;, and it&amp;rsquo;s already in the tool I use every
day. And the way the documentation is written makes me hopeful that more
improvements will be coming in upcoming releases!&lt;/p&gt;
&lt;div class="footnotes" role="doc-endnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;If there&amp;rsquo;s interest, happy to write up a post on my experience on this.&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</content:encoded></item></channel></rss>