执行 git stash --include-untracked 时丢失跟踪文件
Lost tracked files when doing git stash --include-untracked
当我git status
时,有跟踪和未跟踪的文件。那天早些时候,我刚刚得知 git stash --include-untracked
会存储未跟踪的文件。当时它对我有用。所以我认为 git stash --include-untracked
会保存跟踪和未跟踪文件的更改。但是当我 git stash apply
时,只剩下未跟踪文件的更改。跟踪文件的更改丢失。
这里有些可疑,但可能不是藏匿处本身
git stash --include-untracked
,可以简称为 git stash -u
,使 3 次提交用于存储。
前两个与往常一样:一个用于保存您 运行 git stash
时索引中的任何内容,另一个用于保存 [=255] 中的任何内容=]——但只跟踪文件——当时。换句话说,持有索引的 i
提交持有 git write-tree
的结果,而 w
提交持有(相当于)git add -u && git write-tree
的结果(尽管隐藏代码以困难的方式做到这一点,或者在过去的 shell 脚本存储中做到了。
如果你 运行 git stash
没有 --all
或 --include-untracked
,这就是藏匿处的全部内容:它会有i
(索引状态)和 w
(work-tree 状态)的两个提交,它们都将当前提交 C
作为它们的第一个 parent。提交 w
的第二个 i
parent:
...--o--o--C <-- HEAD
|\
i-w <-- stash
如果你做添加-u
或-a
,但是,你会得到一个三个-commit stash : 提交 w
获取第三个 parent,我们可以称之为 u
的提交,它包含未跟踪的文件。第三个parent没有自己的parent(是孤儿/root-commit),所以现在画的是:
...--o--o--C <-- HEAD
|\
i-w <-- stash
/
u
这个新提交的有趣之处在于,它在 work-tree 中的效果也是如此:*提交 u
包含 仅 个未跟踪的文件.**
请记住,提交是所有(跟踪的)文件的完整快照。提交 u
是由——在临时索引中——丢弃所有tracked文件,而是跟踪部分或全部未跟踪 个文件。此步骤要么仅添加 untracked-but-not-ignored 个文件 (git stash -u
),要么添加 所有 个文件 (git stash -a
)。然后Git写入commitu
,使用git write-tree
将临时索引变成树放入commitu
,使得commitu
包含仅 所选文件。
现在这些选定的文件已提交 u
,git stash
将它们从 work-tree 中删除。实际上,它过去只是 运行 git clean
和适当的选项。新发烧友 C-coded git stash
仍然做同样的事情(但是,人们可能希望,错误更少;见下文)。
这与它对 i
and/or w
中的文件所做的类似:它有效地执行 git reset --hard
,因此 work-tree的跟踪文件匹配 HEAD
提交。 (也就是说,除非您使用 --keep-index
,否则它会执行此操作,在这种情况下,它会重置文件以匹配 i
提交。)此时的 git reset
对 [=177 没有影响=]untracked 文件,这些文件在 git reset
的范围之外,并且对当前 b运行ch 没有影响,因为重置故意将其保留在 HEAD
。
虽然在提交 u
中隐藏了一些未跟踪的文件,但是,git stash
然后 从 work-tree 中删除 这些文件。这在以后(也许也是立即)非常重要。
注意: 将 git stash push
与路径规范结合使用时存在错误,这可能会影响所有内容,但尤其会影响使用 -u
或-a
,其中某些版本的 Git 删除了 太多 文件。也就是说,您可能 git stash
只是文件的一部分,但是 Git 会 git reset --hard
或 git clean
所有 文件,或者文件太多。 (我相信今天这些都已修复,但总的来说,我根本不建议使用 git stash
,尤其是花哨的 pathspec 变体。删除实际上没有隐藏的未跟踪文件是特别令人震惊的行为,并且 版本的 Git 可以做到这一点!)
你描述了一个 apply
时间的问题,但可能不是通常的问题
这是你说的:
I thought git stash --include-untracked
would save both tracked and untracked files' change.
一如既往,Git 不保存 更改 ,它保存 快照 。
But when I 'git stash apply`, there is only untracked files' change left. The tracked files' change are lost.
应用普通 (no-untracked-files) 存储可以通过两种方式之一完成,具体取决于您是否使用 --index
标志。没有 --index
的变体更容易解释,因为它实际上只是 忽略 i
提交。 (带有 --index
标志的变体首先在 diff 上使用 git apply --index
,如果失败,建议您尝试不使用 --index
。如果您 想要 --index
的影响,这是一个糟糕的建议,你应该忽略它。不过,对于这个答案,让我们完全忽略 --index
选项。)
注意:这不是--keep-index
标志,而是--index
标志。 --keep-index
标志仅在 创建 存储时适用。 --index
标志在 应用 存储时应用。
要应用 w
提交,直接 Git 运行s git merge-recursive
。作为用户,这不是您应该做的事情,当 git stash
这样做时,这也不是那么明智,但这就是它的作用。效果很像 运行ning git merge
,除了如果你有未提交的更改在您的索引 and/or work-tree 中,可能无法以任何自动方式 return 进入此状态。
如果您从 "clean" 索引和 work-tree 开始,但是——也就是说,如果 git status
说 nothing to commit, working tree clean
——这个合并操作几乎与一个常规的 git merge
或 git cherry-pick
,在很多方面。 (请注意,git merge
和 git cherry-pick
都要求 至少在默认情况下是干净的。)合并操作 运行 与 merge base 设置为提交 w
的 parent,current 或 --ours
提交为当前提交通常,另一个或 --theirs
提交正在提交 w
.
也就是说,假设您的提交图现在看起来像这样:
o--o--A--B <-- branch (HEAD)
/
...--o--o--C
|\
i-w <-- stash
/
u
这样您就可以提交 B
。应用存储的合并操作执行 three-way 合并,C
作为合并基础,w
作为 --theirs
提交,当前的 commit/work-tree 作为--ours
提交。 Git 比较 C
与 B
以查看 我们 更改了什么,以及 C
与 w
以查看 他们改变了,结合了两组的不同。
这就是合并到 B
的方式 运行, 前提是 Git 可以首先 un-stash 提交 u
。此时通常的问题是Git 不能 un-stash u
.
请记住,提交 u
完全(且仅)包含您创建存储时存在的 未跟踪 文件,然后 Git 被删除使用 git clean
(和适当的选项)。 work-tree.中必须仍然缺少这些文件,如果不不存在,git stash apply
将无法提取来自 u
的文件将不会继续。
由于未跟踪的文件是未跟踪的,因此很难知道它们是否发生了变化
But when I 'git stash apply`, there is only untracked files' change left. The tracked files' change are lost.
您谈论未跟踪文件中的更改。
Git 当然不会存储更改,因此您无法通过这种方式找到它们。如果文件未被跟踪,它们现在也不在索引中。所以:您如何知道它们 已更改? 您需要一些其他文件集来与它们进行比较。
提取提交 u
的步骤应该是 all-or-nothing:它应该提取所有 u
文件,或者不提取。如果它确实提取了所有 u
文件,git stash apply
应该继续尝试合并,有点像 git cherry-pick -n
(除了 cherry-pick 也写入索引),提交w
在藏匿处。那应该给你留下提取的 u
文件 和 合并的 w
-vs-C
更改,在你的 work-tree.
如果 C
-vs-work-tree 与 C
-vs-w
之间存在冲突,您应该在 work-tree,并且您的索引应该像往常一样针对冲突合并进行扩展。
如果您可以为您的问题制作一个重现器,那么这里可能会提供大量的清晰度。
我发现这个问题是因为我发现自己处于同样的情况 - 我从另一个 SO 答案中复制并粘贴了 git stash -u
作为一种据称隐藏我未跟踪的文件的方式,然后当我这样做时感到震惊 git stash apply
并且它们没有恢复。
@torek 有两个 and 答案,它们解释了当您 git stash -u
时 git 在做什么以及为什么文件没有恢复。在他们的帮助下,我想我已经拼凑了恢复它们所需的操作。
这将取决于您在此期间没有再次藏匿或做其他 git 事情,如果您有,那么下面的一些可能需要调整。
如果您阅读了@torek 的任何回答,您就会知道 git stash -u
实际上进行了三次提交 - 其中一次有您未跟踪的文件,但它不是被 [=13= 应用的那个] 并且不会显示在 git stash list
.
中
您应该可以通过以下方式找到它:
git show stash^3
如果这看起来像是您未跟踪的文件,那么您很幸运!
现在只需:
git cherry-pick --no-commit $(git rev-parse stash^3)
这将恢复未跟踪的文件...尽管暂存为“要提交的更改”
(git reset
到 un-stage 他们)
当我git status
时,有跟踪和未跟踪的文件。那天早些时候,我刚刚得知 git stash --include-untracked
会存储未跟踪的文件。当时它对我有用。所以我认为 git stash --include-untracked
会保存跟踪和未跟踪文件的更改。但是当我 git stash apply
时,只剩下未跟踪文件的更改。跟踪文件的更改丢失。
这里有些可疑,但可能不是藏匿处本身
git stash --include-untracked
,可以简称为 git stash -u
,使 3 次提交用于存储。
前两个与往常一样:一个用于保存您 运行 git stash
时索引中的任何内容,另一个用于保存 [=255] 中的任何内容=]——但只跟踪文件——当时。换句话说,持有索引的 i
提交持有 git write-tree
的结果,而 w
提交持有(相当于)git add -u && git write-tree
的结果(尽管隐藏代码以困难的方式做到这一点,或者在过去的 shell 脚本存储中做到了。
如果你 运行 git stash
没有 --all
或 --include-untracked
,这就是藏匿处的全部内容:它会有i
(索引状态)和 w
(work-tree 状态)的两个提交,它们都将当前提交 C
作为它们的第一个 parent。提交 w
的第二个 i
parent:
...--o--o--C <-- HEAD
|\
i-w <-- stash
如果你做添加-u
或-a
,但是,你会得到一个三个-commit stash : 提交 w
获取第三个 parent,我们可以称之为 u
的提交,它包含未跟踪的文件。第三个parent没有自己的parent(是孤儿/root-commit),所以现在画的是:
...--o--o--C <-- HEAD
|\
i-w <-- stash
/
u
这个新提交的有趣之处在于,它在 work-tree 中的效果也是如此:*提交 u
包含 仅 个未跟踪的文件.**
请记住,提交是所有(跟踪的)文件的完整快照。提交 u
是由——在临时索引中——丢弃所有tracked文件,而是跟踪部分或全部未跟踪 个文件。此步骤要么仅添加 untracked-but-not-ignored 个文件 (git stash -u
),要么添加 所有 个文件 (git stash -a
)。然后Git写入commitu
,使用git write-tree
将临时索引变成树放入commitu
,使得commitu
包含仅 所选文件。
现在这些选定的文件已提交 u
,git stash
将它们从 work-tree 中删除。实际上,它过去只是 运行 git clean
和适当的选项。新发烧友 C-coded git stash
仍然做同样的事情(但是,人们可能希望,错误更少;见下文)。
这与它对 i
and/or w
中的文件所做的类似:它有效地执行 git reset --hard
,因此 work-tree的跟踪文件匹配 HEAD
提交。 (也就是说,除非您使用 --keep-index
,否则它会执行此操作,在这种情况下,它会重置文件以匹配 i
提交。)此时的 git reset
对 [=177 没有影响=]untracked 文件,这些文件在 git reset
的范围之外,并且对当前 b运行ch 没有影响,因为重置故意将其保留在 HEAD
。
虽然在提交 u
中隐藏了一些未跟踪的文件,但是,git stash
然后 从 work-tree 中删除 这些文件。这在以后(也许也是立即)非常重要。
注意: 将 git stash push
与路径规范结合使用时存在错误,这可能会影响所有内容,但尤其会影响使用 -u
或-a
,其中某些版本的 Git 删除了 太多 文件。也就是说,您可能 git stash
只是文件的一部分,但是 Git 会 git reset --hard
或 git clean
所有 文件,或者文件太多。 (我相信今天这些都已修复,但总的来说,我根本不建议使用 git stash
,尤其是花哨的 pathspec 变体。删除实际上没有隐藏的未跟踪文件是特别令人震惊的行为,并且 版本的 Git 可以做到这一点!)
你描述了一个 apply
时间的问题,但可能不是通常的问题
这是你说的:
I thought
git stash --include-untracked
would save both tracked and untracked files' change.
一如既往,Git 不保存 更改 ,它保存 快照 。
But when I 'git stash apply`, there is only untracked files' change left. The tracked files' change are lost.
应用普通 (no-untracked-files) 存储可以通过两种方式之一完成,具体取决于您是否使用 --index
标志。没有 --index
的变体更容易解释,因为它实际上只是 忽略 i
提交。 (带有 --index
标志的变体首先在 diff 上使用 git apply --index
,如果失败,建议您尝试不使用 --index
。如果您 想要 --index
的影响,这是一个糟糕的建议,你应该忽略它。不过,对于这个答案,让我们完全忽略 --index
选项。)
注意:这不是--keep-index
标志,而是--index
标志。 --keep-index
标志仅在 创建 存储时适用。 --index
标志在 应用 存储时应用。
要应用 w
提交,直接 Git 运行s git merge-recursive
。作为用户,这不是您应该做的事情,当 git stash
这样做时,这也不是那么明智,但这就是它的作用。效果很像 运行ning git merge
,除了如果你有未提交的更改在您的索引 and/or work-tree 中,可能无法以任何自动方式 return 进入此状态。
如果您从 "clean" 索引和 work-tree 开始,但是——也就是说,如果 git status
说 nothing to commit, working tree clean
——这个合并操作几乎与一个常规的 git merge
或 git cherry-pick
,在很多方面。 (请注意,git merge
和 git cherry-pick
都要求 至少在默认情况下是干净的。)合并操作 运行 与 merge base 设置为提交 w
的 parent,current 或 --ours
提交为当前提交通常,另一个或 --theirs
提交正在提交 w
.
也就是说,假设您的提交图现在看起来像这样:
o--o--A--B <-- branch (HEAD)
/
...--o--o--C
|\
i-w <-- stash
/
u
这样您就可以提交 B
。应用存储的合并操作执行 three-way 合并,C
作为合并基础,w
作为 --theirs
提交,当前的 commit/work-tree 作为--ours
提交。 Git 比较 C
与 B
以查看 我们 更改了什么,以及 C
与 w
以查看 他们改变了,结合了两组的不同。
这就是合并到 B
的方式 运行, 前提是 Git 可以首先 un-stash 提交 u
。此时通常的问题是Git 不能 un-stash u
.
请记住,提交 u
完全(且仅)包含您创建存储时存在的 未跟踪 文件,然后 Git 被删除使用 git clean
(和适当的选项)。 work-tree.中必须仍然缺少这些文件,如果不不存在,git stash apply
将无法提取来自 u
的文件将不会继续。
由于未跟踪的文件是未跟踪的,因此很难知道它们是否发生了变化
But when I 'git stash apply`, there is only untracked files' change left. The tracked files' change are lost.
您谈论未跟踪文件中的更改。
Git 当然不会存储更改,因此您无法通过这种方式找到它们。如果文件未被跟踪,它们现在也不在索引中。所以:您如何知道它们 已更改? 您需要一些其他文件集来与它们进行比较。
提取提交 u
的步骤应该是 all-or-nothing:它应该提取所有 u
文件,或者不提取。如果它确实提取了所有 u
文件,git stash apply
应该继续尝试合并,有点像 git cherry-pick -n
(除了 cherry-pick 也写入索引),提交w
在藏匿处。那应该给你留下提取的 u
文件 和 合并的 w
-vs-C
更改,在你的 work-tree.
如果 C
-vs-work-tree 与 C
-vs-w
之间存在冲突,您应该在 work-tree,并且您的索引应该像往常一样针对冲突合并进行扩展。
如果您可以为您的问题制作一个重现器,那么这里可能会提供大量的清晰度。
我发现这个问题是因为我发现自己处于同样的情况 - 我从另一个 SO 答案中复制并粘贴了 git stash -u
作为一种据称隐藏我未跟踪的文件的方式,然后当我这样做时感到震惊 git stash apply
并且它们没有恢复。
@torek 有两个 git stash -u
时 git 在做什么以及为什么文件没有恢复。在他们的帮助下,我想我已经拼凑了恢复它们所需的操作。
这将取决于您在此期间没有再次藏匿或做其他 git 事情,如果您有,那么下面的一些可能需要调整。
如果您阅读了@torek 的任何回答,您就会知道 git stash -u
实际上进行了三次提交 - 其中一次有您未跟踪的文件,但它不是被 [=13= 应用的那个] 并且不会显示在 git stash list
.
您应该可以通过以下方式找到它:
git show stash^3
如果这看起来像是您未跟踪的文件,那么您很幸运!
现在只需:
git cherry-pick --no-commit $(git rev-parse stash^3)
这将恢复未跟踪的文件...尽管暂存为“要提交的更改”
(git reset
到 un-stage 他们)