If I forget to stage a modification/a file that is neccesary for a unit test, I want that that unit test to fail (when running inside of a pre-commit hook). The exception to this is gitignored files/folders (like the node_modules
-folder when dealing with Node.js).
It seems like git stash
might allow me to create such a temporary state. It is also very important that I'm able to restore the state as if nothing happened after running the tests.
To describe the exact behaviour I want, I have created a bash script below:
#!/bin/bash
mkdir temporary-repo && cd temporary-repo || exit 1
git init > /dev/null 2>&1
printf '%s' "0" > ignored
printf '%s' "ignored" > .gitignore
printf '%s' "0" > modified-staged
printf '%s' "0" > modified-unstaged
printf '%s' "0" > modified-unstaged-undo
printf '%s' "0" > modified-unstaged-update
git add . && git commit -m "Initial commit" > /dev/null 2>&1
printf '%s' "1" > new-staged
printf '%s' "1" > modified-staged
printf '%s' "1" > new-unstaged-undo
printf '%s' "1" > new-unstaged-update
printf '%s' "1" > modified-unstaged-undo
printf '%s' "1" > modified-unstaged-update
git add .
printf '%s' "0" > new-unstaged-undo
printf '%s' "0" > modified-unstaged-undo
printf '%s' "1" > new-unstaged
printf '%s' "1" > modified-unstaged
printf '%s' "2" > new-unstaged-update
printf '%s' "2" > modified-unstaged-update
before="$(git status)"
#####################
# CREATE STASH HERE #
#####################
echo "--- Created stash ---"
test "1" = "$(cat new-unstaged-undo)" || echo "ERR: new-unstaged-undo != 1"
test "1" = "$(cat modified-unstaged-undo)" || echo "ERR: modified-unstaged-undo != 1"
test "1" = "$(cat new-staged)" || echo "ERR: new-staged != 1"
test "1" = "$(cat modified-staged)" || echo "ERR: modified-staged != 1"
test -f new-unstaged && echo "ERR: new-unstaged should not exist"
test "0" = "$(cat modified-unstaged)" || echo "ERR: modified-unstaged != 0"
test "1" = "$(cat new-unstaged-update)" || echo "ERR: new-unstaged-update != 1"
test "1" = "$(cat modified-unstaged-update)" || echo "ERR: modified-unstaged-update != 1"
test "0" = "$(cat ignored)" || echo "ERR: ignored != 0"
##################
# POP STASH HERE #
##################
echo "--- Popped stash ---"
test "0" = "$(cat new-unstaged-undo)" || echo "ERR: new-unstaged-undo != 0"
test "0" = "$(cat modified-unstaged-undo)" || echo "ERR: modified-unstaged-undo != 0"
test "1" = "$(cat new-staged)" || echo "ERR: new-staged != 1"
test "1" = "$(cat modified-staged)" || echo "ERR: modified-staged != 1"
test "1" = "$(cat new-unstaged)" || echo "ERR: new-unstaged != 1"
test "1" = "$(cat modified-unstaged)" || echo "ERR: modified-unstaged != 1"
test "2" = "$(cat new-unstaged-update)" || echo "ERR: new-unstaged-update != 2"
test "2" = "$(cat modified-unstaged-update)" || echo "ERR: modified-unstaged-update != 2"
test "0" = "$(cat ignored)" || echo "ERR: ignored != 0"
after="$(git status)"
diff -y <(echo "$before") <(echo "$after")
cd .. && rm -rf temporary-repo
# Create stash
git stash -k -u
# Pop stash
git stash pop
Failing test output:
--- Created stash ---
--- Popped stash ---
ERR: new-unstaged-undo != 0
ERR: modified-unstaged-undo != 0
ERR: new-unstaged-update != 2
ERR: modified-unstaged-update != 2
On branch master On branch master
Changes to be committed: Changes to be committed:
(use "git restore --staged <file>..." to unstage) (use "git restore --staged <file>..." to unstage)
modified: modified-staged modified: modified-staged
> modified: modified-unstaged
modified: modified-unstaged-undo modified: modified-unstaged-undo
modified: modified-unstaged-update <
new file: new-staged new file: new-staged
new file: new-unstaged-undo <
new file: new-unstaged-update <
Changes not staged for commit: | Unmerged paths:
(use "git add <file>..." to update what will be committed) | (use "git restore --staged <file>..." to unstage)
(use "git restore <file>..." to discard changes in working | (use "git add <file>..." to mark resolution)
modified: modified-unstaged | both modified: modified-unstaged-update
modified: modified-unstaged-undo | both added: new-unstaged-undo
modified: modified-unstaged-update | both added: new-unstaged-update
modified: new-unstaged-undo <
modified: new-unstaged-update <
Untracked files: Untracked files:
(use "git add <file>..." to include in what will be committ (use "git add <file>..." to include in what will be committ
new-unstaged new-unstaged
# Create stash
stash=$(git stash create -q)
git stash store "$stash"
git stash show -p | git apply --reverse
git diff --cached | git apply
# Pop stash
git reset --hard -q
git stash apply --index -q
git stash drop -q
Failing test output:
--- Created stash ---
ERR: new-unstaged should not exist
--- Popped stash ---
On branch master On branch master
Changes to be committed: Changes to be committed:
(use "git restore --staged <file>..." to unstage) (use "git restore --staged <file>..." to unstage)
modified: modified-staged modified: modified-staged
modified: modified-unstaged-undo modified: modified-unstaged-undo
modified: modified-unstaged-update modified: modified-unstaged-update
new file: new-staged new file: new-staged
new file: new-unstaged-undo new file: new-unstaged-undo
new file: new-unstaged-update new file: new-unstaged-update
Changes not staged for commit: Changes not staged for commit:
(use "git add <file>..." to update what will be committed) (use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working (use "git restore <file>..." to discard changes in working
modified: modified-unstaged modified: modified-unstaged
modified: modified-unstaged-undo modified: modified-unstaged-undo
modified: modified-unstaged-update modified: modified-unstaged-update
modified: new-unstaged-undo modified: new-unstaged-undo
modified: new-unstaged-update modified: new-unstaged-update
Untracked files: Untracked files:
(use "git add <file>..." to include in what will be committ (use "git add <file>..." to include in what will be committ
new-unstaged new-unstaged
Attempt 1 creates the desired state for running unit tests, but fails to restore the original state. Attempt 2 fails to stash/remove new-unstaged, but successfully restores the original state.
Is there any way to get my desired behavior with git?
Combining the two solutions seem to make my tests pass, and give me my desired behaviour:
# Create stash
git stash -k -u
# Pop stash
git reset --hard -q
git stash apply --index -q
git stash drop -q
Output:
--- Created stash ---
--- Popped stash ---
On branch master On branch master
Changes to be committed: Changes to be committed:
(use "git restore --staged <file>..." to unstage) (use "git restore --staged <file>..." to unstage)
modified: modified-staged modified: modified-staged
modified: modified-unstaged-undo modified: modified-unstaged-undo
modified: modified-unstaged-update modified: modified-unstaged-update
new file: new-staged new file: new-staged
new file: new-unstaged-undo new file: new-unstaged-undo
new file: new-unstaged-update new file: new-unstaged-update
Changes not staged for commit: Changes not staged for commit:
(use "git add <file>..." to update what will be committed) (use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working (use "git restore <file>..." to discard changes in working
modified: modified-unstaged modified: modified-unstaged
modified: modified-unstaged-undo modified: modified-unstaged-undo
modified: modified-unstaged-update modified: modified-unstaged-update
modified: new-unstaged-undo modified: new-unstaged-undo
modified: new-unstaged-update modified: new-unstaged-update
Untracked files: Untracked files:
(use "git add <file>..." to include in what will be committ (use "git add <file>..." to include in what will be committ
new-unstaged new-unstaged