我不太喜欢 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
)三件事:
-
它将当前分支从当前提交移动到指定的新提交(即重置)。 因为指定的新提交是
HEAD
,这是当前的提交,这就像试图从你的厨房开车到你的厨房:你可能会匆匆忙忙地跑一会儿,但你只是在你开始的地方结束。 -
它会重新设置索引。 也就是说,它使索引与提交
HEAD
匹配。 这将取消暂存所有暂存更改,取消删除git rm
-ed 文件,取消添加任何完全新的文件,并从HEAD
提交您修改然后暂存的任何文件中恢复。 -
它重新设置工作树。 也就是说,它使工作树中的所有跟踪文件与它们在 HEAD 提交和(现在)索引中的版本相匹配。
不过,作为一种心理捷径,你可以把它想象成"重新整理索引和工作树"。stash
代码只是将它们都保存为提交,因此现在可以安全地清除它们。现在,索引和工作树看起来都像您刚刚在当前提交上新运行git checkout
时一样。 这就是为什么您现在可以使用git merge
或git 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世界的阅读障碍者解开了!