这里有一些可疑之处,但可能不是藏匿处本身
git stash --include-untracked,可以简写为git stash -u,对存储进行三个提交。
前两个与往常一样:一个用于保存您运行 git stash 时索引中的任何内容,另一个用于保存工作树中的任何内容(但仅跟踪文件)时间。换句话说,保存索引的i 提交保存了git write-tree 的结果,而w 提交保存了git add -u && git write-tree 的结果(尽管存储代码很难做到这一点,或者在过去的 shell 脚本存储中做过)。
如果您运行git stash 没有 --all 或--include-untracked,这就是存储所拥有的全部内容:它将具有i(索引状态)和w 的两个提交(工作树状态),两者都将当前提交 C 作为它们的第一个父级。提交w 有i 作为它的第二个父:
...--o--o--C <-- HEAD
|\
i-w <-- stash
如果你做添加-u 或-a,但是,你会得到一个三个-commit stash:commit w 获得第三个父母,一个提交我们可以调用u,它包含未跟踪的文件。这个第三个父级没有自己的父级(是一个孤儿/根提交),所以现在的绘图是:
...--o--o--C <-- HEAD
|\
i-w <-- stash
/
u
这个新提交的有趣之处以及它在工作树中的影响是:*Commit u 包含仅未跟踪的文件。**
请记住,提交是所有(跟踪的)文件的完整快照。提交u 是通过在临时索引中丢弃所有跟踪文件,而是跟踪一些或所有未跟踪文件。此步骤要么仅添加未跟踪但未忽略的文件 (git stash -u),要么添加所有 文件 (git stash -a)。然后 Git 写入 commit u,使用 git write-tree 将临时索引转换成树放入 commit u,这样 commit u 就仅包含选定的文件。
现在这些选定的文件在提交 u,git stash 将它们从工作树中删除。在实践中,它过去只是使用适当的选项运行git clean。新的更高级的 C 编码 git stash 仍然具有相同的功能(但是,人们可能希望错误更少;见下文)。
这类似于它对i 和/或w 中的文件所做的操作:它有效地执行git reset --hard,因此工作树的跟踪文件与HEAD 提交匹配。 (也就是说,除非您使用--keep-index,否则它会这样做,在这种情况下,它会重置文件以匹配i 提交。)此时git reset 对未跟踪 文件没有影响,它们超出了git reset 的范围,并且对当前分支没有影响,因为重置故意将其保留在HEAD。
虽然在提交 u 中隐藏了一些未跟踪的文件,但 git stash 然后从工作树中删除这些文件。这在以后很重要(也许也很重要)。
注意:在将git stash push 与路径规范结合使用时存在一个错误,这可能会影响一切,但尤其会影响使用-u 或-a 制作的存储变体,其中某些版本的Git删除太多个文件。也就是说,您可能 git stash 只是文件的一部分,但 Git 会 git reset --hard 或 git clean 所有 文件,或太多文件。 (我相信这些今天都已修复,但总的来说,我根本不建议使用 git stash,尤其是不要使用花哨的 pathspec 变体。删除实际上没有隐藏的未跟踪文件是特别令人震惊的行为,并且 一些版本的 Git 可以做到这一点!)
您描述了一个apply-time 问题,但可能不是通常的问题
这就是你所说的:
我认为git stash --include-untracked 会保存已跟踪和未跟踪文件的更改。
与往常一样,Git 不会保存更改,它会保存快照。
但是当我 'git stash apply` 时,只剩下未跟踪文件的更改。跟踪文件的更改丢失。
根据您是否使用--index 标志,以两种方式之一应用普通(无未跟踪文件)存储。没有--index 的变体更容易解释,因为它实际上只是忽略 i 提交。 (带有--index 标志的变体首先在差异上使用git apply --index,如果失败,建议您尝试不使用--index。如果您想要--index 的效果,这个这是一个糟糕的建议,你应该忽略它。不过,对于这个答案,让我们完全忽略 --index 选项。)
注意:这不是--keep-index 标志,而是--index 标志。 --keep-index 标志仅在创建 存储时应用。 --index 标志在应用存储时应用。
要应用 w 提交,Git 直接运行 git merge-recursive。这不是您作为用户应该做的事情,当git stash 这样做时,这也不是那么明智,但这就是它的作用。效果很像运行git merge,只是如果您在索引和/或工作树中有未提交的更改,则可能无法以任何自动方式返回此状态。
如果你从一个“干净”的索引和工作树开始——也就是说,如果git status 表示nothing to commit, working tree clean——这个合并操作几乎与常规的git merge 或git cherry-pick 完全相同,很多方面。 (请注意,git merge 和 git cherry-pick 要求事情是干净的,至少在默认情况下是这样。)合并操作在 merge base 设置为父级的情况下运行提交w,current 或--ours 提交是像往常一样的当前提交,另一个或--theirs 提交是提交w。
也就是说,假设您的提交图现在看起来像这样:
o--o--A--B <-- branch (HEAD)
/
...--o--o--C
|\
i-w <-- stash
/
u
这样你就可以提交B。应用存储的合并操作执行三路合并,C 作为合并基础,w 作为--theirs 提交,当前提交/工作树作为--ours 提交。 Git diffs C vs B 看看 we 发生了什么变化,C vs w 看看 他们 发生了什么变化,并结合了两组差异.
这就是合并到B 的运行方式,假设 Git 可以首先取消隐藏提交 u。此时的常见问题是 Git 无法 un-stash u。
请记住,提交 u 包含(且仅)您进行存储时存在的 untracked 文件,然后 Git 使用 git clean(和适当的选项)删除了这些文件。 这些文件必须仍然不存在于工作树中。如果它们不不存在,git stash apply 将无法从 u 中提取文件并且不会继续.
由于未跟踪的文件是未跟踪的,因此很难知道它们是否发生了变化
但是当我 'git stash apply` 时,只剩下未跟踪文件的更改。跟踪文件的更改丢失。
您谈论的是未跟踪文件中的更改。
Git 当然不会存储更改,因此您无法通过这种方式找到它们。如果文件未被跟踪,它们现在也不在索引中。那么:你怎么知道它们改变了?你需要一些其他的文件来比较它们。
提取提交u 的步骤应该是全有或全无:它应该要么提取所有u 文件,要么不提取。如果它确实提取了所有u 文件,git stash apply 应该继续尝试合并,有点像git cherry-pick -n (除了cherry-pick 也写入索引),在存储中提交w。这应该会在您的工作树中留下提取的 u 文件和合并 w-vs-C 更改。
如果C-vs-work-tree 与 C-vs-w 之间存在冲突,您应该在工作树中存在冲突标记,并且您的索引应该像往常一样展开对于有冲突的合并。
如果您可以为您的问题制作一个重现器,那可能会在这里提供大量的清晰度。