← Back to Blog

What if Merges Understood Your Code?

April 20264 min read

Git's three-way merge is one of the most elegant algorithms in everyday software engineering. Take the common ancestor of two branches, compare what each side changed, and combine the results. It's fast, it's predictable, and it works remarkably well considering it has no idea what the content it's merging actually means. To git, a Python file and a CSV file are the same thing: sequences of lines. And most of the time, that level of understanding is enough. The merge just works, and you move on without thinking about it.

But every developer has had the experience of getting a merge conflict that didn't feel like a real conflict. Alice adds a parameter to process_payment(). Bob adds logging to validate_order(). These are completely independent changes to different functions that don't share any logic or state. But because the two functions happen to sit next to each other in the same file, git's merge algorithm sees that the surrounding context lines overlap and conservatively reports a conflict. Someone has to open the file, look at both changes, realize they don't interact at all, and manually combine them. It takes a minute or two, it's mildly annoying, and life goes on.

The reason this happens is that git's merge is, by design, working at the line level. It can see that two branches both modified lines near each other, but it can't see that those lines belong to different functions and therefore can't possibly interfere. This is a completely reasonable limitation. Git was designed to be content-agnostic, and that generality is a big part of why it works for everything from source code to configuration files to prose. But it does mean that the merge step is sometimes more conservative than it needs to be, and that the conflicts it produces aren't always real disagreements.

To understand why this matters increasingly over time, think about it in terms of how conflict probability scales. If you have K workers (humans or agents) making changes to a codebase, and a file contains E independent entities, the probability of a true conflict, where two workers actually edit the same entity, follows birthday-paradox-style scaling: it's roughly proportional to K²/E. The more entities in the file, the less likely any two workers collide. But at the line level, git doesn't see E independent entities. It sees a single undifferentiated block of text, and any two edits that land near each other in the file look like a potential conflict. So the false conflict rate scales with K² too, but against a much smaller denominator, because the number of "conflict zones" at the line level is much larger than the number of actual entities. As K grows, the gap between true conflicts and reported conflicts widens, and it widens quadratically.

This is where agents make the problem acute. A human developer can pause, look at a false conflict, realize the two changes don't interact, and resolve it in a minute. It's annoying but manageable. An agent can't do this nearly as cleanly. Merge conflicts break the agent's workflow. The agent either has to call an LLM to reason about whether the conflict is real (expensive, slow, error-prone), or it has to stop and ask a human (defeats the purpose of autonomous operation). Every false conflict is a point where the agent's pipeline stalls or degrades. And since agents are the ones who benefit most from parallelism, more agents working means K grows, which means quadratically more false conflicts, which means the merge step becomes the bottleneck of the entire system.

So we built Weave as a git merge driver, which means it plugs directly into git's existing merge pipeline. You don't change your workflow at all. You still use git, still use branches, still merge the way you always have. The only difference is that when git encounters a file that both branches modified, Weave steps in and parses all three versions of the file into structural entities using tree-sitter: functions, classes, methods, imports. It matches entities across versions by name, and then merges each entity independently. If Alice changed entity A and Bob changed entity B, and A ≠ B, those edits are independent by definition and merge cleanly. Weave only reports a conflict when the same entity was changed on both sides in incompatible ways, which is the only case where there's a genuine disagreement that needs judgment.

The reason you can be confident about this isn't just empirical. There's a clean theoretical property underneath it. If change A modifies the set of entities SA and change B modifies the set SB, and those sets don't overlap (SA ∩ SB = ∅), then the result of merging A then B is identical to merging B then A. The order doesn't matter. You always converge to the same result. This property is called confluence, and it falls out of the structure of the problem once you're working at the right level of abstraction. Line-level merging can't make this guarantee, because lines don't have a natural notion of independence. Two line-level changes to different functions can still "interfere" through overlapping context, which is exactly what produces false conflicts. Entity-level merging eliminates that entire failure mode.

For agents, confluence is the critical property, because it lets an agent know before it starts working whether its changes will merge cleanly. If an agent is about to edit entity X, and it can check that no other agent is currently editing X, it has a mathematical guarantee that its work will merge. It doesn't need to hope, or retry, or call an LLM to resolve conflicts afterward. The merge is guaranteed to succeed. This is what makes parallel agent work tractable: not better conflict resolution, but conflict avoidance through the right abstraction. The agent's workflow becomes: claim entity, edit entity, merge entity. No conflict possible unless another agent claimed the same entity, which the coordination layer prevents.

The important thing is that none of this replaces git or changes what git does well. Git's merge algorithm is still doing the heavy lifting. Weave just gives it more information about the structure of the files it's merging, which lets it make better decisions about what's actually a conflict. When Weave can resolve something cleanly, it does. When it can't, it defers to the normal conflict resolution flow, and you deal with it the same way you always have. The result is that the conflicts you actually see are the ones that deserve attention: two workers genuinely changed the same entity in incompatible ways, and someone needs to decide how to combine them. Everything else, the entire K²-scaling class of false conflicts from adjacent edits, just merges.