git - 如何在Git隐藏中查看未跟踪的文件

原文 标签 git

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

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

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

On branch feature/request-reappointment
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
    modified:   app/models/job.rb
    modified:   config/initializers/environmental_email_interceptor.rb

Untracked files:
  (use "git add <file>..." to include in what will be committed)
    app/models/receipt.rb
    db/migrate/20200130091050_create_receipts.rb

no changes added to commit (use "git add" and/or "git commit -a")


如果我表演

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


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

如何显示存储的实际内容,包括未跟踪的文件?

更新:

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

最佳答案

TL; DR

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




如何显示存储的实际内容,包括未跟踪的文件?


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

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

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

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

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



1必须使用分支名称和远程跟踪名称才能仅保留提交哈希ID;标签名称更加灵活。



藏匿处

特殊引用refs/stash(如果存在)指向一次提交。那就是stash@{0}提交。 (它的stash@{1}中的reflog条目也指向每个提交。)因此,存储项(如果存在)由提交组成。这些提交不在任何分支上:2而是通过refs/stash找到。

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

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

由于git stash使用的是低级命令,因此它可以使用它选择的任意一组父项来提交工作树(我称之为w)。它使用iHEAD作为其两个父对象进行提交:

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


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

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

但是,当您执行git stash save -ugit stash save -a时,Git会进行第三次提交,我将其称为u。第三次提交使用的索引将清除所有跟踪的文件,然后将某些或所有未跟踪的文件加载,就像通过git add --force对特定文件名进行加载。3进入第三次提交的文件集取决于关于使用的是-u还是-a-u枚举未被忽略的未跟踪文件,而-a枚举所有未跟踪的文件,即使它们被忽略。 Git将这些文件复制到(正确的)索引中,并在最终提交u之前进行此w提交,而没有任何父项。然后使用w作为其第三父级提交u提交:

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


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

提交u提交后,git stash savegit stash push现在将从工作树中删除u提交中的所有文件。如果u中的某些文件也被忽略,它们仍将被删除。

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

特别是,由于u是根提交,因此git show会将其与empty tree进行比较。如果您不希望使用完全差异,则可以使用git show --name-onlygit ls-tree -r来获取文件列表。要命名提交u,我们可以命名任何隐藏提交-由w指向的任何refs/stash提交对象或它的其中一个reflog条目-并添加^3后缀以表示第三父级。如果存储区只有wi,则^3将失败:没有第三父级,因此没有任何显示。



2如果愿意,您可以将它们实际放置到分支上,但结果充其量是……丑陋。

3内部,git stash使用临时索引而不是实数/主索引来简化此操作。对于w提交也是如此。尽管有一个特殊的索引(即跟踪您的工作树的索引),但是您可以随时创建一个临时索引,将其路径名放入GIT_INDEX_FILE,并对该临时索引使用Git命令,而不是使用专有索引。 。这对于任何需要创建提交(需要使用索引)的命令都很方便,而该命令不需要使用进程中的索引。

相关文章:

git - 所有分支都使用git bundle进行增量备份

git - 在AWS EC2实例上的dockerfile中将git clone作为主机和私有git存储库运行时,主机验证失败错误

git - 如何忽略已经提交的文件?

git - 在git中解决'两者都添加'的合并冲突?

git - 在github上为项目做贡献,如何“在主人之上重新提出我的拉取请求”

bash - 查找本地git分支的REMOTE父分支

git - IntelliJ中的“更改”视图继续列出文件,其中唯一的区别是LF与CRLF

git - Git可以忽略具有相同内容但更新时间戳的文件

git - 为什么git不包含最新的Linux内核?

git - git尝试在结帐时删除目录