There are cases where I want to discard all the files currently in the staging area and only the files currently in the staging area. Essentially I want something that is just like making a commit and then immediately discarding the commit that was just made.
Right now in practice I mostly use git stash
for this purpose and just remember that I don't actually want the thing that I'm stashing.
However, stash
ing and discarding are not really the same thing and stash
appears to capture all the tracked files, not just the ones currently in the staging area, as the script below shows.
Is there a way to completely discard (changes to) exactly the files that are currently in the staging area?
#!/bin/bash
mkdir -p ./a
echo foo > ./a/foo
echo bar > ./a/bar
(
# add initial commit
cd ./a
git init
git add foo
git add bar
git commit --message='initial commit'
# change first line of foo and bar
ed ./foo <<'EOF'
s/$/ aaaaaa/
wq
EOF
ed ./bar <<'EOF'
s/$/ aaaaaaa/
wq
EOF
git add ./foo
git stash save
git status
)
When run, this script produces the following output
$ bash gitrepo.sh
Initialized empty Git repository in ~/git/a/a/.git/
[master (root-commit) a8f0104] initial commit
2 files changed, 2 insertions(+)
create mode 100644 bar
create mode 100644 foo
4
11
4
12
Saved working directory and index state WIP on master: a8f0104 initial commit
HEAD is now at a8f0104 initial commit
I've also tried essentially the same script with git clean -f
and git reset
in place of git stash save
git clean -f
appears to discard neither the changes to foo
nor to bar
.
[master (root-commit) 0c10b47] initial commit
2 files changed, 2 insertions(+)
create mode 100644 bar
create mode 100644 foo
4
11
4
12
On branch master
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
modified: foo
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git checkout -- <file>..." to discard changes in working directory)
modified: bar
git reset
in this specific case appears to simply remove foo
from the staging area.
[master (root-commit) 65fce7b] initial commit
2 files changed, 2 insertions(+)
create mode 100644 bar
create mode 100644 foo
4
11
4
12
Unstaged changes after reset:
M bar
M foo
The current best solution I have so far is the following script, which has the right behavior in the few cases I've tested it with. However, it accomplishes this behavior by creating, committing to, and then deleting a new branch named by a UUID. I'd prefer a way of discarding the changes in the staging area that's less circuitous.
#!/bin/bash
branch="$(python -c 'import uuid; print(str(uuid.uuid4()))')"
git checkout -b "$branch"
git commit -m "commit for $branch"
git checkout -
git branch -D "$branch"
When using gitdiscard
(the script above), the tester script produces the right results, foo's change is discarded, but the unstaged modifications to bar
are still there.
[master (root-commit) e4e81d9] initial commit
2 files changed, 2 insertions(+)
create mode 100644 bar
create mode 100644 foo
4
11
4
12
M bar
M foo
Switched to a new branch 'abebe893-e579-4ecc-9422-6c99051d67f6'
[abebe893-e579-4ecc-9422-6c99051d67f6 05916d5] commit for abebe893-e579-4ecc-9422-6c99051d67f6
1 file changed, 1 insertion(+), 1 deletion(-)
M bar
Switched to branch 'master'
Deleted branch abebe893-e579-4ecc-9422-6c99051d67f6 (was 05916d5).
Non-exhaustive list of similar but non-duplicate questions:
This question is not a duplicate because it refers to discarding untracked files, not changes in the staging area.
This question is not a duplicate because it refers to clearing the entire working directly, not to discarding changes in the staging area.
This question covers untracked files and untracked directories, but doesn't cover discarding changes to files in the staging area.
You might consider the --keep-index
flag for git stash
It does, in fact, the contrary of what you're asking for : it stashes everything but what's in the index.
You could take advantage of this and stash only the unstaged changes, then reset your index and working tree, and finally re-apply the stash.
(I'm unsure of how you want the untracked files to be dealt with, but there's also the stash flag --include-untracked
to keep in mind as a possibility. Use it on the first stash if you need to include them in the final result)
git stash --keep-index [--include-untracked]
git reset --hard
git stash apply