Reputation: 3746
How do I either
git stash
error out if there are both unstaged and staged changes and I haven't specified either --staged
(-S
) or --keep-index
(-k
), orgit stash
default to --keep-index
if invoked without the --staged
option?Ideally as a global config option.
Several times now:
git add -p
just the changes I want to commit (sometimes with temporary edits to files beforehand, when I want to pull apart two atoms of change which happen to touch the same lines or adjacent line, which git
struggles with), thengit stash
, wasting the work of step 1, because git
does the dumbest thing a program can do in that case - destroys user information by default without opportunity to abort or undo, by stashing all changes, both staged and unstaged changes, blended into one diff without any distinction. (edit: the answers reveal git
actually preserves the distinction when stashing, I just didn't know how to retrieve it)Now if I was a computer program, I'd always just execute git commit -m 'TODO: --amend THIS COMMIT'
before git stash
, or I'd always remember to type git stash -k
. But I find it more natural to think "I'll stash these unstaged changes real quick for later, then focus on the larger mental task of writing good commit message"
In fact come to think of it, I've been using git
for close to a decade almost daily, and I have never wanted a git stash
to stash staged changes, let alone fuse staged and unstaged changes back together. I can see how that could be useful if you're in the middle of staging one set of changes and then you want to stash the whole thing, but to me that's an extremely tiny window of time - most of the time, if I have staged changes and unstaged changes, they're meaningfully different pieces of change which I haven't finished separating out into the best atomic commits.
Upvotes: 1
Views: 235
Reputation: 3785
According to the documentation, the index and the working tree are well separated into separated commits after the git stash
command.
If your concern is to restore the index after a git stash pop
I suggest using the --index
option: git stash pop --index
.
An alias can make transparent the use of this option:
git config alias.pop "stash pop --index"
(Edited the alias as mentioned in the comment)
Upvotes: 3
Reputation: 536027
I'm sympathetic with the first part of the question, but I don't believe there is any such automatic option or configuration. I think the best alternative is Don't Do That — that is, if you're going to give a Git command, say what you mean, explicitly. That way, you know what will happen — because you said what should happen. (For the same reason I never say git pull
, and I rarely say git rebase
without saying --onto
.)
At the same time, I'd like to push back sharply on the claims in the second part:
destroys user information
Uh, no. Only if you think that git commit
destroys user information. stash
saves user information, in a commit (actually a pair of commits) — and saves it in a way that makes it easy to restore.
blended into one diff without any distinction
That is not true. The index is saved as a separate commit. Thus it is possible to restore exactly the situation as it was, both the index and the worktree (except, of course, for the parts of the worktree that git stash
doesn't touch by default, namely untracked and ignored files).
The way stash creation actually works is that the index commit has current head as parent, and the worktree commit has the index commit and the current head as parents. (In other words, the stash’s content is a merge commit!) You can see this by doing git log
on a stash entry, and you will see the worktree commit followed by the index commit, either or both ready for you to restore.
Note that I am not defending git stash
. It's weird and I generally try to avoid it (though sometimes it is a good solution to a specific problem).
Addendum: Demonstration that a stash can restore both the work tree and index:
% git status
On branch main
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
new file: a.txt
new file: b.txt
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: b.txt
% git stash
Saved working directory and index state WIP on main: bc7bf2c startover
% git stash apply stash@{0} --index
On branch main
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
new file: a.txt
new file: b.txt
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: b.txt
Upvotes: 3