git diff 没有差异,但由于行结尾的改变,不应该有一些差异吗?

2024-01-02

我的问题可能来自于对 Git 某些方面的误解。当我因 Windows 机器上的更改而在 Mac 上将 CRLF 更改为 LF 行结尾时,我想到了这个问题。

1) 我首先在 OSX 上初始化一个新的存储库,并将所有文件放入受 CRLF 行结尾影响的文件中。

2)第一次提交时,由于设置了 core.autocrlf = input ,git 自动将行结尾更改为 LF

我的本地工作树中的文件仍然具有 CRLF 行结尾,但这里也提供了解决方案(如何规范 Git 中的工作树行结尾? https://stackoverflow.com/questions/15641259/how-to-normalize-working-tree-line-endings-in-git):

删除Index中的文件,并根据上次commit恢复索引+工作树:

git rm --cached -r .
git reset --hard

现在出现了混乱:我的第一个提交 1) 包含转换后的 LF 行结尾,而我的本地树和索引则不包含。因此,我的期望是 git 应该显示工作树/索引和存储库之间的差异。但

git diff HEAD
git diff --chached

没有列出任何更改?


我在评论中提到了其中的一些内容,但它需要很大的空间来给出真正的答案来正确地涵盖它。您觉得奇怪的行为之一是:

  • Often, 坚定的文件只有 LF 行结尾。
  • Often, 工作树文件具有 CRLF 行结尾(Windows 用户往往更喜欢)。
  • 这些可能同时成立,但是,git status and git diff不会提及行结尾的任何变化。

这种行为是必要的、适当的。你运行以下命令是错误的:

git checkout master
git diff

and see a lot of diffs!1 But the actual implementation here is very tricky and can result in some apparent weirdness.

有几个关键要素可以帮助您理解这一点,并理解许多其他 Git 行为。您已经提到了其中一些,但让我们更深入地了解细节并看看howGit 管理行结束。我们需要讨论的事情是:

  • 文件在提交中存储的方式:我喜欢称之为冻干 format;
  • 文件存储在 Git 调用的事物中的方式不同,index or the 暂存区;
  • 文件在您的计算机、工作树中的存储方式,您可以在其中查看和处理/使用它们;和
  • 它们如何从一种存储格式转换为另一种存储格式。

最后一步是解决行尾问题的关键,但它与其他项目纠缠在一起。


1Nonetheless, sometimes that very thing happens, for other reasons. I'll touch on these here too.


提交存储冻干文件

每次提交都会存储每个文件的完整副本 - 好吧,是提交中的每个文件的完整副本,但这显然是同义反复。这个说法背后的想法是,如果你有文件README.md and main.py,比如说,你在你已经完成的地方做了一个新的提交changed main.py但不是README.md,新的提交仍然会生成另一个副本README.md anyway.

