在拉取新版本的项目表单 GIT 后,如何检索特定存储文件的内容



我不太喜欢 GIT,我有以下问题。

我的项目中有一些未提交的文件,我用这个语句隐藏了这些文件:

git stash

然后我拉动了存储库,承担了一个 coolege 的工作,这似乎已经正确覆盖了我隐藏文件的更改。

现在我想检索特定存储文件的内容,而不会丢失我的同事的修改。

我不想覆盖拉取的版本,而只能访问特定文件的隐藏版本的代码。

执行git 存储列表我获得:

$ git stash list
stash@{0}: WIP on master: fd2a59b First version of iterate/aggregate for data received from dataservice
stash@{1}: WIP on master: 4910263 DSS project added

如何从外壳中做到这一点?

我自己解决:

1) 首先使用git stash show语句显示隐藏文件:

$ git stash show
glis-toolkit/src/main/synapse-config/api/glisTest2.xml | 8 ++++++++
1 file changed, 8 insertions(+)

2)然后我通过以下方式将此隐藏版本(从存储0)保存在另一个备份文件中:

$ git show stash@{0}:glis-toolkit/src/main/synapse-config/api/glisTest2.xml  >  glisTest2Bck.xml

我不知道是否可以轻松地将特定文件的差异显示到存储中。

但是,要显示stash@{0}的隐藏代码

git stash show stash@{0} -p

如果您对上一个命令的输出不满意,并且由于您的工作树是干净的,您可以从获取所需的存储开始

# say you want to see diff of file my/file1.txt in stash{0}
git stash pop stash@{0}
# To Check your file by showing the diff between your work and your collegue's
git diff my/file1.txt

返回第一步

git stash

希望有帮助

这个答案有几个部分,因为使用git stash可能非常困难。 在这种情况下,您使用它的方式对于您的用例是正确的,但是对于粗心和新手/不太喜欢 Git 的人来说,这里有很多陷阱。

存储进行多次提交

要知道的主要事情,其他一切都是从中流出的:git stash进行提交。 事实上,它至少进行了两次提交;如果你告诉它(用-u-a),它会进行第三次提交。所有这些提交都不分支上,但它们仍然是提交,因此行为类似于提交。 这些提交的格式是,对于其他 Git 命令,它们看起来就像一种奇怪的合并提交,这意味着一般来说,您希望防止其他 Git 命令过于仔细地查看它们,并且主要使用git stash来处理它们。

索引和工作树,以及git checkout

git stash提交的是索引工作树。 到目前为止,工作树是最容易解释的:Git 以内部的、仅限 Git 的格式存储文件,因此要处理这些文件,您需要一个以普通计算机格式存储文件的地方。 这就是工作树。 你可以在这个工作树中放置不会存储在 Git 中的文件,这是索引第一次出现的地方。

