Hacker News new | ask | show | jobs
by radarsat1 91 days ago
Is it a good thing to have merges that never fail? Often a merge failure indicates a semantic conflict, not just "two changes in the same place". You want to be aware of and forced to manually deal with such cases.

I assume the proposed system addresses it somehow but I don't see it in my quick read of this.

9 comments

It says that merges that involve overlap get flagged to the user. I don't think that's much more than a defaults difference to git really. You could have a version of git that just warns on conflict and blindly concats the sides.
This is kind of how jj handles the situation. git won't let you move on from a rebase if there are conflicts. By comparison, jj will just put a marker in the log pointing out that there are conflicts in a branch. You resolve them whenever you feel like it, but all resolving them does is effectively remove the "conflict" marker and rebase all of the descendent commits (which may clean up merge conflicts, or make them worse).
Indeed. And plenty of successful merges end up with code that won't compile.

FWIW I've struggled to get AI tools to handle merge conflicts well (especially rebase) for the same underlying reason.

Code not compiling is still the good case, because you’ll notice before deployment. The dangerous cases are when it does compile.
I've seen merged code where the memory barriers were misplaced.
Very true.

I realized recently that I've subconsciously routed-around merge conflicts as much as possible. My process has just subtly altered to make them less likely. To the point of which seeing a 3-way merge feels jarring. It's really only taking on AI tools that bought this to my attention.

I'm surprised to see that some people sync their working tree and does not evaluate their patch again (testing and reviewing the assumptions they have made for their changes).
My understanding of the way this is presented is that merges don't _block_ the workflow. In git, a merge conflict is a failure to merge, but in this idea a merge conflict is still present but the merge still succeeds. You can commit with conflicts unresolved. This allows you to defer conflict resolution to later. I believe jj does this as well?

Technically you could include conflict markers in your commits but I don't think people like that very much

If other systems are doing it too then I guess it must be useful

But why is it useful to be able to defer conflict resolution?

I saw in a parallel comment thread people discussing merge commit vs rebase workflow - rebase gives cleaner git history but is a massive pain having to resolve conflicts on every commit since current branch diverged instead of just once on the final result with merge commit.

Is it that? Deferred conflict resolution allows you to rebase but only resolve conflicts at the end?

Delayed conflict resolution in jj is valuable when you're rebasing a long chain of commits. If I rebase a chain of 10 commits and each of the commits has a conflict, I'm stuck in conflict resolution mode until I fix all 10 conflicts. Maybe something else came up, or maybe I got tired of doing conflict resolution and want to do something else. Git's answer is to finish or abandon.

Also, in jj it's pretty easy to rebase a lot of stuff all at once, giving you even more opportunities to create conflicts. Being able to delay resolution can be easier.

Deferred conflict resolution is amazing in jj because I may never return to some of the branches that are in conflict and therefore might never bother resolving them.

I rebase entire trees of commits onto main daily. I work on top of a dev-base commit and it has all kinds of anonymous branches off it. I rebase it and all its subbranches in 1 command and some of those sub branches might now be in a conflicted state. I don’t have to resolve them until I need to actually use those commits.

dev-base is an octopus merge of in-flight PRs of mine. When work is ready to be submitted it moves from being a descendent of dev-base to a parent of dev-base.

Rebasing all my PRs and dev-base and all its descendents is 1 command. Just make sure my @ is a descendent of dev-base and then run: jj rebase -d main

> You can commit with conflicts unresolved.

True but it is not valid syntax. Like, you mean with the conflict lines?

The conflict lines shown in the article are not present in the file, they are a display of what has already been merged. The merge had changes that were too near each other and so the algorithm determined that someone needs to review it, and the conflict lines are the result of displaying the relevant history due to that determination.

In the example in the article, the inserted line from the right change is floating because the function it was in from the left has been deleted. That's the state of the file, it has the line that has been inserted and it does not have the lines that were deleted, it contains both conflicting changes.

So in that example you indeed must resolve it if you want your program to compile, because the changes together produce something that does not function. But there is no state about the conflict being stored in the file.

Isnt that a bit dangerous in its own? If the merge process can complete without conflicts being resolved, doesnt it just push the Problem down the road? All of a sudden you have to deal with failing CI or ghost features that involve multiple people where actually you just should has solved you conflict locally at merge time.
The tooling can force resolution at any step desired.
What's the point of options when there only is one correct answer?
Yeah this seems silly. You can do the same thing in git (add and commit with the conflict still there)! Why you would want to is a real mystery.
It allows review of the way the merge conflict has been resolved (assuming those changes a tracked and presented in a useful way). This can be quite helpful when backporting select fixes to older branches.
In this model, conflicts do not exist, so there are no conflict markers (the UI may show markers, but they get generated from what they call “the weave”)

Because of that, I think it is worse than “but it is not valid syntax”; it’s “but it may not be valid syntax”. A merge may create a result that compiles but that neither of the parties involved intended to write.

They address this; it's not that they don't fail, in practice...

the key insight is that changes should be flagged as conflicting when they touch each other, giving you informative conflict presentation on top of a system which never actually fails.

Isn't that how the current systems work though? Git inserts conflict markers in the file, and then emacs (or whatever editor) highlights them

