13

我使用GCC git 镜像,因为我只使用 C 和 C++ 前端,所以我使用 git 的稀疏检出功能来排除数百个我不需要的文件:

$ git config core.sparseCheckout
true
$ cat .git/info/sparse-checkout 
/*
!gnattools/
!libada/
!libgfortran/
!libgo/
!libjava/
!libobjc/
!libquadmath/
!gcc/ada/
!gcc/fortran/
!gcc/go/
!gcc/java/
!gcc/objc/
!gcc/objcp/
!gcc/testsuite/ada/
!gcc/testsuite/gfortran.dg/
!gcc/testsuite/gfortran.fortran-torture/
!gcc/testsuite/gnat.dg/
!gcc/testsuite/go.dg/
!gcc/testsuite/go.go-torture/
!gcc/testsuite/go.test/
!gcc/testsuite/objc/
!gcc/testsuite/objc.dg/
!gcc/testsuite/obj-c++.dg/
!gcc/testsuite/objc-obj-c++-shared/

这工作了一段时间,但时不时地我注意到其中一些被排除的文件已经返回,有时很多

$ ls gnattools/
ChangeLog  configure  configure.ac  Makefile.in
$ ls  gcc/fortran/ | wc -l 
86

我不确定文件何时重新出现,我做了很多切换到不同的分支(远程跟踪和本地),这是一个非常繁忙的存储库,因此经常有新的更改需要拉取。

作为 git 的相对新手,我不知道如何“重置”我的工作树以再次摆脱这些文件。

作为一个实验,我尝试禁用稀疏签出和拉取,以为我可以在之后再次启用 sparseCheckout 以某种方式更新树,但这并没有很好地工作:

$ git config core.sparseCheckout false
$ git config core.sparseCheckout 
false
$ git pull
remote: Counting objects: 276, done.
remote: Compressing objects: 100% (115/115), done.
remote: Total 117 (delta 98), reused 0 (delta 0)
Receiving objects: 100% (117/117), 64.05 KiB, done.
Resolving deltas: 100% (98/98), completed with 64 local objects.
From git://gcc.gnu.org/git/gcc
   7618909..0984ea0  gcc-4_5-branch -> origin/gcc-4_5-branch
   b96fd63..bb95412  gcc-4_6-branch -> origin/gcc-4_6-branch
   d2cdd74..2e8ef12  gcc-4_7-branch -> origin/gcc-4_7-branch
   c62ec2b..fd9cb2c  master     -> origin/master
   2e2713b..29daec8  melt-branch -> origin/melt-branch
   c62ec2b..fd9cb2c  trunk      -> origin/trunk
Updating c62ec2b..fd9cb2c
error: Your local changes to the following files would be overwritten by merge:
        gcc/fortran/ChangeLog
        gcc/fortran/iresolve.c
        libgfortran/ChangeLog
        libgfortran/io/intrinsics.c
Please, commit your changes or stash them before you can merge.
Aborting

所以显然我对我从未要求过的文件进行了本地修改,AFAIK 从未接触过!

git status没有显示这些变化:

$ git st
# On branch master
# Your branch is behind 'origin/master' by 9 commits, and can be fast-forwarded.
#
# Untracked files:
#   (use "git add <file>..." to include in what will be committed)
#
#       libstdc++-v3/53270.txt
#       libstdc++-v3/TODO

我试过git read-tree -m -u HEAD了,但它什么也没做。

所以我的问题是:

  • 为什么文件会重新出现?
  • 如何让它们再次消失?
  • 我如何防止他们回来?
  • 这是否可能与我的.git/info/exclude文件包含对文件中应该排除(即命名为!)的目录中的文件的引用有关sparse-checkout?我按照说明忽略了与 SVN 相同的文件

    $ git svn show-ignore >> .git/info/exclude

所以我的exclude文件包括路径,如

# /gcc/fortran/
/gcc/fortran/TAGS
/gcc/fortran/TAGS.sub
/gcc/fortran/gfortran.info*

这将在sparse-checkout文件中命名的目录之一之下:

!gcc/fortran/

我试图用一个测试仓库重现这个问题,我克隆了几个副本并编辑每个副本,创建/切换/删除分支并合并它们之间的更改,但在我的玩具测试用例中它永远不会出错。GCC 存储库有点大(超过 2GB),并且“失败”之间的时间(大约一两个星期)太长,无法期望人们尝试准确地重现问题。我没有sparse-checkout尝试过在and中使用相同的路径exclude,因为我今天才想到那里可能存在冲突。

几周前,我在 freenode 上的#git 上询问了这个问题,IIRC 基本上被告知“这可能是一个错误,没有人使用稀疏结帐”,但我希望得到更好的答案 ;-)

更新:

我最近一次看到问题实际上发生了(即文件不存在,然后在单个命令之后出现)是从上游源拉取:

   bac6f1f..6c760a6  master     -> origin/master

显示的更改包括这些重命名:

 create mode 100644 libgo/go/crypto/x509/root.go
 rename libgo/go/crypto/{tls => x509}/root_darwin.go (90%)
 rename libgo/go/crypto/{tls => x509}/root_stub.go (51%)
 rename libgo/go/crypto/{tls => x509}/root_unix.go (76%)
 create mode 100644 libgo/go/crypto/x509/root_windows.go

根据需要,在拉取目录之前,该libgo目录不存在。在拉出该目录并且这些文件(并且没有其他文件)在它下面之后:

$ ls libgo/go/crypto/x509/root_<TAB>
root_darwin.go  root_stub.go    root_unix.go    

我不知道重命名的文件是否丢失了skip-worktree位,我该如何检查?

我很确定在重命名时问题并不总是发生,因为libgfortran/ChangeLog例如上面示例中显示的文件不是新文件或最近重命名的文件。

4

3 回答 3

4

可以使用 修改 skip-worktree 位git update-index --skip-worktree。当您注意到存在的文件时,您可以检查git ls-files -v |grep ^S(S 是标有 skip-worktree 的文件)。

但正如#git 人所说,如果你看到奇怪的行为,那很可能是 git 中的错误。毕竟,这是一个相当深奥的功能。您可能应该将您的发现报告给 git 邮件列表。

编辑:另外,如果您使用的是 git 1.7.7.6,我强烈建议您升级。1.7.10 tree 遥遥领先,我认为它很有可能会解决你的问题。

于 2012-06-27T08:17:46.410 回答
1

就我而言,我正在使用稀疏结帐对 repo 执行一些单元测试。我的一个测试用例创建了包含未包含在我的稀疏结帐子树列表中的文件的提交。

当我尝试时git reset --hard 123456,我收到以下错误:

error: Entry 'a.c' not uptodate. Cannot update sparse checkout.
fatal: Could not reset index file to revision '123456'.

解决方案是通过重新应用稀疏签出规则来删除我的工作树中的文件:

git read-tree -mu HEAD
于 2015-11-19T21:45:39.233 回答
1

检查问题是否在最新的 Git 2.13(2017 年第二季度,5 年后)中仍然存在。
任何 skip-worktree 文件都不应在稀疏结帐期间被修改甚至查看,因为:

preload-index代码已被教导不要打扰索引条目,这些索引条目是未通过“稀疏签出”签出的路径。

请参阅Jeff Hostetler ( )的提交 e596acc(2017 年 2 月 10 日) 。(由Junio C Hamano 合并 -- --提交 c7e234f中,2017 年 2 月 27 日)jeffhostetler
gitster

preload-indexlstat:避免skip-worktree物品

教导preload-index避免lstat()调用设置了跳过工作树位的索引条目。
这是性能优化。

在稀疏签出期间,skip-worktree 位设置在未填充的项目上,因此不存在于工作树中。
每线程预加载索引循环对每个索引条目执行一系列测试,因为它尝试将工作树版本与索引进行比较并将它们标记为最新。
这个补丁捷径有效。

在具有非常大的 repo(450MB 索引)和各种稀疏级别的 Windows 10 系统上,性能在这种{preloadindex=true, fscache=false}情况下提高了 80%,在{preloadindex=true, fscache=true}各种命令的情况下提高了 20%。


在 Git 2.27(2020 年第二季度)中,“ sparse-checkout”以不同的方式管理 skip-worktree。

请参阅提交5644CA2提交EBB5637提交22ab0b3提交1AC83F4提交CD002C1提交4EE5D50提交F56F31A ,Commit 7AF7A25Commit 30E89C1Commit 3CC7A25提交B0A5A12提交72064E提交FA0BDE4,提交D61633A,提交d7dc1e1提交 031ba55(2020 年 3 月 27 日),作者Elijah Newren ( newren)
(由Junio C Hamano 合并 -- gitster--提交 48eee46中,2020 年 4 月 29 日)

unpack-trees: 未能设置SKIP_WORKTREE位总是只是一个警告

审核人:Derrick Stolee
签字人:Elijah Newren

位的设置和清除SKIP_WORKTREE不仅在用户运行'sparse-checkout'时完成;其他命令(例如“checkout”)也会运行,unpack_trees()其中包含处理此特殊位的逻辑。因此,我们需要考虑他们如何处理特殊情况。

几个比较点应该有助于解释更改unpack_trees()处理这些位的方式的基本原理:

  • 暂时忽略稀疏检出,如果您正在切换分支并且有脏更改,则仅当脏文件恰好是具有不同内容的路径之一时,才会认为会阻止分支切换成功的错误。

  • SKIP_WORKTREE一直被认为是建议性的;例如,如果 rebase 或 merge 需要甚至想要将路径具体化作为他们工作的一部分,那么无论SKIP_WORKTREE设置如何,他们总是被允许这样做。
    这已用于未合并的路径,但它通常用于不需要的路径,因为它使代码更简单。
    这是一个尽力而为的考虑,当它实现与SKIP_WORKTREE设置相反的路径时,甚至不需要打印警告消息。

过去,如果您尝试运行例如“git checkout”并且:

  1. 你有一个具体化的路径并且有一些肮脏的变化
  2. 路径列在 $GITDIR/info/sparse-checkout
  3. 这条路径在当前分支和目标分支之间没有区别

然后尽管有上述比较点,但无法设置SKIP_WORKTREE被视为会中止结帐操作的硬错误。

这与其他地方的处理方式完全不一致SKIP_WORKTREE,并且对于用户来说相当烦人,因为将路径物化在工作副本中(带有一个简单的警告)应该完全没有问题。

将任何错误从无法将该SKIP_WORKTREE位切换为警告并允许操作继续进行。

所以消息不再是:

error: The following untracked working tree files would be overwritten by checkout:

但:

warning: The following paths were already present and thus not updated despite sparse patterns:

在 Git 2.28(2020 年第三季度)中,“git clone --no-checkout”状态下“sparse-checkout”的行为在 2.27 中意外更改,已更正。

请参阅Elijah Newren ( ) 的提交 b5bfc08(2020 年 6 月 5日(由Junio C Hamano 合并 -- --提交 a554228中,2020 年 6 月 18 日)newren
gitster

sparse-checkout: 避免暂存删除所有文件

签字人:以利亚·纽伦

sparse-checkout 的目的是更新工作树以使其反映跟踪文件的子集。

因此,它不应该切换分支、提交、下载或上传数据,或者暂存或取消暂存更改。

除了更新工作树之外,sparse-checkout 唯一应该触及的是SKIP_WORKTREE索引的位。

特别是,这设置了一个很好的不变量:运行 sparse-checkout 永远不会改变其中任何文件的状态git status(这反映了我们仅SKIP_WORKTREE在文件可以安全删除时设置该位的事实,即如果文件未修改)。

传统上,我们在_really这个目标上做得很糟糕。

sparse-checkout 的前身涉及手动编辑 .git/info/sparse-checkout 和运行 .git/info/sparse-checkout git read-tree -mu HEAD

该命令将暂存和取消暂存更改,并覆盖工作树中的脏更改。

sparse-checkout 命令的初始实现也好不到哪里去;它只是git read-tree -mu HEAD作为一个子流程调用并且有相同的警告,尽管这个问题在审查评论中反复出现,并且在合并该功能之前就解决了这些问题 [1, 2, 3, 4, 5, 6; 尤其见 4 和 6]。

但是,这些变通方法除了在一些重要情况下禁用该功能外,还遗漏了一种特殊情况。

我稍后再谈。

git read-tree -mu HEAD在 2.27.0 周期中,通过最终用我们想要的东西替换内部等效项来解除禁用该功能:新update_sparsity()函数unpack-trees.c只更新SKIP_WORKTREE索引中的位并更新工作树以匹配。

这个新函数处理所有对旧实现有问题的情况,除了它打破了相同的特殊情况,避免了旧实现的变通办法,但以不同的方式打破了它。

所以...这给我们带来了特殊情况:agit clone用 . 执行--no-checkout

根据标志的含义,--no-checkout不检查任何分支,这意味着您不在一个分支上,需要在克隆后切换到一个分支。

在实现上, HEAD 仍然设置(所以在某种意义上你部分在一个分支上),但是:

  • 索引是“未出生的”(不存在)
  • 工作树中没有文件(.git/ 除外)
  • 下次运行 git switch(或 git checkout)时,它将运行 unpack_trees,并将initial_checkout标志设置为 true。

直到您运行,例如git switch <somebranch>,才会写入索引并填充工作树中的文件。

在这种特殊--no-checkout情况下,传统的read-tree -mu HEAD行为相当于结账——切换到默认分支 (HEAD),写出与 HEAD 匹配的索引,并更新工作树以匹配。

这种特殊情况跳过了原始 sparse-checkout 命令中的 Avoid-making-changes 检查,因此继续存在。

update_sparsity()被引入和使用之后(参见commit f56f31af03 (" sparse-checkout: use new update_sparsity()function", 2020-03-27, Git v2.27.0-rc0 -- merge列在第 #5 批中)), --no-checkout 案例的行为更改:由于 git 对空内存索引的自动激活(请参阅do_read_index()并注意这must_exist是错误的),并且由于 sparse-checkout 的update_working_directory()代码总是在完成后写出索引,我们遇到了一个新错误。

这使得 sparse-checkout 将存储库从具有“未出生”索引的克隆切换(即仍然需要一个initial_checkout),具有记录索引但没有条目的存储库。

因此,不是所有文件git status都被 git 视为尚未在分支上的特殊工件而被删除,而是我们对空索引的记录使 git 突然看起来好像它肯定在所有文件暂存的分支上删除!
随后的结帐或切换必须应对这样一个事实,即它不在一个initial_checkout但有一堆分阶段删除。

确保 sparse-checkout 除了SKIP_WORKTREE位之外没有改变索引中的任何内容;特别是,当索引未诞生时,我们没有签出任何分支,因此无需进行稀疏化或去稀疏化工作。

update_working_directory()只需早点回来。


git reset在 Git 2.35 (Q1 2022) 中," " ( man )的各种操作模式已更好地与稀疏索引一起工作。

请参见Victoria Dye ( ) 的提交 f2a454e提交 4d1cfc1提交 20ec2d0提交 c01b1cb提交 291d77e(2021 年 11 月 29 日)、提交 86609db提交 71471b2(2021 年 10 月 27 日)和提交 1f86b7c(2021 年 10 月 7 日(由Junio C Hamano 合并 -- --提交 f085087中,2021 年 12 月 10 日)vdye
gitster

sparse-index: 更新展开/折叠测试的命令

帮助者:Derrick Stolee
签字者:Victoria Dye

预期( man )能够使用稀疏索引而不扩展它,请将命令替换为( man )。 该命令需要扩展索引才能正常工作,即使在将其余部分与稀疏索引集成后也是如此。git reset --hardsparse-index is expanded and converted backgit reset -- folder1/a
reset

于 2017-03-26T19:05:49.953 回答