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

Eli*_*iot 40 git git-stash refs

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

像这样的东西接近我想要的东西,但并不完全:

git update-ref refs/stash `git stash create "Stash message"`
Run Code Online (Sandbox Code Playgroud)

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

Eli*_*iot 30

感谢查尔斯的小费,我发了一个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
Run Code Online (Sandbox Code Playgroud)

编辑:正如下面的评论中所指出的,将此脚本保存git-stash-snap在路径中的某个位置足以通过键入来调用它git stash-snap.

这里的好处是,即使你删除使用此方法创建的存储,您仍然可以使用悬挂提交的git log [commit-hash]来查看存储消息!

编辑:既然git的2.6.0,您可以添加--create-reflogupdate-ref,然后git stash list会显示这个即使git stash没有使用过.

编辑:Git的不断推出所谓的新的存储子stash push,所以我已经更新了我的建议,从命名这个脚本git-stash-pushgit-stash-snap.

  • 根据这个答案,我将以下内容添加到我的`〜/ .gitconfig`的`[alias]`部分,它应该提供相同的行为:`stash-push ="!f(){if(($ #> 0));然后branch = $(git branch | sed -n's/^ \\*// p'); git update-ref -m \"On $ {branch}:$*\"refs/stash $(git stash create \"On $ {branch}:$*\"); else hash = $(git stash create); msg = $(git log --no-walk --pretty = \"tformat:% -s \"$ hash); git update-ref -m \"$ msg \"refs/stash $ hash; fi;}; f"` (6认同)
  • 如果你将该文件称为`git-stash-push`并将其放在PATH中的某个位置,则无需为其创建别名.`git stash-push`会找到(并调用)`git-stash-push` (5认同)

CB *_*ley 13

您需要将消息传递给update-ref,不是stash create因为stash create没有考虑消息(它不更新任何裁判,所以它没有引用日志条目来填充).

git update-ref -m "Stash message" refs/stash "$(git stash create)"
Run Code Online (Sandbox Code Playgroud)


Mar*_*ski 8

git stash store "$(git stash create)"

将创建类似于您将获得的存储项,git stash而无需实际触摸和清除工作目录和索引。

如果您查看存储清单或查看所有提交图(包括存储),您会发现它的结果与正常调用会得到类似的结果git stash。只是存储列表中的消息是不同的(通常类似于“ stash @ {0}:在主服务器上的WIP: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
Run Code Online (Sandbox Code Playgroud)

多一点解释:

git stash条目是使用具有某些已定义结构的常规提交表示的。基本上,它是(如果你使用或3有2位家长定期提交对象--include-untracked选项)(更多信息12)。

git stash create创建代表存储项的提交,并返回提交对象(具有2或3个父对象)的对象名称(SHA-1)。这是一个悬空的提交(您可以通过调用git fsckafter 来验证它git stash create)。您需要refs/stash指出这个悬而未决的提交,您可以通过git stash store(或git update-ref像其他答案一样,因为git stash store 使用它git update-ref来完成工作)来做到这一点。

这是很好看的实际的源代码git stash push,并看到它基本上调用git stash creategit stash store,然后做一些逻辑,以干净的文件(哪一个取决于你使用什么选项git stash push)。


and*_*ier 5

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

#!/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
Run Code Online (Sandbox Code Playgroud)

我对 Eliot 的脚本进行了以下更改:

  1. 当工作目录干净时,脚本将正常退出
  2. 使用 switch 时-c,如果与上次存储相比没有变化,脚本将退出。如果您将此脚本用作“时间机器”,每 10 分钟自动存储一次,这将非常有用。如果没有任何变化,则不会创建新的存储。如果没有这个开关,你最终可能会得到 n 个相同的连续 stash。

并不是为了让 switch-c正常工作,至少必须存在一个 stash,否则脚本会抛出错误git diff stash@{0}并且什么都不做。

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

 while true ; do date ; git stash-push ; sleep 600 ; done
Run Code Online (Sandbox Code Playgroud)


tan*_*ius 5

下面结合通过@Mariusz Pawelski答案一个类似的回答一个相关的问题,让你感到舒适与消息藏匿。

使用git stash store与消息

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

    git stash store -m "saving intermediate results: my note 1" $(git stash create)
    
    Run Code Online (Sandbox Code Playgroud)
  2. 当您决定要丢弃当前的工作并恢复到以前隐藏的状态时,请先执行另一个git stash. 这会“丢弃”任何未提交的更改,将它们隐藏在存储中,以便在接下来的步骤中应用另一个隐藏状态时不会有合并冲突。

    git stash
    
    Run Code Online (Sandbox Code Playgroud)
  3. 现在查看您在 stash ref 中保存的内容:

    $ git stash list
    
    stash@{0}: saving intermediate results: my note 1
    stash@{1}: saving intermediate results: my note 2
    
    Run Code Online (Sandbox Code Playgroud)
  4. 最后,要恢复到隐藏的先前工作状态,丢弃当前的工作树状态,您可以使用stash@{…}上面输出中的内部索引号来识别要恢复到的存储提交:

    git stash
    
    Run Code Online (Sandbox Code Playgroud)
  5. 如果您发现您想要取回您扔掉的作品:它也会保存在储藏室中,并且可以使用与上述相同的技术进行恢复。

包装为 Git 别名

上面步骤 1 中使用的命令可以与 git 别名一起使用,以使其更方便。创建别名(注意最后的#技术):

$ git stash list

stash@{0}: saving intermediate results: my note 1
stash@{1}: saving intermediate results: my note 2
Run Code Online (Sandbox Code Playgroud)

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

git stash-copy "my note"
Run Code Online (Sandbox Code Playgroud)