Have you read the paper, especially their study design?(p11/302 is a good summary, also last 5 pages for actual instructions). They have sample size of 10 people, and they specifically choose a very basic workflow.
This study design is a real problem -- even after I have read the paper, I am not convinced that I should be using gitless. Their graph looks science-y, but the small sample size and non-representative experiment design means the conclusion do not apply to my work.
Reading this paper is reading a DB benchmark which only used rows with two integer fields in all their tests. Sure, it is interesting reading, but unless your workflow is exactly that, it is useless in real life.
(Disclaimer: it is entirely possible that gitless is better than git in all workflows, not just the one described in the paper. But even if it is the case, I would not know about it, at least not from the gitless site.)
I'm not sure I put a lot of faith in that study to measure anything other than that new users of a VCS are confused by the existence of a staging area.
I don't doubt that not having the staging area would make it better for new users, as it is certainly conceptually simpler to just commit everything. And for those who's revision control model is always linear commits of what's in the working copy, it really is just added work and complexity. But it enables other workflows (that I personally happen to use a lot) that are easy with staging and /very/ inconvenient without.
So yeah, feel free to leave it out of your implementation; it doesn't seem like something that'd fit into the plan9 mindset anyway. But it is removing a very useful tool to some people.
I often make many changes at once and then begin to commit pieces of it incrementally as I go. Each commit should represent a perfectly valid version of the code base, but your working directory rarely represents a valid version during development. Without the staging area I wouldn't be able to begin commiting, and therefore using git, until much later, maybe days later.
2. "git add" pieces that I think are "commit worthy", even if I'm not sure I want to actually commit it yet (still exploratory work).
3. If I decide I don't like the latest set of modifications, I clear them all, going back to what's in my pseudo-commit.
4. Once I'm happy with it as a whole, I turn it into a real commit, complete with message.
What would be really nice is some sort of "pseudo-commit stack" whereby you can do this workflow, but every mini-commit goes into a stack that you can unwind to whatever point you want if it turns out that you've been going down a blind alley. If pseudo-commit stacks had first class treatment, you could even stash them, which would be great if some other emergency came along that you had to deal with now.
Internally, you could create some kind of HEAD-like pointer for this, store commits that go from HEAD to PSEUDOHEAD, and then when you're finally ready to commit, squashes everything from HEAD to PSUEDOHEAD and follows standard commit procedure from there.
Or maybe you could implement it as a "reset HEAD" kind of thing to collapse the pseudo-commits, which then allows you to use standard partial commit semantics if needed.
Actually, now that I think of it, I could do a poor man's version by tagging REALHEAD before I start, making actual, real commits as I go, and then "git reset REALHEAD" to start building the actual commit I want...
Doesn't git reset do what you want? For example git reset <remote>/<branchname> would usually do it for me, but you could just as easily do it to any other commit.
> What would be really nice is some sort of "pseudo-commit stack" whereby you can do this workflow, but every mini-commit goes into a stack that you can unwind to whatever point you want if it turns out that you've been going down a blind alley.
Mercurial patch queue is pretty much that. You can also use temporary commit and phases to ensure they are not visible from outside your local repository.
I have never really understood why git introduces all this weird concepts (staging, stash) for what are just temporary commits in the end.
> What would be really nice is some sort of "pseudo-commit stack" whereby you can do this workflow, but every mini-commit goes into a stack that you can unwind to whatever point you want if it turns out that you've been going down a blind alley.
I use branches for this, and private remote repos. I thought that stash was magical and cool until that time I stashed in the middle of a merge and dug into the details of stash...
If I hate the state of my experimental branch, I kill it. If I want to keep parts, I cherry-pick them over to another experimental branch. When I want to keep fractions of patches, I manually copy deltas across with git-difftool (which was the only real reason I was stashing anyway). And when I finally have a branch that I'm happy with keeping, then I use "git merge -i" to collect the mini-commits into a sensible history
If you are committing subsets of your work, how can you guarantee than each commit is a working version of the code? Some committed code being dependent on uncommitted code. Or am I missing something?
Sometimes, you want to have a temporary snapshot, but not a commit.
So I write some code, it works, but I'm thinking of trying some things out. I stage the working code and make more diffs. Now I can have my editor show the diffs between my stage and my new changes. And I can easily rollback individual files or re-stage some more files.
Until I am satisfied. And now decide to make a commit.
I could just make them all commits, and then do a interactive rebase as well. And I do that too. But sometimes the staging area is just simpler and quicker. With git status I can see it at a glance. Etc.
Same here. Without staging how do I change 10 files but only commit 3 of them? For example working on a change, then notice a bug that should be fixed now. Currently I just stage the 3 files for the hot bug fix, commit those, deploy, then continue working on the remaining 7 files.
It is possible to use git stash for that. In addition to being conceptually simpler it also allows you to run tests on the “partial commit” before commiting it.
You could always just unstash things, I suppose. In the context I was talking about, the stash is only used to prepare a commit, and after that you can unstash everything. For longer term stashing I think named branches you easily checkout are a better alternative.
I wonder how much overlap there is between the "staging area is useless" and the "never rewrite history" crowds.
Personally, I use staging area all the time to create a curated set of commits that make logical sense when putting them up for review.
My workflow is generally to keep a very messy history of `XXX` commits until I am happy with what I've come up with. Then reset back to the branch point and using the staging area to build up logical changesets.
I don't care about the history of my coming up with the solution, I care about presenting the solution in a way that makes sense to reviewers, including future me.
In my opinion, `git add -p` should be the default, and a `-a` or alike to forcibly commit the entire working tree. I have developed a habit that I wouldn't feel comfortable if I haven't reviewed the chunks I am going to make into the commit.
Whereas I don't feel comfortable with untested work, so "git checkout -p" (undo a change) and "git stash -p" (postpone a change) are how I get the workspace clean enough to commit.
While there are many people who don't understand git's staging area, there are far less that understand how to selectively stage/commit chunks, or even that such functionality is available at all.
It should be a crime for people to blindly stage everything. Cleaning up after my colleagues easily falls under the "lost cause" umbrella.
In the case of console.log() and debugger breakpoints, that's not a concern.
Often they're disjoint enough that I'm confident that if the end result works, the intermediate commit works, as the sibling commenter mentioned. (Even if not, on many teams it all gets squashed anyway, the intermediate commit message just makes the Pull Request easier to review.)
I can also often easily comment out the uncommitted stuff.
Finally, if I'm really worried about it, after creating the intermediate commit I can just `git stash`, test it, then `git stash pop` and keep working.
I think many people (eg the "never rewrite history" crowd) don't understand what history is useful for. Ask yourself now: once a branch is merged into master, why do we even keep the history of master? The answer is simple: to track old versions and regressions. The answer is not so that some historian can come along and work out what you did each day of your career. That's not useful. Nobody cares. Git history is not history. It's a sequence of versions of software each of which you believe could be checked out and used.
Have a commit command line that lists only the three files in question. It's really not that hard. If you're going to suggest more complex scenarios, the answer usually boils down to in practice "you want an interactive commit anyways," with perhaps some form of (unpushed) history rewriting to move patch hunks around between different commits.
The staging area is basically a half-baked, special commit that is easy to do manipulation on but hard to figure out what its actual state is (e.g., show me only the diffs that are in the staging area, or let me figure out what the hell is actually going on if I'm doing a complicated rebase). You know what would be better? Turn the staging area into a full, real commit and lower the friction of editing unpushed history to make it easy to move patch hunks in, out, and between these commits. It would help people who want commits to be atomic and therefore break WIP changes into multiple commits before publishing them for review.
We just assume that when we commit, the code has been tested in the local dev environment. Better not be committing untested code to my repos, that will get you reprimanded.
It's interesting how people always talk about staging individual files. Did you know git supports staging individual lines? This is way more powerful and allows you to begin commiting sooner on any code base.
Please explain how doing away with staging will handle my workflow. Right now I'm using staging area to prepare commits. Usually it's all the changed files, but quite often I want to commit only some of the changes - as I prefer to commit a minimal set of changes that is standalone - and thusly:
* introduce several related changes in several files
* stage some of the changes as a standalone commit
* commit the selected changes
* continue working on other changes to create more commits
Anyone else wondering about the name? I’m OK with the absence of staging area, but with such a fundamental change why would it still call itself “Git”? A name like (say) Tig would keep the Git reference alive, but make it obvious the two are different.
That’s a very slippery slope. Most of their tools would be just fine since the notions predate POSIX, and people already expect incompatibilities across platforms (plus Plan 9 ones more or less work similarly as their counterparts). Naming a new not-really-Git thing Git causes more confusion than benefits because Git is not a standard but a “product”.
> The most obvious difference is that Git's index is a bit boneheaded, so I'm ignoring it.
> In fact, the entire concept of the staging area has been dropped, as it's both confusing and clunky. There are now only three states that files can be in: 'untracked', 'dirty', and 'committed'.
> Some usage examples:
git/add foo.c
So… what does `git/add` do?
> Tells the repository to add a file to the next commit.
And wherever that information is stored, so that between running `git/add` and `git/commit`, we can remember what things are going to be part of the commit… that sounds an awful lot like something one might call a "staging" area. How have we not reinvented it here?
> that sounds an awful lot like something one might call a "staging" area.
What you call it is one thing. The actual code behind git's official "staging" implementation is basically an entire magical branch on-disk. The staging code is a big part of why git submodules are so awkward. I don't even use Plan 9, but I'm interested in this implementation.
And that's before you take into account that the staging area's index makes some very interesting decisions:
- Maximum file size of 4g -- but this limit is only present in the staging area, which means it's possible to construct repos with files that can't be checked out into the index.
- Dates have the year 2038 problem.
- Contains non-portable stat fields like the device the file was on.
- Requires a complete rewrite of the whole index file on every change, making it scale O(n) in the size of the repository.
- Requires padding of data fields to 8 bytes -- but then has 12 byte header which the fields are packed after, making all of them misaligned to 4 bytes.
The whole thing screams "thoughtless, bleary-eyed 3 AM hack"
From the user-visible side, it sounds like svn's tracked files, which trip up my co-workers more than git's staging area despite having used svn much longer.
> that sounds an awful lot like something one might call a "staging" area.
Not at all. When you stage something in Git, you add a snapshot of the current version to the staging area. If you modify the file after doing "git add", the new modification will not be committed.
Reading this discussion, I think a lot of people use git with a precise workflow from which they never diverge and don't actually know the tool that well. There are plenty of very surprising design decisions in git.
65 comments
[ 2.6 ms ] story [ 145 ms ] threadThere's a name I haven't seen since 1995.
(Vale dhog)
[1] http://9front.org/
The arguments they make for the actual system make sense to me and I want to see what else exists on the OS research front.
He was a gentle, nice and talented guy. :(
> In fact, the entire concept of the staging area has been dropped, as it's both confusing and clunky.
Sounds like its on the right track.
Take a look at the research by the gitless folks: https://spderosso.github.io/oopsla16.pdf, https://gitless.com/
Truly jumped the shark.
This study design is a real problem -- even after I have read the paper, I am not convinced that I should be using gitless. Their graph looks science-y, but the small sample size and non-representative experiment design means the conclusion do not apply to my work.
Reading this paper is reading a DB benchmark which only used rows with two integer fields in all their tests. Sure, it is interesting reading, but unless your workflow is exactly that, it is useless in real life.
(Disclaimer: it is entirely possible that gitless is better than git in all workflows, not just the one described in the paper. But even if it is the case, I would not know about it, at least not from the gitless site.)
So yeah, feel free to leave it out of your implementation; it doesn't seem like something that'd fit into the plan9 mindset anyway. But it is removing a very useful tool to some people.
1. Make exploratory modifications
2. "git add" pieces that I think are "commit worthy", even if I'm not sure I want to actually commit it yet (still exploratory work).
3. If I decide I don't like the latest set of modifications, I clear them all, going back to what's in my pseudo-commit.
4. Once I'm happy with it as a whole, I turn it into a real commit, complete with message.
What would be really nice is some sort of "pseudo-commit stack" whereby you can do this workflow, but every mini-commit goes into a stack that you can unwind to whatever point you want if it turns out that you've been going down a blind alley. If pseudo-commit stacks had first class treatment, you could even stash them, which would be great if some other emergency came along that you had to deal with now.
Internally, you could create some kind of HEAD-like pointer for this, store commits that go from HEAD to PSEUDOHEAD, and then when you're finally ready to commit, squashes everything from HEAD to PSUEDOHEAD and follows standard commit procedure from there.
Or maybe you could implement it as a "reset HEAD" kind of thing to collapse the pseudo-commits, which then allows you to use standard partial commit semantics if needed.
Actually, now that I think of it, I could do a poor man's version by tagging REALHEAD before I start, making actual, real commits as I go, and then "git reset REALHEAD" to start building the actual commit I want...
Mercurial patch queue is pretty much that. You can also use temporary commit and phases to ensure they are not visible from outside your local repository.
I have never really understood why git introduces all this weird concepts (staging, stash) for what are just temporary commits in the end.
I use branches for this, and private remote repos. I thought that stash was magical and cool until that time I stashed in the middle of a merge and dug into the details of stash...
If I hate the state of my experimental branch, I kill it. If I want to keep parts, I cherry-pick them over to another experimental branch. When I want to keep fractions of patches, I manually copy deltas across with git-difftool (which was the only real reason I was stashing anyway). And when I finally have a branch that I'm happy with keeping, then I use "git merge -i" to collect the mini-commits into a sensible history
Sometimes, you want to have a temporary snapshot, but not a commit.
So I write some code, it works, but I'm thinking of trying some things out. I stage the working code and make more diffs. Now I can have my editor show the diffs between my stage and my new changes. And I can easily rollback individual files or re-stage some more files.
Until I am satisfied. And now decide to make a commit.
I could just make them all commits, and then do a interactive rebase as well. And I do that too. But sometimes the staging area is just simpler and quicker. With git status I can see it at a glance. Etc.
Personally, I use staging area all the time to create a curated set of commits that make logical sense when putting them up for review.
My workflow is generally to keep a very messy history of `XXX` commits until I am happy with what I've come up with. Then reset back to the branch point and using the staging area to build up logical changesets.
I don't care about the history of my coming up with the solution, I care about presenting the solution in a way that makes sense to reviewers, including future me.
Later on, if I notice that I messed something up, I'll create a fixup commit and do an interactive rebase to squash it back into the correct commit.
It should be a crime for people to blindly stage everything. Cleaning up after my colleagues easily falls under the "lost cause" umbrella.
If I really care about things being fully correct at each commit, I'll do an interactive rebase and just stop and test on each commit.
Often they're disjoint enough that I'm confident that if the end result works, the intermediate commit works, as the sibling commenter mentioned. (Even if not, on many teams it all gets squashed anyway, the intermediate commit message just makes the Pull Request easier to review.)
I can also often easily comment out the uncommitted stuff.
Finally, if I'm really worried about it, after creating the intermediate commit I can just `git stash`, test it, then `git stash pop` and keep working.
The staging area is basically a half-baked, special commit that is easy to do manipulation on but hard to figure out what its actual state is (e.g., show me only the diffs that are in the staging area, or let me figure out what the hell is actually going on if I'm doing a complicated rebase). You know what would be better? Turn the staging area into a full, real commit and lower the friction of editing unpushed history to make it easy to move patch hunks in, out, and between these commits. It would help people who want commits to be atomic and therefore break WIP changes into multiple commits before publishing them for review.
And how would you stage & commit only a portion of the changes made to a file?
(Like the parents, I use the staging area daily, and I am quite thankful for its existence.)
I wouldn't. That code is untested, and I have a thing against committing untested code.
Git stash -p, on the other hand, allows me to select what code should be left in the working directory for testing, with no need for a staging area.
* introduce several related changes in several files * stage some of the changes as a standalone commit * commit the selected changes * continue working on other changes to create more commits
> In fact, the entire concept of the staging area has been dropped, as it's both confusing and clunky. There are now only three states that files can be in: 'untracked', 'dirty', and 'committed'.
> Some usage examples:
So… what does `git/add` do?> Tells the repository to add a file to the next commit.
And wherever that information is stored, so that between running `git/add` and `git/commit`, we can remember what things are going to be part of the commit… that sounds an awful lot like something one might call a "staging" area. How have we not reinvented it here?
And that's the real issue at hand.
> we invented a new state "included in next commit but not called stage"
impressive.
What you call it is one thing. The actual code behind git's official "staging" implementation is basically an entire magical branch on-disk. The staging code is a big part of why git submodules are so awkward. I don't even use Plan 9, but I'm interested in this implementation.
- Maximum file size of 4g -- but this limit is only present in the staging area, which means it's possible to construct repos with files that can't be checked out into the index.
- Dates have the year 2038 problem.
- Contains non-portable stat fields like the device the file was on.
- Requires a complete rewrite of the whole index file on every change, making it scale O(n) in the size of the repository.
- Requires padding of data fields to 8 bytes -- but then has 12 byte header which the fields are packed after, making all of them misaligned to 4 bytes.
The whole thing screams "thoughtless, bleary-eyed 3 AM hack"
From the user-visible side, it sounds like svn's tracked files, which trip up my co-workers more than git's staging area despite having used svn much longer.
Not at all. When you stage something in Git, you add a snapshot of the current version to the staging area. If you modify the file after doing "git add", the new modification will not be committed.
Reading this discussion, I think a lot of people use git with a precise workflow from which they never diverge and don't actually know the tool that well. There are plenty of very surprising design decisions in git.