Originally started as an extension to Mercurial, but grew into its own SCM with a cross-platform virtual filesystem in C++ and a distributed server in Rust.
the number of projects that require the scaling factor of something like this is very small. git with lfs scales very well for most repositories. That said the actual flow of git is pretty raw. There are other pretty solid projects out there that support undo commands into git.
Stacked PRs are a blessing and a curse - still not convinced they are the correct way to build software as a team.
Eh, it’s pretty hard to make a case that you don’t want stacked diff sometimes.
Obviously life is simpler if all your work is sufficiently non-intersecting that you can send separate diffs/PRs and e.g. rebase them separately, but if you have Big Feature X and you still want small, single thesis diffs, where else do you turn?
Public projects, sure. But plenty of companies have very large codebases that would benefit from this. Even if Git can handle it it can get very slow with largish repos.
I’ve been using FB’s mercurial fork for years, wishing for all that time that I could have the joy of the fb-hg CLI while remaining compatible with github because that’s where 99+% of the code lives - from my brief experimentation, sapling appears to be that. I look forward to never using the git CLI again :D
Reading the documentation quickly, it looks like you can generate a standard pull request, it's just that the PR may not conform to expectations if there are multiple commits.
Of course, when it comes to github PRs, there are so many different "styles" of pull request, I'm not even sure which one should be considered "standard".
I actually just created my first PR from a sapling repo now - not sure why it’s not documented, but you can push your local development branch to a remote server, and in the case of github, you even get the “it looks like you’ve just pushed a local branch, would you like to turn this branch into a PR?” prompt, and it appears indistinguishable from a branch created with the Git CLI.
The message generated with the hyperlink for creating a PR is actually done server-side, you can customize any git server to do this, more or less. So yes, it should behave approximately the same assuming you can just do the basic push interactions.
Because it is based on Git, jj is not a CRDT, it seems to be a better merge algorithm (even though the details on their algorithms are scarce).
When I say "not a CRDT" I'm obviously talking about HEAD not being a CRDT, a Git repo is append-only, so the history of a Git repo actually is a CRDT (but that's not what the comment above meant).
Looking back when the last change happened: there was a subversion integration with git, but people actually switched to git for proper decentralised version control rather than use it. Then switched to GitHub to recentralise but that’s off topic.
Not everything that uses GitHub/BitBucket/GitLab etc. is (re)centralized. You can have an internal company hosting service and a public hosting solution, for example. I've also used the decentralized capabilities to synchronize between two computers.
There are many reasons why GitHub (or something like it) are popular, such as:
1) not having to host the infrastructure yourself (incl. hosting it on AWS/Azure/etc.)
2) discoverability -- being able to follow people/organizations creating projects you are interested in; being able to search for projects ~ having these on various websites makes it harder to discover them
3) additional functionality/capabilities like static web page hosting (great for things like personal projects), and CI/CD workflows
I remember git-svn being pretty commonly used back in the day (circa 2007). At that point a lot of open-source projects were still using svn and if you wanted to use git locally, git-svn or something similar was how you did it.
My first experience with git was using git-svn to work with my company's internal svn repository, which I did for a couple years before the company stopped using svn. There was no internal desire for decentralized version control (rather the opposite, in fact; they wanted centralized permission management and such).
Somehow that doesn't matter for FB. Yarn is not 10x better than NPM and it took off. React is.... ugh... React and it took over the whole dang industry.
If they deliver the virtual filesystem and server, this will be 10x better for large companies and large organized FOSS teams, who very often hit Git's pain points with relative ease, and the compatibility with Git from the client side means it has very easy onboarding for new users to get their feet wet. I've struggled with all these to varying degrees at almost every job and (non-solo) FOSS project I've been in; I've long wished for something like what's described here many, many times.
No joke, this might solve every major pain point I've had in mid-size-to-large teams in both the FOSS and proprietary world if it can deliver on what it says here, not to mention many issues with monorepo migration, work sharing, subproject management, etc. Many of these problems are very real but mostly ignored or we've decided to live with them.
Git is great, I'm one of the earliest GitHub users there is. I was also an early user of Darcs (which formed the theoretical basis for later competitors like Pijul -- so I'm not unfamiliar with radically different approaches), have a lot of experience with all kinds of administrative Git tasks for large repos; there's still plenty of room to fill gaps with new blood.
This won’t go anywhere even if it’s 20% better than git. To replace git’s network effects, you need to be 10x better.
Nobody is trying to replace git; that’s not a stated goal. Plus git is so entrenched, it’s not going anywhere anytime soon.
However, few companies have more gigantic code bases than Facebook, which not that long ago, had their entire monorepo [1] in Mercurial, which had certain advantages over Git at the time.
So if there’s an organization that knows the pinpoints of version control, I’d put Facebook on that list. They’ve been working on approving version control at scale for more than 8 years.
As long as it’s git-compatible, the git true believers will have nothing to worry about.
I guess each to their own. I want to stage my commit with regular commands, and then have the staging area work with (diff, add/remove etc).
I don't care for an interactive tool, IMHO I prefer using commands that are repeatable and learnable instead of stepping through some interactive workflow all the time.
I agree it's not necessary, but i like having it because it lets me separate what's going to be added before i actually commit.
I still commit small, frequent. But i like `git add -p` to skip debug lines, hardcoded conditions, etc. I don't want to mistakenly auto commit a whole pile of lines and then have to remove debugs/hacks/etc from things i've committed.
Stage + Unstaged is my working area, and the two live together quite nicely to me personally. I could live without it, definitely.. but i'm not sure i'd want to.
In those cases, I find it best to either 1) use the interactive commit tool to not commit debug junk, or 2) put the debug junk in its own commit, which I'll later discard (and, plus, that means you can't accidentally include it in a real commit).
I’ve never used one of the source control systems at these big companies but I use the staging area along with your git-branchless just fine for now. I’m not sure if it’s any less efficient this way.
It's not a big deal either way, but the staging area interacts worse with some operations. For example, if you have staged changes and then get a conflict with `git checkout --merge`, AFAIK there's no way to undo to before the conflict in Git. When using commits, all of the standard merge and undo tactics apply.
> Stage + Unstaged is my working area, and the two live together quite nicely to me personally. I could live without it, definitely.. but i'm not sure i'd want to.
You can just use the tip as your staging. Use interactive amending to move changes from the working copy to the commit, and when you want to "commit", finish up the message.
hg actually has an "unamend" command (part of the "uncommit" standard extension) which... reverts the last amend. Rather than having to remember how to contort reset into the right shape to move changes back out of staging without destroying everything.
`git reset HEAD~` doesn't feel like that much of a contortion to me. It's the destructive change that requires more contortion (`--hard`) which feels fair. Maybe this is stockholm syndrome though.
The way I think of it, there's basically three copies of the file in play: in HEAD, in staging area, and on disk. I cannot trust my memory to remember which variant of "git reset" copies the file in HEAD to the staging area, which variant copies staging area to disk, and which variant copies HEAD to disk (in all cases, the third copy remains uninvolved). Getting it wrong potentially creates unrecoverable data loss. And, unfortunately, this is one of those cases where reading git's documentation is less than helpful.
Combine this with the case where "I want to break one commit into two commits," where now I have to worry about making sure I know if the command is going to change the revision HEAD points to. At least there, the old commit will still exist as backup in the invariable scenario I screw something up.
You `sl commit` that half-done work anyway and then iterate by running `sl amend` many times until your commit is finished. In case you want to amend just part of changes us `sl amend -i`
It stems from the original Mercurial implementation. The goal here is that every operation leaves the repository in a good state that can be pushed/pulled. That's why Mercurial and Sapling rely on commit/amend/uncommit, etc and for example usually discourage the use of interactive rebasing in favor of restack and other operations that add another "state". It facilitates the mental model for developers without actually removing workflows (they are just different).
That's actually a deal breaker to me. Effectively using Git's staging area has become so integral to the way I work with repositories that I don't think I can ever go back to the old style.
Meh. If it has mercurial's revsets instead of gitrevisions(7) I'm game, I'll happily give up the staging if I don't need to open that manpage ever again.
edit: yep, so long git
check if a given commit is included in a bookmarked release:
sl log -r "a21ccf and ancestor(release_1.9)"
The CLI and nomenclature for the staging area (what should be called "draft commit") is awful, but the actual concept is very easy to understand.
I seriously doubt anyone who uses a sane interface to Git (e.g. a GUI) has any trouble with clicking + to add changes to the draft commit before committing it.
Most GUI tools let you automatically add all changes before committing anyway so you don't have to know anything about it if you don't want to.
They just needed to name things better (what is a "soft reset" again?).
The main problem I have with the staging area is that it amounts to being something that's like a commit except for, you know, not actually being a commit, and therefore things that normally work on commits don't necessarily work on the staging area.
A better fix would be to make the staging area an actual commit, and then reframe everything as easy ways to edit the latest commit. (This meshes well with adding features like Mercurial's phases or changeset evolution that make commit editing somewhat safer).
A "draft commit" (I really like that name!) is not a commit, and should not be handled as such. This would make this feature more or less useless.
The whole point of the "draft commit" is that you can easily see changes against your (uncommited!) changes. That helps to build up a commit step by step.
Committing WIP stuff (and maybe even pushing that) makes the history useless. Branches don't help as you end up with millions of WIP branches that are all incompatible to each other (and the evolution that happened elsewhere). Only keeping WIP branches up to date is a full time job than.
Git has already a means to edit the latest commit easily: `git commit --amend`.
> The whole point of the "draft commit" is that you can easily see changes against your (uncommited!) changes. That helps to build up a commit step by step.
> Committing WIP stuff (and maybe even pushing that) makes the history useless.
Here's the thing. I'm a very big proponent of keeping history clean, and making sure that commits are atomic, and exorcising any "typo fixes" or the like commits from history. Not once have I found the concept of a staging area useful. Features like `git commit --amend` or `git add -i` are incredibly useful [1]. But not the staging area itself--it's only a thing that screws me up if I forget to add `-a` to `git commit`.
"Draft commit" also I think elucidates the other problem. You see, drafts of regular documents are frequently shared with other people, multiple versions of them created and shared, etc. Drafts don't become final until it's actually published--and there is utility in being able to track the differences in drafts as they are discussed. If you've got a "draft commit", then it should be able to go through this process--this is basically the process of code review.
Of course, we're already working with a VCS, which is designed to handle different versions of code, so... what if we made the "version history" of commits just... regular commits? Sure, shade them a different color, so you know that a commit is a draft, and you can tell which of the commit's parents [2] is the previous version. And knowing that a commit is a draft, when it actually gets pushed into the trunk, you can commit only that final commit and not include any of the previous history. Since the commits are using the same DAG logic under the head, questions like "what changed between version 2 and 5?" become just regular diff commands [3].
By the way, this system already exists. It's known as changeset evolution in Mercurial, and it appears that Sapling here has adopted it. My workflow in git tries to emulate this model to a degree, but the approach of having branches-based-on-branches doesn't mesh well with how git wants to do things.
[1] The number of times I have painstakingly sorted out which changes go into the commit with `git add -i` onto to immediately and accidentally undo them with a `git commit -a` is quite high. And because the staging area isn't an actual commit, it can't be recovered by digging into the reflog like actual commits can.
[2] If you amend or otherwise modify a commit, it has one parent, which is the previous version; if you rebase a commit, it has two parents, one of them the new commit it's based on and the other is the previous version.
[3] Worth noting that this question often turns out to be difficult to answer with most code review systems. Building a model of "commit history" into your VCS makes it come out for free!
You can also do `sl/hg log -Gr a21ccf+release_1.9`. The graph tells you the relation of selected commits. Last time I checked, git does not have the same --graph rendering yet - it only considers direct parents not ancestors.
Mercurial revsets and phases are two killer features of mercurial that blows any counterpart git has out of the water.
Phases are a property of revisions that essentially let you know their state. By default, there are three phases: public, draft, and secret. You can't rebase a public revision, nor can you have a public revision with a secret parent. So you get out of this concept things like safe rebasing, or barriers that let you keep internal and external repos separate.
But revsets really shine. This is basically a full-on query language for revisions. So you can define a query alias "wip" that specifies all of the, well, interesting revisions: every revision that is not in the public phase (i.e., not in the upstream repo), the tip of the trunk, the current revision, and sufficient ancestor information of these revisions that you can see where you based all of these WIP branches on. In a single query: "(parents(not public()) or not public() or . or head())".
Sure, composing revsets is definitely a somewhat painful process... but it's possible to describe more or less arbitrary sets with a Mercurial revset, and I've never been able to find a similar workable setup in git.
Why do you want to? Not trying to be snarky, but I've been using various source control tools for closer to 20 years than 10 and I can't remember when I've ever needed or would have benefited from revsets. I'm genuinely curious what problem this solves and whether I've just never experienced or have made my own hodge podge solution for it incidentally.
I am used to working in large repos (>100 commits/day), which generally means that something like 'git log --graph' contains a lot of extraneous information.
The most common workflow I have is that I've got a couple of old working branches (like featurea and featureb), and I want to see if I need to update featureb to a newer head or not, or if featureb was based on featurea or featurea-v2. A demonstration of this kind of thing is 'hg wip' here: http://jordi.inversethought.com/blog/customising-mercurial-l....
Another thing I would use revsets for is answering queries like "which of these changes that's on the public repository made its way into the internal repository (which periodically merges from the public repo)?"
It has an interactive commit staging command which accomplishes the same thing. In that case, it unifies the staging area with regular commits, which means you can also manipulate them the same way as regular commits.
AFAICT there are only two workflows involving the staging area: staging partial commits and resolving conflicts. The first case is taken care of by partial commit support, and the second case presumably has its own dedicated mechanism.
My favorite example of the staging area being super weird is `git diff` behavior. By default, git diff will show unstaged changes as well as committed changes, but _not_ staged changes; to see staged changes, you need to use `--cached`. This is especially weird when diffing between a fixed point (e.g. a commit hash) while going through the motions. If it's not clear why this would be weird, try out the following:
* get the hash of the HEAD commit
* run `git diff <hash>` and see that there are no changes
* make some change to a file
* run `git diff <hash>` again and see the change you made
* stage the change with `git add`
* run `git diff <hash>` again and no changes are show!
* commit the change
* run `git diff <hash>` again and the changes are back
It's super bizarre to me that there would be some sort of intermediate state where changes aren't visible. I feel like it would make more sense to have some sort of formatting difference indicating unstaged versus staged but not committed versus committed, but I imagine changing that now would break all sorts of scripts, so we're stuck with it.
Not to subtract from your point at all, but newer versions of Git allow you to use `--staged` as a synonym for `--cached`. That this is the default behaviour still makes no sense, but at least the name does.
That's good to know, but one has to wonder why that didn't change 15 years ago. It's representative of the general problem with Git: no particular problem with having bad UX for a long time without improving it. It seems like things started to get better in the last few years, but seriously it's not like the problems weren't obvious for a long time.
To be honest, I think this behavior is quite useful.
Staging just means "I'm happy with the changes so far but didn't finish everything I want in my commit; let's ignore this changes for now".
What `git diff` does by default becomes useful when you touch the staged changes again. Then you see only the new changes compared to the staged stuff. This helps building up a commit step by step, with some trail and error in between.
Think for example about something like: You use some tool to do some automatic changes. This creates hundreds of changed files. But the result isn't working. You could commit that, sure. But than you would need to rewrite history before pushing because creating not working commits is a terrible idea. Or you could just stage the changes for now. Than you can change / repair the still missing parts. Git diff will helpfully show you only the new changes but ignore the staged stuff as long as it's untouched. You would now for example easily see changes that you made to the automatic rewrites. Without the staging behavior you could only compare with a committed state, and drown in hundreds of changes that are unrelated.
The main problem with the staging area is that quite some GUI tools don't use it correctly. The tools try to "simplify" Git by ignoring how the stating area is supposed to work, or ignore it completely like the infamous JetBrains IDEs. (IDEA is the tool that needed almost 10 years to implement Git sub-modules…). I think the VCS handling in IDEA is on the surface very polished. But when it comes to something like the staging area the UI-wise very crappy VS Code Git support beats that by far. Sublime Merge does also the "right thing"™ and hides the staged changes at the bottom so you see only the the changes to the changes. Exactly as the staging area is meant to be used!
I can see the behavior being useful in some cases (and not a _super_ common cause of mistakes once you learn it), but the issue for me is mainly that it's surprising and easy to get bitten by before you learn it. This describes git entire UX as well in my opinion: the underlying model is very powerful and generally pretty sound, but the API for it does a poor job of making it clear, and generally you have to make a lot of mistakes on the way to learning how to use it properly.
Git is conceptually very sane and logical. But the UX is indeed (still) terrible.
But a lot of people seem to complain about the complexity of the underlying concepts. That makes no sense to me as the concepts are complex because the problem at hand is complex. Also it makes no sense to me that people are constantly crying for a new tool even they have obviously issues with understanding the problem space and all requirements. A new tool would not make all this complexity go away! It could at most try to "hide" some of the complexity by introducing magic. This wouldn't be helpful at all as magic is way worse than bad UX, imho. Bad UX is bad UX you can deal with it. But when magic goes wrong all bets are off and you're usually in deep trouble.
This looks like people would argue to rewrite the back-end (some propose form scratch!) even only the front-end needs some face lift…
I think the only potential improvement I can think of to the underlying model is that a patch-based system does seem like it would alleviate some of my pain points, but the UX for that hasn't exactly been fully figured out either; last time I tried pijul, it certainly seemed compelling feature-wise, but I wasn't able to figure out everything I wanted to do with it easily. It also would mostly just be a quality of life improvement for me at this point too; git is fully capable of doing everything I need it to, but sometimes I might have to google a bit to figure out how exactly to tell it what I want.
It's really just a matter of habit and getting used to no staging area takes short and has huge benefits.
We develop HighFlux[1] which also gets rid of the staging area. It simplifies your mental model of what's going on a lot.
Because everything you save is automatically committed, switching to a different task/branch is also always instant without needing stash.
Because what you're testing locally is what you're committing, I also never have CI failures anymore (with the staging area I frequently had unexpected interactions with unstaged changes and sometimes even accidentally forgotten added files).
Sounds like saying bye-bye to any meaningful history.
Rebasing, cheery-picking, or reverting of commits becomes impossible when every save of a file is pushed.
You could just publish local IDE history… Would be equally "good" I think. (My IDE is saving files every few key strokes btw; the resulting history would be a bloody mess).
Why not go one step farther: Just make an automatic block image of the whole systems of every developer machine every few seconds. You could than just deliver the image. No docker setup needed any more. Just write code. And when the local version works, ship the whole local system just as it is. ;-)
> You could just publish local IDE history… Would be equally "good" I think. (My IDE is saving files every few key strokes btw; the resulting history would be a bloody mess).
Google does this, it has saved my butt on more than one occasion.
> Sounds like saying bye-bye to any meaningful history.
I'm so confused, you just amend the most recent commit, or work with changes unstaged and uncommited. Like my normal workflow is basically "change 2-3 files such that things are passing, hg commit, send for review", and then I continue working on the next thing, either back to HEAD if its unrelated, or on top of the just-pushed changes if it depends on them.
It's vastly simpler than having to git add at random times.
> > You could just publish local IDE history… Would be equally "good" I think. (My IDE is saving files every few key strokes btw; the resulting history would be a bloody mess).
> Google does this, it has saved my butt on more than one occasion.
You mean backups? Yes, backups are a very good idea.
But this has nothing to do with VCS. That are separate topics.
> Like my normal workflow is basically "change 2-3 files such that things are passing, hg commit, send for review", and then I continue working
As long as the requirements are so trivial even CVS would suffice.
But even considerably simple refactorings (in e.g. static languages) can be much more complex. It's easy to end up with hundreds of files changed. Than you need more powerful tools. Doing such things without the staging area is almost impossible to get right. (The only alternative would be quite some rebase sessions; and those are way more complicated than using the staging area upfront; also you would need some way to do diffs against "pined" changes—which is something you get for free with the staging area).
> You mean backups? Yes, backups are a very good idea.
No, I mean that the filesystem I edit code in has a full snapshotted history of every save and I can recover to a particular revision or particular point in time, even one's that weren't committed to vcs[0]. I guess you can call that a "backup", but like it's not what people usually mean.
I have used this to recover some things I was working on three months ago but ended up throwing away.
(citation:
> All writes to files are stored as snapshots in CitC, making it possible to recover previous stages of work as needed. Snapshots may be explicitly named, restored, or tagged for review.
> It's easy to end up with hundreds of files changed.
Sure, doing a rename can touch 100 files, but you should isolate that change to a commit and PR that don't do anything else. Running sed and then committing or using your IDE's refactor feature, and then committing and running tests doesn't require a staging area.
[0]: The truly wild thing about this is that the vcs state is also stored in the snapshotted, point in time recoverable fs, so if you do the equivalent of absolutely botching your git history, you can jump back in time a few minutes and start from a known good state.
I lived through the alternative and the staging area is superior. If it wasn't, I might not be using Git, or at least begrudge when I have to; neither is the case.
But a branch is a commit but the staging area is an index, which is something different. Why two different concepts when one would do? That's my problem with the staging area.
I don't care how this is implemented. You may be right that the implementation could be more streamlined. But I care only about the functionality.
The whole point is to have some form of "draft commit".
The staging area lets me "stash" WIP changes in a transparent way.
Having a "draft commit" feature avoids the need to rewrite "bad commits" after the fact.
The staging area is really useful to build up commits gradually.
When thinking about it I've just realized that the staging area should not only be kept as a feature but could be even extended. You could add a "change-set management system"—which would be essentially multiple staging areas (maybe coupled to improved stash functionality to be able to quickly move / copy changes between change-sets).
Yes, such a thing would very likely need to be built on top of the mechanics behind branches / commits. But this should be transparent and not interfere with the said features, imho.
One of the problems with `git`, and it seems that Sapling is no different, is that there is no one-to-one mapping between the user intent and the underlying SCM.
For a user intent of "implement feature X", there is no UI to "start a new feature". Instead, one has to translate their requirement to the SCM mental model and issue a series of commands to manipulate a DAG of blob hashes that live in 4 different places simultaneously. (work tree, index, local repository and the remote repository.
Highflux allows a simple user requirement to action mapping.
I use HEAD as my staging area, and do all the normal staging-area things, but without the weirdness that comes from the staging area being a different concept to a commit (ie, the diff command Just Works, no need for a separate `--staged` flag to enable special behaviour for that case)
I'm not interested in a simplification of git, sorry.
Git's major value proposition is that they added moving parts until the system worked great. If you don't want named branches, staging, or any other piece of the ideology, then subversion is a fine choice.
Despite the greytexting of this comment, I'm still really interested in the discussion around it -- and I'm really curious to hear from people that have the opposite experience.
Moving from SVN to git felt like liberation because there were suddenly idiomatic ways of expressing states that were sort of smushed together by SVN -- stuff like, "I have some changes in my branch I want to line up for commit" which, became the handy one-word concept, "staging".
The before-times were marked by a lack of these fine distinctions. While they existed in fact, they were obscured in-system.
Like a map, any tool should 'resemble' the sphere of human activity it potentiates, and git resembles our diverse workflows better because it has so many asinine distinctions.
This was always its strength, and indeed, likely the reason the platform is called 'git' in the first place, as 'smarmy git' (English idiom for 'smartass') implies an insufferable drawer-of-distinctions.
And like a smarmy git, it's easier to complain about git than it is to replace it.
In my opinion, Git added too many moving parts in a poorly-designed way. Commits, the staging area, and stashes all implement the same sort of idea, but interact poorly and considerably complicate workflows. Having them is better than not having them, but Git would have been better off if they consolidated into a single idea and concentrated their efforts on that. If you can remove a concept from Git and still support the same workflows equally well, then surely that concept was unnecessary. (Whether the staging area is actually better served by other concepts is a matter of judgment.)
Interesting -- I have the opposite impression, and we're both just simply staring at the same pile of distinctions and disagreeing on whether it 'resembles' the workload.
I imagine this will be settled by git steamrolling sapling in the market, but I wonder if there's a faster (and less network-effected) way to adjudicate? Both your position and mine seem lodged in a taste/touch/feel context, which seems like a data-poor place to make good decisions.
On the other hand, I'd say that absent sufficient data, one should pick the most flexible tool, which I'll bet in this context is the one with the most moving parts, i.e. git.
The findings validate the earlier conceptual design analysis in practically all aspects: https://gitless.com/#research
Git doesn't support certain workflows well. For example, how do you split the contents of the staging area into two separate groups? Sapling handles this the same way it handles splitting commits in general. Essentially, it has a greater set of verbs that act on a smaller set of nouns, where Git has a medium set of verbs that act on a medium set of nouns.
> how do you split the contents of the staging area into two separate groups
What is the workflow behind this ask? I don't understand what the goal is. The basic git workflow:
1. Edit and save a tracked file; the changes appear in the working tree.
2. Select some subset of the changes in the working tree to stage them in the index.
3. Form a commit with the changes in the index.
IIUC you want to add a step in between 2 and 3? But the way I see it, 2 is doing what you want. I can split the set of current changes by selectively adding them to the index in preparation for a commit. I can also selectively un-stage changes if I decide I don't want them to become part of the commit.
Between the changes to a file in whatever editor buffer I'm writing in, saving those to disk, moving changes from unstaged to staged, and forming a commit in any of a variety of ways (plain ol' commit, amending a commit, a fixup commit) I can't imagine what other way I need to slice and dice changes. Maybe it's just a failure of my imagination since I've been using Git for so long now and only more basic things like SVN/TFS/CVS before that.
To accomplish your workflow, there doesn't seem to be any need for a staging area at all. You can use an interactive commit selector to stage a subset of changes (or select a set of changes some other way), and then if you want to keep adding/"staging" changes, you can amend the commit you just made.
Occasionally, I do run into the situation where I've staged some changes and then realize that I want to start staging another commit first, but don't want to lose the changed I already staged. Unstaging my current changes means I have to remember and select them again later. I could also just commit what I have staged and start staging a new commit (and perhaps reorder the commits later), but that shows that the staging area was unnecessary in the first place, and I could have used commits to accomplish the same workflow without adding a new set of concepts to my VCS.
On the other hand, I'd say that absent sufficient data, one should pick the most flexible tool, which I'll bet in this context is the one with the most moving parts, i.e. git.
Outside of the Git fanboy bubble, developers don’t want to have to be version control experts, which once you get past a certain level of Git usage, you have to be, whether you wanted to or not.
Especially if your team doesn’t have that person who can get you out of any Git jam you may get yourself into.
It will also be much faster to get a new developer up to speed using Sapling than Git. And because it’s Git-compatible, if there’s something super advanced that can only be done using the Git command line, that’s still an option.
Telling the intern/junior developer to read the man page for git-log is a non-starter; it’s over 19,000 words!
The best thing for the greybeards is they can continue using Git while others use Sapling and commit to the same repo.
To be fair, if I had a junior dev who couldn't skim a man page of 19 000 words, I would have freestanding concerns about their ability to contribute -- being able to inhale knowledge is a core competency of any engineer.
That said, I hear you when you say that most folks (think) they have better uses of their attention.
I daresay they're wrong, but it's not my place to dictate terms to anyone's curiosity, my own included!
coming from SVN, the biggest thing about is it's distributed nature making everything local and fast. yeah there are a lot of other concepts along with that too, but just being able to work on my laptop,
commiting and making branches to my heart's content, without talking to a server until I'm ready for that step was the killer feature for me.
SVN branches were this doofy copy procedure that made sense once you drank the Kool aid, but they were so unwieldy compared to git
Its interesting how these threads about Git simultaneously have
(a) People arguing git is fine, and shouldn't be simplified
(b) People arguing about the right way to use git, and flame wars about best git workflows
I mean most people simply see (b) and conclude "this is a huge hassle, I don't want to annoy some git-workflow-purist, I'm just going to walk on eggshells on this tool and hope I don't break anything"
It's as much a social problem around conventions, and lack of opinions in the tool itself, then anything about the underlying technology (which is rock solid IMO)
I remember when git was new, and the subversion crowd didn't jump on. Same discussion, different subjects. Git is/was awesome... but there is certainly room for improvement.
As I recall, the Big Deal with CVS-to-SVN migration was that many teams learned to creatively exploit the fact that CVS does versioning on a file-by-file basis. As late as 2008, I had to work in an environment where checking out different versions for different parts of the company's CVS monorepo was required to get anything to build.
I would argue that camp a) is more diehard than your description. Some people will argue to their death how perfect a tool git is and it would be impossible to operate without a tool that exposed so much low level power.
I use git begrudgingly because that’s where the world is, but I long for an improvement in this space.
I dunno, there's a lot of value in having a tool that's flexible and lets you work the way you want—even if that fundamentally means there is no one blessed way to do things.
Git has a lot of incidental complexity and unforced design problems, but the fact that it's inherently flexible is not one.
(c) Git is a disaster that has destroyed source control for 15+ years and the industry is unable to recover from due to Stockholm syndrome
It’s impossible to discuss source control without people coming out of the woodwork to shit on git. It doesn’t work right. It’s too hard. SVN was better. Mercurial should have won. Blah blah blah.
Git isn’t perfect. And the command line has improved (I don’t care much, I use a GUI).
But the number of people who seem to insist that because it doesn’t work for them or they don’t personally like it it’s horrible and everyone should abandon it is crazy.
And it makes trying to read/participate in discussions like this painful.
I agree. I think git is fine, but also would like improvements. I started with CVS and VSS (well really started with copying files to dir_bak heh), then SVN and git. Yelling across the office to tell someone to checkin a file so I could make an edit seems so quaint. VSS had a fun bug that if you ran out of disk space, it would destroy the entire repository - yup.
Is git sometimes obtuse? Sure, but it's fast and incredibly powerful. My everyday commands are easy to use, and if I need something special I go to the documentation - just like any other SCM.
Not a big fan of FB as a company, but I think their open source work is pretty impressive. Various other large companies have the problem of giant monorepos that they constantly need to onboard new developers to, but I can't think of anyone other than FB who consistently released their solutions.
Sure, most people are probably fine with Git once they learned it and if they only work with small to mid sized code bases (like me). But I'm still happy Sapling is out there, I might use it or learn from it if I ever run into the problems it solves.
Facebook has a lot of interesting open source projects, but they tend to abandon them. As far as oss goes, I think Google is the best. As long as you don't mind dealing with 3 different custom build systems within the same codebase, their projects usually have dedicated teams maintaining them.
...and yes, I realize it's weird to say this considering Google is known for abandoning things. Maybe it's just coincidence that I've run into more abandonware from FB than Google?
As far as I can tell, most of zstd's development is still by Facebook employees, though not all of it. I tend to think zstd has enough traction that development would continue even if FB were to abandon the project.
This is probably a naive take, but I think of compression software as something that can be “done”. Unlikely to be a lot of code churn required for such a project to be relevant for a very long time.
How to not abondon a project? I would love to know that.
This is my naive understanding. A for profit company open sources a project that they have been using and developing internally. The have built a philosophy and understanding of the project as they use and develop it. Most of their action regarding the project is that they must use it and they usually don't have any other options.
Because the foundation is already laid the solution for its shortcoming is having just an understanding of them. Then you open source the project knowing that you have developed the project to it's completion.
Now comes the OSS community. Either we request features that goes against the project philosophy or we don't want to get involved because we don't need to compromise and acknowledge the shortcomings because we have options.
A good solution can be open sourcing projects that the org thinks isn't complete and needs further development without compromising security, philosophy and usability. Because if you have a list of things you need, you can ask the OSS community to fix those things rather then be critical of the foundation and philosophy.
Idea for projects not owned by mega-corps (half real, half fantasy):
1. Get the project added as a package to one or more major commercial Linux distros, e.g., RedHat, etc.
2. Grant commit access to one or more devs at the same Linux vendor. Allow them to do whatever they want. You might not like their direction, but it should survive.
3. Retire from the project whenever you like.
4. Also, you could post a note in README about retiring. If people want to add features, ask them to fork, or just grant them commit access and let them go wild.
Good points. Personally, a lot of the appeal of open source is not so much about having free stuff, it's more about being able to learn from others. I learned a ton from the various engines Id Software released back in the day, but have no expectation that they maintain them.
If it comes to stuff I actually want to _use_, I avoid projects backed by a single or a few companies - like Sapling. So from that angle, I'm not particularly impressed either.
Better than Microsoft? I don't think I've ever been able to talk to a human at Google, whereas with Microsoft, I get feedback very quickly on issues and pull requests. Does Google even interact with people with open source? For example, I am using Skia via SkiaSharp, and the only place I know of to go for Skia help and issues is their Google Groups page, a website out of the 2000s. And very few seem to actually monitor the group. I'm not even really sure what Google does in open source. Even the things that are released to the public, like Skia, are well known to come with a huge amount of internal baggage.
Whereas Microsoft has dozens of active projects on GitHub where you can talk directly with the people working on it at Microsoft.
> I'm not even really sure what Google does in open source.
Kubernetes (and a bunch of the offshoots), golang, a bunch of ML things, etc. It's just that many have independent foundations (CNCF) running them now to keep project management independent from a single company.
React Native, named after their open source project that is incredibly successful, widely used, completely reshaped the front end dev world, etc... Not sure if an offshoot of a world class project not gaining traction is a mark against them.
In a way it has. Just not directly. Everyone is abandoning (for example on iOS) imperative style (Cocoa and UIKit) for React style (RxSwift, ComponentKit, SwiftUI). It's the biggest shift in native UI programming in more than a decade.
There recently was a thread here on the impact of layoffs on React. And apparently even Facebook has abandoned react for anything but internal projects.
Tbf I think this was just the learning curve for big tech companies. Google and Facebook face scalability problems other companies 5 years before anyone else so if they don't open source the solutions they come up with, the industry-standard that establishes itself 5 years later will not be compatible with their solution and they will have a hard time recruiting and keeping personnel for their own system.
For GP's use case, its "added some stuff, want to stage the stuff I modified since then to those files"... which I feel is a perfectly normal workflow.
There's nothing saying that one can't add chunks to the staging area and then immediately commit it without invoking that alias afterwards (since it is a very deliberate "add these things" rather than "adding a bunch of things and keep adding."
Not really. I'm not sure I want it to behave the way GP suggests, but I definitely want to have staging, and `git commit -a` is basically an equivalent of having no staging.
The reasons for that are:
1. In the vast majority of cases there are multiple files I want to commit together. Usually I change them multiple times during the process.
2. It almost always starts with some debugging in a couple of other files, and I often want to keep that debugging for a couple of next commits, but `git checkout HEAD` these files in the end.
3. For me, the most popular way of using git rebase → edit (which I do reasonably often) is splitting a commit into 2 by separating files. This is easy enough by just changing a status of a file from "staged" to "modified" (I even have `git unstage` alias for that) and commiting.
So I kinda get why GP wants what he wants. This isn't crazy.
Now, that being said, I personally have absolutely no problems with how git does that now: I've figured out a workflow that solves these problems for me, and everything is ok now. This workflow is basically making many dozens of tiny commits to a branch without even bothering to name them properly, and then just doing `git rebase -i` many-many times while working on a single branch. So I just commit the code I don't intend to keep with a label "drop that", and drop these commits when I'm done. And other commits usually are heavily reordered and squashed into 3-5 larger commits that make some sense on a higher level (like 500 LOC of refactoring first, and then 1 LOC of an actual bug-fix, which usually makes much more sense than just 500 LOC of a bugfix, that solve the problem somehow, but it's absolutely not obvious how exactly). I rarely can figure out that separation before I'm done. In fact, I often fix the problem first, then refactor, then roll-back the fix just to add it again in a separate commit in the end (if the refactoring and the fix affect the same file, which also is often the case).
> Not really. I'm not sure I want it to behave the way GP suggests, but I definitely want to have staging
So I'm replying to OP's very specific usage pattern, and you object with a completely different usage pattern?
> `git commit -a` is basically an equivalent of having no staging.
GP doesn't use the staging as a staging, since they immediately stage all modified files. That means the staging is useless, they can just commit files straight from modified. Which is what `git commit -a` does.
> 1. In the vast majority of cases there are multiple files I want to commit together. Usually I change them multiple times during the process.
OK? `git commit -a` doesn't preclude that. You just use it instead of `git commit`.
> 2. It almost always starts with some debugging in a couple of other files, and I often want to keep that debugging for a couple of next commits, but `git checkout HEAD` these files in the end.
I'm really happy for you. It doesn't work when the files are already staged, which is the case of GP.
> 3. For me, the most popular way of using git rebase → edit (which I do reasonably often) is splitting a commit into 2 by separating files. This is easy enough by just changing a status of a file from "staged" to "modified" (I even have `git unstage` alias for that) and commiting.
You don't need a staging area to craft commits, you can manipulate the tip commit directly. With good enough support for that (which sapling seems to have inherited from mercurial), the staging is just an unnecessary pseudo-commit.
In the argument of monorepo vs not, the usual argument goes like this:
- It's too hard to scale for a large monorepo!
- Google does it just fine!
- But I don't have access to Google's tools!
So kudos to Meta for both solving the problem and making it available to others. It will be interesting to see how useable it is outside of Meta. I know for example that while Netflix open sourced a lot of tools, most of them weren't useable unless you ran all of them together. So far Meta has been good at avoiding that, so hopefully that remains the case.
Mostly people who have this argument don't have a code base large enough to run into actual limitations of git. They run CI with wonky java implementations of git and/or have giant amounts of binaries in their repos. Actually having Gigabytes of source code is pretty rare.
It depends on the size of the company. The Linux kernel has about 1500 active developers. This is a lot, but many companies also exist that reach this size.
I think another thing that matters is how you store branches/code under review. In Linux, each team/person has their own repo. The main "Linus" repo has mostly the finished code. In a company it is much more common for everyone to store their unfinished code centrally. Perhaps this also accounts for some increase in size.
> and/or have giant amounts of binaries in their repos.
Firstly, it's not "giant amounts of binaries" it's "a very small amount of binaries". A few GB is enough to cause significant problems.
Secondly, This _is_ an issue with git. If my project requires binary files, git should handle it. How should we handle logos in a mobile app, branding images on a website, audio files for background? That's before you get to the question of "how does a video game store the source version of a 100GB worth of compressed assets?"
Git LFS is a reasonably good example of git being half baked. It is provider dependent, and requires configuring separately on both the server and client. It turns git into a centralised VCS, removing the option of working offline in the process.
A bit like submodules, LFS has its own warts that seem to multiply when you add more people to the mix. Working with git LFS has been the _only_ time the solution to my problem has been "nuke and clone again", in almost 15 years using source control.
Last time I used git LFS, it didn't support ssh cloning at all, and the issue had been open for years at that time.
Microsoft uses monorepos, and you have access to Microsoft's tools! They chose to work with Git and amend it when necessary, just like Meta did with Mercurial.
A few years ago they had a Virtual File System extension for Git. Now it's a public fork of Git that is intended for large repositories (several hundreds of GB). It adds a `git scalar` command, see https://github.com/microsoft/git/blob/HEAD/contrib/scalar/do...
To use a similar featureset but in the same Git repository you normally use, you can try my https://github.com/arxanas/git-branchless. Then, you can use your usual staging workflows if desired, or use regular Git commands directly.
Its design is inspired by Sapling, and, in fact, it uses some of the same code, such as the segmented changelog implementation. Possibly some of its ideas made their way back to Meta, such as interactive undo?
Jujutsu also supports colocated Git repositories: https://github.com/martinvonz/jj. It also has the working-copy-as-a-commit idea and conflicts are stored in commits (so rebases always succeed). I think it's a step forward compared to git/hg/sl.
I think those two things that arxanas mentioned are the biggest differences (i.e. working-copy-as-commit and conflicts in commits). I haven't used Sapling, but I suspect Jujutsu has better support for moving commits (and parts of commits) around without touching the working copy.
Sapling, on the other hand, has much better support very large repositories, since they've spent a lot of time on that over the years. We're going to copy some of Sapling's solutions to Jujutsu soon, since we're working on integrating it with Google's monorepo (slides: https://docs.google.com/presentation/d/1F8j9_UOOSGUN9MvHxPZX..., recording: https://youtu.be/bx_LGilOuE4).
Oh, in the context of Sapling, I should say that Jujutsu runs the equivalent of `sl restack` after every command. Thanks to first-class conflicts, that always works.
Yep — after trying out Sapling, it aborts many operations due to working copy changes, which I now find to be jarring interruptions to my workflow. Jujutsu (and even git-branchless) have much better support for juggling working copy changes, which is invaluable in a patch-stack workflow.
The Sapling support for remote repositories was a little rough in my opinion. Jujutsu and git-branchless can both co-locate with the Git repository, so you can always drop down to Git commands if there's something you're having trouble doing. (I find the `jj git` commands to also be better at interacting with remotes for now.)
No, the CLI prevents that because the remote is unlikely to know how to interpret conflicts. In a future where the remote understands conflicts, then you'll be able to push conflicts to a remote and collaborate on the conflict resolution.
> To use a similar featureset but in the same Git repository you normally use
What do you mean? Can't you use Sapling in the same Git repository you normally use? The first sentence is "Sapling is a new Git-compatible source control client". Is there something they're not telling us?
EDIT: Looks like it calls out to the git executable occasionally (https://news.ycombinator.com/item?id=33615576) and presumably works on the git object model under the hood, but you can't use `git` on a repo checked out using `sl` nor vice versa. It's a stretch to call it Git-compatible but I guess not completely wrong.
What I mean is that you can't co-locate your Sapling repository with your Git repository (at least, for now). You have to have them in separate directories and push/pull between them.
git-branchless is only an extension to Git, so it naturally operates in the Git repository. Jujutsu has a mode to create the `.jj` directory alongside the `.git` directory and co-locate them, which I find very convenient in practice. (Originally, Jujutsu only supported Git compatibility in the same way as Sapling, via pushes and pulls, but they added co-location later.)
> Looks like it calls out to the git executable occasionally
I believe Jujutsu never calls out to Git, and that all of its `jj git` interop commands are implemented via direct bindings to libgit2. This is less fragile in many ways, but it can also mean that `jj git` interop might be missing some new feature from Git. Fortunately, you can oftentimes just run the Git command directly in the repository when co-locating.
> presumably works on the git object model under the hood
There's no guarantee of this: the Mercurial (and therefore possibly Sapling?) revlog model is a little different from the Git object model, as I understand it. But it doesn't really matter, as long as it interoperates seamlessly. For now, I believe they do literally have a `.git` directory somewhere under the `.sl` directory, but they reserve the right to change that.
> > Looks like it calls out to the git executable occasionally
>
> I believe Jujutsu never calls out to Git
Oh, I was referring to Sapling. I know even less about Jujutsu than I do about Sapling!
> > presumably works on the git object model under the hood
>
> There's no guarantee of this ... but they reserve the right to change that
Interesting. So it would translate between them whenever you push to or pull from a Git repo?
I'm very keen to use Sapling if it's basically a polished interface to Git but less keen if it's an entirely different object model, because then I'm going to have to learn more about what's going on under the hood to understand it properly.
> Oh, I was referring to Sapling. I know even less about Jujutsu than I do about Sapling!
I was just remarking about Jujutsu, in the case that it was important to you for some reason whether or not your VCS called out to Git.
> Interesting. So it would translate between them whenever you push to or pull from a Git repo?
To be honest, I don't know. I suspect that, for now, they store real Git objects, rather than translating on the fly. You'd have to ask a Sapling maintainer.
> I'm very keen to use Sapling if it's basically a polished interface to Git but less keen if it's an entirely different object model, because then I'm going to have to learn more about what's going on under the hood to understand it properly.
I might have muddled some layers of abstraction and brought up something unhelpful. Git's object database and Mercurial's revlog are more comparable in terms of where they lie in the abstraction hierarchy, but these are just the storage layers. In practice, I find the Git and Mercurial object models, as exposed to the user, to be similar enough that I pretty much never have to worry about the differences. (Well, perhaps it's true that Mercurial file contents are not addressed by blob hashes, but do I ever really want to address by "blob hash", or just by "the contents of this file at this commit"?)
What I meant to emphasize is that you can't directly use Git to access Mercurial/Sapling's internal object store, if that's important to you (perhaps for scripting). In comparison, with Jujutsu, if you modify the Git object store on disk, it will try to "import" refs the next time you invoke it in order to update its own internal object store to match.
As a Meta employee for almost 4 years what I will say is I was skeptical at first coming from git, but the sapling system works very well in practice in my experience. I still use git for everything outside of work, but I may consider sapling now.
I'm ex-Meta and now at Google and while they have 'hg' as a wrapper around their fig system, it's lacking so many of the Sapling features I am sad and frustrated occasionally.
I find it interesting that this is open sourced a few days after 11K were laid off.
Was bulk of the team behind this laid off wherein it made sense to open source it to involve the community to take it forward rather than paid resources?
To be clear: I'm NOT criticizing them open sourcing this.
The open sourcing schedule was completely unrelated to the layoffs.
The team working on Sapling has been planning this release for a very long time and will continue working on Sapling to support our internal engineering efforts.
I doubt that the FB people involved with the layoffs are the same FB people who decided to open source the Sapling project. Many FB engineering managers were not aware that the layoffs were being planned. Moreover, it takes up-front planning and design decisions (by more than just a few days) for a private company to open source their internal projects.
I use the staging area to allow me to more easily break larger changes into smaller commits. I am usually all over the place while writing/refactoring code and making commits as I go along doesn't work well.
How does sapling let me take a long list of commits and break them into larger but more manageable chunks?
git add -p allows me to add chunks easily and create commits, git commit --fixup allows me to mark a commit as fixing a previous commit, and with git rebase -i --autosquash I get to easily take those fixup commits and meld them into the previous commits.
Also reviewing a stack of patches is annoying in many cases as I care more about the end result vs each individual commit. But that may just be my experience talking in open source where I am working on smaller but better well defined projects vs a large mono-repo where there may be a lot of changes across many disparate parts of the code base that make it difficult to look at the "whole" vs a patch that is more localized.
Instead of `git add -p`, you would use `sl commit -i` (whose interface I much prefer). To amend into a previous commit, I prefer to switch to it and then just use `sl amend` (+ `sl restack` if necessary), but you can also use `sl fold` IIRC. Instead of `git rebase -i`, you can use `sl histedit` (not a direct replacement for autosquashing, but worth mentioning).
To split a single commit, you can use `sl split`, which is quite difficult in Git. (I miss that feature in Git quite a lot.) You can also use the `sl absorb` command to automagically merge local changes into the previous patches where they seem to belong (roughly speaking, commute changes backwards until they would cause a merge conflict, but it's a little smarter about avoiding certain merge conflicts).
If I switch to a previous commit to amend it, then I would temporarily lose all the other changes I made, and means I can't easily run tests on that particular commit to validate nothing else broke.
It sounds like I would need to:
- switch
- amend the commit
- restack?
- switch back to the HEAD?
`sl histedit` is very similar to `git rebase -i` if you're famiilar with that interface which works just fine.
Other commands useful for amending changes to previous commits:
* `sl goto --merge <hash>` - if theres no conflicts you can just switch commits with pending changes and those pending changes would be applied on top of other commit. If there are conflicts this command would fail https://sapling-scm.com/docs/commands/goto
`sl absorb` can turn that workflow into a single command in many cases: it automatically looks at what hunks you've changed and tries to propagate them back to earlier commits in the stack that touched those same hunks. It's not perfect, but in my experience using this at Meta, it does what you want 90% of the time.
https://sapling-scm.com/docs/introduction/differences-git#sa... says:
"If you want to commit/amend just part of your changes you can use commit/amend -i to interactively choose which changes to commit/amend. Alternatively, you can simulate a staging area by making a temporary commit and amending to it as if it was the staging area, then use fold to collapse it into the real commit."
Yes. Right now we are adding more documents about how internal components (like the commit graph) work. We hope others in the industry find them useful. Feel free to ask questions in GitHub too.
Phabricator[0]: code review/CI solution from Facebook. My company uses it, open development has since been halted by Facebook and we're effectively on abandonware.
Flow[1]: JavaScript typing system from Facebook. My company uses it, open development has since been halted by Facebook so we're effectively on abandonware.
EDIT: React: Javascript framework from Facebook, my company uses it, and while it has its warts it works pretty well all things considered and Facebook has continued to support and evolve it over time!
For all I know Sapling is fantastic and will be developed for years to come. But personally I can't help but feel "once burnt, twice shy" (or in this case, twice burnt once shy). I'd be happy to be wrong here because ergonomics of Git are really frustrating in many places.
Your thought process is completely fair, but just to clarify: Phabricator was never open-sourced by Facebook. The main engineer behind Phabricator (Evan Priestley) left Facebook to create Phacility and open-source Phabricator; that was never a Facebook product.
If memory serves, Evan open sourced Phabricator at Facebook back in 2010 or 2011, then quit to work on it full time.
Shortly after (months, years?) the internal version of Phabricator diverged from the now not FB managed or stewarded OSS one.
However I think it is fair to say, assuming my memory is correct, that Phabricator was open sourced by Facebook at a very different time, before the company really committed to supporting open source projects. At that time it was more ‘if an individual engineer wanted to then go for it’ rather than there being any formal process or consideration of longer term commitments.
That changed fairly shortly afterwards with the creation of the OSS team.
I remember someone transitioning to the newly formed team and moving from Dublin to London to do so in ~2012, as we became housemates :)
Although if Evan had access to the codebase after he was an employee and if it was the Facebook codebase that was open sourced then Facebook were involved. The original post sounded (to me) like the OSS code wasn’t the same as the FB code.
I think that just backs up my point that it was the Wild West back then in terms of individual decision making.
React[0]: JavaScript front-end web framework from Facebook. For good or ill, the most widely-used web framework in the world.
Not to say that Facebook will maintain Sapling, but React does stand as proof that they're not incapable of carrying an open source project to the finish line.
I have my doubts as to whether they can be a good citizen of the open-source community and respect the developers relying on their tools. I routinely see bugs marked “won’t fix” and nonsensical new features in the React Native ecosystem.
I thought one of the main open-source's selling point is the fact you can fork it and maintain it even when the original author abandons it. Any alternative I can think of is a community-owned open-source, which probably wouldn't often work due to limited resources and no initial funding or a corporate-owned closed-source, which once abandoned is dead for good.
HHVM is an interesting data point too - kept PHP compatibility for as long as there were significant open-source users (eg wikipedia), but after PHP7 caught up with a lot of the performance gains, meaning there was little reason to use HHVM in PHP-compatibility mode, they then went off in their own direction with Hacklang (which is still actively developed) to get all the benefits of being PHP-like without the drawbacks of being PHP-compatible.
PHP has some benefits to its design that the vast majority of other languages don’t — deployment is as simple as “stick a .php file on in your website folder”, hitting the “refresh” button gets you the latest code with no “build” or “restart server” step, it’s all stateless shared-nothing so you won’t have data from one request changing the behaviour of another request, etc.
But the implementation has a lot of drawbacks - the language is painful, typing is bolted-on and still incomplete after years of work (eg there are no typed arrays), the standard library is an inconsistent mess thanks to its origins of “take several other language’s standard libraries and duct-tape them together”, etc.
“PHP-like but incompatible” isn’t in itself a useful feature — it is the thing which unlocks a bunch of useful features (a sensible standard library, sensible list/dict datatypes, typed collections, XHP, async functions, generics)
The nice thing is that this is a client that works with vanilla git servers - if you switch to using it now, best case, you get a lifetime of good ergonomics; worst case, you get a few months of good ergonomics before something breaks that upstream doesn’t want to fix, and you go back to using the vanilla git client.
Major pain point of monorepos: Merge-conflicts.
When a merge-conflict happens YOU need to be expert and resolve all conflicts the right way, across the many unrelated domains mixed together.
How does sapling solve that? It can't...
IMHO Sapling looks like "Git for dummies".
And Git teaches some pretty useful concepts, which are worth it.
If you get a merge conflict in a piece of code, you must have touched that code. And if you were capable enough to modify the code to begin with, you should be able to solve a merge conflict around it.
I don't see why merge conflicts would be a pain specific to monorepos. The merge conflict arises when you and some other person modify the same parts of the same file. No matter the repo size the correct solution here is figuring out the intention of the other person by reading their change or talking to them and deciding how to combine it with your change.
The only reason which may make merge conflicts happen slightly more often in monorepo (vs constellation of small repos) is that not having the repo cloned locally is an obstacle to make the change. So some folks won't bother contributing repo that they'd have to clone first and instead they'd file a bug to the owners.
The interactive tool looks amazing. I do interactive rebases quite often and a drag-drop setup is wonderful
However, I don't understand why I would want 1 PR per commit. I feel like that's a non-starter for me.
Is the idea that no one should use branches - so there's only 3 points of interest: HEAD, main, and origin/main? And then is the idea that it's only 1 commit per feature to merge?
So I would work on something, make a PR, continue working on something else without making any git checkouts and then make a new PR?
Generally, you "stack" your commits/PRs and review them in small units (which I think is what you mean by working on something else without making any Git checkouts).
But you can certainly create new "branches" of development which aren't stacked on top of each other. They just don't have to have names. You can consider them to be "anonymous" branches.
The main advantage of 1 commit per PR is to review and commit smaller changes (a single commit at a time).
No, stacking is a (partial) workaround for Github "pull requests" being bad, by reimplementing the ordinary Git way to submit changes (one email per commit, reviewable inline. look at any patch thread at https://public-inbox.org/git/).
Doing this doesn't really make them good, but it makes them at least reviewable.
I would be more concerned about the ease of mistyping `ls` as `sl`, especially if you tried to list a directory whose name is a destructive Sapling command.
Neat! I hope this is a step in the right direction towards and not-so-bespoke SCM.
I do wonder:
1) How it handles large (binary) files. This is a major pain point when using git and even the standard solution (git-lfs) leaves *a lot* to be desired.
2) How does server hosting currently work? I didn’t see any mention and am assuming it’s not an option currently? (two dependencies of Sapling are currently closed source)
542 comments
[ 3.1 ms ] story [ 311 ms ] threadhttps://github.com/facebook/sapling
Stacked PRs are a blessing and a curse - still not convinced they are the correct way to build software as a team.
Obviously life is simpler if all your work is sufficiently non-intersecting that you can send separate diffs/PRs and e.g. rebase them separately, but if you have Big Feature X and you still want small, single thesis diffs, where else do you turn?
...until I got to pull requests (Granted, that is github, not git). But it looks like you cannot generate a standard pull request with it.
https://sapling-scm.com/docs/git/intro#pull-requests
Haven’t tried it yet, looking forward to it.
Of course, when it comes to github PRs, there are so many different "styles" of pull request, I'm not even sure which one should be considered "standard".
GP was probably talking about a time when they used it internally but maybe it didn't have a name by then.
How I think that will happen is using CRDTs against an AST to remove most merge conflicts.
When I say "not a CRDT" I'm obviously talking about HEAD not being a CRDT, a Git repo is append-only, so the history of a Git repo actually is a CRDT (but that's not what the comment above meant).
There are many reasons why GitHub (or something like it) are popular, such as:
1) not having to host the infrastructure yourself (incl. hosting it on AWS/Azure/etc.)
2) discoverability -- being able to follow people/organizations creating projects you are interested in; being able to search for projects ~ having these on various websites makes it harder to discover them
3) additional functionality/capabilities like static web page hosting (great for things like personal projects), and CI/CD workflows
My first experience with git was using git-svn to work with my company's internal svn repository, which I did for a couple years before the company stopped using svn. There was no internal desire for decentralized version control (rather the opposite, in fact; they wanted centralized permission management and such).
Did it? I think it had a short lived period of popularity when npm wasn't deterministic and is now mainly a novelty.
> React is.... ugh... React and it took over the whole dang industry.
Agreed there. But most people hadn't seen components before, they really were 10x better than MVC approach, even for all of React's complexity.
People are SUPER over dealing with using Git on large repos.
No joke, this might solve every major pain point I've had in mid-size-to-large teams in both the FOSS and proprietary world if it can deliver on what it says here, not to mention many issues with monorepo migration, work sharing, subproject management, etc. Many of these problems are very real but mostly ignored or we've decided to live with them.
Git is great, I'm one of the earliest GitHub users there is. I was also an early user of Darcs (which formed the theoretical basis for later competitors like Pijul -- so I'm not unfamiliar with radically different approaches), have a lot of experience with all kinds of administrative Git tasks for large repos; there's still plenty of room to fill gaps with new blood.
Nobody is trying to replace git; that’s not a stated goal. Plus git is so entrenched, it’s not going anywhere anytime soon.
However, few companies have more gigantic code bases than Facebook, which not that long ago, had their entire monorepo [1] in Mercurial, which had certain advantages over Git at the time.
So if there’s an organization that knows the pinpoints of version control, I’d put Facebook on that list. They’ve been working on approving version control at scale for more than 8 years.
As long as it’s git-compatible, the git true believers will have nothing to worry about.
[1]: https://engineering.fb.com/2014/01/07/core-data/scaling-merc...
It's also easy to commit / amend part of your work by selecting the lines to include in nice curses interface (--interactive).
I don't care for an interactive tool, IMHO I prefer using commands that are repeatable and learnable instead of stepping through some interactive workflow all the time.
I still commit small, frequent. But i like `git add -p` to skip debug lines, hardcoded conditions, etc. I don't want to mistakenly auto commit a whole pile of lines and then have to remove debugs/hacks/etc from things i've committed.
Stage + Unstaged is my working area, and the two live together quite nicely to me personally. I could live without it, definitely.. but i'm not sure i'd want to.
You can just use the tip as your staging. Use interactive amending to move changes from the working copy to the commit, and when you want to "commit", finish up the message.
hg actually has an "unamend" command (part of the "uncommit" standard extension) which... reverts the last amend. Rather than having to remember how to contort reset into the right shape to move changes back out of staging without destroying everything.
Combine this with the case where "I want to break one commit into two commits," where now I have to worry about making sure I know if the command is going to change the revision HEAD points to. At least there, the old commit will still exist as backup in the invariable scenario I screw something up.
You can use `git commit -p` to get the same functionality but without explicitly using the staging area.
Is the branch-tip simply the staging area? What if you're only half-done with that final commit?
https://sapling-scm.com/docs/commands/amend
In git, with `git commit --patch`
That's actually a deal breaker to me. Effectively using Git's staging area has become so integral to the way I work with repositories that I don't think I can ever go back to the old style.
edit: yep, so long git
Complete shit is what it is.
It's awkward, messy, inconsistent, and hard to compose.
I seriously doubt anyone who uses a sane interface to Git (e.g. a GUI) has any trouble with clicking + to add changes to the draft commit before committing it.
Most GUI tools let you automatically add all changes before committing anyway so you don't have to know anything about it if you don't want to.
They just needed to name things better (what is a "soft reset" again?).
A better fix would be to make the staging area an actual commit, and then reframe everything as easy ways to edit the latest commit. (This meshes well with adding features like Mercurial's phases or changeset evolution that make commit editing somewhat safer).
The whole point of the "draft commit" is that you can easily see changes against your (uncommited!) changes. That helps to build up a commit step by step.
Committing WIP stuff (and maybe even pushing that) makes the history useless. Branches don't help as you end up with millions of WIP branches that are all incompatible to each other (and the evolution that happened elsewhere). Only keeping WIP branches up to date is a full time job than.
Git has already a means to edit the latest commit easily: `git commit --amend`.
> Committing WIP stuff (and maybe even pushing that) makes the history useless.
Here's the thing. I'm a very big proponent of keeping history clean, and making sure that commits are atomic, and exorcising any "typo fixes" or the like commits from history. Not once have I found the concept of a staging area useful. Features like `git commit --amend` or `git add -i` are incredibly useful [1]. But not the staging area itself--it's only a thing that screws me up if I forget to add `-a` to `git commit`.
"Draft commit" also I think elucidates the other problem. You see, drafts of regular documents are frequently shared with other people, multiple versions of them created and shared, etc. Drafts don't become final until it's actually published--and there is utility in being able to track the differences in drafts as they are discussed. If you've got a "draft commit", then it should be able to go through this process--this is basically the process of code review.
Of course, we're already working with a VCS, which is designed to handle different versions of code, so... what if we made the "version history" of commits just... regular commits? Sure, shade them a different color, so you know that a commit is a draft, and you can tell which of the commit's parents [2] is the previous version. And knowing that a commit is a draft, when it actually gets pushed into the trunk, you can commit only that final commit and not include any of the previous history. Since the commits are using the same DAG logic under the head, questions like "what changed between version 2 and 5?" become just regular diff commands [3].
By the way, this system already exists. It's known as changeset evolution in Mercurial, and it appears that Sapling here has adopted it. My workflow in git tries to emulate this model to a degree, but the approach of having branches-based-on-branches doesn't mesh well with how git wants to do things.
[1] The number of times I have painstakingly sorted out which changes go into the commit with `git add -i` onto to immediately and accidentally undo them with a `git commit -a` is quite high. And because the staging area isn't an actual commit, it can't be recovered by digging into the reflog like actual commits can.
[2] If you amend or otherwise modify a commit, it has one parent, which is the previous version; if you rebase a commit, it has two parents, one of them the new commit it's based on and the other is the previous version.
[3] Worth noting that this question often turns out to be difficult to answer with most code review systems. Building a model of "commit history" into your VCS makes it come out for free!
Phases are a property of revisions that essentially let you know their state. By default, there are three phases: public, draft, and secret. You can't rebase a public revision, nor can you have a public revision with a secret parent. So you get out of this concept things like safe rebasing, or barriers that let you keep internal and external repos separate.
But revsets really shine. This is basically a full-on query language for revisions. So you can define a query alias "wip" that specifies all of the, well, interesting revisions: every revision that is not in the public phase (i.e., not in the upstream repo), the tip of the trunk, the current revision, and sufficient ancestor information of these revisions that you can see where you based all of these WIP branches on. In a single query: "(parents(not public()) or not public() or . or head())".
Sure, composing revsets is definitely a somewhat painful process... but it's possible to describe more or less arbitrary sets with a Mercurial revset, and I've never been able to find a similar workable setup in git.
The most common workflow I have is that I've got a couple of old working branches (like featurea and featureb), and I want to see if I need to update featureb to a newer head or not, or if featureb was based on featurea or featurea-v2. A demonstration of this kind of thing is 'hg wip' here: http://jordi.inversethought.com/blog/customising-mercurial-l....
Another thing I would use revsets for is answering queries like "which of these changes that's on the public repository made its way into the internal repository (which periodically merges from the public repo)?"
We often (a.k.a. constantly) want to see a subset of commits that meet some criteria.
The way Git handles this is by adding more flags, which doesn’t scale.
Just look at the man page for git log… it’s nuts.
Having a concise functional query language [1] is one of Mercurial’s and I guess Sapling’s killer features.
[1]: https://hg.mozilla.org/mozilla-central/help/revsets
AFAICT there are only two workflows involving the staging area: staging partial commits and resolving conflicts. The first case is taken care of by partial commit support, and the second case presumably has its own dedicated mechanism.
* get the hash of the HEAD commit * run `git diff <hash>` and see that there are no changes * make some change to a file * run `git diff <hash>` again and see the change you made * stage the change with `git add` * run `git diff <hash>` again and no changes are show! * commit the change * run `git diff <hash>` again and the changes are back
It's super bizarre to me that there would be some sort of intermediate state where changes aren't visible. I feel like it would make more sense to have some sort of formatting difference indicating unstaged versus staged but not committed versus committed, but I imagine changing that now would break all sorts of scripts, so we're stuck with it.
Staging just means "I'm happy with the changes so far but didn't finish everything I want in my commit; let's ignore this changes for now".
What `git diff` does by default becomes useful when you touch the staged changes again. Then you see only the new changes compared to the staged stuff. This helps building up a commit step by step, with some trail and error in between.
Think for example about something like: You use some tool to do some automatic changes. This creates hundreds of changed files. But the result isn't working. You could commit that, sure. But than you would need to rewrite history before pushing because creating not working commits is a terrible idea. Or you could just stage the changes for now. Than you can change / repair the still missing parts. Git diff will helpfully show you only the new changes but ignore the staged stuff as long as it's untouched. You would now for example easily see changes that you made to the automatic rewrites. Without the staging behavior you could only compare with a committed state, and drown in hundreds of changes that are unrelated.
The main problem with the staging area is that quite some GUI tools don't use it correctly. The tools try to "simplify" Git by ignoring how the stating area is supposed to work, or ignore it completely like the infamous JetBrains IDEs. (IDEA is the tool that needed almost 10 years to implement Git sub-modules…). I think the VCS handling in IDEA is on the surface very polished. But when it comes to something like the staging area the UI-wise very crappy VS Code Git support beats that by far. Sublime Merge does also the "right thing"™ and hides the staged changes at the bottom so you see only the the changes to the changes. Exactly as the staging area is meant to be used!
Git is conceptually very sane and logical. But the UX is indeed (still) terrible.
But a lot of people seem to complain about the complexity of the underlying concepts. That makes no sense to me as the concepts are complex because the problem at hand is complex. Also it makes no sense to me that people are constantly crying for a new tool even they have obviously issues with understanding the problem space and all requirements. A new tool would not make all this complexity go away! It could at most try to "hide" some of the complexity by introducing magic. This wouldn't be helpful at all as magic is way worse than bad UX, imho. Bad UX is bad UX you can deal with it. But when magic goes wrong all bets are off and you're usually in deep trouble.
This looks like people would argue to rewrite the back-end (some propose form scratch!) even only the front-end needs some face lift…
We develop HighFlux[1] which also gets rid of the staging area. It simplifies your mental model of what's going on a lot.
Because everything you save is automatically committed, switching to a different task/branch is also always instant without needing stash.
Because what you're testing locally is what you're committing, I also never have CI failures anymore (with the staging area I frequently had unexpected interactions with unstaged changes and sometimes even accidentally forgotten added files).
1: https://highflux.io/
Rebasing, cheery-picking, or reverting of commits becomes impossible when every save of a file is pushed.
You could just publish local IDE history… Would be equally "good" I think. (My IDE is saving files every few key strokes btw; the resulting history would be a bloody mess).
Why not go one step farther: Just make an automatic block image of the whole systems of every developer machine every few seconds. You could than just deliver the image. No docker setup needed any more. Just write code. And when the local version works, ship the whole local system just as it is. ;-)
Google does this, it has saved my butt on more than one occasion.
> Sounds like saying bye-bye to any meaningful history.
I'm so confused, you just amend the most recent commit, or work with changes unstaged and uncommited. Like my normal workflow is basically "change 2-3 files such that things are passing, hg commit, send for review", and then I continue working on the next thing, either back to HEAD if its unrelated, or on top of the just-pushed changes if it depends on them.
It's vastly simpler than having to git add at random times.
> Google does this, it has saved my butt on more than one occasion.
You mean backups? Yes, backups are a very good idea.
But this has nothing to do with VCS. That are separate topics.
> Like my normal workflow is basically "change 2-3 files such that things are passing, hg commit, send for review", and then I continue working
As long as the requirements are so trivial even CVS would suffice.
But even considerably simple refactorings (in e.g. static languages) can be much more complex. It's easy to end up with hundreds of files changed. Than you need more powerful tools. Doing such things without the staging area is almost impossible to get right. (The only alternative would be quite some rebase sessions; and those are way more complicated than using the staging area upfront; also you would need some way to do diffs against "pined" changes—which is something you get for free with the staging area).
No, I mean that the filesystem I edit code in has a full snapshotted history of every save and I can recover to a particular revision or particular point in time, even one's that weren't committed to vcs[0]. I guess you can call that a "backup", but like it's not what people usually mean.
I have used this to recover some things I was working on three months ago but ended up throwing away.
(citation:
> All writes to files are stored as snapshots in CitC, making it possible to recover previous stages of work as needed. Snapshots may be explicitly named, restored, or tagged for review.
via https://cacm.acm.org/magazines/2016/7/204032-why-google-stor...)
> It's easy to end up with hundreds of files changed.
Sure, doing a rename can touch 100 files, but you should isolate that change to a commit and PR that don't do anything else. Running sed and then committing or using your IDE's refactor feature, and then committing and running tests doesn't require a staging area.
[0]: The truly wild thing about this is that the vcs state is also stored in the snapshotted, point in time recoverable fs, so if you do the equivalent of absolutely botching your git history, you can jump back in time a few minutes and start from a known good state.
Sorry, but no. "No staging area" is what VCSes were like before Git and it was worse, much worse.
It would be very difficult to handle quite some "WIP situations" without the staging area.
I think we’ll be okay with this stack workflow [2] taken from Mercurial’s Evolve command [3].
[1]: https://en.wikipedia.org/wiki/Stockholm_syndrome
[2]: https://sapling-scm.com/docs/overview/stacks
[3]: https://www.mercurial-scm.org/doc/evolution/tutorials/topic-...
I lived through the alternative and the staging area is superior. If it wasn't, I might not be using Git, or at least begrudge when I have to; neither is the case.
The whole point is that this things are treated differently.
The whole point is to have some form of "draft commit".
The staging area lets me "stash" WIP changes in a transparent way.
Having a "draft commit" feature avoids the need to rewrite "bad commits" after the fact.
The staging area is really useful to build up commits gradually.
When thinking about it I've just realized that the staging area should not only be kept as a feature but could be even extended. You could add a "change-set management system"—which would be essentially multiple staging areas (maybe coupled to improved stash functionality to be able to quickly move / copy changes between change-sets).
Yes, such a thing would very likely need to be built on top of the mechanics behind branches / commits. But this should be transparent and not interfere with the said features, imho.
I also don't see anything that couldn't be trivially added to Git. A few lines of shell script would likely suffice. What do I miss here?
you can always just use another commit as a staging area, I figure, and it'll make all the commands simpler and more intuitive so it wins in my book.
And than you have to do gymnastics with rebasing and rewriting history afterwards. Using the staging area upfront is much simpler!
Git's major value proposition is that they added moving parts until the system worked great. If you don't want named branches, staging, or any other piece of the ideology, then subversion is a fine choice.
But most folks moved on from svn for reasons
Moving from SVN to git felt like liberation because there were suddenly idiomatic ways of expressing states that were sort of smushed together by SVN -- stuff like, "I have some changes in my branch I want to line up for commit" which, became the handy one-word concept, "staging".
The before-times were marked by a lack of these fine distinctions. While they existed in fact, they were obscured in-system.
Like a map, any tool should 'resemble' the sphere of human activity it potentiates, and git resembles our diverse workflows better because it has so many asinine distinctions.
This was always its strength, and indeed, likely the reason the platform is called 'git' in the first place, as 'smarmy git' (English idiom for 'smartass') implies an insufferable drawer-of-distinctions.
And like a smarmy git, it's easier to complain about git than it is to replace it.
I imagine this will be settled by git steamrolling sapling in the market, but I wonder if there's a faster (and less network-effected) way to adjudicate? Both your position and mine seem lodged in a taste/touch/feel context, which seems like a data-poor place to make good decisions.
On the other hand, I'd say that absent sufficient data, one should pick the most flexible tool, which I'll bet in this context is the one with the most moving parts, i.e. git.
The findings validate the earlier conceptual design analysis in practically all aspects: https://gitless.com/#research
Git doesn't support certain workflows well. For example, how do you split the contents of the staging area into two separate groups? Sapling handles this the same way it handles splitting commits in general. Essentially, it has a greater set of verbs that act on a smaller set of nouns, where Git has a medium set of verbs that act on a medium set of nouns.
One of my favourite parts of rationality is simply admitting that the data shows you're wrong
What is the workflow behind this ask? I don't understand what the goal is. The basic git workflow:
1. Edit and save a tracked file; the changes appear in the working tree.
2. Select some subset of the changes in the working tree to stage them in the index.
3. Form a commit with the changes in the index.
IIUC you want to add a step in between 2 and 3? But the way I see it, 2 is doing what you want. I can split the set of current changes by selectively adding them to the index in preparation for a commit. I can also selectively un-stage changes if I decide I don't want them to become part of the commit.
Between the changes to a file in whatever editor buffer I'm writing in, saving those to disk, moving changes from unstaged to staged, and forming a commit in any of a variety of ways (plain ol' commit, amending a commit, a fixup commit) I can't imagine what other way I need to slice and dice changes. Maybe it's just a failure of my imagination since I've been using Git for so long now and only more basic things like SVN/TFS/CVS before that.
Occasionally, I do run into the situation where I've staged some changes and then realize that I want to start staging another commit first, but don't want to lose the changed I already staged. Unstaging my current changes means I have to remember and select them again later. I could also just commit what I have staged and start staging a new commit (and perhaps reorder the commits later), but that shows that the staging area was unnecessary in the first place, and I could have used commits to accomplish the same workflow without adding a new set of concepts to my VCS.
... such as `git commit --patch`, which this discussion has taught me many people don't know about, even those who know about `git add --patch`.
(I was one such person a few years ago until my mind was blown by an HN commentor doing me the same favour as I have done here :) .)
Outside of the Git fanboy bubble, developers don’t want to have to be version control experts, which once you get past a certain level of Git usage, you have to be, whether you wanted to or not.
Especially if your team doesn’t have that person who can get you out of any Git jam you may get yourself into.
It will also be much faster to get a new developer up to speed using Sapling than Git. And because it’s Git-compatible, if there’s something super advanced that can only be done using the Git command line, that’s still an option.
Telling the intern/junior developer to read the man page for git-log is a non-starter; it’s over 19,000 words!
The best thing for the greybeards is they can continue using Git while others use Sapling and commit to the same repo.
That said, I hear you when you say that most folks (think) they have better uses of their attention.
I daresay they're wrong, but it's not my place to dictate terms to anyone's curiosity, my own included!
It wouldn’t be that big a deal except pretty much every important git command is similarly complex for noobies.
SVN branches were this doofy copy procedure that made sense once you drank the Kool aid, but they were so unwieldy compared to git
(a) People arguing git is fine, and shouldn't be simplified
(b) People arguing about the right way to use git, and flame wars about best git workflows
I mean most people simply see (b) and conclude "this is a huge hassle, I don't want to annoy some git-workflow-purist, I'm just going to walk on eggshells on this tool and hope I don't break anything"
It's as much a social problem around conventions, and lack of opinions in the tool itself, then anything about the underlying technology (which is rock solid IMO)
I use git begrudgingly because that’s where the world is, but I long for an improvement in this space.
Git has a lot of incidental complexity and unforced design problems, but the fact that it's inherently flexible is not one.
(c) Git is a disaster that has destroyed source control for 15+ years and the industry is unable to recover from due to Stockholm syndrome
It’s impossible to discuss source control without people coming out of the woodwork to shit on git. It doesn’t work right. It’s too hard. SVN was better. Mercurial should have won. Blah blah blah.
Git isn’t perfect. And the command line has improved (I don’t care much, I use a GUI).
But the number of people who seem to insist that because it doesn’t work for them or they don’t personally like it it’s horrible and everyone should abandon it is crazy.
And it makes trying to read/participate in discussions like this painful.
Is git sometimes obtuse? Sure, but it's fast and incredibly powerful. My everyday commands are easy to use, and if I need something special I go to the documentation - just like any other SCM.
Sure, most people are probably fine with Git once they learned it and if they only work with small to mid sized code bases (like me). But I'm still happy Sapling is out there, I might use it or learn from it if I ever run into the problems it solves.
...and yes, I realize it's weird to say this considering Google is known for abandoning things. Maybe it's just coincidence that I've run into more abandonware from FB than Google?
As far as I can tell, most of zstd's development is still by Facebook employees, though not all of it. I tend to think zstd has enough traction that development would continue even if FB were to abandon the project.
https://github.com/kspalaiologos/bzip3
I believe Yann Collet, author of lz4, eventually went on to work for Facebook and finished it there.
Whatever is cool at Google IO XYWX is already abandoned by the time we reach into Google IO XYWX + 1.
This is my naive understanding. A for profit company open sources a project that they have been using and developing internally. The have built a philosophy and understanding of the project as they use and develop it. Most of their action regarding the project is that they must use it and they usually don't have any other options.
Because the foundation is already laid the solution for its shortcoming is having just an understanding of them. Then you open source the project knowing that you have developed the project to it's completion.
Now comes the OSS community. Either we request features that goes against the project philosophy or we don't want to get involved because we don't need to compromise and acknowledge the shortcomings because we have options.
A good solution can be open sourcing projects that the org thinks isn't complete and needs further development without compromising security, philosophy and usability. Because if you have a list of things you need, you can ask the OSS community to fix those things rather then be critical of the foundation and philosophy.
Idea for projects not owned by mega-corps (half real, half fantasy):
1. Get the project added as a package to one or more major commercial Linux distros, e.g., RedHat, etc.
2. Grant commit access to one or more devs at the same Linux vendor. Allow them to do whatever they want. You might not like their direction, but it should survive.
3. Retire from the project whenever you like.
4. Also, you could post a note in README about retiring. If people want to add features, ask them to fork, or just grant them commit access and let them go wild.
[1] https://killedbygoogle.com/
If it comes to stuff I actually want to _use_, I avoid projects backed by a single or a few companies - like Sapling. So from that angle, I'm not particularly impressed either.
Better than Microsoft? I don't think I've ever been able to talk to a human at Google, whereas with Microsoft, I get feedback very quickly on issues and pull requests. Does Google even interact with people with open source? For example, I am using Skia via SkiaSharp, and the only place I know of to go for Skia help and issues is their Google Groups page, a website out of the 2000s. And very few seem to actually monitor the group. I'm not even really sure what Google does in open source. Even the things that are released to the public, like Skia, are well known to come with a huge amount of internal baggage.
Whereas Microsoft has dozens of active projects on GitHub where you can talk directly with the people working on it at Microsoft.
> I'm not even really sure what Google does in open source.
Kubernetes (and a bunch of the offshoots), golang, a bunch of ML things, etc. It's just that many have independent foundations (CNCF) running them now to keep project management independent from a single company.
I... what? Do you have a source or speculating to create fear of future support?
I'm nearly certain this is untrue.
I don't actually want "No" staging area.
What I want is, once I "add" something, the file stays added.
Currently, I have `git st` alias setup :
This auto-updates the staging area for files that were previously staged.So I get `git add` but also don't have to re-add anything manually from there ...
Since I do `git st` quite frequently, this works out for me ...
git add -p
Allows you to select hunks of changes and stage them for committing...
There's nothing saying that one can't add chunks to the staging area and then immediately commit it without invoking that alias afterwards (since it is a very deliberate "add these things" rather than "adding a bunch of things and keep adding."
git commit -p achieves the same, but avoids explicitly using the staging area.
If you want that behaviour, you can just `git commit -a` when you create your commit, then you only have to "git add" brand new unknown files.
The reasons for that are:
1. In the vast majority of cases there are multiple files I want to commit together. Usually I change them multiple times during the process.
2. It almost always starts with some debugging in a couple of other files, and I often want to keep that debugging for a couple of next commits, but `git checkout HEAD` these files in the end.
3. For me, the most popular way of using git rebase → edit (which I do reasonably often) is splitting a commit into 2 by separating files. This is easy enough by just changing a status of a file from "staged" to "modified" (I even have `git unstage` alias for that) and commiting.
So I kinda get why GP wants what he wants. This isn't crazy.
Now, that being said, I personally have absolutely no problems with how git does that now: I've figured out a workflow that solves these problems for me, and everything is ok now. This workflow is basically making many dozens of tiny commits to a branch without even bothering to name them properly, and then just doing `git rebase -i` many-many times while working on a single branch. So I just commit the code I don't intend to keep with a label "drop that", and drop these commits when I'm done. And other commits usually are heavily reordered and squashed into 3-5 larger commits that make some sense on a higher level (like 500 LOC of refactoring first, and then 1 LOC of an actual bug-fix, which usually makes much more sense than just 500 LOC of a bugfix, that solve the problem somehow, but it's absolutely not obvious how exactly). I rarely can figure out that separation before I'm done. In fact, I often fix the problem first, then refactor, then roll-back the fix just to add it again in a separate commit in the end (if the refactoring and the fix affect the same file, which also is often the case).
So I'm replying to OP's very specific usage pattern, and you object with a completely different usage pattern?
> `git commit -a` is basically an equivalent of having no staging.
GP doesn't use the staging as a staging, since they immediately stage all modified files. That means the staging is useless, they can just commit files straight from modified. Which is what `git commit -a` does.
> 1. In the vast majority of cases there are multiple files I want to commit together. Usually I change them multiple times during the process.
OK? `git commit -a` doesn't preclude that. You just use it instead of `git commit`.
> 2. It almost always starts with some debugging in a couple of other files, and I often want to keep that debugging for a couple of next commits, but `git checkout HEAD` these files in the end.
I'm really happy for you. It doesn't work when the files are already staged, which is the case of GP.
> 3. For me, the most popular way of using git rebase → edit (which I do reasonably often) is splitting a commit into 2 by separating files. This is easy enough by just changing a status of a file from "staged" to "modified" (I even have `git unstage` alias for that) and commiting.
https://sapling-scm.com/docs/commands/uncommit
> uncommit part or all of the current commit
You don't need a staging area to craft commits, you can manipulate the tip commit directly. With good enough support for that (which sapling seems to have inherited from mercurial), the staging is just an unnecessary pseudo-commit.
- It's too hard to scale for a large monorepo!
- Google does it just fine!
- But I don't have access to Google's tools!
So kudos to Meta for both solving the problem and making it available to others. It will be interesting to see how useable it is outside of Meta. I know for example that while Netflix open sourced a lot of tools, most of them weren't useable unless you ran all of them together. So far Meta has been good at avoiding that, so hopefully that remains the case.
I think another thing that matters is how you store branches/code under review. In Linux, each team/person has their own repo. The main "Linus" repo has mostly the finished code. In a company it is much more common for everyone to store their unfinished code centrally. Perhaps this also accounts for some increase in size.
Firstly, it's not "giant amounts of binaries" it's "a very small amount of binaries". A few GB is enough to cause significant problems.
Secondly, This _is_ an issue with git. If my project requires binary files, git should handle it. How should we handle logos in a mobile app, branding images on a website, audio files for background? That's before you get to the question of "how does a video game store the source version of a 100GB worth of compressed assets?"
A bit like submodules, LFS has its own warts that seem to multiply when you add more people to the mix. Working with git LFS has been the _only_ time the solution to my problem has been "nuke and clone again", in almost 15 years using source control.
Last time I used git LFS, it didn't support ssh cloning at all, and the issue had been open for years at that time.
A few years ago they had a Virtual File System extension for Git. Now it's a public fork of Git that is intended for large repositories (several hundreds of GB). It adds a `git scalar` command, see https://github.com/microsoft/git/blob/HEAD/contrib/scalar/do...
Its design is inspired by Sapling, and, in fact, it uses some of the same code, such as the segmented changelog implementation. Possibly some of its ideas made their way back to Meta, such as interactive undo?
Jujutsu also supports colocated Git repositories: https://github.com/martinvonz/jj. It also has the working-copy-as-a-commit idea and conflicts are stored in commits (so rebases always succeed). I think it's a step forward compared to git/hg/sl.
Sapling, on the other hand, has much better support very large repositories, since they've spent a lot of time on that over the years. We're going to copy some of Sapling's solutions to Jujutsu soon, since we're working on integrating it with Google's monorepo (slides: https://docs.google.com/presentation/d/1F8j9_UOOSGUN9MvHxPZX..., recording: https://youtu.be/bx_LGilOuE4).
The Sapling support for remote repositories was a little rough in my opinion. Jujutsu and git-branchless can both co-locate with the Git repository, so you can always drop down to Git commands if there's something you're having trouble doing. (I find the `jj git` commands to also be better at interacting with remotes for now.)
Is this pushed to the remote when running as a git porcelain?
What do you mean? Can't you use Sapling in the same Git repository you normally use? The first sentence is "Sapling is a new Git-compatible source control client". Is there something they're not telling us?
EDIT: Looks like it calls out to the git executable occasionally (https://news.ycombinator.com/item?id=33615576) and presumably works on the git object model under the hood, but you can't use `git` on a repo checked out using `sl` nor vice versa. It's a stretch to call it Git-compatible but I guess not completely wrong.
EDIT2: Here's a good summary https://news.ycombinator.com/item?id=33617689
git-branchless is only an extension to Git, so it naturally operates in the Git repository. Jujutsu has a mode to create the `.jj` directory alongside the `.git` directory and co-locate them, which I find very convenient in practice. (Originally, Jujutsu only supported Git compatibility in the same way as Sapling, via pushes and pulls, but they added co-location later.)
> Looks like it calls out to the git executable occasionally
I believe Jujutsu never calls out to Git, and that all of its `jj git` interop commands are implemented via direct bindings to libgit2. This is less fragile in many ways, but it can also mean that `jj git` interop might be missing some new feature from Git. Fortunately, you can oftentimes just run the Git command directly in the repository when co-locating.
> presumably works on the git object model under the hood
There's no guarantee of this: the Mercurial (and therefore possibly Sapling?) revlog model is a little different from the Git object model, as I understand it. But it doesn't really matter, as long as it interoperates seamlessly. For now, I believe they do literally have a `.git` directory somewhere under the `.sl` directory, but they reserve the right to change that.
Oh, I was referring to Sapling. I know even less about Jujutsu than I do about Sapling!
> > presumably works on the git object model under the hood > > There's no guarantee of this ... but they reserve the right to change that
Interesting. So it would translate between them whenever you push to or pull from a Git repo?
I'm very keen to use Sapling if it's basically a polished interface to Git but less keen if it's an entirely different object model, because then I'm going to have to learn more about what's going on under the hood to understand it properly.
I was just remarking about Jujutsu, in the case that it was important to you for some reason whether or not your VCS called out to Git.
> Interesting. So it would translate between them whenever you push to or pull from a Git repo?
To be honest, I don't know. I suspect that, for now, they store real Git objects, rather than translating on the fly. You'd have to ask a Sapling maintainer.
> I'm very keen to use Sapling if it's basically a polished interface to Git but less keen if it's an entirely different object model, because then I'm going to have to learn more about what's going on under the hood to understand it properly.
I might have muddled some layers of abstraction and brought up something unhelpful. Git's object database and Mercurial's revlog are more comparable in terms of where they lie in the abstraction hierarchy, but these are just the storage layers. In practice, I find the Git and Mercurial object models, as exposed to the user, to be similar enough that I pretty much never have to worry about the differences. (Well, perhaps it's true that Mercurial file contents are not addressed by blob hashes, but do I ever really want to address by "blob hash", or just by "the contents of this file at this commit"?)
What I meant to emphasize is that you can't directly use Git to access Mercurial/Sapling's internal object store, if that's important to you (perhaps for scripting). In comparison, with Jujutsu, if you modify the Git object store on disk, it will try to "import" refs the next time you invoke it in order to update its own internal object store to match.
absorb split histedit uncommit unamend revert metaedit
Once you use them, it's hard to go back.
Was bulk of the team behind this laid off wherein it made sense to open source it to involve the community to take it forward rather than paid resources?
To be clear: I'm NOT criticizing them open sourcing this.
The team working on Sapling has been planning this release for a very long time and will continue working on Sapling to support our internal engineering efforts.
How does sapling let me take a long list of commits and break them into larger but more manageable chunks?
git add -p allows me to add chunks easily and create commits, git commit --fixup allows me to mark a commit as fixing a previous commit, and with git rebase -i --autosquash I get to easily take those fixup commits and meld them into the previous commits.
Also reviewing a stack of patches is annoying in many cases as I care more about the end result vs each individual commit. But that may just be my experience talking in open source where I am working on smaller but better well defined projects vs a large mono-repo where there may be a lot of changes across many disparate parts of the code base that make it difficult to look at the "whole" vs a patch that is more localized.
To split a single commit, you can use `sl split`, which is quite difficult in Git. (I miss that feature in Git quite a lot.) You can also use the `sl absorb` command to automagically merge local changes into the previous patches where they seem to belong (roughly speaking, commute changes backwards until they would cause a merge conflict, but it's a little smarter about avoiding certain merge conflicts).
It sounds like I would need to:
- switch - amend the commit - restack? - switch back to the HEAD?
Fold based upon the documentation seems to move older commits into the current commit? vs the other way around? https://sapling-scm.com/docs/commands/fold
This doesn't seem analogous to git rebase --autosquash which merges the mixup into the old commit.
If you want to run tests on commits without checking them out, you might want to try my `git test` command: https://github.com/arxanas/git-branchless/discussions/643
You can use `sl fold --exact` to combine arbitrary commits without including the current one. (To be honest, I just always used `sl histedit`.)
Other commands useful for amending changes to previous commits:
* `sl goto --merge <hash>` - if theres no conflicts you can just switch commits with pending changes and those pending changes would be applied on top of other commit. If there are conflicts this command would fail https://sapling-scm.com/docs/commands/goto
* `sl absorb` - this automagically finds the last commit that touched the lines that are pending and amends that commit with them https://sapling-scm.com/docs/commands/absorb
your workflow resembles mine so I'm in the obligation of mentioning https://github.com/jesseduffield/lazygit which allows you to stage individual lines among other features. https://www.youtube.com/watch?v=CPLdltN7wgE
I also use this workflow. Lots of detached HEAD mode, using the index to commit things piecemeal, then rebase as needed, until I'm finally done.
Sure, but you can use `git commit -p` to get the same benefits without explicitly using the staging area.
Flow[1]: JavaScript typing system from Facebook. My company uses it, open development has since been halted by Facebook so we're effectively on abandonware.
EDIT: React: Javascript framework from Facebook, my company uses it, and while it has its warts it works pretty well all things considered and Facebook has continued to support and evolve it over time!
For all I know Sapling is fantastic and will be developed for years to come. But personally I can't help but feel "once burnt, twice shy" (or in this case, twice burnt once shy). I'd be happy to be wrong here because ergonomics of Git are really frustrating in many places.
0: https://www.phacility.com/phabricator/ 1: https://flow.org/
Your thought process is completely fair, but just to clarify: Phabricator was never open-sourced by Facebook. The main engineer behind Phabricator (Evan Priestley) left Facebook to create Phacility and open-source Phabricator; that was never a Facebook product.
If memory serves, Evan open sourced Phabricator at Facebook back in 2010 or 2011, then quit to work on it full time.
Shortly after (months, years?) the internal version of Phabricator diverged from the now not FB managed or stewarded OSS one.
However I think it is fair to say, assuming my memory is correct, that Phabricator was open sourced by Facebook at a very different time, before the company really committed to supporting open source projects. At that time it was more ‘if an individual engineer wanted to then go for it’ rather than there being any formal process or consideration of longer term commitments.
That changed fairly shortly afterwards with the creation of the OSS team.
I remember someone transitioning to the newly formed team and moving from Dublin to London to do so in ~2012, as we became housemates :)
> left Facebook in April [2011], and shortly after, we open sourced Phabricator
Although if Evan had access to the codebase after he was an employee and if it was the Facebook codebase that was open sourced then Facebook were involved. The original post sounded (to me) like the OSS code wasn’t the same as the FB code.
I think that just backs up my point that it was the Wild West back then in terms of individual decision making.
React[0]: JavaScript front-end web framework from Facebook. For good or ill, the most widely-used web framework in the world.
Not to say that Facebook will maintain Sapling, but React does stand as proof that they're not incapable of carrying an open source project to the finish line.
[0] https://github.com/facebook/react
Overall, I'm glad they do these things, though since it is better to have this code accessible than absent. Good on them!
I have a hard time understanding the benefit of using a language that’s almost like a very popular one, but not quite.
But the implementation has a lot of drawbacks - the language is painful, typing is bolted-on and still incomplete after years of work (eg there are no typed arrays), the standard library is an inconsistent mess thanks to its origins of “take several other language’s standard libraries and duct-tape them together”, etc.
[0]: https://eev.ee/blog/2012/04/09/php-a-fractal-of-bad-design/
How does sapling solve that? It can't...
IMHO Sapling looks like "Git for dummies". And Git teaches some pretty useful concepts, which are worth it.
The only reason which may make merge conflicts happen slightly more often in monorepo (vs constellation of small repos) is that not having the repo cloned locally is an obstacle to make the change. So some folks won't bother contributing repo that they'd have to clone first and instead they'd file a bug to the owners.
However, I don't understand why I would want 1 PR per commit. I feel like that's a non-starter for me.
Is the idea that no one should use branches - so there's only 3 points of interest: HEAD, main, and origin/main? And then is the idea that it's only 1 commit per feature to merge?
So I would work on something, make a PR, continue working on something else without making any git checkouts and then make a new PR?
But you can certainly create new "branches" of development which aren't stacked on top of each other. They just don't have to have names. You can consider them to be "anonymous" branches.
The main advantage of 1 commit per PR is to review and commit smaller changes (a single commit at a time).
Doing this doesn't really make them good, but it makes them at least reviewable.
https://github.com/mtoyoda/sl
I do wonder:
1) How it handles large (binary) files. This is a major pain point when using git and even the standard solution (git-lfs) leaves *a lot* to be desired.
2) How does server hosting currently work? I didn’t see any mention and am assuming it’s not an option currently? (two dependencies of Sapling are currently closed source)
The utility should obviously be called `sap' and not `sl'.
Non native here, could you explain? I know git is a slur and sap is the "tree blood", but are there other meanings I'm missing?