【问题标题】:How to reverse apply a stash?如何反向应用存储?
【发布时间】:2009-06-19 21:34:59
【问题描述】:

我的 git stash 中保存了一个小补丁。我已经使用git stash apply 将它应用到我的工作副本中。现在,我想通过反向应用补丁来取消这些更改(有点像 git revert 会做的事情,但针对存储)。

有人知道怎么做吗?

澄清:我的工作副本还有其他变化。我的特殊情况很难描述,但您可以想象隐藏中的一些调试或实验代码。现在它在我的工作副本中与其他一些更改混合在一起,我想看看有没有来自 stash 的更改的效果。

目前 stash 似乎不支持此功能,但 git stash apply --reverse 将是一个不错的功能。

【问题讨论】:

  • 不能仅仅通过区分当前和以前的版本来创建一个反向补丁吗?然后应用那个?
  • 除了应用的存储之外,工作树中是否有变化?
  • 在此处添加,...应该是常见问题解答,而不是问题...stackoverflow.com/questions/59973103/…

标签: git git-stash


【解决方案1】:

根据git-stash manpage,“存储被表示为一个提交,其树记录工作目录的状态,其第一个父级是创建存储时HEAD 的提交”和git stash show -p给我们“存储在存储中记录的更改,作为存储状态与其原始父级之间的差异。

要保持其他更改不变,请使用git stash show -p | patch --reverse,如下所示:

$ git init
Initialized empty Git repository in /tmp/repo/.git/

$ echo Hello, world >messages

$ git add messages

$ git commit -am 'Initial commit'
[master (root-commit)]: created 1ff2478: "Initial commit"
 1 files changed, 1 insertions(+), 0 deletions(-)
 create mode 100644 messages

$ echo Hello again >>messages

$ git stash

$ git status
# On branch master
nothing to commit (working directory clean)

$ git stash apply
# On branch master
# Changed but not updated:
#   (use "git add <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#       modified:   messages
#
no changes added to commit (use "git add" and/or "git commit -a")

$ echo Howdy all >>messages

$ git diff
diff --git a/messages b/messages
index a5c1966..eade523 100644
--- a/messages
+++ b/messages
@@ -1 +1,3 @@
 Hello, world
+Hello again
+Howdy all

$ git stash show -p | patch --reverse
patching file messages
Hunk #1 succeeded at 1 with fuzz 1.

$ git diff
diff --git a/messages b/messages
index a5c1966..364fc91 100644
--- a/messages
+++ b/messages
@@ -1 +1,2 @@
 Hello, world
+Howdy all

编辑:

对此的轻微改进是使用git apply 代替补丁:

git stash show -p | git apply --reverse

或者,您也可以使用git apply -R 作为git apply --reverse 的简写。

我最近发现这个真的很方便......

【讨论】:

  • 太棒了,谢谢。看起来这可能是一个很好的隐藏功能。
  • 是的,git apply -R 是一种改进,至少对我来说,在我的带有 git bash 的 windows 机器上,patch --reverse 在定位要修补的文件时遇到了问题(不知道为什么替代方案有效)。 +1 和很好的解释
  • @Greg Bacon,嘿,我已经尝试过您概述的脚本,但是当我运行 git stash show -p | git apply -R -v 并显示以下消息时补丁失败:Checking patch messages... error: while searching for: Hello, world Hello again error: patch failed: messages:1。你知道可能出了什么问题吗?
  • 我得到 错误:补丁失败:/src/filename.java:46 错误:src/filename.java 补丁不适用跨度>
  • …| git apply --reverse 适合我。 …| patch --reversecan't find file to patch at input line 5 失败。 Archlinux系统在这里,补丁版本2.7.6。
【解决方案2】:
git checkout -f

将删除所有未提交的更改。

【讨论】:

  • 谢谢,您帮助我完成了并非不适用的阶段性变更。
  • 这要简单得多
  • 这给我带来了一些奇怪的问题。我的单元测试因显示无法读取 ARM 的晦涩消息而失败。我必须清理我的构建文件夹、派生数据,然后重新启动我的机器才能恢复正常。
  • OP 并没有要求这样做,因为这将删除所有未提交的更改,而不仅仅是“存储应用”引入的更改。这可能会导致丢失有用的更改。
