0

更新:我的权限问题以某种方式任意修复,因为现在完全相同的远程分支创建命令有效,所以(正如您可能猜到的)它不是由这个跟踪问题引起的。

现在我只是想解决跟踪问题。谢谢!

原文:我最初在几个文件中应用了以下内容:

    git update-index --assume-unchanged filename.py

参考本文档:

https://git-scm.com/docs/git-update-index#_using_assume_unchanged_bit

从我的工作目录更改中隐藏某些文件(因为将文件添加到.git/info/exclude似乎没有效果 - 尽管路径正确并且尚未提交,但文件仍然可见)。

然后我遇到了一些推送到以前不存在的远程分支的问题,标准

    fatal: Could not read from remote repository.

    Please make sure you have the correct access rights
    and the repository exists. 

所以,鉴于我在运行之前没有访问问题update-index,我决定恢复它,--assume-unchanged因为我没有对我的 git repo 做任何其他混乱。

我能够使用查看这些assume-unchanged文件

    git ls-files -v | grep '^[a-z]'

我试图assume-unchanged通过使用恢复

    git update-index --really-refresh

assume-unchanged如此处关于跨多个文件还原的指示:

撤消 git update-index --assume-unchanged <文件>

这并没有恢复它们,然后我尝试了

    git ls-files -v | grep '^[a-z]' | cut -c 3- | tr '\012' '\000' | xargs -0 git update-index --no-assume-unchanged

根据此建议:

撤消 git update-index --assume-unchanged <文件>

这些文件仍然不可见,我现在也无法通过运行查看它们

    git ls-files -v | grep '^[a-z]'

我还在我的一个文件上进行了实验性尝试:

    git update-index --skip-worktree file.py

按照这里

https://git-scm.com/docs/git-update-index#_skip_worktree_bit

正如我所知skip-worktreeassume-unchanged我希望看到该文件的一些状态变化。依然没有。

如何将文件恢复为可见并在我的工作目录中跟踪更改?

作为一个额外的问题,您知道这可能如何影响我创建新远程分支的访问权限吗?

4

1 回答 1

1