Git 的索引有多种用途,但这是主要的用途:它是"你构建下一个提交的地方"。 也就是说,在任何 Git 存储库中,您首先使用您运行git checkout获取的当前提交。 (在第一个git clone,Git 为你运行git checkout master。 好吧,无论如何,它通常是master的,但它绝对是检查出来的东西。 这将选取一个分支名称,使用它来查找分支的提示提交,并使该分支和提交当前分支并提交。 然后它从该特定提交中填充索引(第一次完全为空),因此现在索引中包含当前(或HEAD)提交中的所有文件。 将这些文件添加到索引时,Git 也会将它们复制到工作树中,因此现在您的 HEAD 提交等于等于工作树的索引。

如果你现在git checkout其他一些分支/提交,Git 会更改HEAD以考虑新的分支和提示提交,从索引和工作树中删除旧文件(对于以前的 HEAD),将文件添加到索引和工作树中,然后再次让 HEAD 提交等于等于工作树的索引。

请注意,在所有这些随机调整中,工作树中不在索引中的任何文件都不会受到影响。这些是跟踪的文件,这就是取消跟踪文件的含义:当且仅当文件在索引中时,才会跟踪文件。(这与git stash本身只有一点关系,但它对于使用 Git 和.gitignore至关重要,因为.gitignore跟踪的文件没有影响。

要自己进行新的提交,首先修改工作树中的文件,然后使用git add将新版本复制到索引。 这使新文件准备好提交,其中包含运行git add时拥有的数据。 这会保留所有现有索引文件,因此新提交仍将保留所有其他文件不变。 要添加索引中尚不存在的文件,请在工作树中创建该文件,然后再次运行git add。 与以前一样,将文件复制到索引中,但这次不会覆盖现有副本。 要删除文件,请运行git rm,这会将其索引工作树中删除。

向索引添加(覆盖现有文件或添加新文件)以及从索引和工作树中删除文件的过程,就是我们所说的暂存文件进行提交时的意思。 这就是为什么索引也被称为"暂存区域":我们将更新的工作树文件复制到其中,以使它们暂存。 请注意,除非你git rm所有内容,否则索引本身实际上永远不会是空的:只是一旦你git commit索引,索引和你所做的提交现在就匹配。 (这使得--allow-empty标志git commit有点误导。

回到git stash,特别是"保存新藏匿处"子命令

同样,git stash save(至少)进行了两次提交:首先,它提交索引本身——这真的很容易,因为 Git总是这样提交——然后它进行第二次提交,实际上包括git add所有跟踪的工作树文件,即将它们复制到索引中,然后再次提交。 但是,出于内部原因,它将第二次提交作为合并提交,合并HEAD和刚刚进行的索引提交。只要我们用git stash来处理这种奇怪的合并,我们就不必在意。只有当我们走出git stash时,我们才会突然关心它。

git stash进行这些提交后,它主要相当于git reset --hard HEAD,尽管这里再次有标志选项可以更改这一点。 (有关此过程的更多信息(也许太多),请参阅如何从"git 存储保存 --all"中恢复?git reset --hard HEAD所做的是重新设置(因此git reset)三件事:

  1. 它将当前分支从当前提交移动到指定的新提交(即重置)。 因为指定的新提交是HEAD这是当前的提交,这就像试图从你的厨房开车到你的厨房:你可能会匆匆忙忙地跑一会儿,但你只是在你开始的地方结束。

  2. 它会重新设置索引。 也就是说,它使索引与提交HEAD匹配。 这将取消暂存所有暂存更改,取消删除git rm-ed 文件,取消添加任何完全新的文件,并从HEAD提交您修改然后暂存的任何文件中恢复。

  3. 它重新设置工作树。 也就是说,它使工作树中的所有跟踪文件与它们在 HEAD 提交和(现在)索引中的版本相匹配。

不过,作为一种心理捷径,你可以把它想象成"重新整理索引和工作树"。stash代码只是将它们都保存为提交,因此现在可以安全地清除它们。现在,索引和工作树看起来都像您刚刚在当前提交上新运行git checkout时一样。 这就是为什么您现在可以使用git mergegit rebase的原因(无论您是否从git pull运行它 - 请记住,在所有这些情况下,git pull只是git fetch后跟合并或变基)。

应用存储

应用(或"弹出")存储的过程可能比保存它的过程复杂,但在实践中,它往往很简单。 好吧,除非它出错了,无论如何。 你只需像以前一样进入一个(通常是干净的)提交和索引和工作树状态,然后运行:

git stash apply

这样做是运行几个git diff命令,以找出:

  • 保存的索引与git stash save保存该索引时当前的提交有什么区别?
  • 保存的工作树与保存该工作树时git stash save当前提交有什么区别?

通常,对于每个文件,最多有一个区别(通过保存的索引或通过保存的工作树):您要么暂存文件,因此差异显示在保存的索引中,要么没有,因此您所做的任何更改都会显示在保存的工作树中(如果没有更改,则根本没有区别)。 然后 Git 会针对每个文件将它们全部粉碎在一起,并将所有更改放入当前的工作树中。 默认情况下,您当前的索引不受影响,尽管有更高级的方法来git stash apply

一旦您对存储已正确应用感到满意,您就可以运行git stash drop。 这会丢弃两个保存的提交(如果git stash save将较早的存储"推送"到stash@{1}中,则有效地"弹出"其余部分,以便较早的存储现在stash@{0},或者只是stash)。

如果您确定要在应用后扔掉它,而不先检查正确性,您可以使用git stash pop. 这实际上只是在内部变成了git stash apply && git stash drop。 (当然,如果您使用的是git stash pop stash@{n},它会传递特定的存储。

一些困难的情况,如 XML 文件

应用每个更改的过程(从提交到保存的索引或保存的工作树的每个git diff)都使用 Git 的全部合并功能。 这是一个简单的机械过程,但它对于大多数文件都非常有效。 不幸的是,XML文件往往会击败Git不完全是超能力。

在这种情况下,您可能希望完全按照此处执行的操作进行操作。

因为git stash save只是进行两次提交(不在分支上),所以您可以使用所有常用的 Git 工具来处理这两个提交。 但是一旦你这样做了,你必须小心,因为工作树提交看起来像一个合并提交。 (嗯,事实上,这是一个合并提交。 幸运的是,git show <commit>:<path>并不关心提交是否是合并:它只是提取文件的保存版本。

每个stash名称或引用日志名称都指向保存的工作树提交。 因此:

git show stash:path/to/file.xml

打印该文件的已保存工作树内容。 将其重定向到工作树中的新文件,让您有机会检查保存的内容。 所以这是一件好事。

不过,要小心git show stash. 对于我们这些有阅读障碍的人来说,1使用它而不是git stash show真的很容易。 但是git show <commit>确实关心<commit>是否是合并提交。 虽然git stash show知道将工作树提交视为单独的非合并提交,但git show- 将stash视为提交 ID,然后工作但会得到工作树提交 - 尝试将其视为合并提交。 它试图显示组合差异,通常最终什么都不显示。 (最令人困惑的情况发生在它确实显示某些内容时,但不是您存储的所有内容,当您第一次暂存文件,然后在工作树中再次修改它时,就会发生这种情况。

TL;DR 摘要(如果还不算太晚):git show stash:path/to/file没问题,但git show stash总是一个错误。 大多数情况下,你想要git stash apply,但对于奇怪的情况,git show stash:path/to/file会给你保存的工作树版本。 而且,对于非常复杂的情况,请参阅我的另一个较长的答案并考虑使用git stash branch.


1世界的阅读障碍者解开了!

最新更新