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
Attempt 1
# 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
Attempt 2
# 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?
CodePudding user response:
Turns out a solution was staring me in the face the entire time.
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