Git stash 删除添加的更改

标签 git git-stash git-index

在开发过程中,我经常将文件的工作版本(但不提交)添加到我的 git 存储库中。我继续处理这些文件,直到它们达到可提交阶段,当我可以提交它们时。所以 repo 看起来像下面

$ git status

# On branch master
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#   modified:   testfile1
#
# Changes not staged for commit:
#   (use "git add <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#   modified:   testfile1
#   modified:   testfile2

当我做 git stash ,然后做一个 git stash pop ,我得到
# On branch master
# Changes not staged for commit:
#   (use "git add <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#   modified:   testfile1
#   modified:   testfile2

问题
  • 为什么 git 不像以前那样把它们藏起来?
  • 我如何一次性存储我的更改,以便在进行存储 pop 时,我会获得较旧的状态,而不是较新的状态?

  • 目前,我手动执行
    git stash --keep-index
    git stash
    git stash pop
    git add <stashed_files>
    git stash pop
    

    问题是
  • 存储和 pop 需要 5 个步骤,我正在寻找 2
  • 我有时可能不记得有 2 个背靠背的储藏室,这使任务稍微复杂化了。

  • 编辑 - 我更喜欢命令行解决方案,因为我在测试服务器上以类似的方式工作。

    最佳答案

    我看到这已经得到了回答,但让我再补充一点,还有一个警告:git stash 中有一个小号.

    当您运行时 git stashgit stash save (默认是 save 所以这些是一样的)不使用 -p , stash脚本——它存在于 git-core目录,其位置因git安装而异,可能在/usr/libexec/git-core/usr/local/libexec/git-core例如——创建一个带有两个(或有时三个)父提交的提交。这些提交按顺序是:

  • 当前指数
  • -u-a ,未跟踪甚至忽略的文件(它还使用 git clean 将它们从工作目录中删除)
  • 工作目录,基于当前工作目录和 HEAD 之间的增量提交(这是 buglet 的来源;见下文)。

  • 然后设置 refs/stash指向这些提交中的最后一个,即工作目录提交。此提交具有作为其父项:
  • HEAD 提交,如 stash^ (第一 parent )
  • 索引提交,如 stash^2 (第二个家长)
  • 未跟踪/忽略的提交,如 stash^3 (第三个父级),如果它存在。

  • 这个存储实际上包含了存储时的所有内容,但是当您使用 git stash pop --index 时,buglet 显示效果最好。或 git stash apply --index恢复“预藏状态”。 (我将在下面单独使用 git stash apply,但 pop 只是 apply 后跟 drop 。)

    现在,如果你只是运行 git stash apply ,正如您所指出的,它为您提供了很多 changes not staged for commit即使您在运行之前仔细地准备了一些文件 git stash save .这是因为像这样将这些更改 merge 在一起要容易得多,无论工作目录状态如何,包括您是否已 checkout 不同的分支或其他任何内容,也包括您是否在运行 git stash apply 之前暂存了一些文件。 . (实际上,git stash apply 使用 git 的 merge 代码来引入工作目录的更改。)

    如果您运行 git stash apply --index但是,stash脚本首先尝试将您在原始 save 时所拥有的内容添加到索引中。 . (如果还没有暂存,这将恢复您的原始状态。)假设它可以做到这一点,它然后尝试类似地设置工作目录(再次使用 merge 机制)。如果它不能正确设置索引,它对索引没有任何作用,并建议您在没有 --index 的情况下重试。 .

    这就是 buglet 出现的地方。假设你从一个文件开始,比如 basefile ,没有变化。您进行更改并进行此操作:
    $ cat basefile
    base
    $ git status --short
    $ echo add to basefile >> basefile; git add basefile
    

    但随后您决定希望工作目录副本与 HEAD 没有任何变化修订:
    $ ed basefile
    21
    2d
    w
    5
    q
    $ git status --short
    MM basefile
    

    这里的棘手之处在于 basefile在索引中修改,然后在工作目录中再次修改,但第二次更改将其带回 HEAD 中的内容犯罪。当您运行时 git stash save , stash 脚本不小心记录了索引版本,就好像它是正在进行的版本一样。

    如果你现在做 git stash apply --index并运行 git status --short :
    $ git stash save
    Saved working directory and index state WIP on master: 94824e1 initial
    HEAD is now at 94824e1 initial
    stash created
    $ git stash apply --index
    # On branch master
    # Changes to be committed:
    #   (use "git reset HEAD <file>..." to unstage)
    #
    #   modified:   basefile
    #
    $ git status --short
    M  basefile
    

    这里 git 已经将索引版本恢复到索引中,然后将工作目录版本设置为与索引版本中相同的内容:
    $ cat basefile
    base
    add to basefile
    

    stash 脚本的修复是一个单词的更改,但到目前为止似乎没有人喜欢它。也许问题在于,如果您在没有 --index 的情况下应用存储,这有效地将索引更改(额外的行, add to basefile )与任何内容结合在一起,以便工作目录版本具有额外的行。但是,这与索引和工作目录版本不同时的行为方式不一致:
    $ git stash drop
    Dropped refs/stash@{0} (61c83c866bc522c58df62320b77e647ffd28aa95)
    $ echo base > basefile
    $ git status --short
    $ echo add to basefile >> basefile
    $ git add basefile
    $ ed basefile
    21
    2c
    different change
    w
    22
    q
    $ git status --short
    MM basefile
    $ git stash save
    Saved working directory and index state WIP on master: 94824e1 initial
    HEAD is now at 94824e1 initial
    $ git stash apply
    # On branch master
    # Changes not staged for commit:
    #   (use "git add <file>..." to update what will be committed)
    #   (use "git checkout -- <file>..." to discard changes in working directory)
    #
    #   modified:   basefile
    #
    no changes added to commit (use "git add" and/or "git commit -a")
    $ cat basefile
    base
    different change
    

    在这里,通过“混合”索引和工作树的变化,但不应用 --index ,我们只恢复工作树的变化。

    (幸运的是,由于我们使用 apply 而不是 pop,我们现在可以改变主意:
    $ git reset --hard HEAD
    HEAD is now at 94824e1 initial
    $ git stash apply -q --index
    $ git status --short
    MM basefile
    

    如果我们查看 index 和 work-dir 版本,我们现在可以看到 basefile 的两个版本.)

    (对 stash 脚本的一个词修复是在以下行中将 HEAD 更改为 $i_tree:
    git diff --name-only -z HEAD -- >"$TMP-stagenames" &&
    

    大约在第 118 行。我将其发布到 git 邮件列表并得到了......蟋蟀。 :-) )

    关于Git stash 删除添加的更改,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19287386/

    相关文章:

    git - 如何检查 stash 文件的源代码而不在 git 中 pop 它们?

    git - 显示 Git 索引中和工作副本中更改的文件

    N 天前的 Git checkout 存储库

    git - stash 提交上的 `WIP` 是什么?

    linux - Gitkraken Bitbucket token 无效

    针对存储的 Git diff

    git - Git暂存区只是一个索引吗?

    git checkout 在 --assume-unchanged 文件上抛出错误

    git - '杀死 : 9' when using git add *

    git - 使用 Git 的编码器协作最佳实践