git/list[1] front-page[2] threads[3] people[4] search[5] about
 

Re: [RFC] git-rebase-clean: mitigating a “semantic conflict cascade” during rebase

From
AMAntonio Mennillo <antoniomennillo87@gmail.com>
Date
Sep 19, 2025, 21:14 UTC
Message-ID
<CACEPZDWgoa18LAhzOzu__jy8ssgR8-PmZb6gmDzCS=q6f25=wA@mail.gmail.com>
In-Reply-To
<aMyGHriNpmekoqD5@fruit.crustytoothpaste.net>
Hi Brian,

Thank you again for the feedback, it gave me a fundamental piece of puzzle about Git's design constraints that I hadn't fully considered.

You're right about tree-sitter being inappropriate for Git core. That approach is too narrow, covering only specific languages. My v2.0.0 already prioritizes Git's existing plumbing to track content changes universally, with tree-sitter as an optional enhancement. This stays content-agnostic and removes that which I can define as a source of nondeterminism and heuristic complexity that it's elegant to avoid.

To summarize the mechanism:
1) Squash the feature branch into a single commit: temporarily
compressing the branch makes all internal semantic dependencies
visible at once, avoiding cascade conflicts during step-by-step rebase
with non-atomic commits.
2) Resolve conflicts once with full context: the semantic issues are
"suspended" in this unified state where everything is coherent.
3) Restore original commits by replaying saved diffs: using a
hash-based approach with git's native diff/apply machinery, the tool
remains file-type agnostic. The restored commits may still break
individually (e.g., won't compile in isolation), but the branch is now
rebased with granular history preserved.
To address your header file example specifically: when common.h no
longer includes stdlib.h after rebasing onto main, you would indeed
need to add #include <stdlib.h> during conflict resolution. With my
tool, you'd add this include once while resolving the squashed
commit's conflicts. The fix then persists through all restored commits
- so instead of potentially fixing the same missing header multiple
times across commits during a normal rebase, you handle it once.
You're correct that the tool cannot determine which specific commit
should ideally contain this fix (that would require compilation
testing), but it prevents the cascading resolution burden while
preserving the granular history.

The key point: this doesn't fix non-atomic commits. The semantic problems remain if you examine individual commits. But it prevents these issues from causing redundant conflict resolution during rebase by compressing the timeline of the branch in a single instant "t" where the semantic non-atomic misalignment appears simultaneously.

The tool trades ideal per-commit correctness for efficiency in real-world scenarios where atomic commits aren't always achievable (junior developers, emergency fixes, legacy codebases).

Regarding expectations: documentation will be clear that this only reduces redundant conflict resolution. It doesn't make commits atomic or understand semantics. It manages semantic inconsistency by compression, intended as reduced dimensionality. The defects remain in history but no longer interfere with rebasing.

I apologize if the code evolution seems rushed - v2.0.0 was developed quickly to establish a working implementation of the core concept. For v3.0.0, I plan to remove tree-sitter entirely to achieve complete language-agnosticism. I'll also need to properly evaluate edge cases like submodules and merge commits through integration testing, though the squash operation should hopefully be quite independent from these structures. My testing focus will be on ensuring determinism, proper rollback capabilities, and full idempotency across all supported scenarios.

The current implementation is available at https://github.com/anthem87/clean-rebase/tree/v2.0.0 if you'd like to explore the approach.

Thanks again for the patience and the attention.

Best regards, Antonio

Il giorno ven 19 set 2025 alle ore 00:22 brian m. carlson <sandals@crustytoothpaste.net> ha scritto:

Show 101 quoted lines
>
> On 2025-09-18 at 17:02:59, Antonio Mennillo wrote:
> > Hi Brian,
> >
> > Thank you for the thoughtful response, and please excuse me if I am a
> > bit clumsy —
> > this is my first interaction with the Git community.
> >
> > > Usually we would recommend that each commit be atomic...
> >
> > I perfectly understand this best practice. In my context, working with
> > many junior developers,
> > it is not always possible to enforce atomic commits. In some
> > “emergency” situations we
> > squashed just to make progress, even if that meant losing granularity.
>
> Yeah, I've seen that at previous employers.  Even where I work now, I
> definitely see differing levels of interest in atomic commits with nice
> commit messages.
>
> > Your explanation really helped me see things more clearly, and I appreciated
> > the constructive (non-toxic) way you put it.
>
> Well, thank you.
>
> > Yes, rerere helps with repeated textual conflicts, but the scenario I
> > worry about
> > is semantic: if commit M changes an interface and commit N implements it, then
> > changing M during rebase can make N semantically invalid. rerere
> > cannot solve that
> > — it remembers markers, but cannot understand that “this
> > implementation no longer
> > matches the interface.”
>
> Yes, this is effectively unavoidable because Git doesn't know the
> structure of your code or data.  For instance, in Rust, I could add a
> new mandatory-to-implement method to a trait and then all
> implementations would need an update.
>
> > That feedback inspired me to improve the tool. Version 2.0.0
> > (literally born today,
> > thanks to your comment) now works differently:
> >
> > 1. Temporarily squash the feature branch (so the rebase is effectively `1/1`)
> > 2. Resolve conflicts once, with the full final context
> > 3. Automatically re-expand the squash by replaying saved diffs, restoring
> >    the original commits (with new hashes, as expected in any rebase)
>
> You haven't described exactly how this works, but it looks like it
> performs a fixed set of transforms (e.g., symbol renames).  That can be
> useful, but it isn't sufficient in a lot of cases.
>
> For instance, in some work on Git I'm doing, I need to include a header
> file for a function.  It was previously included in another header, but
> my rebase onto the main branch had it removed, so I need to add it into
> the C file I'm working on.  The only way I can 100% know which commit
> needs that change programmatically is to try to compile on each commit
> until it fails, and insert it there.  This, of course, requires atomic
> commits.
>
> In a lot of cases, I use git-autofixup[0], which looks at changed lines
> in the diff to determine which commit to squash the changes into. It's a
> great idea, but it has limitations, since sometimes a change logically
> attaches to a commit but isn't close to it in the context of the diff
> (such as my header inclusion change above), and sometimes I've changed
> similar lines in an earlier commit A (where my change should be
> squashed) as well as in later commit B, yet it gets attributed to B
> accidentally.  It's convenient and generally works well, but I have to
> be careful not to trust it entirely.
>
> > I would be very interested in whether the community sees this worth exploring
> > as an **experimental rebase strategy** (something like `git rebase
> > --squash-restore`),
> > or if it overlaps with existing mechanisms I am not aware of.
>
> I think the fact that it adds a dependency on tree-sitter is kind of a
> non-starter for us.  We're currently having a mildly contentious
> discussion over the use of Rust in Git and it won't be mandatory until
> Git 3.0.
>
> It certainly covers some use cases, but it looks like it only handles
> deletions, file renames, and symbol renames, and the latter only for a
> small set of file types.  There's a lot of cases that it doesn't handle,
> and I'm afraid that people will end up disappointed by expecting it to
> magically handle a lot more than it does.  That doesn't mean it isn't a
> useful and valuable tool for many use cases, but users may have much
> higher expectations that it will magically solve their problems when
> that isn't the case.  I have some tools that I've written myself that
> are like that, for instance.
>
> I'll also point out that we try to avoid hard-coding known languages.
> People use lots of languages with Git, some of which are not even known
> to Vim, which has an exceptionally complete list of file types and
> syntax highlighting.  People also do non-code things with Git; for
> instance, I store my creative writing in a repository, and other people
> use Git for working on documentation or technical books.  As much as
> possible, we want tooling that works for at least generic text files and
> ideally, all files.
> --
> brian m. carlson (they/them)
> Toronto, Ontario, CA
Previous: brian m. carlsonNext: Elijah Newren
Message 5 of 8 in “[RFC] git-rebase-clean: mitigating a “semantic conflict cascade” during rebase”
  1. Antonio MennilloSep 16, 2025
  2. brian m. carlsonSep 16, 2025
  3. Antonio MennilloSep 18, 2025
  4. brian m. carlsonSep 18, 2025
  5. Antonio MennilloSep 19, 2025
  6. Elijah NewrenSep 20, 2025
  7. Antonio MennilloSep 21, 2025
  8. Antonio MennilloSep 23, 2025

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.