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.
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.
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…
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)"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.
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!
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).
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?