你在这里有两个不同的问题。您的fatal: Could not read from remote repository错误完全独立于您在索引中设置或清除的任何标志位。这意味着:

  • 对于您用于访问另一个存储库(httphttpsssh://git://)的任何协议,与另一个 Git 的连接失败,或者
  • 连接成功,但据称拥有另一个 Git 存储库的另一个主机说“我在这里看不到 Git 存储库”(在 URL 的其余部分)。

如果连接本身失败,您通常应该在fatal:告诉您发生情况的行之前看到附加信息,例如,无法将主机名解析为 IP 地址、需要但缺少用户名和/或密码等。使用它来诊断不良连接。如果连接成功,但主机说“这里没有 Git 存储库”,请使用您可以获得的有关主机的任何信息来找出存储库的位置(例如,您可以直接登录主机并四处寻找吗?)。


现在,至于assume-unchangedskip-worktree位,恐怕这有点复杂和技术性。您需要了解其中一些内容才能使用 Git,因此值得一试。我在这里还注意到,使用的建议--really-refresh基本上是错误的。出于更新索引中缓存的统计数据的目的,这确实会暂时忽略该assume-unchanged位,但对实际假设不变的位没有影响。清除该位的方法确实是使用--no-assume-unchanged,就像您上面引用的花哨的管道一样,以 . 结尾| xargs -0 git update-index --no-assume-unchanged。跳过工作树位也是如此,除了您使用--no-skip-worktree. 1

索引是什么,这些git update-index命令在做什么?

重要的是要意识到,在使用 Git 时,您始终拥有三个我喜欢称之为活动副本的每个文件。2这三个副本之一是您签出 的任何提交中的任何内容。此副本是严格只读的。你不能改变它:它被冻结成一个提交,并且会留在那个提交中,只要那个提交本身存在——本质上,永远存在。3 这意味着冻结的副本是安全的:您所做的任何事情都不会破坏它;你总能把它拿回来。 查看名为 的文件的冻结副本path/to/file.ext,请使用git show HEAD:path/to/file.ext. HEAD:file语法适用于git show并让您看到当前提交中的冻结副本。(您也可以在任何其他提交中看到冻结的副本。)

现在,这些冻结的副本是一种特殊的、只读的、仅限 Git 的压缩格式。您计算机上的任何其他程序都不能直接访问它们(除非它们对 Git 的内部了解得太多)。而且,由于它们无法更改,因此它们可以用于存档,但对于完成任何实际的新工作却毫无用处。因此,当您git checkout进行一些特定的提交时,Git 会提取所有冻结的文件,将它们转回正常的日常文件,您可以像往常一样查看和使用这些文件。您可以更改这些普通的读/写文件,或者对它们做任何您想做的事情,包括添加新文件和删除现有文件,所有这些都以您对计算机执行任何操作的通常方式进行。

这些可用的、可操作的文件位于 Git 所称的工作树中。这就是你完成工作的地方。 这是每个文件的第二个副本。 您不必在这里做任何特别的事情:这些只是文件。如果您想查看名为 的文件file,请使用您总是用来查看名为file. 它的名字只是file.

每个文件的第三个副本是 Git 偷偷摸摸的地方。这就是索引的用武之地。索引也称为暂存区,或者有时——现在很少见——缓存。这些都是同一事物的名称。 每个文件的第三个副本都在索引file中,您可以查看名为using的文件的索引副本git show :file。也就是说,git show前面的冒号表示:显示索引中的副本。 您在花哨的管道中使用的git ls-files命令还列出了索引中的内容。

该文件的第三个副本采用 Git 用于永久冻结文件存储的格式,但并未完全冻结。您可以随时覆盖它。file您可以用您喜欢的任何新内容替换索引副本。您使用 执行此操作git add,它获取工作树副本(假设您此时已更改该副本)并将索引副本替换为该版本。或者,如果您愿意,可以使用删除索引副本,这将同时删除索引副本工作树副本。filegit rm

从技术上讲,索引中的内容只是关于文件的大量缓存数据,加上一堆标志,以及对文件存储的冻结格式副本的引用。当您第一次检查一些提交时,以便您的HEAD和工作树副本匹配,您的索引副本实际上只是直接重新使用冻结的HEAD副本,所以这根本不需要额外的空间。当您使用git add覆盖它时,Git 会获取工作树副本,将其压缩为冻结的、可用于永久存储的副本,并将该副本放在某个位置4并更新索引引用。

这是git commit使速度如此之快的秘密之一。Git 不必查看您的工作树。它不必重新压缩所有文件。它们已经在你的索引中,准备好了。所要做git commit的就是将预冻结的文件打包成一个提交。它只是提交您运行时索引中的任何内容git commit。因此,考虑这一点的一个好方法是index 是您建议的next commit。做什么git addgit rm做的是更新您提出的提交。只运行git commit你的索引中的快照——在暂存区,准备提交——即使这与之前的提交基本相同。和命令是实际更新索引的内容git addgit rm

这就是为什么以及如何每次提交都是每个文件的完整快照。您不更新的任何文件仍在索引中(“在舞台上”),并将在下一次提交中。

git status使用标志

假设您在当前签出的提交中有 3000 个文件(因此在您的工作树中),并且您在工作树中更改了一个git add文件,并在您的索引/暂存区域中更新它。如果你git status现在运行,git status不要费心告诉你 3000 个文件中有 2999 个是相同的,因为那不是有用的信息。git status告诉您的是一个文件已更新。

git status至少在原则上,这样做的方法是运行两个单独的比较:

  • 首先,git statusHEAD提交中的每个文件与索引中的副本进行比较。对于此处相同的每个文件,它什么也没说。但是如果这里的文件不同,它会说:staged for commit

  • 接下来,git status将索引中的每个文件与工作树中的副本进行比较。同样,如果文件相同,它什么也不说。如果文件不同,它会说:not staged for commit

git status进行这种比较时,由于 Git 将文件内容的内部表示为 blob 哈希 ID,所以第一部分进行得非常快。所以它真的,从字面上看,只是比较每个文件。只需要几毫秒就可以确定 3000 个文件中的 2999 个是相同的,一个是不同的。但第二部分很慢:实际上比较所有 3000 个文件可能需要几秒钟!

所以,git status骗子。这就是索引的缓存方面发挥作用的地方。每个索引条目都包含对准备提交的冻结格式文件的引用;但它也包含一些由操作系统lstat系统调用产生的数据。5 Git 可以lstat对工作树中的文件进行另一个系统调用。在大多数情况下,当且仅当工作树中的文件仍然与Git 在冻结格式中的副本相同时,结果stat数据与 Git 之前保存的数据匹配,正如索引条目所缓存的那样。如果您修改了工作树副本,操作系统也会更新数据。stat

因此,假设您是git status,将索引中的每个文件与其在工作树中的副本进行比较,这样您就可以在必要时说不暂存提交。可以打开每个工作树文件并一直阅读它,并将其内容与解压缩冻结索引副本时获得的内容进行比较。这会告诉你它们是相同还是不同,但是哇,这是很多工作,可能需要几秒钟。但是你有这个缓存stat的数据,如果你将统计数据与另一个结果进行比较lstat,那么这需要更少的工作和时间。所以你这样做。如果lstat结果与缓存的结果匹配,则文件必须相同,您可以什么也不说,继续下一个文件。

但实际上,每个lstat系统调用也很慢。当然,它比读取每个文件要快数千倍,但仍可能需要数百微秒。如果操作系统的速度非常慢lstat,需要 3毫秒怎么办? 对 3000 个文件执行此操作,如果每个文件需要 3 毫秒,则需要 9,这太长了!

Git对此有一个标志。该--assume-unchanged标志是每个索引条目中的一个可设置标志,它告诉 Git:不要费心调用lstat这个工作树副本,只要假设它与缓存的 stat data 匹配。它有第二个稍微更强大的标志 ,--skip-worktree可以达到相同的结果。(它稍微强大一些,因为某些命令,例如git update-index --really-refresh,会忽略第一个标志,但不会忽略第二个。)

如果您设置任一位,则将索引的缓存stat数据与工作树中的真实stat数据进行比较的操作,以判断文件是否真的被修改,只需假设文件没有被修改。清除这两个位,这些 Git 操作stat毕竟会调用。然后git status 应该看到文件的更新,只要stat操作系统返回的数据也更新了。 有一些操作系统级别的技巧可以解决这个问题,但您通常可以使用以下方法击败这些操作系统级别的技巧touch

touch path/to/file

确保现在的stat数据path/to/filestatGit 可能持有的任何缓存数据都更新。

如果有点复杂,这张图片应该足够清晰:索引/暂存区域保存有关每个工作树文件的缓存数据,来自先前的系统调用。如果缓存的数据与操作系统在新调用中报告的内容相匹配,则索引副本必须与工作树副本相匹配。如果您设置了标志位,Git 不会费心进行调用:它只是假设两组数据匹配,因此索引副本与工作树副本匹配,无论这是否真的如此。清除这些位,Git 会返回调用并获得——我们希望——来自操作系统的准确报告。lstat lstatlstatlstat

这张图不再完全正确,因为 Git 现在还可以使用文件系统监视器来避免lstat不必要的调用。但这完全是另一个问题的话题。


1请注意,花哨的管道假定您已LC_COLLATE设置为C,在某些版本中grep服从该LC_COLLATE标志。那是:

git ls-files -v | grep '^[a-z]'

可能会列出每个文件,具体取决于LC_COLLATE. 它还列出--skip-worktree文件,但您必须使用单独的git update-index --no-skip-worktree命令取消设置该标志。这是我写的原因之一git-flagged。(由于匹配太多而列出太多文件grep是无害的:您只会调用一些git update-index实际上不需要运行的命令。)

我没有让我的git-flagged脚本支持新的 fsmonitor 有效/无效位。如果您的系统正在使用 fsmonitor,并且出现问题,那么您遇到了更大的问题,也许应该全局禁用 fsmonitor,通过git configcore.fsmonitor设置。

2这假定一个普通(非--bare)存储库,并且您没有使用git worktree add. 您添加的每个工作树git worktree add都有自己的索引和工作树以及自己的HEAD,因此每个工作树都会获得另外三个这些活动副本。

3一旦您进行了提交并且它获得了特定的哈希 ID,您可以使用该哈希 ID 来查看提交是否仍然存在。如果它确实存在——而且很可能存在——那么你冻结到其中的文件也仍然以冻结的形式存在。

真正摆脱一个糟糕的提交有点困难。这是可以做到的,所以提交并不一定是永远的,但这是思考它们的一种方式。

4 “某处”实际上是直接进入存储库。如果您提交此文件副本,则使用冻结的副本;如果不是,那么 Git最终会清理掉的通常只是剩余的垃圾。除非您的磁盘空间一直非常短缺,否则您无需担心这些git fsck会显示为dangling blobs的东西。稍后让 Git 自行清理它们。

5这特指生成数据的 POSIXlstat系统调用。stat如果你的底层操作系统没有或不使用stat数据,Git 仍然需要缓存一些东西,并且会使用某种合成stat数据,这些数据需要足够好才能完成剩下的工作。

于 2019-08-14T18:04:46.257 回答