【解决方案3】:

git stash[save] 获取您的工作目录状态和索引状态,并将它们隐藏起来,将索引和工作区域设置为HEAD 版本。

git stash apply 带回这些更改,因此git reset --hard 将再次删除它们。

git stash pop 带回这些更改并删除顶部隐藏的更改,因此git stash [save] 在这种情况下将返回到之前的(弹出前)状态。

【讨论】:

    【解决方案4】:

    V1 git 手册页有一个关于取消应用存储的参考。摘录如下。

    较新的 V2 git man page 不包含任何关于取消应用存储的参考,但以下内容仍然可以正常工作

    取消应用存储 在某些用例场景中,您可能希望应用隐藏的更改,做一些工作,然后取消应用最初来自隐藏的更改。 Git 没有提供这样的 stash un-apply 命令,但是可以通过简单地检索与 stash 关联的补丁并反向应用来实现效果:

    $ git stash show -p stash@{0} | git apply -R
    

    同样,如果您不指定存储,Git 会假定最近的存储:

    $ git stash show -p | git apply -R
    

    您可能想要创建一个别名并有效地将 stash-unapply 命令添加到您的 Git。例如:

    $ git config --global alias.stash-unapply '!git stash show -p | git apply -R'
    $ git stash apply
    $ #... work work work
    $ git stash-unapply
    

    【讨论】:

    • 无论出于何种原因,您链接的“Un-applying a Stash”这个有用的部分已从第二版中删除-现在的最新版本是 2.1.146,2019-04-15- i> 这本书的V2- Git Tools - Stashing and Cleaning。可能是因为作者认为我似乎找不到更好的方法来做到这一点。
    • @NadAlaba 感谢您的提醒,已更新答案以记录 v1 和 v2 之间的区别...奇怪的是 git 作者删除了有关取消应用存储的部分
    • 这个答案对于展示如何指定应用的存储特别有用。感谢您提供这些重要信息!对于那些可能还包含未跟踪文件的人,您可以使用git stash show -p --all stash@{3} | git apply -R
    【解决方案5】:

    这早就过了,但如果我正确地解释了这个问题,我已经找到了一个简单的解决方案,请注意,这是我自己的术语的解释:

    git stash [save] 将保存当前更改并将当前分支设置为“干净状态”

    git stash list 给出类似:stash@{0}: On develop: saved testing-stuff

    git apply stash@{0} 会将当前分支设置为 before stash [save]

    git checkout . 将当前分支设置为 after stash [save]

    保存在stash中的代码没有丢失,可以通过git apply stash@{0}再次找到。

    不管怎样,这对我有用!

    【讨论】:

    • 为了确定,我先申请了git stash apply --reverse,然后就像你提到的那样简单地回到git stash apply stash@{x}。工作没有问题。
    【解决方案6】:

    如何反向应用存储?

    除了其他人提到的,最简单的方法是先做

    git reset HEAD
    

    然后检查所有本地更改

    git checkout . 
    

    【讨论】:

    • 这是迄今为止最简单的方法,只要您绝对没有想要保存的本地工作。如果您将错误的存储应用到分支或遇到您不想解决的合并冲突,这是通过将您的工作集完全恢复到分支的最新提交来撤消它的快速简便的方法。
    【解决方案7】:

    你可以应用两个命令

    git reset . // 反转文件

    然后

    git checkout . // 撤销更改

    【讨论】:

    • 请通过为每个命令添加解释来提高答案的质量。谢谢。
    【解决方案8】:

    如果你不小心点击了隐藏,你可以按照我分享的图像来取消隐藏。

    【讨论】:

      【解决方案9】:

      除了@Greg Bacon 的回答,如果二进制文件被添加到索引中并且是使用的存储的一部分

      git stash show -p | git apply --reverse
      

      可能会导致

      error: cannot apply binary patch to '<YOUR_NEW_FILE>' without full index line
      error: <YOUR_NEW_FILE>: patch does not apply
      

      添加--binary 可以解决问题,但遗憾的是还没有弄清楚原因。

       git stash show -p --binary | git apply --reverse
      

      【讨论】:

        【解决方案10】:

        这是对上述答案的补充,但会根据消息添加对 git stash 的搜索,因为在保存新的存储时存储编号可能会更改。 我写了几个 bash 函数:

        apply(){
          if [ "$1" ]; then
            git stash apply `git stash list | grep -oPm1 "(.*)(?=:.*:.*$1.*)"`
          fi
        }
        remove(){
          if [ "$1" ]; then
            git stash show -p `git stash list | grep -oPm1 "(.*)(?=:.*:.*$1.*)"` | git apply -R
            git status
          fi
        }
        
        1. 使用名称(消息)$ git stash save "my stash" 创建存储
        2. 申请名为$ apply "my stash"
        3. 删除命名存储$ remove "my stash"

        【讨论】:

          【解决方案11】:

          我自己也遇到过类似的问题,我认为您只需git reset --hard 就不会丢失您的更改或任何未跟踪的更改。

          如果您阅读 git stash --help 中的文档,它指出 apply 是“像 pop,但不要从存储列表中删除状态”,因此状态仍然存在,您可以将其取回。

          或者,如果您没有冲突,您可以在测试更改后再次git stash

          如果您确实有冲突,请不要担心,git reset --hard 不会丢失它们,因为 “应用状态可能会因冲突而失败;在这种情况下,它不会从存储列表中删除。您需要手动解决冲突,然后手动调用 git stash drop。”

          【讨论】:

            【解决方案12】:
            git stash show -p | git apply --reverse
            

            警告,并非在所有情况下都如此:“git apply -R(man) 未正确处理两次正确触及同一路径的补丁,这已在 Git 2.30(第一季度)中得到纠正2021)。

            这与将路径从常规文件更改为符号链接(反之亦然)的补丁最相关。

            参见Jonathan Tan (jhowtan)commit b0f266d(2020 年 10 月 20 日)。
            (由 Junio C Hamano -- gitster -- 合并于 commit c23cd78,2020 年 11 月 2 日)

            apply:当-R时,也反转部分列表

            帮助:Junio C Hamano
            签字人:Jonathan Tan

            将符号链接更改为文件的补丁由两部分编写(在代码中,表示为“struct patch”):首先,删除符号链接,其次,创建文件。

            当使用-R 应用该补丁时,这些部分是相反的,因此我们得到:(1) 创建符号链接,然后 (2) 删除文件。

            这会在勾选“deletion of a file”部分时出现问题,因为Git观察到所谓的文件不是文件而是符号链接,导致出现“类型错误”的错误信息。

            我们想要的是:(1) 删除文件,然后 (2) 创建符号链接。

            在代码中,这反映在检查删除时从check_preimage() 调用时previous_patch() 的行为。
            创建然后删除的意思是当检查删除时,previous_patch()返回创建部分,触发模式冲突导致“错误类型”错误信息。

            但是先删除后创建是指当检查删除时,previous_patch()返回NULL,所以删除模式是对照lstat检查的,这就是我们想要的。

            还有其他方法,一个补丁可以包含引用同一文件的 2 个部分,例如,在 7a07841c0b 中(“git-apply:处理多次接触同一路径的补丁更好”,2008-06- 27、Git v1.6.0-rc0——merge)。 "git apply -R"(man) 以同样的方式失败,这次提交使这个案例成功。

            因此,在构建部分列表时,在传递-R 时以相反的顺序构建它们(通过添加到列表的前面而不是后面)。

            【讨论】:

              【解决方案13】:

              对我来说,我刚刚在签出时输入了错误的存储库名称。所以实际上没有远程拉。

              因此,除了检查大小写之外,还要检查分支名称​​拼写,或者更好的是,复制并粘贴它 - 以排除它。

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 2019-03-28
                • 2020-10-20
                • 1970-01-01
                • 1970-01-01
                • 2019-02-20
                • 1970-01-01
                • 2019-07-18
                • 2019-07-15
                相关资源
                最近更新 更多