The big red block seems the same as "flagged", unless I'm misunderstanding something

With git, conflicts interrupt the merge/rebase. And if you end up in a situation with multiple rebases/merges/both, it's easy to get a "bad" state, or be forced to resolve redundant conflict(s) over and over.

In Jujutsu and Pijul, for example, conflicts are recorded by default but marked as conflict commits/changes. You can continue to make commits/changes on top. Once you resolve the conflict of A+B, no future merges or rebases would cause the same conflict again.

Thank you for the explanation, i did misunderstand! seems like a neat feature
Yes and no. Most often conflicts could have been handled automatically with better tools. For example I have a script that makes a copy of the whole folder and tries to merge each commit using all of git’s different merge stategies, and all sub stategies, and presents which ones can merge without any conflicts. It has been mind opening. Why git doesn’t have this built-in I don’t understand.

Git also writes (non-logs) to the .git folder for operations that you would assume should have been r/o, but that’s another problem (that affects things later on).

Should you be counting on confusion of an underpowered text-merge to catch such problems?

It'll fire on merge issues that aren't code problems under a smarter merge, while also missing all the things that merge OK but introduce deeper issues.

Post-merge syntax checks are better for that purpose.

And imminently: agent-based sanity-checks of preserved intent – operating on a logically-whole result file, without merge-tool cruft. Perhaps at higher intensity when line-overlaps – or even more-meaningful hints of cross-purposes – are present.

> It'll fire on merge issues that aren't code problems under a smarter merge, while also missing all the things that merge OK but introduce deeper issues.

That has not been my experience at all. The changes you introduced is your responsibility. If you synchronizes your working tree to the source of truth, you need to evaluate your patch again whether it introduces conflict or not. In this case a conflict is a nice signal to know where someone has interacted with files you've touched and possibly change their semantics. The pros are substantial, and it's quite easy to resolve conflicts that's only due to syntastic changes (whitespace, formatting, equivalent statement,...)

If you're relying on a serialized 'source of truth', against which everyone must independently ensure their changes sanely apply in isolation, the. you've already resigned yourself to a single-threaded process that's slower than what improved merges aim to enable.

Sure, that works – like having one (rare, expensive) savant engineer apply & review everything in a linear canonical order. But that's not as competitive & scalable as flows more tolerant of many independent coders/agents.

Decentralization in this case means one can secede easily from the central authority. So anyone working on a project can easily split away from the main group at any time. But every project have a clear governance where the main direction is set and the canonical version of the thing being under version control is stored.

That canonical version is altered following a process and almost every project agrees that changes should be proposed against it. Even with independent agents, there should be a way to ensure consensus and decides the final version. And that problem is a very hard one.

And yet after all these year of git supporting no source of truth we still fall back on it. As long as you have an authoritative version and authoritative release then you have one source of truth. Linus imagined everyone contributing with no central authority and yet we look to GitHub and Gitlab to centralize our code. Git is already decentralized and generally we find it impractical.
He's not saying you shouldn't have conflicts; just that it's better to have syntax-aware conflict detection. For example if two people add a new function to the end of the same file, Git will always say that's a conflict. A syntax-aware system could say that they don't conflict.
> Should you be counting on confusion of an underpowered text-merge to catch such problems?

This does not really follow from my statement.

I said that underpowered text merge should not silently accept such situations, not that it is the only way to catch them. It doesn't replace knowing something about what you are merging, but it is certainly a good hint that something may be wrong or unexpected.

> Post-merge syntax checks are better for that purpose.

Better, yes, but I was addressing semantic issues, not syntactical. I have seen syntactically valid merges result in semantic inconsistency, it does happen.

I do agree with your last statement.. unit & integration tests, agent checks or whathaveyou, these all contribute to semantic checking, which is a good thing.

Can they be relied on here? Maybe? I guess the jury is still out. My testing philosophy is "you can only test for what you think of testing". And tests and agent checks have a signal to noise ratio, and are only as useful as their SNR allows.

There is no guaranteed way to stop bugs from happening, if there were it likely would have been discovered by now. All we can do is take a layered approach to provide opportunities for them to get caught early. Removing one of those layers (merge conflicts) is not clearly a good thing, imho, but who knows.. if agent checks can replace it, then sure, I'm all for it.

Probably depends on what is in the merge. Lately I've been collaborating a ton on PRDs and software specs in markdown (now that agents have gotten pretty good at turning it into usable code) and using git had been pretty painful. Especially when working with a domain expert whose not as technical, git is proving to almost be more of a barrier than an aid.

For this kind of work (which I suspect will only get more common), a CRDT-based VCS makes a lot of sense.

I agree. Nevertheless I wonder if this approach can help with certain other places where Git sometimes struggles, such as whether or not two commits which have identical diffs but different parents should be considered equivalent.

In the general case, such commits cannot be considered the same — consider a commit which flips a boolean that one branch had flipped in another file. But there are common cases where the commits should be considered equivalent, such as many rebased branches. Can the CRDT approach help with e.g. deciding that `git branch -d BRANCH` should succeed when a rebased version of BRANCH has been merged?

> Conflicts are informative, not blocking. ... Conflicts are surfaced for review when concurrent edits happen “too near” each other, but they never block the merge itself.

So conflicts are still surfaced for review.