如何在Git隐藏中查看未跟踪的文件 [英] How to see untracked files in Git stashes

查看:153
本文介绍了如何在Git隐藏中查看未跟踪的文件的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我定期将我完成的工作保存在新的(未跟踪)文件中,我希望以后能够找到此工作.找到它的明显方法似乎是 git show .

我刚刚发现,当我使用 git show 时,Git会完全忽略这些文件(但幸运的是,在弹出隐藏代码时,它们不会忽略它们),似乎无法可靠地找到隐藏代码.

为了更具体一点,假设我有

 分支功能/请求重新任命尚未进行提交的更改:(使用"git add< file> ..."更新将提交的内容)(使用"git restore< file> ..."放弃工作目录中的更改)修改:app/models/job.rb修改:config/initializers/environmental_email_interceptor.rb未跟踪的文件:(使用"git add< file> ..."将要提交的内容包括在内)app/models/receipt.rbdb/migrate/20200130091050_create_receipts.rb没有添加任何更改来提交(使用"git add"和/或"git commit -a") 

如果我执行

  git stash save -u'其他改进'git show stash @ {0} 

Git将忽略未跟踪的文件.这不仅是命令行中Git的行为,而且Fork和GitKraken之类的GUI工具也存在相同的问题,大概是继承自Git的.

如何显示隐藏的 actual 内容,包括未跟踪文件的 ?

更新:

尽管GitKraken和Fork无法以任何存储方式显示未跟踪的文件,但Tower 2(旧版本,因为我拒绝切换到基于订阅的定价模型,因此很旧)成功.我得给他们一些信誉,如果不付出代价的话,我今天就买一本新书.

解决方案

TL; DR

要查看文件名,请使用 git ls-tree -r stash ^ 3 ;要查看其内容,请使用 git show stash ^ 3 .

如何显示隐藏的 actual 内容,包括未跟踪文件的 ?

未跟踪的文件通常不存储在中.有一个例外-您正在使用它,所以我稍后会解决.但是显示这些文件很棘手.使用它们更加困难.我认为 git stash 前端需要在这里进行一些工作.

但是,首先,重要的是要了解Git在内部如何工作.Git 存储库主要是一对数据库.一个(几乎是最大的)(最多)持有 commits 和其他Git对象.较小的数据库包含名称:分支名称(例如 master ),标记名(例如 v2.1 ),远程跟踪名称(例如 origin/master),依此类推.分支名称和标记名称等是通用 ref reference 的特定形式.引用拥有单个Git哈希ID,最常见的是提交哈希ID. 1

每个提交本身也可以保存一个或多个先前提交的哈希ID.这些是提交的 parents .对于普通(非隐藏)提交,这些通常形成一个很好的简单链:最后一个提交将倒数第二个作为其父级记住.倒数第二个提交会记住父母(最后一个提交是祖父母),祖父母会记住另一个父母,依此类推.按照定义,具有两个父项的提交是合并提交.

因此,分支名称只是保存了该哈希的ID,我们想说的是该分支/上/上的最后一次提交.多个名称可以选择相同的哈希ID,并且/或者通过从某个分支的尖端提交开始并向后工作,哈希ID可以从某个分支可达.因此,在Git中,提交通常位于多个分支上.

提交本身包含所有(跟踪)文件的快照.Git通过将跟踪的文件写入Git的 index 中,然后使用 git write-tree 将文件写入内部树对象,然后执行 git commit-tree ,它使用 git write-tree 编写的树写入提交对象.因此,所有提交都在索引中具有其来源.而且,根据定义,索引中的任何文件都会被跟踪.因此,这使我们有些困惑.


1 需要 分支名称和远程跟踪名称,以保存个提交哈希ID;标签名称更加灵活.


藏匿处

特殊ref refs/stash (如果存在)指向一次提交.那是 stash @ {0} 提交.(它在 stash @ {1} 中的reflog条目也指向每个提交.)因此,该存储存在时,由提交组成.这些提交位于 no 分支上: 2 而是通过 refs/stash 找到的.

普通存储区具有合并提交的 form ,但不是相同的内容. git stash 的作用是使用上述非常低级的 git write-tree git commit-tree 方法进行两次提交.根据运行 git stash save git stash push 时索引中的内容,第一次这样的提交很容易: git write-tree 已经写完索引中的所有内容,因此将这两个命令放在一起,使该索引提交,我称之为 i (而 git stash 文档称为).

