" I make the green 'OK' button now red, and you make it blue"
I did not fully read the introduction yet, but in my mind, in a truly content-addressed system this is not a conflict:
you have the hash of a main() function which ultimately makes the button red, and the other guy has a main() function that makes the button blue. No conflict in the physical sense. Yes, philosophycally there is a conflict, which is resolved by you deciding which main() function you find more useful.
Developer A makes the button red, developer B changes the label from "OK" to "Accept", both inside the same function.
You can't just pick one or the other, you have to combine the changes.
Or two developers add two different fields to a type. Or ...
Sadly the docs only have placeholders for "Concurrent work and resolving edit conflicts" and "Pull requests and collaboration". Would be very interesting to read the developers thoughts on this.
The system seems to support patches, forking and merging.
Hmmm, interesting. It is getting a bit philosophical: you can view it as a conflict. But you can also view it as: hm we have these nice 2 main functions. Let's create a merged one. Kind of what was previously conflict resolution becomes part of regular programming... A slight perspective change.
Mostly they are just not relying on Git for conflict detection and resolution, but bringing it up into the Unison system itself, in order to deal with it more gracefully and intelligently.
There's nothing philosophical about it! There will exist a conflict as far as Git (or any other) version control system is concerned. That's all he saying!
My small understanding is that the "(or any other)" part is not correct in your statement, and that you are locked into thinking of version control as being git-like.
I think the implication is that version control is not git like, and that it's not that we both changed the text on line 17, rather we both made additions to the underlying structure.
Indeed, it's impossible for us to both edit the same file, because files are never modified. They are immutable, like a blockchain transaction.
I haven't quite understood how it works in practice, though, but definitely don't think "git".
But git works the same way. All files are immutable. All directory
trees are immutable. Basically all version control works that way at some level.
Storing data this way doesn't solve the problem of merging the two changes into a new single change. When you can't do that automatically, it's called a conflict.
The only mutable data in git is the list of hashes you've given names to. You can use git without branch names if you want, living in a world of pure immutable hashes. It doesn't do anything to help you get rid of merge conflicts.
That's like saying Git has no conflicts because a file can have different contents in different commits.
A conflict can only arise when trying to unify (aka merge) two different states. The difference is the representation, with Git (naively explained) mapping file paths to text blobs , and Unison mapping identifiers to AST nodes.
Then how do you know which main function to use? That information has to be stored somewhere. Or does the user provide the hash for the main function they wish to run?
It seems to me that if we both edit the same function then we end up with three functions in store (original + two modified). Now obviously I want the main function to call my new version function and you want the main function to call your version. So in the end there is a conflict somewhere - even in the git sense.
If that conflict always shows up in one single line saying what hash of the main function is the one to use - then it doesn’t seem like a huge improvement in terms of conflict handling. Conflicts bubble to the root hash. I assume they thought about this and have some sort of tooling for “traversing” conflicts from the root, choosing my function, your function, or allowing a merged function to be created.
If it works precisely as you say, then most likely the last committer wins, and that's bad.
The most hopeful case is that there is ONE place that isn't 'strictly append only', and that's, for lack of a better term, the 'main pointer'. Which hash is the actual state of the app as it should be compiled and deployed right now?
Then THAT conflict, together with tooling to diff 2 different hashes, should swiftly lead to figuring it out.
But then you're still kinda hosed; how do you merge 2 nodes? It sounds like you can't; you can only pick one. With git, you can do a text merge.
I get the beauty of AST based storage and reasoning, but, hey, trying to use git to merge 2 versions of a word doc together is also a disaster, so it sounds like it'd be the same here.
In that sense, unison is worse than your average (text based) language, not better.
That's not actually a downside though; it's different. If I demerited the language for this, that's akin to complaining about a delicious roasted bell pepper because it doesn't taste anything like an onion.
But I do find it a spot irksome that the otherwise well written tour is implying something here that doesn't appear to be true. Perhaps I'm merely misinterpreting it and reading more into that sentence than is implied.
> The most hopeful case is that there is ONE place that isn't 'strictly append only', and that's, for lack of a better term, the 'main pointer'. Which hash is the actual state of the app as it should be compiled and deployed right now?
I think that's how it works. The way I understand it from reading the tour, there is a separate store for the code and for the name mappings. Both main functions would merge without conflict into the code store, but the mappings would conflict.
This [1] seems to be the "head pointer", and I assume it would conflict when merging. In fact, would it not conflict on every merge even if unrelated code was changed, since it hashes on the value of the entire codebase?
Edit: After thinking some more, it wouldn't conflict on a file-level, since the file itself has a different name. But there would be now two files in the _head folder, and I assume the `ucm` tool would detect that and present the user with merging options?
Looks pretty practical to me? Here is a described situation:
Developer A made a change which adds feature X and makes a button blue.
Developer B made a change which adds feature Y and makes a button green.
Now the system does not allow both feature X and feature Y at the same time. Our options are either drop X, drop Y, or expend more effort/programming time to make a version which includes both X and Y.
The description above applies equally well to Unison code CAS system and regular program in git. Sure, one is at the file level, and other at the function level - but you still need same kind of tooling. You want to look at the changes and somehow produce a merged version.
> Yes, philosophically there is a conflict, which is resolved by you deciding which main() function you find more useful.
it's not just philosophical though. In git terms, when merging the two sets of changes, someone has to choose which of these functions goes onto the master branch. git calls this a "merge conflict".
No changes are ever made to an existing file, so there are never any merge conflicts. All changes result in a new object/file, named based on a content hash.
So in git terms, there are never any updates, only additions, and therefore never any merge conflicts.
Git doesn't store updates or additions. It only stores new objects/files, named based on a content hash.
When the git interface shows you 'updates', it's just looking at the contents of different commits and guessing how the files are related. It's not part of the git data model. You could apply the exact same processing to Unison commits.
So the systems work the same way. They make a new object/file based on parent objects. And when it can't create that new object/file automatically, that's called a "conflict".
I did not say or imply that there were. it's a new version, of course.
> so there are never any merge conflicts
Does not follow. There is only one HEAD (latest version) of foo.txt on the master branch. In a merge, it has two (or more) parent objects (1).
> All changes result in a new object/file ... therefore never any merge conflicts.
And yet somehow git calls the process of deciding what's in this new (version of the) file with two parents "resolving a merge conflict". Because the two parent changes can't be automatically reconciled. They are in conflict.
I did not fully read the introduction yet, but in my mind, in a truly content-addressed system this is not a conflict: you have the hash of a main() function which ultimately makes the button red, and the other guy has a main() function that makes the button blue. No conflict in the physical sense. Yes, philosophycally there is a conflict, which is resolved by you deciding which main() function you find more useful.