[HN Gopher] The future of version control
___________________________________________________________________
The future of version control
Author : c17r
Score : 345 points
Date : 2026-03-22 15:16 UTC (7 hours ago)
(HTM) web link (bramcohen.com)
(TXT) w3m dump (bramcohen.com)
| bos wrote:
| This is sort of a revival and elaboration of some of Bram's ideas
| from Codeville, an earlier effort that dates back to the early
| 2000s Cambrian explosion of DVCS.
|
| Codeville also used a weave for storage and merge, a concept that
| originated with SCCS (and thence into Teamware and BitKeeper).
|
| Codeville predates the introduction of CRDTs by almost a decade,
| and at least on the face of it the two concepts seem like a
| natural fit.
|
| It was always kind of difficult to argue that weaves produced
| unambiguously better merge results (and more limited conflicts)
| than the more heuristically driven approaches of git, Mercurial,
| et al, because the edit histories required to produce test cases
| were difficult (at least for me) to reason about.
|
| I like that Bram hasn't let go of the problem, and is still
| trying out new ideas in the space.
| dboreham wrote:
| Note that CRDT isn't "a thing". The CRDT paper provides a way
| to think about and analyze eventually consistent replication
| mechanisms. So CRDTs weren't "introduced", only the "CRDT way
| of discussing replication". Every concrete mechanism described
| in the CRDT paper is very old, widely used for decades
| beforehand.
|
| This means that everything that implements eventual consistency
| (including Git) is using "a CRDT".
| hrmtst93837 wrote:
| If you stretch "CRDT" to mean any old eventually consistent
| thing, almost every Unix tool morphs into one under a loose
| enough definition. That makes the term much less useful,
| because practical CRDTs in 2024 usually mean opaque merge
| semantics, awkward failure modes, and operational complexity
| that has very little in common with the ancient algorithms
| people point at when they say "Git is a CRDT too". "Just Git"
| is doing a lot of work there.
| mweidner wrote:
| While this is technically correct, folks discussing CRDTs in
| the context of text editing are typically thinking of a
| fairly specific family of algorithms, in which each character
| (or line) is assigned an immutable ID drawn from some
| abstract total order. That is the sense in which the original
| post uses the term (without mentioning a specific total
| order).
| gritzko wrote:
| In 2007 Bram said to me that my Causal Tree algorithm is a
| variant of weave. Which is broadly correct. In these 20 years,
| the family of weave-class algos grew quite big. In my 2020
| article, I devoted the intro to making their family portrait
| https://arxiv.org/abs/2002.09511 Could have been a separate
| article.
| bramcohen wrote:
| The whole point of using a proper CRDT is that it's easy to
| reason about what it does. It took me a while to figure out the
| details of how to build one.
| logicprog wrote:
| This seems like an excellent idea. I'm sure a lot of us have been
| idly wondering why CRDTs aren't used for VCS for some time, so
| it's really cool to see someone take a stab at it! We really do
| need an improvement over git; the question is how to overcome
| network effects.
| righthand wrote:
| Well over half of all people can't tell you the difference
| between git and Github. The latter being owned by a corporation
| that needs the network effect to keep existing.
| vishvananda wrote:
| This is actually a very interesting moment to potentially
| overcome network effects, because more and more code is going
| to be written by agents. If a crdt approach is measurably
| better for merging by agent swarms then there is incentive to
| make the switch. It also much easier to get an agent to change
| its workflow than a human. The only tricky part is how much git
| usage is in the training set so some careful thought would need
| to be given to create a compatibility layer in the tooling to
| help agents along.
| NetOpWibby wrote:
| Overcoming network effects cannot be the goal; otherwise, work
| will never get done.
|
| The goal should be to build a full spec and then build a code
| forge and ecosystem around this. If it's truly great, adoption
| will come. Microsoft doing a terrible job with GitHub is great
| for new solutions.
| ZoomZoomZoom wrote:
| The key insight in the third sentence?
|
| > ... CRDTs for version control, which is long overdue but hasn't
| happened yet
|
| Pijul happened and it has hundreds - perhaps thousands - of hours
| of real expert developer's toil put in it.
|
| Not that Bram is not one of those, but the post reads like you
| all know what.
| simonw wrote:
| I hadn't heard of Pijul. My first search took me to
| https://github.com/8l/pijul which hasn't been updated in 11
| years, but it turns out that's misleading and the official repo
| at https://nest.pijul.com/pijul/pijul had a commit last month.
|
| ... and of course it is, because Pijul uses Pijul for
| development, not Git and GitHub!
| idoubtit wrote:
| The canonical website is https://pijul.org. The homepage has
| a link to the pijul source repository.
| ozten wrote:
| They should mirror on GitHub for marketing purposes
| nicoty wrote:
| How would they do that if they don't use git for version
| control? Does GitHub allow other forms of version control
| other than git?
| simonw wrote:
| SQLite does it despite using Fossil - their mirror is at
| https://github.com/sqlite/sqlite
|
| Git is so established now that it's sensible for
| alternative VCS to have a mode where they can imitate the
| Git protocol - or seven without that you can still
| checkout the latest version of your repo and git push
| that on a periodic basis.
| codethief wrote:
| > I hadn't heard of Pijul
|
| I'm surprised! Pijul has been discussed here on HN many, many
| times. My impression is that many people here were hoping
| that Pijul might eventually become a serious Git contender
| but these days people seem to be more excited about Jujutsu,
| likely because migration is much easier.
| simonw wrote:
| Looks like it makes the homepage only once or twice a year
| (using points>50 as a proxy for that), had more buzz around
| five years ago: https://hn.algolia.com/?dateRange=all&page=
| 0&prefix=false&qu...
| jedberg wrote:
| I too am here all the time and have never heard of it. But
| it looks interesting.
| vova_hn2 wrote:
| I have a weird hobby: about once a year I go to the theory page
| [0] in pijul manual and see if they have fixed the TeX
| formatting yet.
|
| You would think that if a better, more sound model of storing
| patches is _your whole selling point_ , you would want to make
| as easy as possible for people who are interested in the
| project to actually understand it. It is really _weird_ not to
| care about the first impression that your manual makes on a
| curious reader.
|
| Currently, I'm about 6 years into the experiment.
|
| Approximately 2 years in (about 4 years ago), I've actually
| went to the Pijul Nest and reported [1] the issue. I got an
| explanation on fixing this issue locally, but weirly enough,
| the fix _still wasn 't actually implemented on the public
| version_.
|
| I'll report back in about a year with an update on the
| experiment.
|
| [0] https://pijul.org/manual/theory.html
|
| [1] https://nest.pijul.com/pijul/manual/discussions/46
| AceJohnny2 wrote:
| > _It is really weird not to care about the first impression
| that your manual makes on a curious reader._
|
| On the contrary, I think this is an all-too-familiar pitfall
| for the, er... technically minded.
|
| "I've implemented it in the code. My work here is done. The
| rest is window dressing."
| rbsmith wrote:
| Do you use Pijul?
|
| From time to time, I do a 'pijul pull -a' into the pijul source
| tree, and I get a conflict (no local work on my part). Is there
| a way to do a tracking update pull? I didn't see one, so I toss
| the repo and reclone. What works for you in tracking what's
| going on there?
| radarsat1 wrote:
| 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.
| mikey-k wrote:
| This
| recursivecaveat wrote:
| 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.
| hungryhobbit wrote:
| 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.
| bigfishrunning wrote:
| 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
| rectang wrote:
| 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?
| gojomo wrote:
| 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.
| skydhash wrote:
| > 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,...)
| gojomo wrote:
| 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.
| yammosk wrote:
| 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.
| skydhash wrote:
| 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.
| IshKebab wrote:
| 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.
| jwilliams wrote:
| 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.
| layer8 wrote:
| Code not compiling is still the good case, because you'll
| notice before deployment. The dangerous cases are when it
| does compile.
| jwilliams wrote:
| 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.
| Gibbon1 wrote:
| I've seen merged code where the memory barriers were
| misplaced.
| skydhash wrote:
| 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).
| conradludgate wrote:
| 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
| rightbyte wrote:
| > You can commit with conflicts unresolved.
|
| True but it is not valid syntax. Like, you mean with the
| conflict lines?
| ericpauley wrote:
| 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.
| fweimer wrote:
| 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.
| furyofantares wrote:
| 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.
| Someone wrote:
| 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.
| simonw wrote:
| This thing is really short.
| https://github.com/bramcohen/manyana/blob/main/manyana.py is 473
| lines of dependency-free Python (that file only imports difflib,
| itertools and inspect) and of that ~240 lines are implementation
| and the rest are tests.
| zahlman wrote:
| It's really impressive what can be done in a few hundred lines
| of well-thought-out Python without resorting to brutal hacks.
| People complain about left-pad incidents etc. in the JS world
| but I honestly feel like the Python ecosystem could do with
| more, smaller packages on balance. They just have to be put
| forward by responsible people who aren't trying to make a point
| or inflate artificial metrics.
| josephg wrote:
| I bet you can make a small, beautiful implementation of this
| algorithm in most languages. Most algorithms - even ones that
| take generations of researchers to figure out - end up tiny
| in practice if you put the work in to understand them
| properly and program them in a beautiful way. Transformers
| are the same. Genius idea. But a very small amount of code to
| implement.
|
| This is an implementation of FugueMax (Weidner and Kleppmann)
| done using a bunch of tricks from Yjs (Jahns). There's
| generations of ideas here, by lots of incredibly smart
| people. And it turns out you can code the whole thing up in
| 250 lines of readable typescript. Again with no dependencies.
|
| https://github.com/josephg/crdt-from-
| scratch/blob/master/crd...
| sayrer wrote:
| The joke is there in the name. It is wrong (on purpose): it
| should be "Manana". That term means "tomorrow" in your Spanish
| class, but it can mean "later/future/morning" or even "later
| this afternoon".
|
| In English, you might think of "procrastination" or "we'll get
| to it."
|
| In Portuguese, you would say "proxima semana", literally "next
| week", but it means "we'll get to it" (won't get to it).
| mikey-k wrote:
| Interesting idea. While conflicts can be improved, I personally
| don't see it as a critical challenge with VCS.
|
| What I do think is the critical challenge (particularly with Git)
| is scalability.
|
| Size of repository & rate of change of repositories are starting
| to push limits of git, and I think this needs revisited across
| the server, client & wire protocols.
|
| What exactly, I don't know. :). But I do know that in my current
| role (mid-size well-known tech company) is hitting these limits
| today.
| layer8 wrote:
| One solution is to decompose your code into modules with stable
| interfaces and reference them as versioned dependencies.
| procaryote wrote:
| What kind of scalability issues have you had with git?
|
| Is it because of a monorepo?
| lifeformed wrote:
| My issue with git is handling non-text files, which is a common
| issue with game development. git-lfs is okay but it has some
| tricky quirks, and you end up with lots of bloat, and you can't
| merge. I don't really have an answer to how to improve it, but it
| would be nice if there was some innovation in that area too.
| miloignis wrote:
| I really think something like Xet is a better idea to augment
| Git than LFS, though it seems to pretty much only be used by
| HuggingFace for ML model storage, and I think their git plugin
| was deprecated? Too bad if it ends up only serving the
| HuggingFace niche.
| rectang wrote:
| Has there ever been a consideration for the git file format to
| allow storage of binary blobs uncompressed?
|
| When I was screwing around with the Git file format, tricks I
| would use to save space like hard-linking or memory-mapping
| couldn't work, because data is always stored compressed after a
| header.
|
| A general copy-on-write approach to save checkout space is
| presumably impossible, but I wonder what other people have
| traveled down similar paths have concluded.
| zahlman wrote:
| What strategies would you like to use to diff the binaries? Or
| else how are you going to avoid bloat?
|
| Is it actually okay to try to merge changes to binaries? If two
| people modify, say, different regions of an image file (even in
| PNG or another lossless compression format), the sum of the
| visual changes isn't necessarily equal to the sum of the byte-
| level changes.
| jayd16 wrote:
| Totally agree. After trying to flesh out Unreal's git plugin,
| it really shows how far from ideal git really is.
|
| Partial checkouts are awkward at best, LFS locks are somehow
| still buggy and the CLI doesn't support batched updates.
| Checking the status of a remote branch vs your local (to
| prevent conflicts) is at best a naive polling.
|
| Better rebase would be a nice to have but there's still so much
| left to improve for trunk based dev.
| gregschoeninger wrote:
| We're working on this project to help with the non-text file
| and large file problem: https://github.com/Oxen-AI/Oxen
|
| Started with the machine learning use case for datasets and
| model weights but seeing a lot of traction in gaming as well.
|
| Always open for feedback and ideas to improve if you want to
| take it for a spin!
| samuelstros wrote:
| Improving on "git not handling non-text files" is a semantic
| understanding aka parse step in between the file write.
|
| Take a docx, write the file, parse it into entities e.g.
| paragraph, table, etc. and track changes on those entities
| instead of the binary blob. You can apply the same logic to
| files used in game development.
|
| The hard part is making this fast enough. But I am working on
| this with lix [0].
|
| [0] https://github.com/opral/lix
| gnarlouse wrote:
| I think something like this needs to be born out of analysis of
| gradations of scales of teams using version control systems.
|
| - What kind of problems do 1 person, 10 person, 100 person, 1k
| (etc) teams really run into with managing merge conflicts?
|
| - What do teams of 1, 10, 100, 1k, etc care the most about?
|
| - How does the modern "agent explosion" potentially affect this?
|
| For example, my experience working in the 1-100 regime tells me
| that, for the most part, _the kind of merge conflict_ being
| presented here is resolved by assigning subtrees of code to
| specific teams. For the large part, merge conflicts don 't
| happen, because teams coordinate (in sprints) to make orthogonal
| changes, and long-running stale branches are discouraged.
|
| However, if we start to mix in agents, a 100 person team could
| quickly jump into a 1000 person team, esp if each person is using
| subagents making micro commits.
|
| It's an interesting idea definitely, but without real-world data,
| it kind of feels like this is just delivering a solution without
| a clear problem to assign it to. Like, yes merge-conflicts are a
| bummer, but they happen infrequently enough that it doesn't break
| your heart.
| CuriouslyC wrote:
| Team scale doesn't tend to impact this that much, since as
| teams grow they naturally specialize in parts of the codebase.
| Shared libs can be hotspots, I've heard horror stories at large
| orgs about this sort of thing, though usually those shared libs
| have strong gatekeeping that makes the problem more one of
| functionality living where it shouldn't to avoid gatekeeping
| than a shared lib blowing up due to bad change set merges.
| tasuki wrote:
| > What kind of problems do 1 person, 10 person, 100 person, 1k
| (etc) teams really run into with managing merge conflicts?
|
| > What do teams of 1, 10, 100, 1k, etc care the most about?
|
| Oh god no! That would be about the worst way to do it.
|
| Just make it conceptually sound.
| gnarlouse wrote:
| Probably, but just introducing CRDTs also feels like the
| wrong way to approach the problem! :)
| jillesvangurp wrote:
| > How does the modern "agent explosion" potentially affect
| this?
|
| This changes everything. Agents don't really care what
| versioning software is used. They can probably figure out
| whatever you are using. But they'll likely assume it's
| something standard (i.e. Git) so the easiest is to not get too
| adventurous. Also, the reasons to use something else mostly
| boil down to user friendliness and new merge strategies.
| However, lately I just tell codex to pull and deal with merge
| conflicts. It's not something I have to do manually anymore.
| That removes a key reason for me to be experimenting with
| alternative version control systems. It's not that big of a
| problem anymore.
|
| Git was actually designed for massive teams (the Linux kernel)
| but you have to be a bit disciplined using it in a way that
| many users in smaller teams just aren't. With agentic coding
| tools, you can just codify what you want to happen in
| guardrails and skills. Including how to deal with version
| control and what process to follow.
|
| Where more advanced merge strategies could be helpful is the
| type of large scale refactoring that are now much easier with
| agentic coding tools. But doing that in repositories with lots
| of developers working on other changes is not something that
| should happen very often. And certainly not without a lot of
| planning and coordination probably.
| jFriedensreich wrote:
| starts with "based on the fundamentally sound approach of using
| CRDTs for version control". How on earth is crdt a sound base for
| a version control system? This makes no sense fundamentally, you
| need to reach a consistent state that is what you intended not
| what some crdt decided and jj shows you can do that also without
| blocking on merges but with first level conflicts that need to be
| resolved. ai and language aware merge drivers are helping so much
| here i really wonder if the world these "replace version control"
| projects were made for still exists at all.
| miloignis wrote:
| The rest of the article shows exactly how a CRDT is a sound
| base for a version control system, with "conflicts" and all.
| skydhash wrote:
| But the presentation does not show how it resolves conflicts.
| For the first example, Git has the 3 way-merge that shows the
| same kind of info. And a conflict is not only to show that
| two people have worked on a file. More often than not, it
| highlight a semantic changes that happened differently in two
| instances and it's a nice signal to pay attention to this
| area. But a lot of people takes merge conflicts as some kind
| of nuisance that prevents them from doing their job (more
| often due to the opinion that their version is the only good
| one).
| jFriedensreich wrote:
| where?
| nozzlegear wrote:
| > _ai and language aware merge drivers are helping so much here
| i really wonder if the world these "replace version control"
| projects were made for still exists at all._
|
| I really wonder what kinds of magical AI you're using, because
| in my experience, Claude Code chokes and chokes hard on complex
| rebases/merge conflicts to the point that I couldn't trust it
| anymore.
| jauntywundrkind wrote:
| In case the name doesn't jump out at you, this is Bram Cohen,
| inventory of Bittorrent. And Chia proof-of-storage (probably
| better descriptions available) cryptocurrency.
| https://en.wikipedia.org/wiki/Bram_Cohen
|
| It's not the same as capturing it, but I would also note that
| there are a wide _wide_ variety of ways to get 3-way merges / 3
| way diffs from git too. One semi-recent submission (2022
| discussing a 2017) discussed diff3 and has some excellent
| comments (https://news.ycombinator.com/item?id=31075608),
| including a _fantastic_ incredibly wide ranging round up of merge
| tools (https://www.eseth.org/2020/mergetools.html).
|
| However/alas git 2.35's (2022) _fabulous_ zdiff3 doesn 't seems
| to have any big discussions. Other links welcome but perhaps
| https://neg4n.dev/blog/understanding-zealous-diff3-style-git...?
| It works excellently for me; enthusiastically recommended!
| ulrikrasmussen wrote:
| The thing about how merges are presented seems orthogonal to how
| to represent history. I also hate the default in git, but that is
| why I just use p4merge as a merge tool and get a proper 4-pane
| merge tool (left, right, common base, merged result) which shows
| everything needed to figure out why there is a conflict and how
| to resolve it. I don't understand why you need to switch out the
| VCS to fix that issue.
| crote wrote:
| Seconding the use of p4merge for easy-to-use three-pane
| merging. Just like most other issues with Git, if your merges
| are painful it's probably due to terrible native UX design -
| not due to anything _conceptually_ wrong with Git.
| TacticalCoder wrote:
| Thirding it except I do it from Emacs. Three side-by-side
| pane with left / common ancestor / right and then below the
| merge result. By default it's not like that but then it's
| Emacs so anything is doable. I hacked some elisp code a great
| many years ago and I've been using it ever since.
|
| No matter the tool, merges should _always_ be presented like
| that. It 's the only presentation that makes sense.
| MarsIronPI wrote:
| What tool do you use? Does Magit support it natively?
| skydhash wrote:
| I think you need to enable 3 way merge by default in
| git's configuration, and both smerge (minor mode for
| solving conflicts) and ediff (major mode that encompass
| diff and patch) will pick it up. In the case of the
| latter you will have 4 panes, one for version A, another
| for version B, a third for the result C, and the last is
| the common ancestor of A and B.
|
| Addendum: I've since long disabled it. A and B changes
| are enough for me, especially as I rebase instead of
| merging.
| jwr wrote:
| Isn't that what ediff does?
| roryokane wrote:
| Did you know that VS Code added support for the same four-
| pane view as p4merge years ago? I used p4merge as my merge
| tool for a long time, but I switched to VS Code when I
| discovered that, as VS Code's syntax highlighting and text
| editing features are much better than p4merge's.
|
| I also use the merge tool of JetBrains IDEs such as IntelliJ
| IDEA (https://www.jetbrains.com/help/idea/resolve-
| conflicts.html#r...) when working in those IDEs. It uses a
| three-pane view, not a four-pane view, but there is a menu
| that allows you to easily open a comparison between any two
| of the four versions of the file in a new window, so I find
| it similarly efficient.
| stbtrax wrote:
| how do you set that up? the default git tool version seems
| to be 3 pane
| ramchip wrote:
| Looks like it's "Show Base" under the top-level "..."
| menu when working on a merge conflict
|
| https://github.com/microsoft/vscode/issues/155277#issueco
| mme...
| roryokane wrote:
| Even if you don't use p4merge, you can set Git's
| merge.conflictStyle config to "diff3" or "zdiff3" (https://git-
| scm.com/docs/git-config#Documentation/git-config...). If you do
| that, Git's conflict markers show the base version as well:
| <<<<<<< left ||||||| base def calculate(x):
| a = x * 2 b = a + 1 return b =======
| def calculate(x): a = x * 2
| logger.debug(f"a={a}") b = a + 1 return b
| >>>>>>> right
|
| With this configuration, a developer reading the raw conflict
| markers could infer the same information provided by Manyana's
| conflict markers: that the right side added the logging line.
| ktm5j wrote:
| I'm on my phone right now so I'm not going to dig too hard
| for this, but you can also configure a "merge tool" (or
| something like that) so you can use Meld or Kompare to make
| the process easier. This has helped me in a pinch to work out
| some confusing merge conflicts.
| newsoftheday wrote:
| I started using Meld years ago and continue to find people
| who've never heard of it. It's a pretty good tool.
| psychoslave wrote:
| That still have an issue with the vocabulary. Things like
| "theirs/our" is still out of touch but it's already better
| than a loose spatial analogy on some representation of the
| DAG.
|
| Something like base, that is "common base", looks far more
| apt to my mind. In the same vein, endogenous/exogenous would
| be far more precise, or at least aligned with the concern at
| stake. Maybe "local/alien" might be a less pompous vocabulary
| to convey the same idea.
| kungito wrote:
| After 15 years i still cant remember which is which. I get
| annoyed every time. Maybe I should invest 15 minutes
| finally to remember properly
| IgorPartola wrote:
| Let's see if I get this wrong after 25 years of git:
|
| ours means what is in my local codebase.
|
| theirs means what is being merged into my local codebase.
|
| I find it best to avoid merge conflicts than to try to
| resolve them. Strategies that keep branches short lived
| and frequently merging main into them helps a lot.
| marcellus23 wrote:
| That's kind of the simplest case, though, where "theirs"
| and "ours" makes obvious sense.
|
| What if I'm rebasing a branch onto another? Is "ours" the
| branch being rebased, or the other one? Or if I'm
| applying a stash?
| IgorPartola wrote:
| > What if I'm rebasing a branch onto another?
|
| Just checkout the branch you are merging/rebasing into
| before doing it.
|
| > Or if I'm applying a stash?
|
| The stash is in that case effectively a remote branch you
| are merging into your local codebase. ours is your local,
| theirs is the stash.
| sheept wrote:
| "Ours" and "theirs" make sense in most cases (since
| "ours" refers to the HEAD you're merging into).
|
| Rebases are the sole exception (in typical use) because
| ours/theirs is reversed, since you're merging HEAD into
| the other branch. Personally, I prefer merge commits over
| rebases if possible; they make PRs harder for others to
| review by breaking the "see changes since last review"
| feature. Git generally works better without rebases and
| squash commits.
| sebmellen wrote:
| Wow, interesting to see such a diametrically opposed
| view. We've banned merge commits internally and our
| entire workflow is rebase driven. Generally, I find that
| rebases are far better at keeping Git history clean and
| clearly allowing you to see the diff between the base
| you're merging into and the changes you've made.
| em-bee wrote:
| a better (more confusing) example:
|
| i have a branch and i want to merge that branch into
| main.
|
| is ours the branch and main theirs? or is ours main, and
| the branch theirs?
| IgorPartola wrote:
| I always checkout the branch I am merging something into.
| I was vaguely aware I could have main checked out but
| merge foo into bar but have never once done that.
| Sharlin wrote:
| git checkout mybranch git rebase main
|
| A conflict happens. Now "ours" is _main_ and "theirs" is
| _mybranch_ , even though from your perspective you're
| still on mybranch. Git isn't, however.
| IgorPartola wrote:
| Ah that's fair. This is why I would do a `git merge main`
| instead of a rebase here.
| ljm wrote:
| I have met more than one person who would doggedly
| tolerate rebase, not even using rerere, instead of doing
| a simple 'git merge --no-ff' to one-shot it, not
| understanding that rebase touches every commit in the
| diff between main and not simply the latest change on
| HEAD.
|
| Not a problem if you are a purist on linear history.
| clktmr wrote:
| The thing is, you'll typically switch to master to merge
| your own branch. This makes your own branch 'theirs',
| which is where the confusion comes from.
| IgorPartola wrote:
| Not me. I typically merge main onto a feature branch
| where all the conflicts are resolved in a sane way. Then
| I checkout main and merge the feature branch into it with
| no conflicts.
|
| As a bonus I can then also merge the feature branch into
| main as a squash commit, ditching the history of a
| feature branch for one large commit that implements the
| feature. There is no point in having half implemented
| and/or buggy commits from the feature branch clogging up
| my main history. Nobody should ever need to revert main
| to that state and if I really really need to look at that
| particular code commit I can still find it in the feature
| branch history.
| throwaway7783 wrote:
| Yep. This is the only model that has worked well for me
| for more than a decade.
| KPGv2 wrote:
| > ours means what is in my local codebase
|
| Since it's always one person doing a merge, why isn't it
| "mine" instead of "ours"? There aren't five of us at my
| computer collaboratively merging in a PR. There is one
| person doing it.
|
| "Ours" makes it sound like some branch everyone who's
| working on the repo already has access to, not the active
| branch on _my_ machine.
| afiori wrote:
| iirc ours is always the commit the merge is starting
| from. the issue is that with a merge your current commit
| is the merging commit while with a rebase it is reversed.
|
| I suspect that this could be because the rebase command
| is implemented as a serie of merges/cherry-picks from the
| target branch.
| Sharlin wrote:
| git checkout mybranch git rebase main
|
| Now git takes main and starts cloning (cherry-picking, as
| you said) commits from mybranch on top of it. From git's
| viewpoint it's working on top of main, so if a conflict
| occurs, main is "ours" and mybranch is "theirs". But from
| your viewpoint you're still on mybranch, and indeed are
| left on mybranch when the rebase is complete. (It's a
| _different_ mybranch, of course; once the rebase is
| completed, git moves mybranch to point to the new
| (detached) HEAD.) Which makes "ours" and "theirs"
| exactly the opposite of what the user expects.
| XorNot wrote:
| Man do I hate this behavior because it would be really
| some by just using the branch names rather then "ours"
| and "theirs"
| orthoxerox wrote:
| I had to make an alias for rebasing, because I kept doing
| the opposite: git checkout master
| #check out the branch to apply commits to git
| rebase mybranch #Apply all commits from mybranch
|
| Now I just write rebase-current-branch
|
| and it does what I want: fetches origin/master and
| rebases my working branch on top of it.
|
| But "ours"/"theirs" still keeps tripping me up.
| cerved wrote:
| Tip, you may want to use origin/HEAD over origin/master
| awesome_dude wrote:
| This is one of my pain points, and one time I googled and
| got the real answer (which is why it's such a pain
| point).
|
| That answer is "It depends on the context"
|
| > The reason the "ours" and "theirs" notions get swapped
| around during rebase is that rebase works by doing a
| series of cherry-picks, into an anonymous branch
| (detached HEAD mode). The target branch is the anonymous
| branch, and the merge-from branch is your original (pre-
| rebase) branch: so "--ours" means the anonymous one
| rebase is building while "--theirs" means "our branch
| being rebased".[0]
|
| [0] https://stackoverflow.com/questions/25576415/what-is-
| the-pre...
| flutetornado wrote:
| I ended up creating a personal vim plugin for merges one
| night because of a frustrating merge experience and never
| being able to remember what is what. It presents just two
| diff panes at top to reduce the cognitive load and a
| navigation list in a third split below to switch between
| diffs or final buffer (local/remote, base/local,
| base/remote and final). The list has branch names next to
| local/remote so you always know what is what. And most of
| the time the local/remote diff is what I am interested in
| so that's what it shows first.
| mamcx wrote:
| Seriously!
|
| Why not show the names of the branch + short Id (and when
| is not direct name, at least "this is from NAME")
| sheept wrote:
| doesn't it? Next to the conflict markers, it'll display
| HEAD, the ref name, or the short commit hash.
| cerved wrote:
| It does
| KPGv2 wrote:
| I'll be honest, as a fairly skilled and experienced
| programmer who isn't a git expert, I know what HEAD
| means, but when I'm rebasing I really have no idea. It
| all seems to work out in the end because my collaborative
| work is simple and usually 2-3 people only, so I'm never
| rebasing against a ton of commits I lack context for
| (because 90% of them are _my_ commits since I 'm usually
| dealing with PRs to my open source projects rather than
| someone else's).
|
| HEAD is "the thing we're editing now" but that's not
| terribly useful when rebasing since you're repeatedly
| editing a fake history.
| wakawaka28 wrote:
| >Maybe "local/alien" might be a less pompous vocabulary to
| convey the same idea.
|
| That is more alien and just as contrived. If you merge
| branches that you made, they're both local and "ours". You
| just have to remember that "ours" is the branch you are on,
| and "theirs" is the other one. I have no idea what happens
| in an octopus merge but anyway, the option exists to show
| commit titles along with markers to help you keep it
| straight.
| IshKebab wrote:
| This is better but it still doesn't really help when the
| conflict is 1000 lines and one side changed one character and
| the other deleted the whole thing. That isn't theoretical -
| it happens quite regularly.
|
| What you really need is the ability to diff the base and
| "ours" or "theirs". I've found most different UIs can't do
| this. VSCode can, but it's difficult to get to.
|
| I haven't tried p4merge though - if it can do that I'm sold!
| cxr wrote:
| > I don't understand why you need to switch out the VCS to fix
| that issue.
|
| For some reason, when it comes to this subject, most people
| don't think about the problem as much as they think they've
| thought about it.
|
| I recently listened to an episode on a well-liked and respected
| podcast featuring a guest there to talk about version control
| systems--including their own new one they were there to promote
| --and what factors make their industry different from other
| subfields of software development, and why a new approach to
| version control was needed. They came across as thoughtful but
| exasperated with the status quo and brought up issues worthy of
| consideration while mostly sticking to high-level claims. But
| after something like a half hour or 45 minutes into the
| episode, as they were preparing to descend from the high level
| and get into the nitty gritty of their new VCS, they made an
| offhand comment contrasting its abilities with Git's,
| referencing Git's approach/design wrt how it "stores diffs"
| between revisions of a file. I was bowled over.
|
| For someone to be in that position and not have done even a
| cursory amount of research before embarking on a months (years)
| long project to design, implement, and then go on the talk
| circuit to present their VCS really highlighted that the
| familiar strain of NIH is still alive, even in the current era
| where it's become a norm for people to be downright resistant
| to writing a couple dozen lines of code themselves if there is
| no existing package to import from NPM/Cargo/PyPI/whatever that
| purports to solve the problem.
| hn_throwaway_99 wrote:
| > they made an offhand comment contrasting its abilities with
| Git's, referencing Git's approach/design wrt how it "stores
| diffs" between revisions of a file. I was bowled over.
|
| It seems like you have taken offense to the phrase "stores
| diffs", but I'm not sure why. I understand how commit
| snapshots and packfiles work, and the way delta compression
| works in packfiles might lead me to calling it "storing
| diffs" in a colloquial setting.
| galaxyLogic wrote:
| Can it merge ordinary directories (in addition to git-
| bracnehs)?
| killerstorm wrote:
| Yeah, also JetBrains IDEs like IntelliJ have very nice merge
| UI.
|
| Perhaps the value of doing it on SCM level is that it can
| remember what you did. Git has some not-so-nice edge cases.
| lemonwaterlime wrote:
| See vim-mergetool[1]. I use it to manage merge conflicts and it's
| quite intuitive. I've resolved conflicts that other people didn't
| even want to touch.
|
| [1]: https://github.com/samoshkin/vim-mergetool
| mentalgear wrote:
| Looks like vscode diff view .
| skybrian wrote:
| It sounds interesting but the main selling point doesn't really
| reasonate:
|
| If you haven't resolved conflicts then it probably doesn't
| compile and of course tests won't pass, so I don't see any point
| in publishing that change? Maybe the commit is useful as a
| temporary state locally, but that seems of limited use?
|
| Nowadays I'd ask a coding agent to figure out how to rebase a
| local branch to the latest published version before sending a
| pull request.
| dcre wrote:
| This is a reasonable reaction -- pretty sure I felt the same
| way when I heard about jujutsu's first-class conflicts[0] --
| but it turns out to be really useful not to be stuck inside an
| aberrant state while conflicts are in the process of being
| resolved.
|
| [0]: https://docs.jj-vcs.dev/latest/conflicts/
| dzaima wrote:
| In git if you, say, do some `git rebase -i`, edit some commit,
| continue the rebase, and hit a conflict, and realize you edited
| something wrong that caused the conflict, your only option is
| aborting the entire rebase and starting over and rebuilding all
| changes you did.
|
| In jj, you just have a descending conflict, and if you edit the
| past to no longer conflict the conflict disappears; kinda as if
| you were always in interactive rebase but at all points have
| the knowledge of what future would look like if you `git rebase
| --continue`d.
|
| Also really nice for reordering commits which can result in
| conflicts, but leaves descendants non-conflicting, allowing
| delaying resolving the conflicts after doing other stuff, or
| continuing doing some reordering instead of always starting
| from scratch as with `git rebase -i`.
| WCSTombs wrote:
| For the conflicts, note that in Git you can do
| git config --global merge.conflictstyle diff3
|
| to get something like what is shown in the article.
| mentalgear wrote:
| > [CRDT] This means merges don't need to find a common ancestor
| or traverse the DAG. Two states go in, one state comes out, and
| it's always correct.
|
| Well, isn't that what the CRDT does in its own data structure ?
|
| Also keep in mind that syntactic correctness doesn't mean
| functional correctness.
| Retr0id wrote:
| Yes.
|
| There are many ways to instantiate a CRDT, and a trivial one
| would be "last write wins" over the whole source tree state.
| LWW is obviously not what you'd want for source version
| control. It is "correct" per its own definition, but it is not
| useful.
|
| Anyone saying "CRDTs solve this" without elaborating on the
| specifics of their CRDT is not saying very much at all.
| mweidner wrote:
| You can think of the semantics (i.e., specification) of any
| CRDT as a function that inputs the operation history DAG and
| outputs the resulting user-facing state. However, algorithms
| and implementations usually have a more programmatic
| description, like "here is a function `(internal state, new
| operation) -> new internal state`", both for efficiency (update
| speed; storing less info than the full history) and because
| DAGs are hard to reason about. But you do see the function-of-
| history approach in the paper "Pure Operation-Based Replicated
| Data Types" [1].
|
| [1] https://arxiv.org/abs/1710.04469
| alunchbox wrote:
| Jujutsu honestly is the future IMO, it already does what you have
| outlined but solved in a different way with merges, it'll let you
| merge but outline you have conflicts that need to be resolved for
| instance.
|
| It's been amazing watching it grow over the last few years.
| aduwah wrote:
| The only reason I have not defaulted to jj already is the
| inability to be messy with it. Easy to make mistakes without
| "git add"
| dzaima wrote:
| But you do have the op log, giving you a full copy of the log
| (incl. the contents of the workspace) at every operation, so
| you can get out of such mistakes with some finagling.
|
| You can choose to have a workflow where you're never directly
| editing any commit to "gain back autonomy" of the working
| copy; and if you really want to, with some scripting, you can
| even emulate a staging area with a specially-formatted commit
| below the working copy commit.
| llyama wrote:
| You can be messy. The lack of an explicit staging area
| doesn't restrict that. `jj commit` gives the same mental
| model for "I want to commit 2 files from the 5 I've changed".
| phtrivier wrote:
| A suggestion : is there any info to provide in diffs that is
| faster to parse than "left" and "right" ? Can the system have
| enough data to print "bob@foo.bar changed this" ?
| lasgawe wrote:
| This is a really interesting and well thought out idea,
| especially the way it turns conflicts into something informative
| instead of blocking. The improved conflict display alone makes it
| much easier to understand what actually happened. I think using
| CRDTs to guarantee merges always succeed while still keeping
| useful history feels like a strong direction for version control.
| Looks like a solid concept!
| a-dub wrote:
| doesn't the side by side view in github diff solve this?
|
| conflict free merging sounds cool, but doesn't that just mean
| that that a human review step is replaced by "changes become
| intervals rather than collections of lines" and "last set of
| intervals always wins"? seems like it makes sense when the
| conflicts are resolved instantaneously during live editing but
| does it still make sense with one shot code merges over long
| intervals of time? today's systems are "get the patch right" and
| then "get the merge right"... can automatic intervalization be
| trusted?
|
| edit: actually really interesting if you think about it. crdts
| have been proven with character at a time edits and use of the
| mouse select tool.... these are inherently intervalized (select)
| or easy (character at a time). how does it work for larger
| patches can have loads of small edits?
| socalgal2 wrote:
| > [CRDT] This means merges don't need to find a common ancestor
| or traverse the DAG. Two states go in, one state comes out, and
| it's always correct.
|
| Funny, there was just a post a couple of days ago how this is
| false.
|
| https://news.ycombinator.com/item?id=47359712
| monster_truck wrote:
| Not this again
| codemog wrote:
| Nobody should have these types of problems in the age of AI
| agents. This kind of clean up and grunt work is perfect for AI
| agents. We don't need new abstractions.
| twsted wrote:
| Version control systems are more important than ever with AI.
| BlueHotDog2 wrote:
| This is cool and i keep thinking about CRDTs as a baseline for
| version control, but CRDTs has some major issues, mainly the fact
| that most of them are strict and "magic" in the way they actually
| converge(like the joke: CRDTs always converge, but to what). i
| didn't read if he's using some special CRDT that might solve for
| that, but i think that for agentic work especially this is very
| interesting
| bob1029 wrote:
| I think there are still strong advantages to the centralized
| locking style of collaboration. The challenge is that it seems to
| work best in a setting where everyone is in the same physical
| location while they are working. You can break a lock in 30
| seconds with your voice. Locking across time zones and date lines
| is a nonstarter by comparison.
| fn-mote wrote:
| It seems like in a reasonable sized org you should not be
| merging so often that "centralized locking ... across time
| zones" should be an issue.
|
| Are people really merging that often? What is being merged? Doc
| fixes?
| sibeliuss wrote:
| Why must everyone preprocess their blog posts with ChatGPT? It is
| such a disservice to ones ideas.
| newsoftheday wrote:
| OK, I'll stick with git.
| nkmnz wrote:
| I don't quite understand how CRDTs should help with merges. The
| difficult thing about merges is not that two changes touch the
| same part of the code; the difficult thing is that two changes
| can touch different parts of the code and still break each other
| - right?
| AceJohnny2 wrote:
| Eh. It's a matter of visible pain vs invisible pain.
|
| Developers are quite familiar with Merge Conflicts and the
| confusing UI that git (and SVN before it, in my experience)
| gives you about them. The "ours vs theirs" nomenclature which
| doesn't help, etc. This is something that VCSs can improve on,
| QED this post.
|
| Vs the scenario you're describing (what I call Logical
| Conflicts), where two changes touching different parts of the
| code (so it doesn't emerge as a Merge Conflict) but still
| breaking each other. Like one change adding a function call in
| one file but another change changing the API in a different
| file.
|
| These are painful in a different way, and not something that a
| simple text-based version control (which is all of the big
| ones) can even see.
|
| Indeed, CRDTs do not help with Logical Conflicts.
| gavinhoward wrote:
| Bram Cohen is awesome, but this feels a little bare. I've put
| much more thought into version control ([1]), including the use
| of CRDTs (search for "# History Model" and read through the
| "Implementing CRDTs" section).
|
| [1]: https://gavinhoward.com/uploads/designs/yore.md
| AceJohnny2 wrote:
| That's worth making a separate post! (and I recommend rendering
| it to HTML)
|
| But "bare" is part of the value of Cohen's post, I think. When
| you want to publicize a paradigm shift, it helps to make it in
| small, digestible chunks.
| 63stack wrote:
| Is this the Bram Cohen who made bittorrent? There is
| surprisingly little information on this page.
| vessenes wrote:
| Yes
| MattCruikshank wrote:
| For anyone who thinks diff / merge should be better - try Beyond
| Compare from Scooter Software.
| steveharing1 wrote:
| Git is my first priority until or unless i see anything more
| robust than this one.
| barrkel wrote:
| I don't really get the upside of focus on CRDTs.
|
| The semantic problem with conflicts exists either way. You get a
| consistent outcome and a slightly better description of the
| conflict, but in a way that possibly interleaves changes, which I
| don't think is an improvement at all.
|
| I am completely rebase-pilled. I believe merge commits should be
| avoided at all costs, every commit should be a fast forward
| commit, and a unit of work that can be rolled back in isolation.
| And also all commits should be small. Gitflow is an anti-pattern
| and should be avoided. Long-running branches are for patch
| releases, not for feature development.
|
| I don't think this is the future of VCS.
|
| Jujutsu (and Gerrit) solves a real git problem - multiple
| revisions of a change. That's one that creates pain in git when
| you have a chain of commits you need to rebase based on feedback.
| gzread wrote:
| People see that CRDTs have no conflicts and proclaim them as
| the solution to all problems, not seeing that some problems
| inherently have conflicts and either can't be represented by
| CRDTs at all, or that the use of CRDTs resolves conflicts in a
| way that's worse than if you actually thought about conflict
| resolution. E.g. that multiplayer text editor that interleaved
| characters from simultaneous edits.
| IgorPartola wrote:
| I used to use rebase much more than merge but have grown to be
| more nuanced over the years:
|
| Merge commits from main into a feature branch are totally fine
| and easier to do than rebasing. After your feature branch is
| complete you can do one final main-to-feature-branch merge and
| then merge the feature branch into main with a squash commit.
|
| When updating any branch from remote, I always do a pull rebase
| to avoid merge commits from a simple pull. This works well
| 99.99% of the time since what I have changed vs what the remote
| has changed is obvious to me.
|
| When I work on a project with a dev branch I treat feature
| branches as coming off dev instead of main. In this case I
| merge dev into feature branches, then merge feature branches
| into dev via a squash commit, and then merge main into dev and
| dev into main as the final step. This way I have a few merge
| commits on dev and main but only when there is something like
| an emergency fix that happens on main.
|
| The problem with always using a rebase is that you have to
| reconcile conflicts at every commit along the way instead of
| just the final result. That can be a lot more work for commits
| that will never actually be used to run the code and can in
| fact mess up your history. Think of it like this:
|
| 1. You create branch foo off main.
|
| 2. You make an emergency commit to main called X.
|
| 3. You create commits A, B, and C on foo to do your feature
| work. The feature is now complete.
|
| 4. You rebase foo off main and have to resolve the conflict
| introduced by X happening before A. Let's say it conflicts with
| all three of your commits (A, B, and C).
|
| 5. You can now merge foo into main with it being a fast forward
| commit.
|
| Notice that at no point will you want to run the codebase such
| that it has commits XA or XAB. You only want to run it as XABC.
| In fact you won't even test if your code works in the state XA
| or XAB so there is little point in having those checkpoints.
| You care about three states: main before any of this happened
| since it was deployed like that, main + X since it was deployed
| like that, and main with XABC since you added a feature. git
| blame is really the only time you will ever possibly look at
| commits A and B individually and even then the utility of it is
| so limited it isn't worth it.
|
| The reality is that if you only want fast forward commits,
| chances are you are doing very little to go back and extract
| code out of old versions a of the codebase. You can tell this
| by asking yourself: "if I deleted all my git history from main
| and have just the current state + feature branches off it, will
| anything bad happen to my production system?" If not, you are
| not really doing most of what git can do (which is a good
| thing).
| barrkel wrote:
| I am now wholly bought into the idea of having a feature
| branch with (A->B->C) commits is an anti-pattern.
|
| Instead, if the feature doesn't work without the full chain
| of A+B+C, either the code introduced in A+B is orphaned
| except by tests and C joins it in; or (and preferably for a
| feature of any significance), A introduces a feature flag
| which disables it, and a subsequent commit D removes the
| feature flag, after it is turned on at a time separate to
| merge and deploy.
| IgorPartola wrote:
| I treat each feature branch as my own personal playground.
| There should be zero reason for anyone to ever look at it.
| Sometimes they aren't even pushed upstream. Otherwise, just
| work on main with linear history and feature flags and
| avoid all this complexity that way.
|
| Just like you don't expect someone else's local codebase to
| always be in a fully working state since they are actively
| working on it, why do you expect their working branch to be
| in a working state?
| DSMan195276 wrote:
| I think you're somewhat missing the point - if the code from
| A and B only works if joined with C, then you should squash
| them all into one commit so that they can't be separated. If
| you do that then the problem you're describing goes away
| since you'll only be rebasing a single commit anyway.
|
| Whether this is valuable is up to you, but IMO I'd say it's
| better practice than not. People do dumb things with the
| history and it's harder to do dumb things if the commits are
| self-contained. Additionally if a feature branch includes
| multiple commits + merges I'd much rather they squash that
| into a single commit (or a couple logical commits) instead of
| keeping what's likely a mess of a history anyway.
| IgorPartola wrote:
| That is literally what I advocate you do for the main
| branch. A feature branch is allowed to have WIP commits
| that make sense for the developer working on the branch
| just like uncommitted code might not be self contained
| because it is WIP. Once the feature is complete, squash it
| into one commit and merge it into main. There is very
| little value to those WIP commits (rare case being when you
| implement algorithm X but then change to Y and later want
| to experiment with X again).
| bathwaterpizza wrote:
| One downside of squash merging is that when you need to
| split your work across branches, so that they're
| different PRs, but one depends on the other, then you
| have to do a rebase after every single one which had
| dependencies is merged.
| hackrmn wrote:
| When you say "unit of work", unit of _which_ work are you
| referring to? The problem with rebasing is that it takes one
| set of snapshots and replays them on top of another set, so you
| end up with two "equivalent" units of work. In fact they're
| _the same_ indeed -- the tree objects are shared, except that
| if by "work" you mean changes, Git is going to tell you two
| different histories, obviously.
|
| This is in contrast with [Pijul](https://pijul.org) where
| changes are patches and are commutative -- you can apply an
| entire set and the result is supposed to be equivalent
| regardless of the order the patches are applied in. Now _that_
| is unit of work" I understand can be applied and undone in
| "isolation".
|
| Everything else is messy, in my eyes, but perhaps it's orderly
| to other people. I mean it would be nice if a software system
| defined with code could be expressed with a set of independent
| patches where each patch is "atomic" and a feature or a fix
| etc, to the degree it is possible. With Git, that's a near-
| impossibility _in the graph_ -- sure you can cherry-pick or
| rebase a set of commits that belong to a feature (normally on a
| feature branch), but _why_?
| barrkel wrote:
| By "unit of work", I mean the atomic delta which can, on its
| own, become part of the deployable state of the software. The
| thing which has a Change-Id in Gerrit.
|
| The delta is the important thing. Git is deficient in this
| respect; it doesn't model a delta. Git hashes identify the
| tip of a tree.
|
| When you rebase, you ought to be rebasing the change, the
| unit of work, a thing with an identity separate and
| independent of where it is based from.
|
| And this is something that the jujutsu / Gerrit model fixes.
| josephg wrote:
| CRDTs should be able to give you better merge and rebase
| behaviour. They essentially make rebase and merge commits the
| same thing - just different views on a commit, and potentially
| different ways to present the conflict. CRDTs also behave
| better when commits get merged multiple times in complex graphs
| - you don't run into the problem of commits conflicting with
| themselves.
|
| You should also be able to roll back a single commit or chain
| of commits in a crdt pretty easily. It's the same as the undo
| problem in collaborative editors - you just apply the inverse
| of the operation right after the change. And this would work
| with conflicts - say commits X and Y+Z conflict, and you're in
| a conflicting state, you could just roll back commit Y which is
| the problem, while keeping X and Z. And at no point do you need
| to resolve the conflict first.
|
| All this requires good tooling. But in general, CRDTs can store
| a superset of the data stored by git. And as a result, they can
| do all the same things and some new tricks.
| sroussey wrote:
| In theory, maybe. In practice... last write wins (LWW) is a
| CFDT operator, so replace every mention of CRDT with LWW and
| issues will more obvious.
|
| Really though, the problem with merges is not conflicts, it's
| when the merged code is wrong but was correct on both sides
| before the merge. At least a conflict draws your attention.
|
| When I had several large (smart but young) teams merging left
| and right this would come up and they never checked merged
| code.
|
| Multiply by x100 for AI slop these days. And I see people
| merge away when the AI altered tests to suit the broken code.
| josephg wrote:
| > In practice... last write wins (LWW) is a CFDT operator,
| so replace every mention of CRDT with LWW and issues will
| more obvious.
|
| Yeah. A lot of people are also confused by the twin
| meanings of the word "conflict". The "C" in CRDT stands for
| "Conflict (free)", but that really means "failure free".
| Ie, given any two concurrent operations, there is a well
| defined "merge" of the two operations. The merge operation
| can't fail.
|
| The second meaning is "conflict" as in "git commit
| conflict", where a merge gets marked as requiring human
| intervention.
|
| Once you define the terms correctly, its possible to write
| a CRDT-with-commit-conflicts. Just define a "conflict
| marker" which are sometimes emitted when merging. Then
| merging can be defined to always succeed, sometimes
| emitting conflict markers along the way.
|
| > Really though, the problem with merges is not conflicts,
| it's when the merged code is wrong but was correct on both
| sides before the merge.
|
| CRDTs have strictly more information about whats going on
| than Git does. At worst, we should be able to remake git on
| top of CRDTs. At best, we can improve the conflict
| semantics.
| catlifeonmars wrote:
| Can we stop using line-oriented diffs in favor of AST-oriented
| diffs?
|
| Is it just lack of tooling, or is there something fundamentally
| better about line-oriented diffs that I'm missing? For the
| purpose of this question I'm considering line-oriented as a
| special case of AST-oriented where the AST is a list of lines
| (anticipating the response of how not all changes are
| syntactically meaningful or correct).
| Aperocky wrote:
| Outside of the merit of the idea itself, I thought I was going to
| look at a repository at least as complete as Linus when he
| released git after 3 weeks, especially with the tooling we had
| today.
|
| Slightly disappointed to see that it is a 470 line python file
| being touted as "future of version control". Plenty of things are
| good enough in 470 lines of python, even a merge conflict
| resolver on top of git - but it looks like it didn't want
| anything to do with git.
|
| Prototyping is almost free these days, so not sure why we only
| have the barest of POC here.
| ithkuil wrote:
| It clearly says in the article that this is just a demo
| merlindru wrote:
| I recently found a project called sem[1] that does git diffs but
| is aware of the language itself, giving feedback like "function
| validateToken added", "variable xyzzy removed", ...
|
| i think that's where version control is going. especially useful
| with agents and CI
|
| [1] https://ataraxy-labs.github.io/sem/
| lowbloodsugar wrote:
| Araxis merge. Four views. Theirs, ours, base and "what you did so
| far in this damned merge hell".
| echrisinger wrote:
| Has anyone considered a VCS that integrates more vertically with
| the source code through ASTs?
|
| IE if I change something in my data model, that change & context
| could be surfaced with agentic tooling.
| aggregator-ios wrote:
| What CRDT's solve is conflicts at the system level. Not at the
| semantic level. 2 or more engineers setting a var to a different
| value cannot be handled by a CRDT.
|
| Engineer A intended value = 1
|
| Engineer B intended value = 2
|
| CRDT picks 2
|
| The outcome could be semantically wrong. It doesn't reflect the
| intent.
|
| I think the primary issue with git and every other version
| control is the terrible names for everything. pull, push, merge,
| fast forward, stash, squash, rebase, theirs, ours, origin,
| upstream and that's just a subset. And the GUI's. They're all
| very confusing even to engineers who have been doing this for a
| decade. On top of this, conflict resolution is confusing because
| you don't have any prior warnings.
|
| It would be incredibly useful if _before_ you were about to edit
| a file, the version control system would warn you that someone
| else has made changes to it already or are actively working on
| it. In large teams, this sort of automation would reduce
| conflicts, as long as humans agree to not touch the same file.
| This would also reduce the amount of quality regressions that
| result from bad conflict resolutions.
|
| Shameless self plug: I am trying to solve both issues with a
| simpler UI around git that automates some of this and it's free.
| https://www.satishmaha.com/BetterGit
| jnsie wrote:
| > It would be incredibly useful if before you were about to
| edit a file, the version control system would warn you that
| someone else has made changes to it already or are actively
| working on it. In large teams, this sort of automation would
| reduce conflicts, as long as humans agree to not touch the same
| file. This would also reduce the amount of quality regressions
| that result from bad conflict resolutions.
|
| Bringing me back to my VSS days (and I'd much rather you
| didn't)
| aggregator-ios wrote:
| I knew I should have put a trigger warning, because I was
| thinking of this as I was typing it. Sorry!
| mbfg wrote:
| well, the mismatch here is widened by the fact that almost
| everyone it seems uses git with a central, prominent, visible,
| remote repository. Where as git was developed with the a true
| distributed vision. Now sure that truely distributed thing only
| becomes final when it reaches some 'central' repo, but it's
| quite a big different than we all do.
| j1elo wrote:
| For that you need a _very centralized_ VCS, not a decentralized
| one. Perforce allows you to lock a file so everybody else
| cannot make edits to it. If they implemented more fine-grained
| locking within files, or added warnings to other users trying
| to check them out for edits, they 'd be just where you want a
| VCS to be.
|
| How, or better yet, why would Git warn you about a potential
| conflict beforehand, when the use case is that everyone has a
| local clone of the repo and might be driving it towards
| different directions? You are just supposed to pull commits
| from someone's local branch or push towards one, hence the
| wording. The fact that it makes sense to cooperate and work on
| the same direction, to avoid friction and pain, is just a
| natural accident that grows from the humans using it, but is
| not something ingrained in the design of the tool.
|
| We're collectively just using Git for the silliest and simplest
| subset of its possibilities -a VCS with a central source of
| truth-, while bearing the burden of complexity that comes with
| a tool designed for distributed workloads.
| sensanaty wrote:
| I haven't used them, but doesn't SVN or Mercurial do something
| like this? It blocks people from working on a file by locking
| them, the problem is that in large teams there are legitimate
| reasons for multiple people to be working on the same files,
| especially something like a large i18n file or whatever.
| josephg wrote:
| > CRDT picks 2
|
| They don't have to.
|
| The crdt library knows that value is in conflict, and it
| decides what to do about it. Most CRDTs are built for realtime
| collab editing, where picking an answer is an acceptable
| choice. But the crdt can instead add conflict marks and make
| the user decide.
|
| Conflicts are harder for a crdt library to deal with - because
| you need to keep merging and growing a conflict range. And do
| that in a way that converges no matter the order of operations
| you visit. But it's a very tractable problem - someone's just
| gotta figure out the semantics of conflicts in a consistent way
| and code it up. And put a decent UI on top.
| ballsweat wrote:
| Cool timing.
|
| I recently built Artifact:
| https://www.paganartifact.com/benny/artifact
|
| Mirror: https://github.com/bennyschmidt/artifact
|
| In case anyone was curious what a full rewrite of git would look
| like in Node!
|
| The main difference is that on the server I only store deltas,
| not files, and the repo is "built".
|
| But yeah full alternative to git with familiar commands, and a
| hub to go with it.
| injidup wrote:
| I'm confused about what this solves. They give the example of
| someone editing a function and someone deleting the same function
| and claim that the merge never fails and then go on to
| demonstrate that indeed rightly the merges still fails. There are
| still merge markers in the sources. What is the improvement
| exactly?
| galkk wrote:
| Yeah, the author fails to present his case even in the intro
|
| > A CRDT merge always succeeds by definition, so there are no
| conflicts in the traditional sense -- 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. This project works that out.
|
| It has clear contradiction. Crdt always succeed by definition,
| no conflicts in traditional sense so (rephrasing) conflicting
| changes are marked as conflicted. Emm, like in any other source
| control?
|
| In fact, after rereading that intro while writing that answer I
| start suspect at least smell of an ai writing.
| fleebee wrote:
| The README of the repo offers a hint:
|
| > _The code in this project was written artisanally. This
| README was not._
| josephg wrote:
| The benefit of using a crdt for this is that you can get
| better merge semantics. Rebase and merge become the same
| thing. Commits can't somehow conflict with themselves. You
| can have the system handle 2 non conflicting changes on the
| same line of code if you want. You can keep the system in a
| conflict state and add more changes if you want to. Or undo
| just a single commit from a long time ago. And you can put
| non text data in an crdt and have all the same merge and
| branching functionality.
| ballsweat wrote:
| Everyone should vibe code a VCS from scratch in their fave
| language.
|
| It's an awesome weekend project, you can have fun visualizing
| commits in different ways (I'm experimenting with shaders), and
| importantly:
|
| This is the way forward. So much software is a wrapper around S3
| etc. now is your chance to make your own toolset.
|
| I imagine this appeals more to DIYer types (I use Pulsar IDE lol)
| braidedpubes wrote:
| Do I have it right that it's basically timestamp based, except
| not based on our clocks but one it manages itself?
|
| So as long as all updates have been sent to the server from all
| clients, it will know what "time" each character changed and be
| able to merge automatically.
|
| Is that it basically?
| shitfilleddonut wrote:
| It seems more like the past of version control
| EGreg wrote:
| I remember I met Bram Cohen (of Bittorent fame!) around 15 years
| ago. Around that time is when I had started building web-based
| distributed collaborative systems, starting with Qbix.com and
| then spun off a company to build blockchain-based smart contracts
| through Intercoin.org etc.
|
| Anyway, I wanted to suggest a radical idea based on my
| experience:
|
| Merges are the wrong primitive.
|
| What organizations (whethr centralized or distributed projects)
| might actually need is:
|
| 1) Graph Database - of Streams and Relations
|
| 2) Governance per Stream - eg ACLs
|
| A code base should be automatically turned into a graph database
| (functions calling other functions, accessing configs etc) so we
| know exactly what affects what.
|
| The concept of what is "too near" each other mentioned in the
| article is not necessarily what leads to conflicts. Conflicts
| actually happen due to conflicting graph topology and propagating
| changes.
|
| People should be able to clone some stream (with permission) and
| each stream (node in the graph) can be versioned.
|
| Forking should happen into workspaces. Workspaces can be
| GOVERNED. Publishing some version of a stream just means relating
| it to your stream. Some people might publish one version, others
| another.
|
| Rebasing is a first-class primitive, rather than a form of
| merging. A merge is an extremely privileged operation from a
| governance point of view, where some actor can just "push" (or
| "merge") thousands of commits. The more commits, the more chance
| of conflicts.
|
| The same problem occurs with CRDTs. I like CRDTs, but reconciling
| a big netsplit will result in merging strategies that create lots
| of unintended semantic side effects.
|
| Instead, what if each individual stream was guarded by policies,
| there was a rate limit of changes, and people / AIs rejected most
| proposals. But occasionally they allow it with M of N sign offs.
|
| Think of chatgpt chats that are used to modify evolving
| artifacts. People and bots working together. The artifacts are
| streams. And yes, this can even be done for codebases. It isnt
| about how "near" things are in a file. Rather it is about whether
| there is a conflict on a graph. When I modify a specific function
| or variable, the system knows all of its callers downstream. This
| is true for many other things besides coding too. We can also
| have AI workflows running 24/7 to try out experiments as a swarm
| in sandboxes, generate tests and commit the results that pass.
| But ultimately, each organization determines whether they want to
| rebase their stream relations to the next version of something or
| not.
|
| That is what I'm building now with https://safebots.ai
|
| PS: if anyone is interested in this kind of stuff, feel free to
| schedule a calendly meeting w me on that site. I just got started
| recently, but I'm dogfooding my own setup and using AI swarms
| which accelerates the work tremendously.
| theknarf wrote:
| You can't use CRDTs for version control, having conflicts is the
| whole point of version control. Sometimes two developers will
| make changes that fundamentally tries to change the code in two
| different ways, a merge conflict then leaves it up to the
| developer who is merging/rebasing to make a choice about the
| semantics of the program they want to keep. A CRDT would just
| produce garbage code, its fundamentally the wrong solution. If
| you want better developer UX for merge conflicts then there are
| both a bunch of tooling on top of Git, as well as other version
| control systems, that try to present it in a better way; but that
| has very little to do with the underlaying datastructure. The
| very fact that cherry-picking and reverting becomes difficult
| with this approach should show you that its the wrong approach!
| Those are really easy operations to do in Git.
| tbrownaw wrote:
| > _You can 't use CRDTs for version control_
|
| You misunderstand what is being proposed.
|
| Using CRDTs to calculate the results of a merge _does not
| require_ being allowed to commit the results of that
| calculation, and doesn 't even require that you be able to
| physically realize the results in the files in your working
| copy.
|
| .
|
| Consider for example if you want to track and merge scalar
| values. Maybe file names if you track renames, maybe file
| properties if you're not just using a text listing (ie
| .gitattributes) for that, maybe file content hash to decide
| whether to actually bother running a line-based merge.
|
| One approach is to use what Wikipedia says is called an OR-
| set[1], with the restriction that a commit can only have a
| single unique value; if it was previously in the set then it
| keeps all the same tags, if it wasn't then it gets a new tag.
|
| That restriction is where the necessity of conflict resolution
| comes in. It doesn't have to be part of the underlying
| algorithms, just the interface with the outside world.
|
| [1] https://en.wikipedia.org/wiki/Conflict-
| free_replicated_data_...
| Retr0id wrote:
| If semantics-layer conflicts still have to be detected
| somehow, and resolved by hand, what value is the underlying
| CRDT providing?
| astrostl wrote:
| Disagree. We all are -- or should be -- Linux kernel developers.
| What's more, we should align to a specific and singular VCS
| worldview informed by BitKeeper, which no longer exists, whether
| or not we used it. Therefore Git. Thank you for your attention to
| this matter!
| simultsop wrote:
| You sound more like a DOS dev instead of linux.
| Ferret7446 wrote:
| I'm pretty sure jujutsu already does this, and it's interopable
| with git
| nailer wrote:
| You're still treating code as text which it isn't (in the same
| way you wouldn't treat JSON as text) it's actually more like an
| AST.
|
| Jujutsi (jj) does that. And it's git compatible.
| dcre wrote:
| I love jj and think virtually every git user should switch to
| it, but I don't think it treats text as an AST. What do you
| mean?
| mweidner wrote:
| I'm surprised to see the emphasis on tracking lines of text,
| which ties in to the complexity of merge vs merge-the-other-way
| vs rebase. If we are committed to enhancing the change history,
| it seems wiser to go all in and store high-level, semantically-
| meaningful changes, like "move this code into an `if` block and
| add `else` block ...".
|
| Consider the first example in the readme, "Left deletes the
| entire function [calculate]. Right adds a logging line in the
| middle". If you store the left operation as "delete function
| calculate<unique identifier>" and the right operation as "add
| line ... to function calculate", then it's obvious how to get the
| intended result (calculate is completely deleted), regardless of
| how you order these operations.
|
| I personally think of version control's job not as collaborating
| on the actual files, but as collaborating on the canonical order
| of (high-level) operations on those files. This is what a branch
| is; merge/rebase/cherry-pick are ways of updating a branch's
| operation order, and you fix a conflict by adding new operations
| on top. (Though I argue rebase makes the most sense in this
| model: your end goal is to append to the main branch.)
|
| Once you have high-level operations, you can start adding high-
| level conflict markers like "this operation changed the docs for
| function foo; flag a conflict on any new calls to foo". Note that
| you will need to remember some info about operations' original
| context (not just their eventual order in the main branch) to
| surface these conflicts.
| philwelch wrote:
| Regardless of the merits of CRDT's, I'm just glad someone is
| finally trying to create a new version control system. Everyone
| loves to complain about Git but nobody's actually tried to move
| beyond it in two decades.
| KPGv2 wrote:
| I've tested out jj a bit, and doesn't it solve the issues
| presented at the link already? I don't work on a team where I
| need VC better than git, so I just stick with it for my own
| private use, but I did test jj out of curiosity, and I could've
| sworn this is basically the same pitch as switching to jj (but
| for the CRDT under the hood).
___________________________________________________________________
(page generated 2026-03-22 23:00 UTC)