第二个提交比较棘手,但是本质上, git stash 的操作是运行 git add -u (尽管实际上没有使用 git add -u ,从而在各种版本的 git stash 中引入错误,其中某些错误在某些情况下会产生错误的工作树提交).这将更新索引,以使其保留所有跟踪的文件的状态,直到它们在工作树中为止.然后 git write-tree 后跟 git commit-tree 可以很好地记录您的工作树,当然还要减去所有未跟踪的文件.

因为 git stash 使用的是低级命令,它可以使工作树与它选择的任何父组一起提交(我称其为 w ).它使用 i HEAD 作为其两个父对象进行提交:

  ...-- o--o<-分支(HEAD)| \- 

i 提交类似于任何普通的提交,而 w 提交则类似于合并.它实际上不是合并-只是添加到索引中的工作树快照-但它具有当前分支的尖端提交作为其第一父级,而具有其第二个父级则是 i .

进行此隐藏之后, git stash save 进行 git reset --hard ,以便您的索引和工作树与 HEAD 匹配. HEAD 本身永远不会移动,未跟踪的文件也不会保存并且不受影响.

但是,当您执行 git stash save -u git stash save -a 时,Git会进行 third 提交,我呼叫 u .此 third 提交使用一个索引,该索引会清除所有 tracked 文件,然后加载一些或所有 untracked 文件,就好像通过 git add --force 来指定特定的文件名. 3 进入第三次提交的文件集取决于您是否使用了 -u -a : -u 枚举未被忽略的未跟踪文件,而 -a 枚举所有未跟踪的文件,即使它们 被忽略.Git将这些文件复制到(很好的)索引中,并进行此 u 提交,而根本没有父级,之前使其成为最终的 w 提交.然后,以 u 作为其第三父级的 w 提交:

  ...-- o--o<-分支(HEAD)| \-/ü 

因此, w ^ 1 是分支尖端的提交, w ^ 2 是索引commit i ,而 w ^ 3 u 提交. w 继续类似于合并提交(这次是章鱼合并),但其快照与任何两次提交的存储相同.

已经完成了 u 提交, git stash save git stash push 的操作,现在从您的工作中删除-树化 u 提交中的所有文件.如果 u 中的某些文件也被忽略,它们仍将被删除.

如果Git无法将 u 提交提取到当前工作树中,则应用三个提交的存储将失败.因此,了解第三次 u 提交中的内容绝对有用.但是没有 git stash ____ (用动词填充空格)来显示是否存在commit u ,更不用说了.因此,我们必须依靠较低级别的Git命令.

尤其是,由于 u root提交,因此 git show 会将其与

If I perform

git stash save -u 'miscellaneous improvements'
git show stash@{0}

Git will omit the untracked files. Not only is this the behavior of Git from the command line, but GUI tools like Fork and GitKraken have the same problem, presumably inherited from Git.

How can I show the actual contents of the stash, including the untracked files?

UPDATE:

although GitKraken and Fork failed to show the untracked files in any stashes, Tower 2 (an old version, since I refused to switch to their subscription-based pricing model) succeeded. I have to give them some credit, and if not for the price I'd buy a new copy today.

解决方案

TL;DR

To see the file names, use git ls-tree -r stash^3; to see their contents, use git show stash^3.

Long

How can I show the actual contents of the stash, including the untracked files?

The untracked files usually aren't in the stash. There is an exception—and you're using it, so I'll get to that in a moment. But showing those files is tricky. Using them is even harder. The git stash front end needs some work here, in my opinion.

First, though, it's important to understand how Git works internally. A Git repository is primarily a pair of databases. One—the biggest one, almost always—holds commits and other Git objects. A smaller database holds names: branch names like master, tag names like v2.1, remote-tracking names like origin/master, and so on. Branch names and tag names and the like are specific forms of the general purpose ref or reference. A ref holds a single Git hash ID, most commonly a commit hash ID.1

Each commit itself can also hold one or more previous-commit hash IDs. These are the parents of the commit. For normal (non-stash) commits, these usually form a nice simple chain: the last commit remembers, as its parent, the second-to-last. That second-to-last commit remembers its parent—the last commit's grandparent—and the grandparent remembers another parent and so on. A commit with two parents is a merge commit, by definition.

