我希望能够使用索引的当前状态为我的项目运行测试,忽略未提交的工作更改(我稍后计划将其添加到预提交 Hook )。但是,我无法弄清楚如何以一种永远不会导致 merge 冲突的方式删除然后恢复非索引更改。我需要这个,因为它是由脚本运行的,所以它在完成后不应该改变存储库状态。
git stash --include-untracked --keep-index
和 git stash pop
很接近,但在很多情况下它会导致 merge 冲突,即使没有更改两个命令之间的位置。
例如:
mkdir project; cd project; git init .;
# setup the initial project with file.rb and test.rb
cat > file.rb <<EOF
def func()
return 42
end
EOF
cat > test.rb <<EOF
#!/usr/bin/env ruby
load './file.rb'
if (func() == 42)
puts "Tests passed"
exit 0
else
puts "Tests failed"
exit 1
end
EOF
chmod +x test.rb
git add .
git commit -m "Initial commit"
# now change file.rb and add the change
cat > file.rb <<EOF
def func()
return 10 + 32
end
EOF
git add file.rb
# now make a breaking change to func, and don't add the change
cat > file.rb <<EOF
def func()
return 20 + 32 # not 42 anymore...
end
EOF
从这里我想针对索引的当前状态运行测试,并恢复未提交的更改。预期结果是测试通过,因为重大更改未添加到索引中。
以下命令不起作用:
git commit --include-untracked --keep-index
./test.rb
git stash pop
问题出现在 git stash pop
- 发生 merge 冲突。
我能想到的唯一其他解决方案是进行临时提交,然后存储剩余的更改,然后使用 git reset --soft HEAD~
回滚提交,然后 pop 存储。然而,这既麻烦又麻烦,而且我不确定在预提交 Hook 中运行有多安全。
这个问题有更好的解决方案吗?
最佳答案
和你一样,我跑
git stash --keep-index --include-untracked
然后我可以运行测试等等。
下一部分很棘手。这些是我尝试过的一些东西:
git stash pop
可能因冲突而失败,这是 Not Acceptable 。git stash pop --index
可能因冲突而失败,这是 Not Acceptable 。git checkout stash -- .
应用所有跟踪的更改(良好),但也暂存它们( Not Acceptable ),并且不从存储中恢复未跟踪的文件( Not Acceptable )。存储仍然存在(很好——我可以git stash drop
)。git merge --squash --strategy-option=theirs stash
可能因冲突而失败,这是 Not Acceptable ,即使没有冲突,它也不会从存储中恢复未跟踪的文件( Not Acceptable )。git stash && git stash pop stash@{1} && git stash pop
(尝试以相反的顺序应用变更集)可能因冲突而失败,这是 Not Acceptable 。
但是我找到了一组可以满足我们要求的命令:
# 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' |切-z -c 4- | tr '\0' '\n'
在初始存储之前,然后循环并在恢复后删除它们。
显然,如果工作树有任何未跟踪的文件或未暂存的更改,您应该只运行初始 git stash --keep-index --include-untracked
。要检查你可以使用测试 git status --porcelain | egrep --silent '^(\?\?|.[DM])'
在你的脚本中。
我相信这比现有的答案更好——它不需要任何中间变量(除了树是否脏,以及恢复存储后需要删除哪些文件的记录),有更少的命令,并且不需要为了安全而关闭垃圾收集。有中间存储,但我认为这正是它们的用途。
这是我当前的预提交 Hook ,它执行所有提到的操作:
#!/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
欢迎任何改进。
关于git - 您如何使用存储删除然后恢复非索引更改,而不会在 git 中发生 merge 冲突?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13889242/