Git命令在不修改工作树的情况下保存存储?

时间:2011-06-11 10:51:27

标签: git git-stash refs

我一直想使用git命令来保存存储而不修改我的工作树,作为一个轻量级备份,可以安全地从任何git重置或我可能做的任何事情来搞砸我的索引。基本上功能相当于“git stash save&& git stash apply”,但工作副本永远不会被触及,因为这可能会使某些文本编辑器/ IDE变得暴躁。

这样的事情正在接近我想要的,但并不完全:

git update-ref refs/stash `git stash create "Stash message"`

这在功能上有效,但我遇到的问题是,即使实际的存储提交确实包含了我的消息,“git存储列表”中也没有显示存储消息。考虑到藏匿的大小,隐藏消息非常重要。

5 个答案:

答案 0 :(得分:30)

感谢Charles的提示,我发了一个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的建议。

答案 1 :(得分:13)

您需要将消息传递给update-ref,而不是stash create,因为stash create没有收到消息(它不会更新任何引用,因此它没有reflog条目填充)。

git update-ref -m "Stash message" refs/stash "$(git stash create)"

答案 2 :(得分:3)

受到艾略特解决方案的启发,我稍微扩展了他的剧本:

#!/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的脚本实施了以下更改:

  1. 当工作目录干净时,脚本将正常退出
  2. 当使用switch -c时,如果与上一个存储相比没有变化,则脚本将退出。如果您将此脚本用作"时间机器",每隔10分钟进行一次自动存储,这将非常有用。如果没有任何更改,则不会创建新的存储。如果没有这个开关,你可能会得到n个连续的stashes,它们是相同的。
  3. 不是说为了使交换机-c正常工作,必须至少存在一个存储,否则脚本会在git diff stash@{0}上抛出错误而不执行任何操作。

    我将此脚本用作"时间机器",使用以下bash循环每10分钟进行一次快照:

     while true ; do date ; git stash-push ; sleep 600 ; done
    

答案 3 :(得分:3)

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 stash条目是使用具有某些已定义结构的常规提交表示的。基本上,这是一个常规提交对象,具有2个父对象(如果使用--include-untracked选项则为3个)(更多信息12)。

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来清理文件(哪个取决于您在{ {1}}。

答案 4 :(得分:0)

以下内容将answer by @Mariusz Pawelskia similar answer组合为一个相关问题,使您可以轻松地隐藏信息。

git stash store与消息一起使用

  1. 使用git stash create创建存储提交,然后使用git stash store将其保存到存储中。这不会更改工作树中的任何文件。您还可以添加一条有用的消息,以便以后再次找到合适的版本。

    git stash store -m "saving intermediate results: my note 1" $(git stash create)
    
  2. 当您决定要还原到保存到存储引用中的先前状态时,请先执行另一个git stash以防止以后在应用先前状态时发生合并冲突:

    git stash
    
  3. 现在查找存储在引用中的内容:

    $ git stash list
    
    stash@{0}: saving intermediate results: my note 1
    stash@{1}: saving intermediate results: my note 2
    
  4. 最后,要恢复到已隐匿的先前工作状态,放弃当前工作树状态,您将使用输出中stash@{…}内部的索引号进行操作上面确定要还原到的隐藏提交:

    git stash apply 1
    
  5. 如果您发现自己想扔掉,您还可以将其丢弃:可以将其保存在存储栏中,并可以使用与上述相同的技术进行还原。

包装为Git别名

以上步骤1中使用的命令可以与git别名一起使用,以使其更加方便。要创建别名(请注意final # technique):

git config --global alias.stash-copy '!git stash store -m "saving intermediate results: $1" $(git stash create) #'

从现在开始,您可以像这样使用它:

git stash-copy "my note"