我一直想要使用git命令来保存存储而不修改我的工作树,作为一个轻量级备份,可以安全地从任何git重置或我可能做的任何事情来搞砸我的索引。基本上功能相当于“git stash save && git stash apply”,但工作副本永远不会被触及,因为这可能会使某些文本编辑器/ IDE变得暴躁。
像这样的东西接近我想要的东西,但并不完全:
git update-ref refs/stash `git stash create "Stash message"`
这在功能上有效,但我遇到的问题是,即使实际的存储提交确实包含了我的消息,“git存储列表”中也没有显示存储消息。考虑到存储的大小,存储消息非常重要。
感谢查尔斯的小费,我发了一个bash脚本来完成我想要的东西(我遇到的问题只是一个别名)。它需要一个可选的存储消息,就像git stash save一样。如果没有提供,它将使用git stash生成的默认消息。
#!/bin/sh
#
# git-stash-snap
# Save snapshot of working tree into the stash without modifying working tree.
# First argument (optional) is the stash message.
if [ -n "$1" ]; then
git update-ref -m "$1" refs/stash "$(git stash create \"$1\")"
else
HASH=`git stash create`
MESSAGE=`git log --no-walk --pretty="tformat:%-s" "$HASH"`
git update-ref -m "$MESSAGE" refs/stash "$HASH"
fi
编辑:正如下面的评论所指出的,将此脚本保存为路径中某处的git-stash-snap
足以通过键入git stash-snap
来调用它。
这里的好处是,即使你删除使用此方法创建的存储,您仍然可以使用悬挂提交的git log [commit-hash]来查看存储消息!
编辑:从git 2.6.0你可以添加--create-reflog
到update-ref
然后git stash list
将显示这个,即使之前没有使用过git stash
。
编辑:Git引入了一个名为stash push
的新存储子命令,所以我更新了我的建议,将这个脚本从git-stash-push
命名为git-stash-snap
。
您需要将消息传递给update-ref
,而不是stash create
,因为stash create
不接收消息(它不会更新任何ref,因此它没有填充的reflog条目)。
git update-ref -m "Stash message" refs/stash "$(git stash create)"
git stash store "$(git stash create)"
将创建类似于git stash
所能获得的存储条目,而不实际触及并清除您的工作目录和索引。
如果您检查存储列表或查看所有提交图(包括存储),您将看到它与正常调用git stash
所获得的结果相似。只是隐藏列表中的消息是不同的(通常它类似于“stash @ {0}:WIP on master:14e009e init commit”,这里我们将得到“stash @ {0}:通过”git stash store“创建”“
$ git status --short
M file.txt
A file2.txt
$ git stash list
$ git stash store "$(git stash create)"
$ git stash list
stash@{0}: Created via "git stash store".
$ git stash show 'stash@{0}'
file.txt | 2 +-
file2.txt | 2 ++
2 files changed, 3 insertions(+), 1 deletion(-)
$ git log --oneline --graph --all
* 85f937b (refs/stash) WIP on master: 14e009e init commit
|\
| * 26295a3 index on master: 14e009e init commit
|/
* 14e009e (HEAD -> master) init commit
$ git status
M file.txt
A file2.txt
更多解释:
git存储条目使用具有一些定义结构的普通提交来表示。基本上它是一个常规的提交对象,有2个父母(如果你使用--include-untracked
选项,则为3)(更多信息1,2)。
git stash create
创建此提交,表示存储条目并返回提交对象的object name(SHA-1)(具有2或3个父项的那个)。这是一个dangling commit(您可以通过在git fsck
之后调用git stash create
来验证它)。你需要让refs/stash
指向这个悬挂的提交,你可以通过git stash store
(或者像其他答案中的git update-ref
一样,因为git stash store
uses git update-ref
来完成它的工作)。
看看git stash push
的实际源代码是好的,看看它基本上调用git stash create
and git stash store
,然后用some logic来清理文件(哪一个取决于你在git stash push
中使用的选项)。
在艾略特的解决方案的启发下,我稍稍扩展了他的脚本:
#!/bin/sh
#
# git-stash-push
# Push working tree onto the stash without modifying working tree.
# First argument (optional) is the stash message.
#
# If the working dir is clean, no stash will be generated/saved.
#
# Options:
# -c "changes" mode, do not stash if there are no changes since the
# last stash.
if [ "$1" == "-c" ]; then
CHECK_CHANGES=1
shift
fi
if [ -n "$1" ]; then
MESSAGE=$1
HASH=$( git stash create "$MESSAGE" )
else
MESSAGE=`git log --no-walk --pretty="tformat:%-s" "HEAD"`
MESSAGE="Based on: $MESSAGE"
HASH=$( git stash create )
fi
if [ "$CHECK_CHANGES" ]; then
# "check for changes" mode: only stash if there are changes
# since the last stash
# check if nothing has changed since last stash
CHANGES=$( git diff stash@{0} )
if [ -z "$CHANGES" ] ; then
echo "Nothing changed since last stash."
exit 0
fi
fi
if [ -n "$HASH" ]; then
git update-ref -m "$MESSAGE" refs/stash "$HASH"
echo "Working directory stashed."
else
echo "Working tree clean, nothing to do."
fi
我对Eliot的脚本实施了以下更改:
-c
时,如果与上一个存储相比没有变化,脚本将退出。如果您将此脚本用作“时间机器”,每10分钟自动存储一次,这将非常有用。如果没有任何更改,则不会创建新的存储。如果没有这个开关,你可能会得到n个连续的stashes,它们是相同的。不是为了使交换机-c
正常工作,必须至少存在一个存储,否则脚本会在git diff stash@{0}
上抛出错误并且什么都不做。
我使用此脚本作为“时间机器”,使用以下bash循环每10分钟快照一次:
while true ; do date ; git stash-push ; sleep 600 ; done