So, a branch name simply holds the hash ID of the commit we'd like to say is the last commit in / on / of that branch. Multiple names can select the same hash ID, and/or a hash ID can be reachable from some branch by starting at its tip commit and working backwards. So commits, in Git, are often on more than one branch.

The commits themselves contain snapshots of all of your (tracked) files. Git makes them by writing the tracked files into Git's index and then using git write-tree, which writes the files into internal tree objects, followed by git commit-tree, which writes a commit object using the tree(s) written by git write-tree. So all commits have their origin in the index. And, any file in the index is tracked, by definition. So this gets us to a bit of a puzzle.


1Branch names and remote-tracking names are required to hold only commit hash IDs; tag names are more flexible.


The stash

The special ref refs/stash, if it exists, points to one commit. That is the stash@{0} commit. (Its reflog entries in stash@{1} on up also point to one commit each.) So the stash, when it exists, consists of commits. These commits are on no branch:2 they're found through refs/stash instead.

A normal stash has the form of a merge commit, but not the same substance. What git stash does is to make two commits, using the very low-level git write-tree and git commit-tree method described above. The first such commit, based on whatever is in your index when you run git stash save or git stash push, is easy: git write-tree already just writes whatever is in the index, so the two commands, put together, make this index commit, which I call i (and the git stash documentation calls I).

The second commit is trickier, but essentially, what git stash does is run git add -u (though without actually using git add -u and thereby introducing bugs in various versions of git stash, some of which in some cases produce the wrong work-tree commit). This updates the index so that it holds all tracked files as of their state in the work-tree. Then git write-tree followed by git commit-tree makes a good snapshot of your work-tree, minus of course any untracked files.

Because git stash is using the low-level commands, it can make the work-tree commit—I call it w—with any set of parents it chooses. It makes this commit with both i and HEAD as its two parents:

...--o--o   <-- branch (HEAD)
        |\
        i-w

The i commit looks like any ordinary commit, and the w commit resembles a merge. It's not actually a merge—it's just a snapshot of your work-tree, as added to the index—but it has as its first parent the tip commit of your current branch, and as its second parent, commit i.

After making this stash, git stash save does a git reset --hard, so that your index and work-tree match HEAD. The HEAD itself never moves, and untracked files weren't saved and are unaffected.

When you do a git stash save -u or git stash save -a, however, Git makes a third commit, which I call u. This third commit uses an index that is cleared of all tracked files, and is then loaded with some or all of your untracked files, as if by git add --force on specific file names.3 The set of files that go into this third commit depends on whether you used -u or -a: -u enumerates untracked files that are not ignored, and -a enumerates all untracked files, even if they are ignored. Git copies those files into the (well, an) index and makes this u commit, with no parent at all, before it makes the final w commit. Then it makes the w commit with u as its third parent:

...--o--o   <-- branch (HEAD)
        |\
        i-w
         /
        u

Thus, w^1 is the commit at the tip of the branch, w^2 is the index commit i, and w^3 is the u commit. w continues to resemble a merge commit—this time, an octopus merge—but its snapshot is the same as for any two-commit stash.

Having made a u commit, git stash save or git stash push now removes from your work-tree all of the files that are in the u commit. If some files in u are also ignored, they're still removed.

Applying a three-commit stash fails if Git cannot extract the u commit into your current work-tree. So it's definitely useful to know what is in this third u commit. But there's no git stash ____ (fill in the blank with a verb) to show whether commit u even exists, much less what is in it. We must therefore fall back on lower-level Git commands.

In particular, since u is a root commit, git show will diff it against the empty tree. You can use git show --name-only or git ls-tree -r to get the list of files, if you don't want a full diff. To name commit u, we can name any stash commit—any of the w commit objects pointed to by refs/stash or one of the reflog entries for it—and add the ^3 suffix to mean third parent. If the stash has only w and i, the ^3 will fail: there is no third parent, and hence nothing to show.


2You can, if you like, actually get them onto a branch, but the result is ... ugly at best.

3Internally, git stash uses a temporary index instead of the real / main index, to make this easier. It does that for the w commit too. While there is one special index, the index, that tracks your work-tree, you can create a temporary index at any time, put its path name into GIT_INDEX_FILE, and use Git commands with that temporary index instead of using the distinguished index. That's handy for any command that needs to create a commit—which requires using the index—that doesn't want to disturb the index in the process.

这篇关于如何在Git隐藏中查看未跟踪的文件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