显然,每次都重新提交每个文件会极大地浪费磁盘空间。 Git 通过一些巧妙的技巧来避免这种情况。第一个明显的问题是每个存储的文件都是压缩的(与gzip or bzip or rar; Git实际上使用zlib https://zlib.net/压缩)。对于大多数文件,压缩它们可以减少它们占用的空间。典型的源代码压缩得很好。压缩已经压缩的文件往往会适得其反——这是不在 Git 中存储压缩文件的原因之一!——但不会使它们变得足够大而成为问题,因此 Git 只是对所有内容运行 zlib deflate。

The more important trick here, though, is that once Git has frozen a file into a commit, that file is absolutely, totally, 100% read-only. There's a strong technical reason for this, in that Git stores everything—all of what it calls objects—in a simple key-value database, where the keys are hash IDs formed by hashing the value, and the value is the byte-string that is the file's data, prefixed with the object type and size.2 Since the key itself depends on the data, you literally can't change the data: if you try, you get instead a new and different object with a new and different hash ID.3 The old object is still there in the database, with its old key and old stored bytes: the compressed and frozen, i.e., freeze-dried, file is still there.

What this means is that Git never has to store the same file again after all. It can just re-use the file from the previous commit! That is, if we just made a new commit with a new and different main.py, well, Git had to write the new different main.py to a new freeze-dried object, but we made it with the same old README.md, so Git can just re-use the previous freeze-dried README.md.4

Git 对这些冻干文件的术语是斑点对象。 Blob 和提交是 Git 四种对象类型中的两种。为了完整起见,剩下的两个是tree and 带注释的标签,但我们不需要担心这里的那些。我们只需要查看 blob 对象,因为提交就是retainblob(间接地——通过树对象!),提交对象(轻轻地)。


2The prefix ensures that, e.g., commit <size>\000<commit data> has a different hash from blob <size>\000<copy of the commit's data>. Git wants to be able to extract the type from the object, so the fact that you can read out an existing commit and create a file with those contents and store it as a file, means that the type-prefix is necessary.

哈希函数是一种加密函数,部分原因是您不能故意摆弄它来造成冲突,但主要只是为了获得真正良好的哈希分布。强制哈希冲突是理论上可能和could be这对 Git 来说是未来的一个问题,因此 Git 正在转向更长、更安全的哈希。看新发现的 SHA-1 冲突对 Git 有何影响? https://stackoverflow.com/q/42433126/1256452

3Git checks that the hash ID it used to find the object matches the hash of the data, when extracting the data from the object. This acts as a data-corruption test: if the hash of the data, as retrieved by the key, does not match the original key, Git knows that the on-disk data are invalid and tells you that.

4Later, Git compresses these key-value-store objects even further, by taking objects that have been sitting around for a while and packing them into what Git calls a pack file. The objects in a pack file are delta-compressed https://en.wikipedia.org/wiki/Delta_encoding against other objects in that pack file. To do the delta encoding, Git undoes the zlib deflation, finds overlapping byte sequences—there tend to be a lot of these in source code—and builds a delta encoded version that says take the old copy of the file and make these changes to it: a binary, byte-coded variant of what you see as a git diff. These deltified pack objects then all go into a single pack file. There's a huge amount of effort that goes into deciding what gets deltified against what: it's not just "new version of file vs old version of file".

更高级别的 Git 软件只是说获取哈希 ID 为 H 的对象。如果该对象作为未打包的对象存在,Git 会在重新 zlib 膨胀时获取该对象。否则,Git 将查看每个包文件。如果该对象在那里,Git 可以从它的 deltified 片段中重新组装它,所有这些都来自那个包文件。上一层的代码永远不必知道文件是单个对象还是存储在包中的片段。因此,准确地说,在objectlevel,Git 只进行 zlib 压缩,不进行 delta 压缩。增量编码,如果它发生的话,就会发生below对象级别。


冻干文件重新水合到工作树中

这部分非常简单:只有一个问题,我们将其留到下一节。一次提交是一个快照every文件,但它们都是这种仅 Git 冻干的形式。它们完全被冻结,这对于存档来说很好,但在转换回来之前,甚至无法使用;只要它们被冻结,就无法获得任何东西new完工。因此,它们必须重新水化,就像以前一样:以您的特定操作系统需要的任何方式变回普通文件,存储在普通目录/文件夹中。重新水化冻干的提交文件的结果是工作树。

索引/暂存区

这就是我提到的问题出现的地方。而不是直接提取文件to在你的工作树中,Git 首先将提交提取到 Git 所谓的索引(在某些地方)或暂存区域(在其他文档中)中。索引是什么以及它的作用在合并操作期间变得更加复杂,但在大多数情况下,描述起来很简单:它是提议的下一次提交.

当 Git 进行新的提交时,Git 不会使用工作树。有一些类似于 Git 的版本控制系统do使用工作树作为建议的下一次提交,它们往往更容易使用,但也慢得多。使用这些时,您告诉系统进行新的提交,它实际上会再次冻结每个文件,进入一个新的提交。

另一方面,Git 说:嘿,等等!我们已经冻干了您的大部分文件。代替重新冷冻干燥every文件新的提交,让我们强制你,用户,这样做关于您更改的特定文件,让你跑git add在他们!所以 Git 首先提取每个文件to索引,然后再重新填充到工作树中。这git add命令冻结干燥工作树中的文件并将其复制到索引中,替换先前提交中已经存在的文件,或者,如果它是一个新文件,则在索引中创建一个以前不存在的新文件。不管怎样,现在该文件已准备好进入下一次提交......您的所有文件也已准备好didn't git add。他们还在那里git checkout,准备进入新的提交。

这就是所有疯狂的地方tracked vs 未追踪的文件来自.跟踪文件很简单现在索引中的任何文件。未跟踪的文件是目前位于工作树中但目前不在索引中的任何文件。您可以随时将一个文件放入索引中:git add file。您可以随时获取一个文件out现在的索引:git rm file or git rm --cached file。使用git rm将文件取出both索引and工作树,同时使用git rm --cached仅将文件从索引中取出,不保留工作树文件。

当然,你做的其他事情also修改索引。最明显的一点就是git checkout经常不得不replace索引,或者至少是它的一部分。这些细节可能会变得非常棘手——请参阅当当前分支上有未提交的更改时签出另一个分支 https://stackoverflow.com/q/22053757/1256452- 但它确实归结为将文件放入索引中,或将它们取出,以及将文件放入工作树中,或取出它们,或者(例如,git rm --cached or git reset --mixed)在更改索引中的内容时保留工作树。

无论指数如何变化(或不变),要记住的主要事情是:在任何时候,每个文件最多有三个活动副本:

  • 一份是当前(HEAD) 犯罪。您可以使用以下命令查看此内容git show HEAD:file。您根本无法更改此文件,您所能做的就是更改名称的提交HEAD通过创建新的提交或使用来调用git checkout移动到不同的提交。

  • One copy is the freeze-dried one in your index. You can view this with git show :file or git show :0:file.5 You can replace it with a new one from your work-tree using git add.

  • 最后一个副本是工作树中正常的日常读/写副本。您可以对此使用任何常规的非 Git 命令。

I say 最多三个这里因为,例如,当然未追踪的文件不在您的索引中(无论它是否在HEADcommit),或者从未提交的全新文件可能同时位于索引和工作树中,但不在HEAD。一般来说,每种情况下有多少个副本应该是显而易见的。

Note that the index actually just holds the blob hash ID of the freeze-dried file, which is already saved in Git's object store. If you commit the file, the blob hash becomes permanent, as the commit itself now uses it. Otherwise the object can eventually expire (though not while its hash remains in the index).6


5The number zero here is the staging number, which has to do with merges. The default number is zero, and except during merge conflicts, everything is always just in staging slot zero—so you can use :0: or just : to mean in the index.

6There was a very nasty bug in git worktree add for a while. The garbage collector did not account for the extra index file, nor the per-worktree refs, associated with each work-tree. It never scanned these extra index files and refs, and if any particular hash appeared in only such an index or ref, Git would sometimes expire such objects, even though the added work-tree needed them! This was fixed in Git 2.15.


行尾、涂抹和清洁过滤器

现在您已经习惯了 Git 始终存储每个文件最多三个副本的想法,now我们可以看到 Git 中的行尾操作是如何工作的。此外,我们可以看到如何定义smudge and clean过滤器及其工作原理。

从冻干形式中取出文件的过程HEAD提交并放入索引非常简单:Git 只是确定文件的相对路径,例如README.md or dir1/dir2/file.py,并在索引的适当位置腾出空间(索引经过精心安排,以便快速访问)并填充关键信息about那里的冻干副本。 Git 还将有关工作树副本的一些信息填充到该文件的索引条目中,我们稍后会看到。

由于索引只保存冻干文件的哈希 ID,因此索引中的内容是exactly如果你现在就提交的话,下一次提交将会是什么。如果索引中的内容来自HEAD提交,它是exactly里面有什么HEAD commit.

与所有冻结的、哈希 ID 键控的对象一样,这里没有什么可以改变的。您可以使用新的不同哈希 ID 创建一个新的不同对象,并且由于您可以将新哈希 ID 写入索引,因此您可以批量替换索引副本,但由于您无法将新哈希 ID 填充到现有提交中,您无法更改提交。如果您确实更改了索引,请将其更改为exactly您打算放入下一次提交的内容。

同时,什么进入工作树 is a 复水副本文件的。提交的副本和索引副本被冻干:它们采用仅限 Git 的格式。工作树副本是普通的。有一个转变绝对必须发生在从 Git 中取出,放入工作树中过程,每一次。有一个相应的转换绝对必须发生在git add冻干文件并将其填充到对象存储和索引中过程,每一次。

所以:为什么不,在转型过程中,also进行行尾过滤吗?这正是 Git 所做的:

  • 将文件从索引复制到工作树(git checkout,大部分):如果工作树文件应该有 CRLF 行结尾,Git 可以将 blob 中的仅 LF 行结尾转换为工作树中的 CRLF 行结尾。事实上,它可以插入任何你可能想要的任意“脏”东西,通过你的污迹过滤器。一般来说,我们可以将其称为弄脏文件.

  • 将文件从工作树复制到索引(git add,大多数情况下):如果提交的文件应具有仅 LF 行结尾,Git 可以在写入 blob 对象时将任何 CRLF 结尾转换为仅 LF 结尾。事实上,它可以通过您的过滤器“清除”您在污迹过滤器中添加的任何“污垢”。清洁过滤器。我们可以将其称为清理文件.

Git 在这里提供了三种内置的行尾涂抹和清理模式。如果您想要其他过滤器,则必须编写自己的涂抹和清洁过滤器:

  • 什么也不做:保持索引和工作树匹配。这适用于所有二进制数据。一般来说,它也适用于 Linux 系统,其中行首先不应该有 CRLF 结尾,因此,如果存储库中的所有内容始终与工作树中的所有内容匹配,并且没有任何内容有 CRLF 结尾,则永远不会有任何问题。

  • 在写入工作树上执行 LF-to-CRLF,在写入索引上执行 CRLF-to-LF。这适用于 Windows 用户的某些文本文件。

  • 对 write-to-work-tree 不执行任何操作,但对 write-to-index 执行 CRLF-to-LF。这是 Git 调用的模式input。在我看来,它并不是特别适合任何事情。这可能就是为什么input主要是向后兼容的功能。您可以设置相同的模式eol=lf不过,在`.gitattributes 文件中。

git diff and git status与涂抹/清洁/等相比

What git diff所做的或打算做的主要是:

  • 将整个提交与另一个完整提交进行比较;或者
  • 将任何提交与建议的下一个提交(即索引)进行比较;或者
  • 比较对工作树的任何提交;或者
  • 将建议的下一次提交(索引)与工作树进行比较。

其中一些操作专门针对 blob(提交或索引中的冻干文件)进行操作。相对而言,这很容易:它们已经处于它们将永远处于的任何形式。无需进行生产线末端的摆弄、弄脏或清洁。但是,如果行尾过滤器或污迹过滤器有问题,则任何将提交或索引与工作树进行比较的操作都会出现问题。changed工作树中有什么。

有两种明显的方法可以解决这个问题。 Git 可以:

  • 清理工作树文件(通过将它们添加到某个位置,例如添加到临时索引),然后比较清理后的文件;或者
  • 重新弄脏索引或提交副本(通过将它们提取到某个地方,例如临时文件),然后比较弄脏的文件。

这两个都很慢:它们意味着重新复制every使用这些功能的文件,每次您将某些东西与工作树进行比较。 Git 会在必要时执行此操作(并且根据源代码,它可以执行任一操作 - 我不确定何时会发生哪一个)。但 Git 试图变得更聪明。

如果您刚刚签出一个文件 - 刚刚将其从索引复制到工作树 - 工作树副本must根据定义,匹配索引副本,无论工作树副本有多么“脏”。同样,如果你现在git add编辑一个文件 - 只是将其从工作树复制到索引 - 索引副本must根据定义,匹配工作树副本,无论索引副本有多“干净”。 Git 在索引中保存了一堆操作系统级别的信息about文件的工作树副本,与文件的索引副本相比。如果这两者匹配,Git 就会assume索引和工作树副本匹配。

请注意,Git在关键情况下保留这个假设,即使不应该。特别是,假设您有一个已提交的文件,该文件仅包含 LF 行结尾,并且您使用以下命令配置了存储库.gitattributes和/或其他告诉 Git 的设置:无论以哪种方式复制此文件时,请根据复制方向进行 LF / CRLF 转换。从那时起,你改变了.gitattributes或其他设置,以便 Git 重新提取文件now,它不会做任何事情,如果你git add文件now,它什么也不做——这会将带有 CRLF 行结尾的文件版本添加到索引中。

Git 会坚持文件的索引和工作树副本匹配,即使它们不再匹配。如果您更改设置back切换到 Git 进行翻译的模式,现在文件再次匹配。在任何时候,Git 都坚持要求文件匹配,因为它使用索引的文件状态信息来绕过真正检查的艰苦工作。

The git status命令部分包括运行两个git diff命令,一个进行比较HEAD到索引,以及将索引与工作树进行比较。第一个 diff 没有行结束问题,因此这里无需担心,但第二个 diff 具有常见的索引与工作树问题。它实际上使用相同的代码git diff,所以它在认为事物干净与否方面的行为方式是相同的。

git add --renormalize

The git add命令在某些情况下采用类似的快捷方式。这可以让你做类似的事情git add .无需 Git 重新压缩和冻干every文件在你的工作树中:它只重新压缩和冷冻干燥基于时间戳等的文件,看起来他们确实需要它。如果您更改了清理设置,这当然会很糟糕,因为当 Git 认为文件已经干净时,文件可能需要一些真正的清理。

The git add --renormalize操作告诉 Git:击败特殊情况代码。根据操作系统文件时间戳等,不要相信索引和工作树是相同的;真正做到add,真正应用清洁工艺。因此,如果发生此问题,这是解决此问题的一种简单方法。 (我在 StackOverflow 上看到过有关它无法工作的报告,但从未使用过重现器。)

这些并不是问题的唯一根源

请注意,可以:

  • 提交具有实际 CRLF 行结尾的文件
  • 稍后,指示 Git 应提取并写入仅使用 LF 行结尾的此类文件
  • 进入这样的状态,在提取文件后,不应将其视为“干净”

有时,根据操作系统的变化,尽管 Git 试图巧妙地处理文件时间戳等,但这种情况确实会发生。

但更常见的是,您会看到以下情况:

$ git clone <repo>
$ cd <the-clone>
$ git status

当您在 Windows 或 MacOS 系统上时显示修改的文件,其中您有不区分大小写的本地文件系统,并且您刚刚克隆了在具有区分大小写文件系统的 Linux 系统上编写的存储库。

Linux 用户可以进行包含两个的提交不同的文件其名称仅大小写不同,例如README.MD and ReadMe.md。当 Mac 或 Windows 系统上的 Git 将这两个不同的文件提取到工作树时,它通常会首先创建其中一个文件README.MD——然后去创建另一个,ReadMe.md,但最终覆盖了内容README.MD包含已提交的内容(现在已索引复制)ReadMe.md.

你看到的是修改过的README.MD,以未修改的ReadMe.md,因为你的工作树只有一个名为README.MD与承诺的内容ReadMe.md.

除了让你的 Linux 同事停止这样做之外,没有什么好的解决方案。 Git 可能应该有一些奇特的方式来处理它,但它没有。它is可以在不启动 Linux 系统的情况下完成此任务,但到目前为止,启动 Linux VM 是最好的方法easiest的方式来处理它。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

git diff 没有差异,但由于行结尾的改变,不应该有一些差异吗? 的相关文章

  • 如何使用 vim 作为寻呼机设置彩色 git diff

    我无法配置 git 来遵循我的请求 使用 vim 作为差异分页器 在交互模式下添加文件时保留差异颜色 My gitconfig setup color ui auto diff false pager diff vim 通过此配置 交互模式
  • Sublime Text 2 的 git 提交错误[重复]

    这个问题在这里已经有答案了 我正在 Udacity 上学习 如何使用 Git 和 GitHub 课程 我正在按照示例进行操作 但遇到了问题 我自己尝试通过谷歌搜索和反复试验来解决问题大约两个小时 我认为是时候在 StackOverflow
  • Git合并策略:空格使默认显示不冲突并带来意想不到的结果

    经过多次试验 我得到了这个简单的测试用例场景 a gt b gt c master gt d gt b gt e branch Where b 是樱桃精选b e is a merge from master b 之后完成c and c对相同
  • 将 git dcommits 切换到 svn 分支

    I had master dcommit到 和rebase来自 颠覆trunk 我创建了一个中间 Subversion 分支tc 合并来自 2 个不同分支的更改 使用 git branch master git svn branch tc
  • GIT 中的 Fastlane 应用程序文件?如何检索当前的 apple_id?

    我为我的 XCode 项目设置了 Fastlane 来实现构建自动化 至少 Fastfile 包含构建选项 应该位于 GIT 中 但我也很高兴 Appfile 团队标识符 也位于 GIT 中 主要问题 Appfile 应该包含我的 appl
  • IntelliJ Git 集成 - git --version 空输出

    我目前正在尝试使用 IntelliJ 2016 2 的 Git 集成 但每当我将其指向可执行文件时 我都会遇到以下问题 这在技术上并不会阻止集成工作 但它确实会导致更新索引等问题 我正在运行 Windows 7 完全全新安装 但我在以前的
  • GIT:无效路径“.editorconfig”

    从 master 克隆项目时出现以下错误 错误 无效路径 editorconfig 致命 无法签出工作树 警告 克隆成功 但结账失败 您可以使用 git status 检查签出的内容 并使用 git Restore source HEAD
  • Git Add - 致命:添加文件失败

    我的 git cmd exe 窗口如下所示 git add NextFolder error unable to create temporary file No such file or directory error NextFolde
  • 在 Xcode 9 上切换分支

    我无法找到使用 Xcode 9 切换分支的菜单项 工作副本菜单似乎已经消失 有任何想法吗 Xcode 9 Xcode 8 Press 2 to open the new Source Control Navigator 右键单击master
  • 目标路径已存在且不是空目录

    我克隆了一个 git 存储库 但不小心弄乱了 所以我重新克隆并显示消息 目标路径已存在且不是空目录 我尝试过删除 Mac 中带有目标路径名称的文件夹 但没有成功 我对编码非常陌生 因此我们将不胜感激 对于根文件夹 以及任何其他文件夹 对于那
  • 为什么 git 无法识别我的本地存储库?

    我刚刚回到一个我已经使用 Git 大约 6 个月的项目 看到了这个 cd d DEVELOP BlenderAe My repo root git status fatal not a git repository or any of th
  • 如何使用 PyGithub 创建新存储库

    如何使用 PyGithub 在 Github 上创建新的存储库 我特别想知道如何使用create repo http jacquev6 net PyGithub v1 github objects AuthenticatedUser htm
  • VS2013 - GIT 不工作

    我最近安装了VS2013 每当我尝试使用 GIT 执行任何操作时 我都会在输出窗口中收到以下两条消息 libgit2 引发错误 类别 未知 错误 本机库未提供错误消息 该问题可能主要出现在远程存储库获取中 在克隆远程存储库之前 请打开远程
  • Git:如何维护项目的两个分支并仅合并共享数据?

    假设我有一个项目的两个分支 IMClient MacOS 和 IMClient Windows 它们的代码仅 比方说 一个目录 main 有所不同 所有其他目录都包含与系统无关的代码并且可以互换 有些工作人员在 Windows 版本上工作
  • “git merge --squash”的正确用例是什么?

    有些人喜欢git merge squash由于以下原因 压缩到单个提交使您有机会清理混乱的 WIP 提交 并为您要合并的更改提供良好的理由 https coderwall com p qkrmjq git merge squash http
  • 在功能分支上运行测试

    我有一个构建配置 其中包含连接到 git 分支的测试 VCS 根dev 3 个构建步骤和 1 个触发器 这些是我的构建步骤 构建测试 运行测试 构建和部署 我想为分支运行所有这些构建步骤dev但只有其中两个 构建和运行测试 用于分支匹配fe
  • 在 git 中管理 schema.rb 的首选方法是什么?

    我不想添加schema rb to gitignore 因为我希望能够从该文件加载新的数据库架构 然而 保持检查状态会导致各种虚假冲突 而这些冲突很容易通过新的解决方案解决 db migrate reset 基本上我想要一种方法 将 sch
  • git 的精简包是什么?

    我还没有找到太多关于瘦包的信息 并且手册页的信息对此相当含糊 我知道这与连接速度慢有关 但是什么是 连接速度慢 它的优点和缺点是什么 什么时候应该使用它 什么时候不应该使用它 根据记录 手册页 index pack https www gi
  • 如何与其他用户共享 bitbucket 存储库?

    我正在使用 Bit 存储桶 并且我想与一位朋友分享我的存储库 我用的是免费的个人账户 似乎有一个选项可以在位桶中创建团队 但它说它将把我的帐户从个人帐户转换为团队帐户 我不要那个 我如何授予其他用户访问此存储库的权限 有一个共享链接选项 如
  • Git子模块绝对工作树路径配置

    这是我的子模块redmine 仪表板配置文件 子模块配置文件 core repositoryformatversion 0 filemode true bare false logallrefupdates true worktree Us

随机推荐