和你一样,我跑
git stash --keep-index --include-untracked
然后我可以运行测试等等。
下一部分很棘手。这些是我尝试过的一些事情:
git stash pop
可能会因冲突而失败,这是不可接受的。
git stash pop --index
可能会因冲突而失败,这是不可接受的。
git checkout stash -- .
应用所有跟踪的更改(良好),但也将它们暂存(不可接受),并且不会从存储中恢复未跟踪的文件(不可接受)。藏匿处仍然存在(很好——我可以git stash drop
)。
git merge --squash --strategy-option=theirs stash
可能会因冲突而失败,这是不可接受的,即使没有冲突,它也不会从存储中恢复未跟踪的文件(不可接受)。
git stash && git stash pop stash@{1} && git stash pop
(尝试以相反的顺序应用变更集)可能会因冲突而失败,这是不可接受的。
但是我发现了一组命令可以满足我们的要求:
# Stash what we actually want to commit
git stash
# Unstash the original dirty tree including any untracked files
git stash pop stash@{1}
# Replace the current index with that from the stash which contains only what we want to commit
git read-tree stash
# Drop the temporary stash of what we want to commit (we have it all in working tree now)
git stash drop
为了减少输出,并浓缩为一行:
git stash --quiet && git stash pop --quiet stash@{1} && git read-tree stash && git stash drop --quiet
据我所知,唯一不能恢复的是在索引中添加然后从工作树中删除的文件(它们最终会被添加并呈现)和在索引中重命名的文件和然后从工作树中删除(相同的结果)。出于这个原因,我们需要在初始存储之前查找与这两种情况匹配的文件,git status -z | egrep -z '^[AR]D' | cut -z -c 4- | tr '\0' '\n'
然后在恢复后循环并删除它们。
git stash --keep-index --include-untracked
显然,如果工作树有任何未跟踪的文件或未暂存的更改,您应该只运行初始。要检查这一点,您可以git status --porcelain | egrep --silent '^(\?\?|.[DM])'
在脚本中使用测试。
我相信这比现有的答案要好——它不需要任何中间变量(除了树是否脏,以及在恢复存储后需要删除哪些文件的记录),具有更少的命令和不需要为了安全而关闭垃圾收集。有中间藏匿处,但我认为这正是它们的用途。
这是我当前的预提交钩子,它完成了提到的所有事情:
#!/bin/sh
# Do we need to tidy up the working tree before tests?
# A --quiet option here doesn't actually suppress the output, hence redirection.
git commit --dry-run >/dev/null
ret=$?
if [ $ret -ne 0 ]; then
# Nothing to commit, perhaps. Bail with success.
exit 0
elif git status --porcelain | egrep --silent '^(\?\?|.[DM])'; then
# There are unstaged changes or untracked files
dirty=true
# Remember files which were added or renamed and then deleted, since the
# stash and read-tree won't restore these
#
# We're using -z here to get around the difficulty of parsing
# - renames (-> appears in the string)
# - files with spaces or doublequotes (which are doublequoted, but not when
# untracked for unknown reasons)
# We're not trying to store the string with NULs in it in a variable,
# because you can't do that in a shell script.
todelete="$(git status -z | egrep -z '^[AR]D' | cut -z -c 4- | tr '\0' '\n')"
else
dirty=false
fi
if $dirty; then
# Tidy up the working tree
git stash --quiet --keep-index --include-untracked
ret=$?
# Abort if this failed
if [ $ret -ne 0 ]; then
exit $ret
fi
fi
# Run tests, remember outcome
make precommit
ret=$?
if $dirty; then
# Restore the working tree and index
git stash --quiet && git stash pop --quiet stash@{1} && git read-tree stash && git stash drop --quiet
restore_ret=$?
# Delete any files which had unstaged deletions
if [ -n "$todelete" ]; then
echo "$todelete" | while read file; do
rm "$file"
done
# Abort if this failed
if [ $restore_ret -ne 0 ]; then
exit $restore_ret
fi
fi
fi
# Exit with the exit status of the tests
exit $ret
欢迎任何改进。