"git stash apply" 与交互模式

65

我有一系列的文件在一个stash (stash{0}) 中,我想要仅仅使用这些文件中的某些部分/块(通常称为交互模式)使用git apply命令。

这是可能的吗?

我看到可以执行:

git stash save -p 'Stash name'

但似乎无法实现

git stash apply -p 'Stash name'

你知道实现它的方法吗?


1
请参考以下链接:https://dev59.com/WXNA5IYBdhLWcg3wEZeT#1105666 - 0 _
1
请参考以下链接:https://dev59.com/WXNA5IYBdhLWcg3wEZeT#59231358 - 0 _
@0_在上面的评论中提供的神秘链接背后的背景是:那些链接是回答问题如何从git stash中提取单个文件(或文件的更改)?。虽然该问题在目标上非常相似,但本问题及其响应侧重于从存储中交互式地修补。 - drmuelr
4个回答

105

这是可能的吗?

是的,它是可能的!

git checkout -p stash@{0}

您可以将stash@{0}中的0替换为要应用的存储编号。

如果不确定要应用哪个存储编号,请使用git stash listgit show -p stash@{n}

不要忘记在确定不再需要该存储时使用git stash drop stash@{n},因为git checkout不会自动删除存储。

为什么它有效?

关键在于意识到存储实质上是与标签和分支一样的提交引用

实际上,它们存储在.git/refs/stash中,每个存储哈希值占据一行。

注意事项

如下所述,@mgadda在下面的评论中提到,git checkout -p尝试应用提交和当前工作区之间的整个差异。

在git stash的情况下,如果您尝试应用的stash是针对不同提交执行的,则git checkout -p stash@{n}将尝试交互式地应用提交stash@{n}和当前工作区提交之间的所有差异,包括它们不同的父提交。

例如,如果您正在尝试将保存在“许多提交之前”的stash应用于当前工作区,git checkout -p stash@{n}将尝试不仅应用stash中的更改,还将尝试撤消基于stash的提交和当前提交之间发生的所有更改。

相反,如果您试图应用一个“来自未来”的存储,即应用到比存储基于的提交更早的分支上,则git checkout -p stash@{n}将尝试同时应用当前提交和未来提交之间发生的所有其他更改,除了存储本身的更改。
(如果您想知道,从并行分支中git checkout -p stash@{n}获取存储将尝试还原当前提交和原始分支点之间的所有更改,并应用分支点和另一个分支之间的所有更改,除了存储中的更改)。

解决方法

有一些解决方法,但对于每种情况都不完美:
    1. 当你使用git checkout -p stash@{n}时,请非常小心接受补丁。
    1. 在执行git checkout -p ...之前,先执行git stash pop,然后再执行git stash。但是,如果您想要部分应用您的存储以避免冲突,则这并没有真正帮助。在这种情况下,请参见下面的解决方案4。
    1. 如果您有一个由git支持的图形差异工具(如meld),则可以使用git difftool并仅“应用左侧”您感兴趣的更改:
    • git difftool -d stash@{n}比较整个存储区及其所有文件

    • git difftool stash@{n} -- path/to/file比较单个文件

    1. (基于@andrew的答案) 在分离的头上,回到您感兴趣的存储区的“父”提交,应用存储区,交互地重新存储您感兴趣的部分,然后返回并重新应用较小的存储区。
逐步操作:
git checkout stash@{n}^  # notice the "^". 

# Now you're in a detached head in the parent commit of the stash.
# It can be applied cleanly:
git stash apply stash@{n}

# Now save only the diffs you're interested in:
git stash -p

# remove the rest of the old stash
git checkout -- .  # be careful or you could remove unrelated changes

# go back to the branch where you want to apply the smaller stash
git checkout <my previous branch>

# apply the smaller stash
git stash pop

2
这里需要提到一个警告:因为stash只是提交,这意味着它们也有父提交,这些父提交不能保证与您想要交互应用更改的父提交相同。经验法则:如果您从当前检出提交以外的某个提交中隐藏了更改,则此技术将无法按预期执行。解决方法:应用来自您的stash的整个更改集(使用git stash pop),然后再次stash(git stash)。现在,您可以像所需的那样进行git checkout -p。 - mgadda

13

我经常在 Git Bash 中做的事情是

git stash show -p 'stash@{0}' >tmp.patch

然后我编辑文件并删除我不想要的部分。 最后我说:

<tmp.patch git apply

或者

<tmp.patch patch -p1

然而,它不适用于二进制文件,但是对于它们也没有被接受的答案(使用checkout -p)。


不错的替代方案,谢谢!我认为,就像被接受的答案中所说的那样,在创建补丁之前,需要检出存储引用的父提交;否则,它将包括许多中间提交(在“当前检出的提交”和“存储的父提交”之间),正如mgadda的评论中提到的那样。 - Kamafeather
不,tmp.patch文件的内容不取决于您在创建它时检出的内容。只有git apply会受到影响,如果受影响的代码行在此期间发生更改,则会报告合并冲突。 - user829755
1
抱歉,我漏掉了“我编辑文件并删除不需要的部分”的部分。在我看来,这也是令人烦恼的部分,因为手动编辑容易出错。 - Kamafeather

4

一种可能的方法是重置索引,然后使用交互式添加

# 0. ensure there are no uncommitted changes
git status

# 1. apply a changeset as is
git stash apply stash@{n}
# ... fix or discard conflicts if any

# 2. reset the index 
git reset

# 3. interactively add the required chunks (except new files)
git add -p

# 4. stash all other changes
git stash save --keep-index "comment"
# 4. or just discards all other changes in the working tree
git checkout-index -f -a

# 5. commit
git commit -m "comment"

另一种方法是使用交互式重置来替代交互式添加。
# 0. ensure the working tree does not have unstaged changes
git status

# 1. apply a changeset as is
git stash apply stash@{n}
# ... fix or discard conflicts if any

# 2. interactively exclude the unneeded chunks from the index 
git reset -p

# 3. stash all other changes
git stash save --keep-index "comment"
# 3. or just discards all other changes in the working tree
git checkout-index -f -a

# 4. commit
git commit -m "comment"

2

我认为没有一种方法可以按块(甚至按文件)应用更改。您需要应用存储,然后交互式地存储不想要的更改(使用git stash save -p)。如果您担心冲突,您可以先存储任何未提交的更改,应用您的存储,存储任何有冲突的块,然后应用另一个存储。


1
是的,我主要是想避免冲突才问这个问题。目的是从(假设)branch_A 获取一些更改,并能够将它们放在branch_B上,直接避免这两个分支可能存在的冲突。你的解决方案可行,但恰恰是我想避免的“复杂”方式 ;-P - Kamafeather
1
相反的做法是,只对我需要的代码块进行交互式保存(使用 git stash save),然后在所需的分支上还原它,而不是交互式地应用我需要的代码块(使用 git stash apply)。这似乎是最好的方法。 - Kamafeather

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接